IRQ 스레드화(threaded IRQ)로 실시간성 확보하기

인터럽트 핸들러(hard IRQ)는 인터럽트 컨텍스트에서 실행되기 때문에 sleep이 불가능하고, 처리하는 동안 로컬 CPU의 인터럽트가 막혀 그 코어의 실시간 태스크가 우선순위와 무관하게 끼어들 수 없다. PREEMPT_RT 계열 커널이나 지연시간에 민감한 워크로드가 threaded IRQ(스레드화된 인터럽트)로 처리를 일반 커널 스레드로 옮기는 이유다. 이 글에서는 이미 스레드로 동작 중인 인터럽트를 찾아, 그 스레드의 스케줄링 정책과 우선순위를 직접 조정하는 방법을 정리한다.

Hard IRQ와 Threaded IRQ 비교

항목Hard IRQ (top half)Threaded IRQ (bottom half)
실행 컨텍스트인터럽트 컨텍스트일반 커널 스레드(irq/N-devname)
sleep 가능 여부불가능가능(락 획득, 메모리 할당 등)
스케줄링 정책 제어불가능chrt로 조회/변경 가능(기본 SCHED_FIFO)
선점 가능 여부더 높은 우선순위 태스크도 끼어들 수 없음더 높은 우선순위 RT 태스크에 선점당함
등록 APIrequest_irq()request_threaded_irq()

request_threaded_irq()로 등록된 인터럽트는 최소한의 확인만 하는 primary handler(하드 IRQ)와 실제 처리를 담당하는 스레드(secondary handler)로 나뉜다. IRQF_ONESHOT 플래그를 쓰면 스레드가 처리를 끝낼 때까지 해당 IRQ 라인이 계속 마스킹되어, primary handler가 다시 호출되며 인터럽트 폭주를 일으키는 상황을 막는다.

이미 스레드화된 인터럽트 찾기

threadirqs 부팅 옵션 없이도, 드라이버가 직접 IRQF_ONESHOT으로 요청한 인터럽트는 기본적으로 스레드로 동작한다. 6코어 Ubuntu 24.04 VM에서 ps -eLo로 확인하면 다음과 같다.

$ ps -eLo pid,tid,comm,psr | grep 'irq/'
     68     68 irq/9-acpi        1
    652    652 irq/18-vmwgfx     5

irq/9-acpiirq/18-vmwgfx가 일반 태스크처럼 PID를 갖고 특정 코어(PSR 컬럼)에서 실행 중인 게 보인다. 나머지 IRQ(ahci, ehci_hcd 등)는 드라이버가 threaded handler를 요청하지 않아 여전히 hard IRQ로만 처리된다.

스케줄링 정책과 우선순위 확인/조정

일반 커널 스레드이므로 chrt, taskset 같은 표준 스케줄링 도구가 그대로 적용된다.

$ chrt -p 68
pid 68's current scheduling policy: SCHED_FIFO
pid 68's current scheduling priority: 50
$ taskset -p 68
pid 68's current affinity mask: 2

기본적으로 SCHED_FIFO 우선순위 50으로 생성되는데, 이는 다수의 커널 IRQ 스레드가 공유하는 값이라 특정 인터럽트만 더 급하게 처리하고 싶다면 우선순위를 개별 조정해야 한다. 실제로 우선순위를 올려봤다.

$ sudo chrt -p -f 60 68
$ chrt -p 68
pid 68's current scheduling policy: SCHED_FIFO
pid 68's current scheduling priority: 60

# 원복
$ sudo chrt -p -f 50 68

hard IRQ는 이런 조정 자체가 불가능하지만, 스레드로 옮겨진 순간부터는 일반 RT 태스크와 동일한 스케줄링 규칙을 받는다. taskset으로 확인한 affinity(CPU1)도 이전 글에서 다룬 smp_affinity와 별개로, 스레드 자체를 특정 코어에 고정하는 용도로 쓸 수 있다.

주의사항

항목내용
우선순위 역전IRQ 스레드 우선순위를 과도하게 올리면 오히려 다른 RT 태스크가 굶주릴 수 있다. 정말 지연에 민감한 인터럽트만 선별적으로 조정할 것
threadirqs로 전체 강제 스레드화부팅 파라미터 threadirqs를 추가하면 force_irqthreads가 켜져 IRQF_ONESHOT 없는 나머지 hard IRQ도 전부 스레드로 감싸진다(PREEMPT_RT 기본값). 런타임 변경 불가·재부팅 필요라 이 글에서는 재현하지 않음
컨텍스트 스위치 오버헤드스레드화는 지연시간의 예측 가능성(determinism)을 높이는 대신, hard IRQ 대비 스케줄링 오버헤드가 추가된다. 처리량보다 지연 예측성이 중요한 경우에만 유효한 트레이드오프
IRQF_ONESHOT과 레벨 트리거레벨 트리거 인터럽트를 스레드화할 때 IRQF_ONESHOT이 빠지면 스레드가 처리를 마치기 전에 같은 인터럽트가 반복 발생할 수 있다

마무리

ps -eLo로 이미 스레드화된 인터럽트를 찾고, chrt/taskset으로 그 스케줄링 정책·우선순위·affinity를 직접 조정할 수 있다는 게 이번 글의 핵심이다. hard IRQ 단계에서는 손댈 수 없던 값들이 스레드로 넘어오는 순간부터는 일반 RT 태스크와 똑같이 다뤄진다는 점이 threaded IRQ의 실질적인 이점이다.

참고

Kernel.org – Genericirq Documentation
Linux Foundation RT Wiki – Threaded IRQs

답글 남기기