一次觸摸屏中斷調試引發的深入探究【原創】


 首先非常感謝陳莉君老師的指點,題目名字也是陳老師起的,也很榮幸此文章能在蝸窩上發表一次,感謝郭大俠給的機會

 如下為本人原創,在解決問題的過程中的一點心得,如果有描述不准確的地方還請各位指出,非常感謝

Linux內核版本:linux-4.9.18

曾有一次調試觸摸屏的時候遇到如下的問題

/startup/modules #
 [  233.370296] irq 44: nobody cared (try booting with the "irqpoll" option)
[  233.376983] CPU: 0 PID: 0 Comm: swapper Tainted: G           O    4.9.18 #8
[  233.383912] Hardware name: Broadcom Cygnus SoC
[  233.388378] [<c010cbfc>] (unwind_backtrace) from [<c010a5fc>] (show_stack+0x10/0x14)
[  233.396103] [<c010a5fc>] (show_stack) from [<c0145d38>] (__report_bad_irq+0x24/0xa4)
[  233.403821] 
[<c0145d38>] (__report_bad_irq) from [<c0145fdc>] (note_interrupt+0x1c8/0x274)
[  233.412052] 
[<c0145fdc>] (note_interrupt) from [<c014400c>] (handle_irq_event_percpu+0x44/0x50)
[  233.420715] 
[<c014400c>] (handle_irq_event_percpu) from [<c0144040>] (handle_irq_event+0x28/0x3c)
[  233.429550] 
[<c0144040>] (handle_irq_event) from [<c0146574>] (handle_simple_irq+0x70/0x78)
[  233.437868] 
[<c0146574>] (handle_simple_irq) from [<c01438d8>] (generic_handle_irq+0x18/0x28)
[  233.446366] 
[<c01438d8>] (generic_handle_irq) from [<c02adb3c>] (iproc_gpio_irq_handler+0xd0/0x11c)
[  233.455376] 
[<c02adb3c>] (iproc_gpio_irq_handler) from [<c01438d8>] (generic_handle_irq+0x18/0x28)
[  233.464297] 
[<c01438d8>] (generic_handle_irq) from [<c0143980>] (__handle_domain_irq+0x80/0xa4)
[  233.472959] 
[<c0143980>] (__handle_domain_irq) from [<c01013d0>] (gic_handle_irq+0x50/0x84)
[  233.481275] [<c01013d0>] (gic_handle_irq) from [<c010b02c>] (__irq_svc+0x6c/0x90)
[  233.488723] Exception stack(0xc0901f60 to 0xc0901fa8)
[  233.493754] 1f60: c0112900 c0717028 c0901fb8 00000000 c093af4c 00000000 00000335 c0826220
[  233.501896] 1f80: 00000001 414fc091 df9eab80 00000000 c0900038 c0901fb0 c010843c c0108440
[  233.510034] 1fa0: 60000013 ffffffff
[  233.513514] [<c010b02c>] (__irq_svc) from [<c0108440>] (arch_cpu_idle+0x2c/0x38)
[  233.520887] [<c0108440>] (arch_cpu_idle) from [<c013a6ec>] (cpu_startup_entry+0x50/0xc0)
[  233.528956] [<c013a6ec>] (cpu_startup_entry) from [<c0800d70>] (start_kernel+0x414/0x4b0)
[  233.537097] handlers:
[  233.539363] 
[<c014408c>] irq_default_primary_handler threaded [<bf03ff68>] synaptics_rmi4_irq [synaptics_dsx]
[  233.549300] Disabling IRQ #44

首先我們順着錯誤跟蹤linux內核來看下

kernel/irq/spurious.c

因此有提示的log信息可以看出,是走的else的分支,bad_action_ret(action_ret)返回為0

通過此函數的dump_stack的信息,可以追溯到調用者

 

drivers/pinctrl/bcm/pinctrl-iproc-gpio.c

 

 

kernel/irq/chip.c

handle_level_irq

===> handle_irq_event  (kernel/irq/handle.c)

===> handle_irq_event_percpu   (kernel/irq/handle.c)

===>__handle_irq_event_percpu  (kernel/irq/handle.c)

 

根據log,我們可以在下圖看到note_interrupt,即說明noirqdebug=0

