Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


워크큐(Workqueue) - struct work_struct->data

workqueue를 디버깅할 때 여러 변수를 점검해야 하거든요.
그 중에 하나가 struct work_struct->data 변수이거든요

일단 데이터 구조체는 아주 명령어로 볼 수 있죠.
crash64> struct -o work_struct
struct work_struct {
   [0] atomic_long_t data;
   [8] struct list_head entry;
  [24] work_func_t func;
}

이번 기회에 struct work_struct->data 값들을 정리하려고 하는데요.
결과는 아래와 같아요. 가끔 디버깅 할 때 아래 매크로 값들을 찾다가 짜증날 때가 가끔 있거든요.
workque macro Hexa Value
WORK_STRUCT_COLOR_BITS 0x4
WORK_OFFQ_POOL_SHIFT 0x5
WORK_STRUCT_PWQ 0x4
WORK_STRUCT_WQ_DATA_MASK 0xffffff00
WORK_STRUCT_NO_POOL 0xffffffe0
WORK_STRUCT_STATIC 0x0
WORK_STRUCT_COLOR_BITS 0x4
WORK_STRUCT_PENDING 0x1
WORK_STRUCT_DELAYED 0x2
WORK_STRUCT_LINKED 0x8
WORK_NR_COLORS 0xf
WORK_NO_COLOR 0xf
WORK_STRUCT_FLAG_BITS 0x8
WORK_OFFQ_FLAG_BASE 0x4
__WORK_OFFQ_CANCELING 0x4
WORK_OFFQ_CANCELING 0x10
WORK_OFFQ_POOL_SHIFT 0x5
WORK_OFFQ_LEFT 0x1b
WORK_OFFQ_POOL_BITS 0x1b
WORK_OFFQ_POOL_NONE 0x7ffffff
WORK_STRUCT_FLAG_MASK 0xff
WORK_BUSY_PENDING 0x1
WORK_BUSY_RUNNING 0x2

위 매크로는 아래 함수를 통해서 설정되요.
static inline void set_work_data(struct work_struct *work, unsigned long data,
unsigned long flags)
{
WARN_ON_ONCE(!work_pending(work));
atomic_long_set(&work->data, data | flags | work_static(work));
}

그럼 워크큐 동작 시에서 위 매크로들이 실제 어느 함수에서 설정되는지 잠깐 코드를 잠깐 볼까요?
clear_work_data 함수를 보면, WORK_STRUCT_NO_POOL를 설정하는 코드를 볼 수 있어요.
콜스택 흐름은 cancel_work_sync -> __cancel_work_timer -> clear_work_data이에요.
static void clear_work_data(struct work_struct *work)
{
 smp_wmb(); /* see set_work_pool_and_clear_pending() */
 set_work_data(work, WORK_STRUCT_NO_POOL, 0);
}

WORK_STRUCT_NO_POOL 플레그는 INIT_WORK() 함수로 워크큐를 초기화할 때 설정되요.
아래 코드를 보면 set_work_data()를 쓰지 않고 바로 struct work_struct->data필드를 설정하네요.
#define __INIT_WORK(_work, _func, _onstack) \
do { \
static struct lock_class_key __key; \
\
__init_work((_work), _onstack); \
(_work)->data = (atomic_long_t) WORK_DATA_INIT(); \
lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0); \
INIT_LIST_HEAD(&(_work)->entry); \
(_work)->func = (_func); \
} while (0)

#define WORK_DATA_INIT() ATOMIC_LONG_INIT(WORK_STRUCT_NO_POOL)
#define WORK_DATA_STATIC_INIT() \
 ATOMIC_LONG_INIT(WORK_STRUCT_NO_POOL | WORK_STRUCT_STATIC)

WORK_STRUCT_PWQ | WORK_OFFQ_CANCELING 플래그는 아래 함수에서 설정되는군요.
static void mark_work_canceling(struct work_struct *work)
{
unsigned long pool_id = get_work_pool_id(work);

pool_id <<= WORK_OFFQ_POOL_SHIFT;
set_work_data(work, pool_id | WORK_OFFQ_CANCELING, WORK_STRUCT_PENDING);
}

static bool work_is_canceling(struct work_struct *work)
{
unsigned long data = atomic_long_read(&work->data);

return !(data & WORK_STRUCT_PWQ) && (data & WORK_OFFQ_CANCELING);

다음에는 위 함수들이 실제 어떤 콜 트레이스로 호출되는지 점검해보겠습니다.

Reference(프로세스 관리)
4.9 프로세스 컨택스트 정보는 어떻게 저장할까?
 4.9.1 컨택스트 소개
 4.9.2 인터럽트 컨택스트 정보 확인하기
 4.9.3 Soft IRQ 컨택스트 정보 확인하기
 4.9.4 선점 스케줄링 여부 정보 저장
4.10 프로세스 디스크립터 접근 매크로 함수
 4.10.1 current_thread_info()
 4.10.2 current 매크로란
4.11 프로세스 디버깅
 4.11.1 glibc fork 함수 gdb 디버깅
 4.11.2 유저 프로그램 실행 추적 

    덧글

    댓글 입력 영역