Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

6363
1898
209235


[리눅스커널] IRQ 스레드 생성 예제 코드 분석 - 다른 리눅스 시스템 6. 인터럽트 후반부 처리

6.3.3 다른 리눅스 시스템에서 IRQ 스레드 생성 예제 코드 분석

라즈비안에선 92번 인터럽트를 처리할 한 개 IRQ 스레드만 생성합니다. 1개 IRQ 스레드만 생성하는 코드 밖에 없으니 이번에는 다른 리눅스 시스템에서 IRQ 스레드를 생성하는 예제 코드를 소개합니다

이번에는 request_threaded_irq() 함수를 호출해서 IRQ 스레드를 생성하는 과정을 살펴보겠습니다. 분석할 코드는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/drivers/usb/dwc3/gadget.c]
1 static int dwc3_gadget_start(struct usb_gadget *g,
2 struct usb_gadget_driver *driver)
3 {
4 struct dwc3 *dwc = gadget_to_dwc(g);
5 unsigned long flags;
6 int ret = 0;
7 int irq;
8
9 irq = dwc->irq_gadget;
10 ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
11 IRQF_SHARED, "dwc3", dwc->ev_buf);

먼저 request_threaded_irq() 함수에 전달하는 인자를 살펴봅시다.
- irq: 인터럽트 번호
- dwc3_interrupt: 인터럽트 핸들러
- dwc3_thread_interrupt: 인터럽트 스레드 핸들 함수
- IRQF_SHARED: 인터럽트 플래그
- "dwc3": 인터럽트 이름
- dwc->ev_buf: 인터럽트 핸들러와 인터럽트 스레드 핸들 함수에 전달하는 매개인자

5장에서 인터럽트 핸들러를 설정할 때 썼던 request_irq() 함수와 유사해 보입니다. request_irq() 함수를 호출할 때와 차이점은 request_threaded_irq() 함수는 IRQ 스레드 핸들러인 dwc3_thread_interrupt() 함수 이름을 세 번째 인자로 전달합니다.

request_threaded_irq() 함수를 호출하면 해당 인터럽트에 대한 전용 IRQ 스레드가 생성됩니다. 리눅스 커널에서 IRQ 스레드 이름을 어떻게 결정할까요?? 인터럽트 번호가 47이면 IRQ 스레드 이름은 "irq/47-dwc3"가 됩니다.

인터럽트 발생 후 dwc3_interrupt() 이란 인터럽트 핸들러에서 인터럽트에 대한 처리를 한 다음 "irq/47-dwc3" IRQ 스레드를 깨울지 결정합니다. 이후 "irq/47-dwc3" IRQ 스레드가 깨어나면 스레드 핸들러인 dwc3_thread_interrupt() 함수가 호출됩니다. 이 세부 동작은 다음 절에서 살펴볼 예정입니다.

이번에는 인터럽트 핸들러인 dwc3_thread_interrupt() 함수를 보면서 세부 동작을 확인합시다. 
[https://elixir.bootlin.com/linux/v4.19.30/source/drivers/usb/dwc3/gadget.c]
static irqreturn_t dwc3_interrupt(int irq, void *_evt)
{
struct dwc3_event_buffer *evt = _evt;
return dwc3_check_event_buf(evt);
}

dwc 인터럽트가 발생하면 dwc3_interrupt() 이란 인터럽트 핸들러가 실행됩니다. dwc3_interrupt() 함수는 특별한 동작을 하지 않습니다. 바로 dwc3_check_event_buf() 함수를 호출합니다.

dwc3_check_event_buf() 함수 구현부는 다음과 같습니다.
[https://elixir.bootlin.com/linux/v4.19.30/source/drivers/usb/dwc3/gadget.c#L3107] 
1 static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
2 {
3 struct dwc3 *dwc = evt->dwc;
4 u32 amount;
5 u32 count;
6 u32 reg;
7
8 if (pm_runtime_suspended(dwc->dev)) {
9 pm_runtime_get(dwc->dev);
10 disable_irq_nosync(dwc->irq_gadget);
11 dwc->pending_events = true;
12 return IRQ_HANDLED;
13 }
...
14 if (amount < count)
15 memcpy(evt->cache, evt->buf, count - amount);
16
17 dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
18
19 return IRQ_WAKE_THREAD;
20}

위 함수를 눈여겨보면 시스템 상태에 따라 12 번째 줄에서는 IRQ_HANDLED 그리고 19 번째 줄에서는 IRQ_WAKE_THREAD를 반환합니다. 

인터럽트가 발생한 후 IRQ 스레드를 깨워서 인터럽트 후반부로 후속 처리를 할 필요가 없을 때가 있습니다. 이 조건에서 다음 12 번째 줄 코드와 같이 IRQ_HANDLED를 반환합니다.
8 if (pm_runtime_suspended(dwc->dev)) {
9 pm_runtime_get(dwc->dev);
10 disable_irq_nosync(dwc->irq_gadget);
11 dwc->pending_events = true;
12 return IRQ_HANDLED;
13 }

IRQ 스레드가 해당 인터럽트 핸들러 이후 IRQ 스레드에서 인터럽트에 대한 후속 처리를 해야 할 때는 IRQ_WAKE_THREAD를 반환합니다.
17 dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
18
19 return IRQ_WAKE_THREAD;
20}

이후 IRQ 스레드가 깨어난 후 IRQ 스레드 핸들러인 dwc3_thread_interrupt() 함수가 실행됩니다. 인터럽트 핸들러에서 바로 처리하지 못한 일을 수행합니다. 

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




     "혹시 글을 읽고 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실하게 답변 올려 드리겠습니다!"


# Reference 인터럽트 후반부 처리








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




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



핑백

덧글

댓글 입력 영역