커널 관련_Bottom Half IRQ

1231·2026년 5월 15일

"인터럽트가 자주 발생할때 후반부 처리 기법으로 Soft IRQ나 Tasklet 기법을 사용하는것이 좋다."

인터럽트가 매우 많이 발생하는 하드웨어는 어떤것이 있는가?
일반적으로 네트워크 인터페이스가 그 경우에 해당한다고 볼 수 있다.

그러면 저사양 임베디드 장비에서 자주 사용되는 enc28j60 이더넷 모듈과 비글본 블랙 보드 내장 네트워크 인터페이스인 CPSW는 어떤식으로 인터럽트 후반부 처리를 하는지 알아보겠음.

enc28j60 모듈 사진

enc는 연약한 모듈이라 커패시터나 외부 전원 모듈 없이 3 byte 이상을 전압 초기화? 없이 한번에 write하려고 하면 맛이 가버려서 리셋전까지는 모든 read 에 0을 반환함. 따라서 커패시터와 외부 전원 모듈이 필요함.

패킷 수신 처리를 위한 workqueue, threaded IRQ(Top Half NULL), napi(new api)

ENC28J60 4.19 vs 6.6

packet 수신 -> EPKTCNT register 1증가 -> generate Interrupt

드라이버 측에서는 후반부 처리에서, EPKTCNT만큼 SRAM buffer read하면서, EPKTCNT를 1씩 감소시켜야한다.

비글본 블랙 커널 4.19.94.ti-r42 에서 확인해봤음.
drivers/net/ethernet/microchip/enc28j60.c

device tree에서 해당 하드웨어와 호환되는 드라이버(현재 enc28j60.c)가 매핑되었을때 실행되는 probe함수.

쭉 net_device 할당하고, private data에 spi driver 구조체 저장해두고...하는데 1576번째 줄부터 Interrupt 후반부 처리를 위한 Workqueue 동작들을 등록하는 부분임. 그리고 1600번째 줄을 보면 request_irq함수로 Interrupt Handler를 등록하는 부분이 있음.

같은 파일에 아래와 같이 enc28j60_irq() 함수 존재.

probe함수에 존재하는 INIT_WORK...

enc28j60_irq_work_handler?

이런식으로 인터럽트 플래그에 따라 다르게 처리하는것을 볼 수 있다.

enc28j60_hw.h에 정의되어있음.

다음은 rx를 처리하는 부분.

실제 RX Interrupt를 처리하는 함수는 enc28j60_rx_interrupt()임을 알 수 있다.

1103: EPKTCNT 레지스터를 읽어온다.
하드웨어내에서 패킷 저장한 갯수를 기록한 레지스터.

1113: 이 packet count가 0이 될때까지 hw_rx를 수행.

읽을 주소(현재 읽을 패킷이 저장된 sram buffer의 주소), 길이, status들을 읽어옴

리눅스 네트워크 스택에서 패킷을 처리하는 단위인 skb 구조체 할당, 이 skb구조체에 sram buffer의 패킷 복사.

그 후, netif_rx_ni(skb)로 리눅스 네트워크 스택에 올림.

그러면 왜 Workqueue를 사용하는가? IRQ thread나 Soft IRQ를 사용하지 않는 이유가 무엇이냐?

비글본 블랙 최신 커널 버전에서는 Threaded IRQ 기법으로 변경됨.

https://github.com/beagleboard/linux/blob/master/drivers/net/ethernet/microchip/enc28j60.c

단순 requset_irq 는 Bottom Half가 NULL이었지만, 여기서는 Top Half가 NULL인 경우임.

기존 Top half Handler가 IRQ_WAKE_THREAD를 return하면 Bottom Half handler가 호출되는 과정인데, 여기서는 NULL로 되어있음.

그러면 어떻게 동작하는가?

request_threaded_irq가 정의된 manage.c 에는 다음과 같이 주석이 적혀있음.

NULL이라면, primary handler가 install된다.

즉,

 */
	ret = request_threaded_irq(spi->irq, irq_default_primary_handler, enc28j60_irq, IRQF_ONESHOT,
				   DRV_NAME, priv);
                   

와 동일한것임.

근데 왜 Workqueue -> Threaded IRQ로 바뀌었는가?

Workqueue는 등록된 스레드를 일반 작업으로 설정, priority가 다른 작업들과 비슷함.
IRQ Thread는 Real-Time priority 를 가지므로, Workqueue에 등록된 작업들보다는 빠르게 처리될 수 있음.

SPI 통신의 경우 TX,RX 레지스터를 확인(wait)하고 return 하는 spi 서브시스템의 특성상, 처리 속도가 상대적으로 느리다. 거기에, enc28j60 모듈은 10Mbps의 굉장히 느린 이더넷 모듈이다. 따라서 Workqueue에 등록시켜서 하드웨어적으로 느린것, 그냥 느리게 처리하자는 철학을 가지고 있었는데, 커널 버전이 업데이트되면서 소프트웨어적으로라도 빠르게 처리하자는 형식으로 변경되었음.

메일링 리스트를 보면 이렇게 threaded interrupt 로의 변경 패치가 있음을 알 수 있다.

내용은 다음과 같다.

요약: SPI버스가 잠들 수 있기 때문에 workqueue를 사용했었지만,
(1) 먼저 인터럽트 스레드를 깨우고, (2) 그다음에 다시 워크 아이템을 스케줄링 하는 2단계는 너무 비효율적이다. 최신 커널에서는 IRQ thread를 사용해서 바로 인터럽트를 스레드로 처리할 준비가 되어있는데, 굳이 이렇게 Workqueue에 등록해야하는 과정을 거칠 필요가 없다. 따라서 현대적인 threaded IRQ로 변경하였다. sleep은 어차피 workqueue와 threaded IRQ 둘다 허용하기 때문에, 굳이 단계를 나눠서 bottom Half를 처리할 이유가 없다는것임.

과거 방식 (Double Indirection):

HW Interrupt → Kernel IRQ Thread → Workqueue (kworker) → SPI 처리

단점: 거쳐야 하는 단계가 많아 응답이 느림.

현대 방식 (Direct Handling):

HW Interrupt → Threaded IRQ (Direct SPI 처리)

CPSW NAPI(New API)

그렇다면 인터럽트가 엄청나게 많이 발생한다면 어떻게 될까?
Interrupt Storm이 발생하면?
TODO: CPSW의 napi

ENC28J60은 패킷이 올 때마다 인터럽트를 치는 방식이라 패킷이 초당 수만 개씩 쏟아지면 인터럽트 폭풍(Interrupt Storm)으로 시스템이 다운됨.

반면 비글본 블랙 내장 랜카드인 CPSW 등 고성능 칩셋에서 쓰는 NAPI 방식은 최초 패킷 도달 시에만 인터럽트를 켜고, 그다음부터는 인터럽트를 아예 꺼버린 뒤 커널이 주기적으로 버퍼를 직접 긁어가는 폴링(Polling) 방식으로 전환

0개의 댓글