Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

19113
1478
166889


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

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

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

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

인터럽트 소개  
   * 리눅스 커널에서의 인터럽트 처리 흐름    
인터럽트 컨텍스트  
인터럽트 핸들러는 언제 호출될까?  
인터럽트 핸들러는 어떻게 등록할까?  
인터럽트 디스크립터  
인터럽트 디버깅  


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

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

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



  

핑백

덧글

  • 최재국 2020/05/11 00:19 # 삭제 답글

    제가 커널 4.19.115 버전에서 디바이스 드라이버 만들고 있는데용
    인터럽트 서비스 함수를 수행하는 동안에 다른 인터럽트가 발생하지 않도록 인터럽트 서비스 함수를 등록하려면 플래그를 써야하는데
    책에 너무옛날꺼라서 그런지 4.19.115 버전에는 SA_INTERRUPT도 없고 그나마 최근꺼인 IRQF_DISABLED도 없더라고요.
    비슷한거 찾아봤는데 아무리 찾아봐도 없더라고용 이럴때는 어떻게 등록하나요???
  • AustinKim 2020/05/11 18:17 #

    인터럽트가 발생하면 해당 CPU의 인터럽트 라인을 비활성화한 후 인터럽트 핸들러를 처리하는 구조로 동작합니다.
    따라서, 인터럽트 핸들러가 처리 중에 인터럽트를 비활성화하는 부분은 고려하지 않아도 됩니다.

    추가로 인터럽트가 발생할 때의 아래와 같은 ftrace 메시지를 보면 d란 알파벳이 보입니다. 이는 해당 CPU의 인터럽트 라인을 비활성화했다는 의미입니다.
    kworker/0:1-31 [000] d.h. 592.790968: irq_handler_entry: irq=17 name=3f00b880.mailbox

    출처:
    https://github.com/wikibook/linux-kernel/blob/master/6%EC%9E%A5.%EC%9D%B8%ED%84%B0%EB%9F%BD%ED%8A%B8%20%ED%9B%84%EB%B0%98%EB%B6%80%20%EC%B2%98%EB%A6%AC/6.5.2/ftrace_log.c

    Thanks,
    Austin Kim
  • 최재국 2020/05/12 13:29 # 삭제 답글

    선생님 그럼 우선 순위가 높은 인터럽트가 발생하면 지금 수행중인 인터럽트 서비스를 중지시키고 우선 순위가 높은 인터럽트가 실행이되야하는데
    등록할때부터 비활성화가 되면 우선순위라는게 왜있는거죠..ㅠㅠㅠㅠㅠㅠㅠㅠ
  • 최재국 2020/05/12 14:27 # 삭제

    좀 찾아봤는데

    인터럽트 핸들러를 등록시키면 디폴트로 인터럽트를 비활성화 되니까 인터럽트 서비스 함수에서 enable_irq() 함수를 쓰면 다른 인터럽트를 허용하게 되기때문에 우선순위가 높은 인터럽트가 실행이 가능하게 되는 것인가요????.. 무식한질문 죄송합니당..ㅠ.ㅠ.ㅠ.
  • AustinKim 2020/05/12 14:41 #

    리눅스 커널을 처음 접하신다면 '인터럽트'들 사이의 우선 순위는 고려하지 않으셔도 좋습니다. 리눅스 커널에서 인터럽트 사이의 우선순위를 설정하는 함수를 지원하지 않거든요. 외부 주변 기기에서 올려주는 인터럽트보다 시스템 내부에서 발생하는 인터럽트가 우선적으로 처리된다는 정도만 아셔도 개발에 문제가 없을 것입니다.

    만약 실전 개발자이시면 인터럽트 컨트롤러 소스와 문서를 분석하시면 되는데, 아래 링크에 있는 정보를 참고하시면 좋을 것 같습니다.

    http://jake.dothome.co.kr/ic/
    https://developer.arm.com/docs/ihi0048/b/arm-generic-interrupt-controller-architecture-version-20-architecture-specification

    Thanks,
    Austin Kim
댓글 입력 영역