Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

138199
1107
135866


[리눅스커널][인터럽트] 인터럽트 핸들러 등록 시 플래그 설정 5. 인터럽트

5.4.3 인터럽트 핸들러 등록 시 플래그 설정

이 내용을 다루기 전에 인터럽트 신호에 대해서 조금 살펴보겠습니다.

인터럽트 속성 플래그를 소개하기 전 이 내용을 다루기 전에 인터럽트 신호에 대해 살펴보겠습니다.

가끔 인터럽트가 제대로 안 올라온다거나 디바이스 드라이버를 처음 올려서 기본 동작을 확인할 때 오실로스코프 장비로 인터럽트 파형을 측정합니다. 가끔 하드웨어 개발자와 분쟁이 생깁니다. 인터럽트을 발생시키는 하드웨어에 노이즈가 껴서 제대로 인터럽트 신호를 제대로 전달을 못 하면 드라이버에서 인터럽트를 제대로 처리하지 않는 것처럼 보일 수 있습니다. 가끔은 인터럽트 신호가 엄청나게 자주 발생해서 시스템 오동작을 유발할 때도 있습니다. 이를 리눅스 커널 커뮤니티에서 IRQ Storm(인터럽트 폭풍)이라고 합니다.

이렇게 인터럽트 동작에 조금이라도 의문이 생기면 인터럽트 파형을 측정하는 것이 좋습니다. 그런데 인터럽트 파형을 해석하기 위해서 몇 가지를 더 알아야 합니다. 파형은 크게 인터럽트를 인지하는 방식에 따라 크게 에지-트리거 인터럽트와 레벨-트리거 인터럽트로 분류할 수 있습니다.

에지-트리거 인터럽트는 전기 신호가 어떤 상태에서 다른 상태로(보통은 하이(high) -> 로우(low)로) 넘어가는 순간 인터럽트를 감지하는 방식입니다. 반대로 레벨-트리거 인터럽트는 인터럽트 라인을 하이(high)로 유지할 때 인터럽트로 식별합니다.

이를 그림으로 표현하면 아래와 같습니다.
 
   [그림 5.13] 인터럽트 신호종류와 플래그

커널에서 이미 인터럽트 신호 식별 방식에 따라 인터럽트를 설정하도록 이미 플래그를 정의해놨습니다. 
[https://elixir.bootlin.com/linux/v4.19.30/source/include/linux/interrupt.h]
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010

IRQF_TRIGGER_RISING 플래그는 LOW에서 HIGH로 올라갈때, IRQF_TRIGGER_FALLING 플래그는 HIGH에서 LOW로 떨어질 때를 의미합니다. IRQF_TRIGGER_HIGH 플래그는 신호가 HIGH로 유지될 때, IRQF_TRIGGER_LOW 플래그는 신호가 LOW로 유지될 때를 뜻합니다. 이 패턴으로 인터럽트 신호를 식별하겠다는 것입니다.

이렇게 인터럽트 신호를 어떻게 식별하는지 확인하고 이에 맞는 플래그로 인터럽트 핸들러를 설정해야 합니다. 이 파라미터는 request_irq() 함수의 세 번째 플래그(unsigned long flags)로 채워 넣어야 합니다.
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
    const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}

인터럽트 플래그 설정 예제 코드 알아보기

이어서 다른 인터럽트 핸들러에서는 어떻게 인터럽트 플레그를 설정하는지 확인해보겠습니다. 
[https://elixir.bootlin.com/linux/v4.19.30/source/drivers/mmc/core/slot-gpio.c]
01 void mmc_gpiod_request_cd_irq(struct mmc_host *host)
02 {
03 struct mmc_gpio *ctx = host->slot.handler_priv;
04 int irq = -EINVAL;
05 int ret;
...
06 if (irq >= 0) {
07 if (!ctx->cd_gpio_isr)
08 ctx->cd_gpio_isr = mmc_gpio_cd_irqt;
09 ret = devm_request_threaded_irq(host->parent, irq,
10 NULL, ctx->cd_gpio_isr,
11 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
12 ctx->cd_label, host);
...

11 번째 줄 코드를 보면 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING로 인터럽트 핸들러를 설정하는 코드를 볼 수 있습니다. 인터럽트 신호가 바뀔 때 인터럽트로 판정하는 경우입니다. 

참고로 IRQF_ONESHOT 플래그는 인터럽트 핸들러에서 인터럽트를 처리하는 동안 인터럽트를 다시 처리하지 않게 설정합니다. 

request_threaded_irq() 대신 devm_request_threaded_irq() 함수을 쓰는 이유는 인터럽트를 disable_irq() 함수를 써서 인터럽트 등록 해지할 때 메모리에서 해당 인터럽트 디스크립터를 해제할 수 있기 때문입니다.

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

Thanks,
Austin Kim(austindh.kim@gmail.com)

# Reference (인터럽트 처리)


핑백

덧글

댓글 입력 영역