Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

593
557
422264


[리눅스커널][인터럽트후반부] IRQ 스레드 핸들 irq_thread() 함수 분석 6. 인터럽트 후반부 처리

IRQ 스레드 핸들 irq_thread() 함수 분석 

이번에는 IRQ 스레드 핸들 함수가 어떤 과정으로 실행하는지 점검해 보겠습니다.

커널 쓰레드가 생성되면 무한 루프를 돌면서 쓰레드 상태에서 따라 정해진 동작을 수행하는 스레드 핸들 함수가 실행합니다. IRQ 스레드는 irq_thread() 함수가 이 역할을 수행합니다. 

먼저 IRQ 스레드를 깨우면 실행하는 irq_thread() 함수를 소개합니다.
1 static int irq_thread(void *data)
2 {
3 struct callback_head on_exit_work;
4 struct irqaction *action = data;   
5 struct irq_desc *desc = irq_to_desc(action->irq);
6 irqreturn_t (*handler_fn)(struct irq_desc *desc,
7 struct irqaction *action);
8
9 if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,
10 &action->thread_flags))
11 handler_fn = irq_forced_thread_fn;
12 else
13 handler_fn = irq_thread_fn;   
14
15 init_task_work(&on_exit_work, irq_thread_dtor);
16 task_work_add(current, &on_exit_work, false);
17
18 irq_thread_check_affinity(desc, action);
19
20 while (!irq_wait_for_interrupt(action)) {
21 irqreturn_t action_ret;
22
23 irq_thread_check_affinity(desc, action);
24
25 action_ret = handler_fn(desc, action); //<<--[3]
26 if (action_ret == IRQ_HANDLED)
27 atomic_inc(&desc->threads_handled);
28 if (action_ret == IRQ_WAKE_THREAD)
29 irq_wake_secondary(desc, action);

30 wake_threads_waitq(desc);
31 }

