Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

81235
1036
103651


[리눅스커널] 워크큐: set_work_pwq() 함수 분석 8장. 워크큐

struct work_struct.entry를 워커 풀에 등록하기 직전 struct work_struct.data 멤버를 WORK_STRUCT_PWQ 로 저장합니다.
static void set_work_pwq(struct work_struct *work, struct pool_workqueue *pwq,
 unsigned long extra_flags)
{
set_work_data(work, (unsigned long)pwq,
      WORK_STRUCT_PENDING | WORK_STRUCT_PWQ | extra_flags);
}

data 변수와 WORK_STRUCT_WQ_DATA_MASK(0xFFFF_FF00) 값과 AND 비트 연산한 결과를 
struct pool_workqueue 구조체로 캐스팅한 다음 struct pool_workqueue 멤버인 pool을 리턴합니다.

다음은 라즈베리파이에서 주기적으로 실행하는 flush_to_ldisc() 가 핸들러 함수인 워크를 Trace32로 확인한 내용입니다.

struct work_struct.data는 0xba340205입니다. 마지막 바이트가 5이니 WORK_STRUCT_PWQ(4)와  AND 연산한 결과는 1입니다. 또한 0xba340205 값과 WORK_STRUCT_WQ_DATA를 AND 연산하면 struct pool_workqueue 주소에 접근할 수 있습니다.

다음은 WORK_STRUCT_WQ_DATA_MASK(0xFFFF_FF00) enum값을 계산하는 과정입니다.
WORK_STRUCT_WQ_DATA_MASK = 0xFFFF_FF00

우선 다음 연산 과정으로 WORK_STRUCT_FLAG_MASK는 0xFF임을 확인했습니다. 그런데 WORK_STRUCT_WQ_DATA_MASK는 다음 연산으로 정의돼 있습니다.
WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK

 ~은 비트 반전 연산자입니다. WORK_STRUCT_FLAG_MASK 를 2진수 기준으로 0은 1, 1은 0으로 비트 반전하는 계산 과정은 다음과 같습니다.
WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK
~(0000 0000 0000 0000 0000 0000 1111 1111) - 이진수
 (1111 1111 1111 1111 1111 1111 0000 0000) - 이진수
0xFFFF_FF00  - 16진수

WORK_STRUCT_FLAG_MASK은 255이며 16진수 이진수별 값은 다음과 같습니다.
WORK_STRUCT_FLAG_MASK = 255 = 0xFF(16진수) = 1111 1111(이진수)

WORK_STRUCT_FLAG_MASK은 소스 코드를 보면 다음 규칙으로 계산합니다.
WORK_STRUCT_FLAG_MASK = (1UL << WORK_STRUCT_FLAG_BITS) - 1

1을 왼쪽으로 WORK_STRUCT_FLAG_BITS 값만큼 비트 시프트한 결과에 1를 빼는 연산입니다.
연산 과정은 다음과 같습니다.
(1UL << WORK_STRUCT_FLAG_BITS) - 1
(1UL << 8) - 1
(256) - 1
 255

WORK_STRUCT_FLAG_BITS이 8이니 1을 왼쪽으로 비트 쉬프트 한 결과에 1을 뺀 결과가 WORK_STRUCT_FLAG_MASK인 겁니다.

다음은 WORK_STRUCT_FLAG_BITS enum 값을 계산할 차례입니다. 연산 결과 WORK_STRUCT_FLAG_BITS는 8입니다.
WORK_STRUCT_FLAG_BITS = 8

WORK_STRUCT_FLAG_BITS은 두 매크로는 더한 값입니다.
WORK_STRUCT_FLAG_BITS = WORK_STRUCT_COLOR_SHIFT + WORK_STRUCT_COLOR_BITS

연산 과정은 다음과 같습니다.
WORK_STRUCT_COLOR_SHIFT + WORK_STRUCT_COLOR_BITS
WORK_STRUCT_FLAG_BITS = 8 = 4 + 4 = WORK_STRUCT_FLAG_BITS + WORK_STRUCT_COLOR_BITS

각 enum 정의문은 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.14.43/source/include/linux/workqueue.h]
1 enum {
2 WORK_STRUCT_PENDING_BIT = 0, /* work item is pending execution */
...
3 #ifdef CONFIG_DEBUG_OBJECTS_WORK
4 WORK_STRUCT_STATIC_BIT = 4, /* static initializer (debugobjects) */
5 WORK_STRUCT_COLOR_SHIFT = 5, /* color for workqueue flushing */
6 #else
7 WORK_STRUCT_COLOR_SHIFT = 4, /* color for workqueue flushing */
8 #endif
9 WORK_STRUCT_COLOR_BITS = 4,
...
10 WORK_STRUCT_FLAG_BITS = WORK_STRUCT_COLOR_SHIFT + WORK_STRUCT_COLOR_BITS,
...
11 WORK_STRUCT_FLAG_MASK = (1UL << WORK_STRUCT_FLAG_BITS) - 1,
12 WORK_STRUCT_WQ_DATA_MASK = ~WORK_STRUCT_FLAG_MASK,
...
};

이번에는 12번 줄 코드를 보겠습니다.
12 pool_id = data >> WORK_OFFQ_POOL_SHIFT;
13 if (pool_id == WORK_OFFQ_POOL_NONE)
14 return NULL;

struct work_data.data 멤버를 WORK_OFFQ_POOL_SHIFT(5) 만큼 오른쪽으로 쉬프트한 결과를 pool_id에 저장합니다.

WORK_OFFQ_POOL_SHIFT enum 매크로는 다음 코드로 치환되어 5라는 값을 저장합니다.
WORK_STRUCT_COLOR_SHIFT = 4,
WORK_OFFQ_FLAG_BITS = 1,
WORK_OFFQ_FLAG_BASE = WORK_STRUCT_COLOR_SHIFT,
WORK_OFFQ_POOL_SHIFT = WORK_OFFQ_FLAG_BASE + WORK_OFFQ_FLAG_BITS,

이렇게 워크큐는 enum으로 정의한 매크로 연산으로 비트 연산을 수행합니다. 워크큐는 32비트 및 64비트 아키텍처에서 호환성을 유지하면서 실행해야 하므로 이렇게 복잡하게 enum 매크로 연산을 하는 겁니다.

마지막 16줄 코드입니다.
16 return idr_find(&worker_pool_idr, pool_id);

pool_id 아이디로 워커 풀 주소를 읽어서 리턴하는 구문입니다. worker_pool_idr는 각 노드 별 워커풀이 등록됐음을 참고하세요.

#Reference 워크큐
워크큐 소개
워크큐 종류 알아보기
워크란  
워크를 워크큐에 어떻게 큐잉할까?
   워크를 큐잉할 때 호출하는 워크큐 커널 함수 분석   
워커 쓰레드란
워크큐 실습 및 디버깅
   ftrace로 워크큐 동작 확인   
   인터럽트 후반부로 워크큐 추가 실습 및 로그 분석 
   Trace32로 워크큐 자료 구조 디버깅하기 
딜레이 워크 소개  
   딜레이 워크는 누가 언제 호출할까?
라즈베리파이 딜레이 워크 실습 및 로그 확인  



핑백

덧글

댓글 입력 영역