# Interrupt (Bottom Halves) Interrup handler的limitations * interrupt handler會打斷其他人, code or 其他interrupt handlers.所以必須盡可能的快 * interrupt handler最好只disable自己那根,最壞情況會disable全部在同CPU上的其它根,這種情況等同全面阻斷了其它hardware跟cpu的溝通 * interrupt handler因為跟hardware溝通的特性,通常都是timing-critical的 * interrupt handler不在process context執行,所以,他們不能block(不能sleep),這限制了他的行為。 # 什麼情況該放bottom halves or top * If the work is time sensitive, perform it in the interrupt handler * If the work is related to the hardware, perform it in the interrupt handler * If the work needs to ensure that another interrupt (particularly the same interrupt) does not interrupt it, perform it in the interrupt handler * For everything else, consider performing the work in the bottom half # why Bottom halves? interrupt handler在跑的時候目前這根irq會在所有cpu disable掉,更壞的情況是如果你的handler有註冊IRQF_DISABLED還會把local processor的所有IRQ都disabled掉,所以處理interrupt handler時間拉得越長,整個system的response tim and performance都會嚴重下降。 > Processing incoming network traffic should not prevent the kernel’s receipt of keystrokes.The solution is to defer some of the work until later # Bottom halves * BH interface(2.6後已淘汰) * task qeuee(2.6後已被淘汰) * Softirqs(平行運算,效率較高)(since2.3) * Softirqs are a set of ***statically*** defined bottom halves that * even two of the same type can run concurrently on any processor * softirqs必須在compile time註冊 * Tasklets(基於softirqs)(效率較低)(since2.3) * flexible, ***dynamically*** created bottom halves built on top of softirqs * 兩個不同的tasklets可以在不同的cpu上同時執行 * 但兩個相同的tasklets不能在不同的cpug上同時執行 * 所以你可以用tasklets來設計出performance導向的還是easytouse導向的? * tasklets可以動態註冊 * Workqueues(since2.5) > Kernel timers是另一種deferring work的方法,Another mechanism for deferring work is kernel timers. Unlike the mechanisms discussed in the chapter thus far, timers defer work for a specified amount of time. That is, although the tools discussed in this chapter are useful to defer work to any time but now, you use timers to defer work until at least a specific time has elapsed.Therefore, timers have different uses than the general mechanisms discussed in this chapter. A full discussion of timers is given in Chapter 11, “Timers and Time Management.” > # Softirqs 跟tasklets比起來比較少用 source code 在kernel/softirq.c # **Implementing Softirqs** * Softirqs are statically allocated at compile time * Unlike tasklets, you cannot dynamically register and destroy softirqs * structure使用softirq_action (<linux/interrupt.h>) ![](https://i.imgur.com/ztCmAOp.png) there are NR_SOFTIRQS個 softirqs可以註冊 現在的kernel剩9個可以註冊 ![](https://i.imgur.com/wGvoYhg.png) ![](https://i.imgur.com/twnjjhC.png) # The Softirq Handler ``` void softirq_handler(struct softirq_action *) ``` ![](https://i.imgur.com/I1R5csG.png) # Executing Softirqs * A registered softirq must be marked before it will execute.This is called ***raising the softirq*** * 通常interrupt handler會在softirq執行前去把他mark起來 * Pending的softirqs通過以下幾個步驟驗證執行 * In the return from hardware interrupt code path * In the ***ksoftirqd*** kernel thread * In any code that explicitly checks for and executes pending softirqs, such as the networking subsystem * 執行透過呼叫__do_softirq(),會invoke do_softirq().大概就是loops每個pending的softirqs看誰要執行然後去把他的handler叫起來。 # Work Queues * Work queues永遠在process context執行 * 可被sched() * 可sleep() * 如果你的設計考量需要sleep只能使用work queue * 如果你的設計考量不能sleep,只能softirq, tasklets * 通常會用到work queue的情況 * you need to allocate a lot of memory * obtain a semaphore * perform block I/O # Implementing Work Queues per processor各自有一個work queue(kernel thread) events/n (n代表processor number) * 通常用default thread就夠了,不用自己create * Processorintense and performance-critical work might benefit from its own thread ![](https://i.imgur.com/UkpCLRt.png) ![](https://i.imgur.com/vjzsTbu.png) ![](https://i.imgur.com/eUEIfIK.png) ![](https://i.imgur.com/uWcfS7J.png) ![](https://i.imgur.com/HjIR6qf.png) # Using Work Queues ![](https://i.imgur.com/cdyCgni.png) your Work Queue Handler > void work_handler(void *data) > * the function run in process context * default, interrupts are enabled, no locks held * function can sleep * work handlers不能碰user space memory 因為沒有mapping到kernel threads. kernel只能在跑behalf of a user-space process的時候去碰user space,像是system call ![](https://i.imgur.com/rbUUEeO.png) # Softirq & Tasklets的主要區別 ``` include/linux/interrupt.h /* PLEASE, avoid to allocate new softirqs, if you need not _really_ high frequency threaded job scheduling. For almost all the purposes tasklets are more than enough. F.e. all serial device BHs et al. should be converted to tasklets, not to softirqs. */ enum { HI_SOFTIRQ=0, /* High Priority */ TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS }; ``` The key differences between softirq and tasklet are: Allocation * Softirqs are statically allocated at compile-time. Unlike tasklets, you cannot dynamically register and destroy softirqs. * Tasklets can be statically allocated using DECLARE_TASKLET(name, func, data) or can also be allocated dynamically and initialized at runtime using tasklet_init(name, func, data) Concurrency * Softirqs can run concurrently on several CPUs, even if they are of the same type because softirqs are reentrant functions and must explicitly protect their data structures with spinlocks. * Tasklets are non-reentrant and tasklets of the same type are always serialized: in other words, the same type of tasklet cannot be executed by two CPUs at the same time. However, tasklets of different types can be executed concurrently on several CPUs. Processing * Softirqs are activated by means of the raise_softirq(). The pending softirqs are processed by do_softirq() and ksoftirqd kernel thread after being enabled by local_bh_enable() or by spin_unlock_bh() * Tasklets are a bottom-half mechanism built on top of softirqs i.e. tasklets are represented by two softirqs: **HI_SOFTIRQ** and **TASKLET_SOFTIRQ**. Tasklets are actually run from a softirq. T**he only real difference in these types is that the HI_SOFTIRQ based tasklets run prior to the TASKLET_SOFTIRQ tasklets**. So, tasklet_schedule() basically calls raise_softirq(TASKLET_SOFTIRQ) * Note that softirqs (and hence tasklets and timers) are run on return from hardware interrupts, or on return from a system call. Also as soon as the thread that raised the softirq ends, that single softirq (and on other) is run to minimize softirq latency. ![](https://i.imgur.com/Bqip7R3.png) Q: workqueue怎麼保證要放到哪個cpu的queue中去執行,怎麼保證工作的順序性? A: take `media/dvb/dvb-core/dvb_net.c` for example 先init workqueue ![](https://i.imgur.com/3GK3IWT.png) 把driver function掛上 ![](https://i.imgur.com/KToa7Lo.png) schedule_work會把掛上去的work立刻schedule, 當 current cpu/0 wakes up events/0就會開始執行 current cpu/1 wakes up events/1就會開始執行 (如果不想馬上執行可以改 call schedule_delayed_work(&work, delay);) ![](https://i.imgur.com/hNVd6C5.png) ![](https://i.imgur.com/qOqWYbP.png) 這裡要說明一下kernel的get_cpu()機制 get_cpu()會disable preemption,然後立即取得目前這個跑到這段code的cpu,而是哪個cpu完全是隨機的,看run time狀態是哪個cpu schedule到這段程式而定,使用workqueue的人是沒有權力指定哪個cpu的,我們只能把想做的工作安排進workqueue佇列中,剩下什麼時候跑到就由cpu scheduler自己決定了 ![](https://i.imgur.com/nSFo15q.png) ![](https://i.imgur.com/DYb0qRx.png) 補充: 如果你認為使用預設的worker thread無法處理龐大工作量。此時可以create一個專有的worker thread。 > Create_workqueue(const char *name): > 為每個processer都create一個worker thread。 > Create_singlethread_workque(const char *name): > 只為當下的cpu create一個worker thread。 記得這邊的name就是thread的name,往後可以在程式執行起來以後由ps aux看到。