首先非常感謝陳莉君老師的賞識,題目名字也是陳老師幫忙起的。
大家好,我叫張昺華,中間那個字和“餅”字一個讀音,嘿嘿,相信不少人都不認識我的第二個字吧,這是我一個很聰明的表哥(當時他只有9歲)從新華字典裡取名的,光明正大的意思,希望我一生光明磊落。當時的技術不發達,加上這個字太特殊了,導致基本上大考都沒我名字,就連戶口本都是手寫的……因為這個字也讓我很容易上課被點名,因為太特別了,老師們總是喜歡叫上一叫……可是現在技術發達了,照樣經常遇到很多人不認識這個字就會隨便輸入,比如說張鍋華,張口華,張日華…有次在醫院拿藥,竟然有人把我的名字輸入成了張菊花(淚奔…),不過可能因為它的特殊性吧,讓我交到了非常多的好朋友,高中的230,大學的社聯公關部,啟明星支教隊,電氣2班,科創的小夥伴兒們以及現在公司裡的很多朋友……
2014年我畢業於廣東省惠州學院,我從小酷愛理科,當看到陳莉君老師在《Linux核心之旅》上發的學生在課堂上用課本劇的方式來詮釋技術時,想到了自己初中的時候老師就是這樣鍛煉我們的,很喜歡那樣的學習氛圍,自己大學時和小夥伴們一起創立了科技創新協會,目的就是可以一群人在研究技術,一起做有意思的東西,一起分享技術帶給我們的快樂,也希望中國有更多的人熱愛技術,喜歡一起研究、分享技術,然後可以一起用我們的技術來做一些好玩的東西,可以為這個社會創造一些東西來改善人們的生活。
如下為本人原創,在解決問題的過程中的一點心得(要感謝公司的權哥與泉哥的鼎力支援),如果有描述不準確的地方還請各位指出,非常感謝
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] [
[ 233.396103] [
[ 233.403821]
[
[ 233.412052]
[
[ 233.420715]
[
[ 233.429550]
[
[ 233.437868]
[
[ 233.446366]
[
[ 233.455376]
[
[ 233.464297]
[
[ 233.472959]
[
[ 233.481275] [
[ 233.488723] Exception stack(0xc0901f60 to 0xc0901fa8)
[ 233.493754] 1f60: c0112900 c0717028 c0901fb8 00000000 c093af4c 0000000000000335 c0826220
[ 233.501896] 1f80: 00000001 414fc091 df9eab80 00000000 c0900038 c0901fb0c010843c c0108440
[ 233.510034] 1fa0: 60000013 ffffffff
[ 233.513514] [
[ 233.520887] [
[ 233.528956] [
[ 233.537097] handlers:
[ 233.539363]
[
[ 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 intiproc_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
#defineSPURIOUS_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] irq44: nobody cared (try booting with the “irqpoll” option)
…
[ 233.549300] DisablingIRQ #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會一直++的情況,從而產生本文最開頭的錯誤
到此這個問題就已經分析完了
如下只是個小記錄:
這個函式的作用是檢查是否有中斷巢狀
【作者】張昺華
【新浪微博】張昺華–sky
【部落格園】 http://www.cnblogs.com/sky-heaven/
【知乎】 http://www.zhihu.com/people/zhang-bing-hua
【我的作品—旋轉倒立擺】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from;=y1.7-2
【我的作品—自平衡自動循跡車】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from;=y1.7-2
參考:
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