Chapter 7 - Interrupts and Interrupt Handlers
Hardware와 통신 → polling, Interrupt의 두 가지 방법
polling은 비효율적, Interrupt를 이용해 Hardware가 일하는 동안 cpu는 다른 일 가능
Interrupt는 Interrupt Handler에 담겨있는 동작을 하게 됨
Interrupts
- Hardware가 processor에 물리적 전기 신호를 보내는 것을 허용
- Clock과는 무관하게 보내짐 (비동기적 신호)
- 각각의 Device들 마다 고유의 interrupt value를 가지고 있어서 processor가 interrupt 발생 시 해당 value를 확인하는 것으로 어떤 Device에서 interrupt가 발생했는지를 파악할 수 있음
- IRQ(Interrupt request) line
- 기본적으로 IRQ line은 정적으로 배정되어있지만, 동적으로 배정되는 경우도 있음
- PCI bus
- Exception은 clock에 맞춰서 발생하는 동기적 신호
- software interrupt
Interrupt Handlers
- Interrupt Service Routine이라고도 함
- Interrupt가 발생했을 때 해당 Interrupt를 어떻게 처리할 것인지에 대한 코드
- Interrupt context를 수행하며 이는 atomic하게 이루어진다
- Performance를 위해 Interrupt발생 시 최대한 빨리, 짧게 수행되어야 함
Top Halves VS Bottom Halves
- Top half에서는 Interrupt의 발생을 확인하고 Interrupt context를 포함한 Interrupt발생 후 즉각적으로 cpu가 처리해주어야 할 일들을 처리한다
- Bottom Half에서는 Device가 실제적으로 하는 일들을 한다
Registering an Interrupt Handler
Device driver가 Interrupt handler와 IRQ line을 등록한다
/include/linux/interrupt.h request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
irq
- Interrupt numberhandler
- 실제로 동작할 Interrupt handler (함수포인터)flags
- handler의 특성을 나타내주는 flag들/include/linux/interrupt.h #define IRQF_SHARED 0x00000080 #define IRQF_PROBE_SHARED 0x00000100 #define __IRQF_TIMER 0x00000200 #define IRQF_PERCPU 0x00000400 #define IRQF_NOBALANCING 0x00000800 #define IRQF_IRQPOLL 0x00001000 #define IRQF_ONESHOT 0x00002000 #define IRQF_NO_SUSPEND 0x00004000 #define IRQF_FORCE_RESUME 0x00008000 #define IRQF_NO_THREAD 0x00010000 #define IRQF_EARLY_RESUME 0x00020000 #define IRQF_COND_SUSPEND 0x00040000
IRQF_DISABLED
- 해당 irq가 동작할때 다른 interrupt들을 disable한다IRQF_SAMPLE_RANDOM
- 해당 irq를 커널 엔트로피 풀의 일부로 참여시킨다. 주기적 동작을 막는다.(실행주기를 예측할 수 없게 만든다 -- 4.11에서 사라짐)IRQF_TIMER
- 타이머 인터럽트용 플래그IRQF_SHARED
- 하나의 interrupt line을 공유할 수 있게 한다 (Shared handler)
name
- 해당 irq를 사용하는 device의 이름dev
- Shared handler에서 식별자로 쓰이는 기기 고유값 (주로 기기의 generic pointer 사용)
sleep하지 않는다는게 보장될 때 call해야한다
해당 irq를 더이상 사용하지 않을 때 free할 수 있다.
/kernel/irq/manage.c free_irq(unsigned int irq, void *dev)
- 해당 interrupt line이 공유되고 있지 않다면 handler를 지우고 interrupt line도 disable시킨다.
- 해당 interrupt line이 공유되고 있다면 handler만 지우고, 해당 line을 공유하는 handler들이 모두 삭제되었을 때 interrupt line을 disable시킨다.
Writing an Interrupt Handler
/drivers/net/ethernet/adaptec/starfire.c
static irqreturn_t intr_handler(int irq, void *dev_instance)
- return type이 irqreturn_t
- 만약 불려온 interrupt handler가 해당 device가 등록한 handler가 아니라면 IRQ_NONE을 반환, 맞다면 IRQ_HANDLED 반환
Shared Handler
- 하나의 Interrupt line을 여러 개의 Interrupt handler들이 사용하고 있는 경우를 말함
- Shared handler가 되려고 하는 handler들이 모두 등록 시 IRQF_SHARED flag가 set되어있을 때만 가능
Interrupt Context
- process와 연관되어있지 않다.
- sleep할 수 없다.
- 짧고 단순해야한다.
- 2-page kernel stack을 사용했을 때는 interrupt handler들이 커널스택을 공유하여 사용했지만, single page kernel stack을 사용할 때는 handler마다 각각의 스택을 가지고 있다.
Implementing Interrupt handlers
do_IRQ()
→handle_irq_event()
→handle_irq_event_percpu()
→__handle_irq_event_percpu()
- Interrupt 발생 시 cpu는
do_IRQ()
를 call -- architecture dependent - 해당 Interrupt line이 enable되어있다면
handle_irq_event()
를 call - 2.6 버전에서는 IRQF_SAMPLE_RANDOM flag가 set되어있을 때만
add_interrupt_randomness()
함수를 수행했지만, 4.11버전 기준으로 무조건 해당 함수를 안정성 차원에서 실행시킨다. - 4.11버전 기준으로 irq와 irqaction 대신 descriptor를 이용해 묶어서 한번에 인자를 보내준다.
/kernel/irq/handle.c handle_irq_event(struct irq_desc *desc)