Ch10 Interrupt Handling¶
More: Interrupt Handling More
Installing an Interrupt Handler¶
Interrupt handler is similar to userspace signal handler.
Driver install and uninstall interrupt handler by request_irq()
and free_irq()
. (include/linux/interrupt.h
)
request_irq()
flags
SA_INTERRUPT: fast interrupt
SA_SHIRQ: interrupt can be shared between devices
SA_SAMPLE_RANDOM: interrupt can contribute the entropy poll for
/dev/random
and/dev/urandom
void *dev_id
: an unique id to index interrupt handler, use the pointer to driver’s own private_data area. For example, network drivers usually usestruct netdev *
asdev_id
.It is used for shared interrupt lines.
dev_id
can be NULL if interrupt is not shared.Interrupt handler will get
dev_id
as parameter, we can pass device context to handler by it.
/proc/interrupts
and/proc/stat
Autodetecting the IRQ number
How to get IRQ number of device?
some well-designed devices: read from PCI config space or status byte of device IO ports
autoprobing:
probe_irq_on()
,probe_irq_off()
. refer: https://elixir.free-electrons.com/linux/v4.13/source/include/linux/interrupt.h#L660[supplement] hard coding to SoC’s device tree. We can get IRQ number of platform device by
platform_get_irq()
Implementing a Handler¶
Interrupt handler
clear “interrupt pending” bit of interface board. Sometimes handler does it at last instead of first. Some devices don’t need it.
awake processes sleeping on the device.
interrupt handler should execute in a minimum amount of time.
defer a long computation to tasklet or workqueue to schedule computation at safer time.
disable interrupt in the handler??
Some restriction in the interrupt handler. They are same as kernel timer.
not execute in process context => can’t transfer data to or from userspace.
can’t sleep. like calling
wait_event()
, allocate memory withoutGFP_ATOMIC
, and locking semaphore.can’t call
schedule()
.
Interrupt handler parameters and return value
parameters: irq number,
dev_id
, and cpu registersdev_id
: device own private_data pointer, passed byrequest_irq()
.
return value: whether there was actually an interrupt to handle.
IRQ_HANDLED
orIRQ_NONE
Enable and Disable Interrupt¶
disable single interrupt
void disable_irq(int irq); void disable_irq_nosync(int irq); void enable_irq(int irq);
disable interrupt for specific interrupt line.
can’t disable shared interrupt lines. (
SA_SHIRQ
)mask IRQ on PIC(Programmable Interrupt Controller), thus disable this IRQ on all processors.
nested call: calling disable() 2 times needs 2 enable() call to reenable it.
disable_irq_nosync()
:disable_irq()
should wait interrupt controller complete, and then disable interrupt. It may deadlock if the thread callingdisable_irq()
hold the resource interrupt handler needs.disable_irq_nosync()
shouldn’t wait. but it may cause race condition.
disable all interrupt:
# disable interrupt but save their previous state. restore the previous interrupt state. void local_irq_save(unsigned long flags); void local_irq_restore(unsigned long flags); # disable and enable interrupt on the current processor void local_irq_disable(void); void local_irq_enable(void);
Top and Bottom Halves¶
workqueue
把 work 放進 workqueue 後, worker pool 就會安排 worker thread 一個一個執行 work. work 不使用 user context, 而用自己獨立的 workqueue context, 所以不能跟 userspace 交換資料. Linux 上 kthread 之中的 kworker 就是 workqueue 裡的 worker thread.
跟 multi-thread 的程式之中, 把運算 offload 到 thread pool 上執行的概念相似. 只是 worker pool 跟 thread pool allocate cpu 的 policy 不同. (e.g. http server 收到 connection 後, 用 condition variable 叫醒 thread 幫忙 handle request.)