驅動程式 - Linux Device Driver (LDD) - 使用範例 - Assembly (ARM) - 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

ldd.S

    .global init_module
    .global cleanup_module

    .equ BUTTON,               27
    .equ IRQF_TRIGGER_RISING,  1
    .equ POCKETBEAGLE_SOFTIRQ, 10

    .section .modinfo, "ae"
__UNIQUE_ID_0: .asciz "license=GPL"
__UNIQUE_ID_1: .asciz "author=Steward Fu"
__UNIQUE_ID_2: .asciz "description=Linux Driver"

    .section .data
btn_irq:     .dcb 4
irq_name:    .asciz "gpio_irq"
softirq_msg: .asciz "softirq_handler\n"

    .align 2
    .section .text
softirq_handler:
    push {lr}
    ldr r0, =softirq_msg
    bl printk
    pop {pc}

irq_handler:
    push {lr}
    mov r0, #POCKETBEAGLE_SOFTIRQ
    bl raise_softirq
    mov r0, #1
    pop {pc}

init_module:
    push {r4, r5, lr}

    mov r0, #BUTTON
    bl gpio_to_desc
    bl gpiod_to_irq
    ldr r1, =btn_irq
    str r0, [r1]

    ldr r1, =irq_handler
    mov r2, #0
    mov r3, #IRQF_TRIGGER_RISING
    ldr r4, =irq_name
    mov r5, #0
    push {r4, r5}
    bl request_threaded_irq
    pop {r4, r5}

    mov r0, #POCKETBEAGLE_SOFTIRQ
    ldr r1, =softirq_handler
    bl open_softirq

    mov r0, #0
    pop {r4, r5, pc}

cleanup_module:
    push {lr}
    ldr r0, =btn_irq
    ldr r0, [r0]
    mov r1, #0
    bl free_irq
    mov r0, #POCKETBEAGLE_SOFTIRQ
    bl raise_softirq_irqoff
    pop {pc}
    .end

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

完成

# softirq_handler