对中断的理解handle_level_irq【原创】

 如下为本人原创,在解决问题的过程中的一点心得,如果有描述不准确的地方还请各位指出,非常感谢

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

 

Kernel/irq/manage.c

Irq_thread

 

这个函数的作用是检查是否有中断嵌套

参考:

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

猜你喜欢

转载自www.cnblogs.com/sky-heaven/p/8909251.html