4 번째 줄 코드를 먼저 보겠습니다.
1 static int irq_thread(void *data)
2 {
3 struct callback_head on_exit_work;
4 struct irqaction *action = data;

 스레드 핸들인 매개 인자인 data을 struct irqaction 타입의 포인터형 action 지역 변수로 캐스팅합니다. struct irqaction 구조체인 action 변수의 thread_fn 필드는 IRQ 스레드 핸들러 함수 주소를 저장합니다.

이해를 돕기 위해 Trace32 프로그램으로 본 인터럽트 디스크립터를 소개합니다.
(struct irq_desc *) (struct irq_desc*)0xB008B300
...
    (struct irqaction *) action = 0xBB4E6E40  
      (irq_handler_t) handler = 0x8061EC00 = bcm2835_mmc_irq, // 인터럽트 핸들러
      (void *) dev_id = 0xBB47B010  // 인터럽트 핸들러 핸들
      (void *) percpu_dev_id = 0x0 = ,
      (struct irqaction *) next = 0x0 = ,
      (irq_handler_t) thread_fn = 0x8061DCC4 = bcm2835_mmc_thread_irq, // IRQ Thread 핸들러
      (struct task_struct *) thread = 0xBB516CC0 // “irq/92-mmc1” IRQ Thread의 태스크 디스크립터

struct irq_desc 구조체 action 필드는 0xBB4E6E40 주소를 가르킵니다. 여기서 irq_thread() 함수에서 전달하는 void 타입 data 포인터는 0xBB4E6E40주소를 저장합니다. 

struct irqaction 구조체 필드인 handle와 thread_fn를 보면 request_threaded_irq() 함수에서 지정한 인터럽트 핸들러와 IRQ 스레드 핸들러 함수를 볼 수 있습니다. action->thread_fn 필드에 접근해서 IRQ 스레드 핸들러 함수를 호출하는 것입니다. 

이제 코드 분석으로 돌아가서 13번째 줄 코드를 보겠습니다. 
13 handler_fn = irq_thread_fn;

irq_thread_fn 란 함수 주소를 함수 포인터인 handler_fn 변수에 저장합니다. 여기서 irq_thread_fn 은 IRQ 스레드 핸들러 함수를 호출하는 커널 함수입니다.

다음 25 번째 줄 코드를 보겠습니다.
25 action_ret = handler_fn(desc, action);

함수 포인터인 handler_fn를 호출합니다. handler_fn란 함수 포인터에 irq_thread_fn을 등록했으니 irq_thread_fn() 함수를 호출합니다.

함수 포인터로 호출되는 irq_thread_fn() 함수를 확인할 차례입니다. 
1 static irqreturn_t irq_thread_fn(struct irq_desc *desc,
2 struct irqaction *action)
3 {
4 irqreturn_t ret;
5
6 ret = action->thread_fn(action->irq, action->dev_id);
7 irq_finalize_oneshot(desc, action);
8 return ret;
9 }

위 6번째 줄 코드를 보면 struct irqaction 구조체 thread_fn 필드로 IRQ 스레드 핸들 함수를 호출합니다. thread_fn 필드는 함수 포인터와 비슷한 동작을 수행합니다. 92번 인터럽트인 경우 6 번째 줄 코드에서 bcm2835_mmc_thread_irq() 함수를 호출하는 것입니다.

여기까지 배운 내용을 다이어그램으로 정리하겠습니다. 눈끔 화살표는 코드 흐름을 표현하니 눈여겨봅시다.
 
[그림 6.5] "irq/92-mmc1" IRQ Thread 실행 흐름도

위 그림에서 실행 순서별 동작을 살펴보겠습니다.

[1]: arch_cpu_idle 함수 실행 중 92번 인터럽트가 발생해서 인터럽트 벡터인 __irq_svc가 실행합니다. 

[2]: __handle_irq_event_percpu() 함수까지 실행한 후 92번 인터럽트 핸들러인 bcm2835_mbox_irq() 함수를 호출합니다.

이번에는 가운데 Interrupt Handler 박스를 볼 차례입니다.
[3]: bcm2835_mbox_irq() 인터럽트 핸들러에서 IRQ_WAKE_THREAD를 반환합니다.

[4]: irq_wake_thread() 함수를 호출해서 "irq/92-mmc1" IRQ 스레드를 깨웁니다. irq_wake_thread() 함수를 호출했으니 커널은 "irq/92-mmc1" IRQ 스레드를 실행하도록 스케줄링을 합니다.

[5]: 인터럽트에 대한 처리를 마쳤으니 다시 __irq_svc 인터럽트 벡터로 되돌아갑니다.
     인터럽트 발생으로 실행을 중단한 프로세서는 arch_cpu_idle 함수에서 일을 시작합니다.

마지막으로 IRQ 스레드 박스를 볼 차례입니다.
[6]: IRQ 스레드를 핸들인 irq_thread() 함수가 실행합니다. irq_wake_thread() 함수를 호출했기 때문입니다.

[7]: 커널은 "irq/92-mmc1" IRQ 스레드를 실행합니다. 이제 "irq/92-mmc1" IRQ 스레드 핸들인 bcm2835_mbox_threaded_irq() 함수를 호출해서 92번 인터럽트에 대한 후반부 처리를 합니다.

이번 소절에서는 상세한 코드 분석으로 누가 언제 IRQ 스레드를 실행하는지 배웠습니다. 

이제 누가 언제 IRQ 스레드를 실행하는지 물어보면 자신있게 대답할 수 있을 것입니다. 인터럽트 핸들러에서 IRQ_WAKE_THREAD를 반환할 때 IRQ 스레드 실행의 분기점입니다.

다음 절에서 라즈베리파이에서 92번 "irq/92-mmc1" IRQ 스레드가 실제로 어떻게 동작하는지 ftrace 로그 분석을 해 보겠습니다.


# Reference 인터럽트 후반부 처리
IRQ 스레드 
IRQ 스레드는 어떻게 생성할까?
IRQ 스레드는 누가 언제 실행할까?
IRQ 스레드 디버깅
Soft IRQ 소개
Soft IRQ 서비스
Soft IRQ 서비스는 누가 언제 처리할까?
   Soft IRQ 서비스 실행 진입점
   Soft IRQ 서비스 요청 점검
   Soft IRQ 서비스 실행
   ksoftirq 스레드 깨우기
ksoftirqd 스레드
   ksoftirqd 스레드란
   ksoftirqd 스레드는 언제 깨울까?
   ksoftirqd 핸들 run_softirqd() 함수 분석
Soft IRQ 컨택스트
   Soft IRQ 컨택스트란
   Soft IRQ 컨택스트 시작점
   Soft IRQ 컨택스트 종료
   Soft IRQ 컨택스트 관련 주요 함수 
태스크릿
   태스크릿은 무엇일까?
   태스크릿은 어떻게 등록할까?
   태스크릿은 언제 실행할까?
Soft IRQ 디버깅
   ftrace Soft IRQ 이벤트 소개
   Soft IRQ 서비스 핸들러 실행 확인

핑백

덧글

댓글 입력 영역