Kernel/irq/handle.c

 

因為上面我們已經分析過bad_action_ret(action_ret)返回為0

因此在note_interrupt函數里面只會從如下分支進去

Kernel/irq/spurious.c

從上圖可以看出,如果想出現那樣的錯誤,必須滿足條件

desc->irqs_unhandled > 99900 為真

如要要滿足如上條件的話,那么只有如下地方會讓irqs_unhandled++

Kernel/irq/spurious.c

通過上圖,我們可以看到,必須滿足條件:

action_ret == IRQ_NONE為真

再繼續看回如下圖,action_ret就是retval

 

res即為action_ret

而 action->handler的回調函數是

request_threaded_irq線程化注冊中斷的第2個參數

 

kernel/irq/manage.c

因為handler為NULL,所以handler = irq_default_primary_handler

 

action_ret = IRQ_WAKE_THREAD

Kernel/irq/spurious.c

 

經過如上圖,我們可以發現action_ret = IRQ_NONE

 

那么我們接下來看看到底是怎么被調用到這里的,一個中斷的產生又是怎樣的?

首先handle_level_irq這個函數是在這里注冊到kernel中的

drivers/pinctrl/bcm/pinctrl-iproc-gpio.c

static int iproc_gpio_probe(struct platform_device *pdev)

===> gpiochip_irqchip_add

Include/linux/gpio/driver.h

typedef    void (*irq_flow_handler_t)(struct irq_desc *desc);

這里即gpiochip->irq_handler = handle_level_irq

struct irqaction *action

 

一個中斷開始的時候

arch/arm/kernel/entry-armv.S

這里有一個全局的handle_arch_irq

 

這個全局的handle_arch_irq會在如下地方被賦值

arch/arm/kernel/setup.c

void __init setup_arch(char **cmdline_p)

===> handle_arch_irq被賦值

那么接下來我們就要找到mdesc->handle_irq又是在哪里被賦值了呢?

 

drivers/irqchip/irq-gic.c

這里有這樣的函數set_handle_irq

 

接下來我們看下這個函數的實現就知道了

arch/arm/kernel/irq.c

 

那么這個set_handle_irq又是在哪里被調用的呢?

針對內核版本Linux-4.9.18

drivers/irqchip/irq-gic.c

gic_of_init

===>__gic_init_bases

===>set_handle_irq

Include/linux/irqchip.h

Include/linux/of.h

 

Include/linux/of.h

因此我們得出一個結論:

handle_arch_irq = gic_handle_irq

一個中斷開始后,從entry-armv.S中進入

 

handle_domain_irq

===> __handle_domain_irq

===>generic_handle_irq

===>generic_handle_irq_desc

 

這里的desc->handle_irq 其實就是handle_level_irq

這里是如何轉換過去的呢?

drivers/pinctrl/bcm/pinctrl-iproc-gpio.c

gpiochip_set_chained_irqchip

===> irq_set_chained_handler_and_data

===> __irq_do_set_handler

Kernel/irq/chip.c

 

回歸到最初的問題,之前我們分析出如下的結論:

如果想出現log那樣的錯誤,必須滿足條件

desc->irqs_unhandled > 99900 為真

如要要滿足如上條件的話,那么只有讓irqs_unhandled++

那么滿足這個條件就必須action_ret == IRQ_NONE

#define SPURIOUS_DEFERRED        0x80000000

如下圖:

也就是必須要滿足handled != desc->threads_handled_last 為假

這里handled = threads_handled

而desc->threads_handled_last會在如下位置設置為SPURIOUS_DEFERRED

 

再看下圖

Kernel/irq/manage.c

Irq_thread

 

 

這里會一直將threads_handled++ ,這里handled = threads_handled

直到滿足handled != desc->threads_handled_last 為假

 

那么為什么這個threads_handled會一直++呢?

因為這里:

 

上圖是正確的修改,如果gpiochip_irqchip_add的第四個參數是handle_simple_irq的話,

那么就會出現threads_handled會一直++的情況,從而產生本文最開頭的錯誤

[  233.370296] irq 44: nobody cared (try booting with the "irqpoll" option)

[  233.549300] Disabling IRQ #44

 

