Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

5363
1898
209234


[리눅스커널][태스크릿] 태스크릿(struct tasklet_struct)은 어떻게 등록할까? 6. 인터럽트 후반부 처리

먼저 태스크릿을 등록하는 2가지 방법을 소개합니다.
1. 태스크릿 전역 변수 선언
  : DECLARE_TASKLET() 혹은 DECLARE_TASKLET_DISABLED() 함수 호출
2. 태스크릿 초기화 함수 호출
  : tasklet_init() 함수 

DECLARE_TASKLET() 혹은 DECLARE_TASKLET_DISABLED() 함수로 태스크릿 등록하기

DECLARE_TASKLET() 혹은 DECLARE_TASKLET_DISABLED() 매크로를 써서 태스크릿을 초기화하는 방법입니다. 태스크릿 전역 변수는 컴파일 타임에 자료구조가 정해집니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
1 #define DECLARE_TASKLET(name, func, data) \
2 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
3
4 #define DECLARE_TASKLET_DISABLED(name, func, data) \
5 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

각 함수별로 전달하는 인자 타입은 다음과 같습니다.
name: struct tasklet_struct 타입
func: 태스크릿 핸들러 함수
data: 태스크릿 핸들러 함수 매개인자

1 번째 줄 코드를 보면 첫 번째 인자인 name으로 struct tasklet_struct 타입 전역 변수를 선언합니다. 선언된 name 전역 변수 각 필드에 초기화 값을 저장합니다.

DECLARE_TASKLET() 함수로 태스크릿을 초기화하면 struct tasklet_struct 필드가 바뀝니다.
1 #define DECLARE_TASKLET(name, func, data) \
2 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
struct tasklet_struct
{
struct tasklet_struct *next = NULL;
unsigned long state = 0;
atomic_t count = ATOMIC_INIT(0);
void (*func)(unsigned long) = func;
unsigned long data = data;
};

ATOMIC_INIT(0) 은 0을 의미합니다.

struct tasklet_struct 구조체에서 볼드체로 된 부분을 눈으로 따라가 보기실 바랍니다.

DECLARE_TASKLET_DISABLED() 함수가 DECLARE_TASKLET() 함수와 다른 점은 atomic_t count 필드를 ATOMIC_INIT(1)로 설정한다는 점입니다. 태스크릿에서 atomic_t count 필드가 0이면 태스크릿이 활성화된 상태입니다. atomic_t count 필드를 1로 설정해 태스크릿은 기본으로 비활성화합니다.

이번에는 DECLARE_TASKLET_DISABLED() 함수를 써서 태스크릿을 초기화하는 코드를 소개합니다.
[https://elixir.bootlin.com/linux/v4.20/source/drivers/tty/vt/keyboard.c]
DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);

위와 같인 선언하면 keyboard_tasklet 전역 변수 필드는 다음과 같습니다. 
  (static struct tasklet_struct) keyboard_tasklet = (
    (struct tasklet_struct *) next = 0x0 = ,
    (long unsigned int) state = 0,
    (atomic_t) count = ((int) counter = 1),
    (void (*)()) func = 0x8050636C = kbd_bh,
    (long unsigned int) data = 0)

기본으로 태스크릿을 비활성화해 초기화한 후 tasklet_enable() 함수를 호출하면 태스크릿을 활성화할 수 있습니다. tasklet_enable() 함수 코드는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
01 static inline void tasklet_enable(struct tasklet_struct *t)
02{
03 smp_mb__before_atomic();
04 atomic_dec(&t->count);
05 }

tasklet_enable() 함수 코드를 보면 05 번째 줄과 같이 struct tasklet_struct 구조체 count 필드를 -1만큼 감소시킵니다.

tasklet_enable() 함수를 호출해 태스크릿을 활성하는 예제는 다음 코드 3번째 줄입니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/drivers/tty/vt/keyboard.c]
01 int __init kbd_init(void)
02 {
...
03 tasklet_enable(&keyboard_tasklet);
04 tasklet_schedule(&keyboard_tasklet);
05
06 return 0;
07 }

tasklet_init() 함수로 태스크릿 초기화하기

태스크릿을 초기화하려면 다음 함수 선언부에서 보이는 인자에 적절한 값을 저장한 다음
tasklet_init() 함수를 호출하면 됩니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
extern void tasklet_init(struct tasklet_struct *t,
 void (*func)(unsigned long), unsigned long data);

함수에 전달하는 인자의 의미를 소개합니다.

struct tasklet_struct *t;
드라이버에서 태스크릿을 생성하면 태스크릿을 식별하는 구조체입니다.

void (*func)(unsigned long);
태스크릿 핸들러 함수입니다. 태스크릿 서비스를 등록하면 실행한 콜백 함수 주소를 저장합니다.

unsigned long data;
태스크릿 콜백 함수로 전달하는 매개인자입니다.
디바이스 드라이버 핸들과 같이 태스크릿 서비스가 실행할 때 제어하려는 인자입니다.
인터럽트 핸들러를 호출할 때 전달하는 매개인자와 같은 개념입니다.

tasklet_init() 함수 구현부 코드를 보겠습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/kernel/softirq.c]
01 void tasklet_init(struct tasklet_struct *t,
02   void (*func)(unsigned long), unsigned long data)
03 {
04 t->next = NULL;
05 t->state = 0;
06 atomic_set(&t->count, 0);
07 t->func = func;
08 t->data = data;
09 }

tasklet_init() 함수를 호출하면 1 번째 인자로 전달되는 태스크릿을 핸들하는 struct tasklet_struct 구조체 t 인자에 기본 설정을 합니다.

04~06 번째 줄 코드를 보겠습니다.
04 t->next = NULL;
05 t->state = 0;
06 atomic_set(&t->count, 0);

다음 태스크릿 핸들을 저장하는 next 필드에 NULL을 지정하고 state 필드를 0으로 바꿉니다.
이어서 count 필드도 0으로 바뀝니다.

07~08 번째 줄은 tasklet_init() 함수로 전달된 인자를 태스크릿 핸들에 설정하는 코드입니다.
07 t->func = func;
08 t->data = data;

07 번째 줄은 태스크릿 핸들러 함수를 설정하고 08 번째 줄은 태스크릿 핸들러에 전달하는 매개인자를 저장합니다.

* 강의 동영상도 있으니 같이 들으시면 좋습니다.





"이 포스팅이 유익하다고 생각되시면 댓글로 응원해주시면 감사하겠습니다.  
혹시 글을 읽고 궁금점이 있으면 댓글로 질문 남겨주세요. 상세한 답글 올려 드리겠습니다!"

# Reference 인터럽트 후반부 처리








6.9 Soft IRQ 서비스는 누가 언제 처리하나?




6.13 Soft IRQ 디버깅
6.13.1 ftrace Soft IRQ 이벤트 분석 방법
6.13.2 /proc/softirqs로 Soft IRQ 서비스 실행 횟수 확인


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2


 




핑백

덧글

댓글 입력 영역