태스크릿
1. 태스크릿이란
태스크릿은 Soft IRQ 서비스 중 하나의 동적으로 Soft IRQ를 사용할 수 있는 인터페이스이며 드라이버 레벨에서 태스크릿을 자주 활용합니다.
softirq_to_name 배열에서 TASKLET을 확인할 수 있으며 이를 토대로 태스크릿이 Soft IRQ 서비스 중 하나임을 알 수 있습니다.
linux# vim kernel/softirq.c

Soft IRQ 서비스를 실행하다는 것은 Soft IRQ 서비스 핸들러 함수를 호출한다는 의가 됩니다. 그렇다면 Soft IRQ 태스크릿 서비스를 실행할 때는 tasklet_action() 함수가 호출되며, 이것은 softirq_vec 배열에서 확인할 수 있습니다.
2. 태스크릿 자료구조
태스크릿 각필드를 확인해 보면,
struct tasklet_struct *next;
다음 태스크릿을 가리키는 용도의 포인터
unsigned long state;
태스크릿의 세부 상태정보를 저장하는 필드이며, TASKLET_STATE_SCHED와 TASKLET_STATE_RUN로 타입을 저장합니다. SCHED는 태스크릿 핸들러를 처리하지 않은 상태이고, RUN은 태스크릿 핸들러를 실행 중인 상태입니다.
atomic_t count;
태스크릿 실행 여부를 판단하는 필드이며, 0이어야 태스크릿을 실행합니다.
void (*func)(unsigned long);
태스크릿 핸들러 함수 주소를 저장하는 필드이며 콜백함수 주소를 저장합니다.
unsigned long data;
태스크릿 핸들러 함수에 전달되는 매개변수입니다.
linux# vim include/linux/interrupt.h

3. 태스크릿 등록 방법
태스크릿 등록 방법을 알아보겠습니다.
1) DECLARE_TASKLET() 혹은 DECLARE_TASKLET_DISABLED() 함수
DECLARE_TASKLET() 혹은 DECLARE_TASKLET_DISABLED 매크로 함수를 써서 태스크릿을 초기화하는 방법 있습니다.
name은 struct tasklet_struct 타입, func은 태스크릿 핸들러 함수, data는 태스크릿 핸들러 함수에 전달되는 매개변수입니다.
여기서 ATOMIC_INIT 부분에서 태스크릿의 count 필드를 0 혹은 1로 하여 활성화 혹은 비활성화합니다.
linux# vim kernel/softirq.c

2) tasklet_init() 함수
태스크릿을 tasklet_init() 함수를 호출해서 초기화합니다.
첫 번째 인자를 tasklet_struct 구조체의 t 인자에 기본 설정 정보를 저장합니다.
553~555번 줄을 보면 다음 태스크릿 핸들을 저장하는 next 필드에 NULL을 지정하고 state 필드를 0으로 바꿉니다. 그리고 count 필드에 0을 전달합니다.
556,7번 줄에서 tasklet_init() 함수로 전달된 인자에서 태스크릿 핸들 함수와 매개변수를 저장합니다.
linux# vim kernel/softirq.c

4. 태스크릿 실행 요청 확인
그림처럼 태스크릿의 전체 흐름을 통해 확인해 보겠습니다.

(1) 부분은 인터럽트가 발생한 후 인터럽트 핸들러에서 tasklet_schedule() 함수를 호출합니다. 이를 태스크릿 스케줄링이라고 하며 Soft IRQ 서비스 중 태스크릿을 통해서 태스크릿 핸들러 실행을 요청하는 것입니다.
책에는 tasklet_schedule() 함수가 softirq.c에 있는 것으로 나오는데 현재 버전에서는 interrupt.h 파일에 있습니다.
tasklet_struct 구조체 타입인 &t->state가 TASKLET_STATE_SCHED이 아니면 TASKLET_STATE_SCHED를 tasklet_struct 구조체의 state 필드에 설정하고 __tasklet_schedule() 함수를 호출합니다.
linux# vim include/linux/interrupt.h

__tasklet_schedule() 함수를 확인합니다. 특별한 작업을 하지 않고, 두 번째 인자로 태스크릿을 핸들링하는 tasklet_vec와 세 번째 인자로 TASKLET_SOFTIRQ 플래그를 전달합니다.
linux# vim kernel/softirq.c

이어서 호출하는 __tasklet_schedule_common() 확인합니다.
479번 줄에서 인터럽트 상태 정보를 저장한 후 실행 중인 CPU 라인의 인터럽트를 비활성화합니다.
481~483번 줄에서 실행 요청을 할 태스크릿을 tasklet_vec 연결 리스트의 tail에 추가합니다. tasklet_vec는 percpu 타입의 변수로서 CPU 코어의 개수만큼 메모리 공간에서 위치합니다.
485번 줄에서 TASKLET_SOFTIRQ Soft IRQ 서비스를 요청해서 __do_softirq() 함수를 호출합니다.
487번 줄은 인터럽트 상태 정보를 복원하면서 해당 CPU 라인 인터럽트의 발생을 활성화합니다.

위 그림의 (2) 단계에서는 태스크릿 서비스 핸들러가 호출되는 과정을 확인할 수 있습니다.
292번 줄을 보면 __do_softirq() 함수에서 tasklet_action() 함수를 호출합니다.

tasklet_action() 함수는 인자로 softirq_action 구조체 포인터, tasklet_vec 전역변수에서 percpu오프셋을 적용한 주소, 태스크릿 플래그를 추가하여 tasklet_action_common() 함수를 호출합니다.
this_cpu_ptr() 함수는 현재 실행 중인 CPU 번호에 맞게 percpu 오프셋을 적용한 주소로 변환합니다.

520번 줄은 atomic_read() 함수를 호출해 tasklet_struct 구조체 타입의 &t->count가 0인지 확인합니다. 그래서 0 아니면 태스크릿 핸들러 함수를 호출하지 않습니다.
524번 줄의 t->func(t->data) 중요한 부분으로 태스크릿 핸들러 함수를 호출합니다.

책에 나온 예로, keyboard_tasklet 경우 func 필드에는 kdb_bh() 함수가 있다고 보여줍니다.
이것으로 태스크릿에 요청, 실행 과정 등을 알아보았습니다.
다음 내용에서 Soft IRQ 디버깅 내용을 확인하겠습니다.
감사합니다.
'Embedded : : Linux > : : Linux Kernel' 카테고리의 다른 글
| [스케줄링] 프로세스 스케줄링의 주요 개념 (0) | 2024.10.12 |
|---|---|
| [인터럽트 하반부] Soft IRQ 디버깅 (0) | 2024.10.12 |
| [인터럽트 하반부] Soft IRQ 서비스 (0) | 2024.10.12 |
| [인터럽트 하반부] 인터럽트 하반부와 IRQ 스레드 (0) | 2024.10.12 |
| FD 번호할당 alloc_fd() | RCU 로직 | expand_files (0) | 2024.10.12 |