這里我們就要對handle_simple_irq 與handle_level_irq做個分析了,具體的分析大家可以網上看蝸窩的資料以及csdn上很多對這塊有詳細的描述,我這里簡單敘述下我個人的理解

首先上代碼:

大家可以看出來,handle_simple_irq做的事情很簡單,而handle_level_irq卻做了這個動作:

mask_ack_irq(desc); 因為是電平中斷,如果不做mask中斷的動作的話,會因為中斷電平一直是有效電平導致中斷控制器會源源不斷地給cpu發中斷

而handle_simple_irq就是非常簡單的處理中斷,沒有mask中斷,原本代碼是寫的handle_simple_irq,而觸摸屏的中斷是設置為線程化的,並且為電平觸發方式,那么如果沒有mask該中斷,那么當一次線程化中斷處理函數還未執行完成的時候,又會有源源不斷地中斷一直進來,那么就會出現threads_handled會一直++的情況,從而產生本文最開頭的錯誤

到此這個問題就已經分析完了

 

感謝網友smcdef的建議

我們分析如下

中斷控制器怎么判斷哪個是哪個handle_level_irq?
中斷控制器流程:
gic_handle_irq
    ===>handle_domain_irq
        ===>__handle_domain_irq
            ===> irq_find_mapping 
            ===>generic_handle_irq
 
irq_find_mapping這個函數來找之前irq_create_mapping的irq映射
找到返回中斷號后,再去執行handle_level_irq
 
 

因此不同的驅動在注冊的時候可以注冊自己所需要的中斷控制器驅動方式

request_threaded_irq
    ===>__setup_irq
        ==>__irq_set_trigger
            ===>irq_set_type

所以可以這樣改,驅動注冊的時候可以通過這個函數來設置自己的中斷控制器需要走哪種流控處理


內核也有其他驅動有類似的改法
drivers/gpio/gpio-aspeed.c
static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type)
{
    u32 type0 = 0;
    u32 type1 = 0;
    u32 type2 = 0;
    u32 bit, reg;
    const struct aspeed_gpio_bank *bank;
    irq_flow_handler_t handler;
    struct aspeed_gpio *gpio;
    unsigned long flags;
    void __iomem *addr;
    int rc;

    rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit);
    if (rc)
        return -EINVAL;

    switch (type & IRQ_TYPE_SENSE_MASK) {
    case IRQ_TYPE_EDGE_BOTH:
        type2 |= bit;
    case IRQ_TYPE_EDGE_RISING:
        type0 |= bit;
    case IRQ_TYPE_EDGE_FALLING:
        handler = handle_edge_irq;
        break;
    case IRQ_TYPE_LEVEL_HIGH:
        type0 |= bit;
    case IRQ_TYPE_LEVEL_LOW:
        type1 |= bit;
        handler = handle_level_irq;
        break;
    default:
        return -EINVAL;
    }

    spin_lock_irqsave(&gpio->lock, flags);

    addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE0);
    reg = ioread32(addr);
    reg = (reg & ~bit) | type0;
    iowrite32(reg, addr);

    addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE1);
    reg = ioread32(addr);
    reg = (reg & ~bit) | type1;
    iowrite32(reg, addr);

    addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE2);
    reg = ioread32(addr);
    reg = (reg & ~bit) | type2;
    iowrite32(reg, addr);

    spin_unlock_irqrestore(&gpio->lock, flags);

    irq_set_handler_locked(d, handler); return 0;
}

 

 

如下只是個小記錄:

這個函數的作用是檢查是否有中斷嵌套

 

 

 

 

參考:

http://www.wowotech.net/irq_subsystem/request_threaded_irq.html

http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html

https://blog.csdn.net/tiantao2012/article/details/78062621

https://blog.csdn.net/tiantao2012/article/details/78094691

https://blog.csdn.net/zhao2272062978/article/details/70599978

https://blog.csdn.net/droidphone/article/details/7467436

https://blog.csdn.net/droidphone/article/details/7445825

https://blog.csdn.net/droidphone/article/category/1118447

https://blog.csdn.net/phenix_lord/article/details/45116259

https://blog.csdn.net/phenix_lord/article/details/45116595

https://blog.csdn.net/phenix_lord/article/details/45116689

 https://blog.csdn.net/DroidPhone/article/details/7489756


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM