驅動程式 - Linux Device Driver (LDD) - 使用範例 - C/C++ (PocketBeagle) - SoftIRQ



參考資訊:
http://jimmychenhaha.blogspot.com/2016/09/softirq.html
https://www.cnblogs.com/wang_yb/archive/2013/04/23/3037268.html

Linux Kernel將中斷處理切成top-half和bottom-half兩部份,top-half指的就是中斷副程式本身,而bottom-half指的就是延遲處理的副程式,如果中斷要處理的事情不需太耗時,就直接在中斷副程式裡處理就可以,如果無法立即完成(如:需要等待或者傳遞大量資料),則可以安排一個延遲處理的副程式,softirq就是一種延遲處理的機制,雖然是延遲處理,不過,softirq也是執行在比較高優先級別的等級,因此,不適合處理太過耗時的事情,但是適用於快速處理的場景,因為,高優先級別的副程式,如果太耗時,將延後Context Switch的發生(單核心來說),導致系統效能出問題,softirq本身是可重複進入的(reentrant),因此,在撰寫softirq程式時,必須要考慮到這個問題,不過有了bottom-half機制,確實可以有效解決中斷阻塞問題

使用步驟:

1. open_softirq()
2. raise_softirq()
3. raise_softirq_irqoff()

由於softirq需要修改Kernel,因此,請先修改檔案(kernel/softirq.c)

const char * const softirq_to_name[NR_SOFTIRQS] = { 
    "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "IRQ_POLL",
    "TASKLET", "SCHED", "HRTIMER", "RCU", "PocketBeagle"
};
EXPORT_SYMBOL(open_softirq);
EXPORT_SYMBOL(raise_softirq_irqoff);
EXPORT_SYMBOL(raise_softirq);

include/linux/interrupt.h

enum {
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    BLOCK_SOFTIRQ,
    IRQ_POLL_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the numbering. Sigh! */
    RCU_SOFTIRQ,     /* Preferable RCU should always be the last softirq */
    POCKETBEAGLE_SOFTIRQ,
    NR_SOFTIRQS
};

P.S. 接著重新編譯Kernel並且更新zImage

main.c

#include <linux/init.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Steward Fu");
MODULE_DESCRIPTION("Linux Driver");
 
#define BUTTON 27

void softirq_handler(struct softirq_action *action)
{
    printk("%s\n", __func__);
}

static irqreturn_t irq_handler(int irq, void *arg)
{
    raise_softirq(POCKETBEAGLE_SOFTIRQ);
    return IRQ_HANDLED;
}

int ldd_init(void)
{
    request_irq(gpio_to_irq(BUTTON), irq_handler, IRQF_TRIGGER_RISING, "gpio_irq", NULL);
    open_softirq(POCKETBEAGLE_SOFTIRQ, softirq_handler);
    return 0;
}
 
void ldd_exit(void)
{
    raise_softirq_irqoff(POCKETBEAGLE_SOFTIRQ);
    free_irq(gpio_to_irq(BUTTON), NULL);
}
 
module_init(ldd_init);
module_exit(ldd_exit);

ldd_init: 設定GPIO中斷以及softirq延遲處理副程式
irq_handler: 安排一個softirq延遲處理
softirq_handler: 列印字串
ldd_exit: 釋放中斷資源以及關閉softirq

完成

# softirq_handler