Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

119187
803
94448


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

워크큐를 생성하려면 alloc_workqueue() 함수를 호출해야 합니다. alloc_workqueue() 함수 구현부를 보면 __alloc_workqueue_key() 함수로 치환됨을 알 수 있습니다.
#define alloc_workqueue(fmt, flags, max_active, args...) \
__alloc_workqueue_key((fmt), (flags), (max_active), \
      NULL, NULL, ##args)

워크큐를 표현하는 자료구조는 struct workqueue_struct입니다. 이 자료구조 기준으로 메모리 할당을 한 후 각 멤버를 초기화하는 것이 alloc_workqueue() 함수의 역할입니다. 

alloc_workqueue() 함수에 전달하는 인자들의 속성을 알아보면 다음과 같습니다.

fmt 
워크큐 이름을 지정하며 struct workqueue_struct.name 이라는 멤버에 저장됩니다.
"events", "events_highpri", "events_unbound", "events_freezable", "events_power_efficient", "events_freezable_power_efficient" 이름이 등록됩니다.  

flags
워크큐 속성 정보를 저장합니다. 이 인자는 struct workqueue_struct.flags 멤버에 저장되며 워크큐 커널 함수가 워크큐 종류에 따라 다르게 처리하는 기준 정보입니다. flags는 워크큐 종류에 따라 다르게 설정하는데 설정하는 enum 타입 매크로는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.14.43/source/include/linux/workqueue.h]
enum {
WQ_UNBOUND = 1 << 1,  
WQ_FREEZABLE = 1 << 2,  
WQ_MEM_RECLAIM  = 1 << 3, 
WQ_HIGHPRI  = 1 << 4, 
WQ_CPU_INTENSIVE = 1 << 5, 
WQ_SYSFS  = 1 << 6, 
WQ_POWER_EFFICIENT = 1 << 7,
...
}

각각 enum 값의 의미는 워크큐를 소개할 때 살펴보겠습니다.

max_active
struct workqueue_struct.saved_max_active에 저장합니다.

alloc_workqueue() 함수를 어느 함수에서 호출해서 워크큐를 생성할까요? 정답은 workqueue_init_early () 함수입니다. 
[https://elixir.bootlin.com/linux/v4.14.43/source/kernel/workqueue.c]
1 int __init workqueue_init_early(void)
2 {
3 int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL };
4 int i, cpu;
...
5 system_wq = alloc_workqueue("events", 0, 0);
6 system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0);
7 system_long_wq = alloc_workqueue("events_long", 0, 0);
8 system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,
9     WQ_UNBOUND_MAX_ACTIVE);
10 system_freezable_wq = alloc_workqueue("events_freezable",
11       WQ_FREEZABLE, 0);
12 system_power_efficient_wq = alloc_workqueue("events_power_efficient",
13       WQ_POWER_EFFICIENT, 0);
14 system_freezable_power_efficient_wq = 
15 alloc_workqueue("events_freezable_power_efficient",
16       WQ_FREEZABLE | WQ_POWER_EFFICIENT,
17       0);
18 BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq ||
19        !system_unbound_wq || !system_freezable_wq ||
20        !system_power_efficient_wq ||
21        !system_freezable_power_efficient_wq);

5~17번째 줄 코드는 워크큐를 생성하고 18~21번 줄 코드는 워크큐가 제대로 생성됐는지 점검하는 동작입니다.

먼저 5번째 줄 코드를 보겠습니다.
5 system_wq = alloc_workqueue("events", 0, 0);

“event”이란 이름으로 워크큐를 생성해서 system_wq이란 전역 변수에 저장합니다. system_wq는 보통 시스템 워크큐라고도 부릅니다. 대부분 디바이스 드라이버에서 쓰며 멀티 쓰레드용으로 씁니다.

다음은 6번째 줄 코드입니다.
6 system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0);

system_highpri_wq 워크큐를 생성하는 코드입니다. system_highpri_wq 워크큐는 시스템 워크큐에서 쓰는 워커 쓰레드보다 우선순위가 높은 워커 쓰레드를 처리합니다. struct workqueue_struct.flags는 WQ_HIGHPRI로 저장합니다.


WQ_HIGHPRI 플래그를 설정하면 후속 함수 흐름에서 어떤 과정으로 동작하는지 알아봅시다.

플래그를 WQ_HIGHPRI로 설정하면 __alloc_workqueue_key() 함수에서 alloc_and_link_pwqs() 함수를 실행할 때 cpu_worker_pools[1]에 위치한 워커풀를 해당 워크큐에 할당합니다. 해당 코드는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.14.43/source/kernel/workqueue.c]
1 static int alloc_and_link_pwqs(struct workqueue_struct *wq)
2 {
3 bool highpri = wq->flags & WQ_HIGHPRI;
4 int cpu, ret;
5
...
6 for_each_possible_cpu(cpu) {
7 struct pool_workqueue *pwq =
8 per_cpu_ptr(wq->cpu_pwqs, cpu);
9 struct worker_pool *cpu_pools =
10 per_cpu(cpu_worker_pools, cpu);
11
12 init_pwq(pwq, wq, &cpu_pools[highpri]);

3번째 줄 코드에서 워크큐 wq->flags 멤버와 WQ_HIGHPRI enum 매크로와 AND 연산을 합니다. WQ_HIGHPRI로 설정된 워크큐인 경우 highpri이라는 지역 변수는 1이 됩니다.

9~10번째 줄 코드는 percpu 타입 변수인 cpu_worker_pools 전역 변수에 접근해서 각 CPU 별 struct worker_pool 구조체 주소를 읽습니다. 우선순위를 높혀서 처리하는 워커풀은 per-cpu 타입 변수인 cpu_worker_pools[1]에 위치합니다.

12번 줄 코드는 init_pwq() 함수를 호출해서 워커 풀을 설정합니다.

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


핑백

덧글

댓글 입력 영역