Linux核心中斷之中斷呼叫流程
本文基於RockPI 4A
單板Linux4.4核心介紹中斷呼叫流程。
一、異常向量表
ARMv8包括兩種執行狀態:AArch64和AArch32。
AArch64中不再使用AArch32中的7種特權模式,而是提出了Exception Levels的概念,包括:
1)EL0:用於使用者態程式,許可權最低
2)EL1:給核心使用,許可權稍高
3)EL2:虛擬化相關,許可權更高
4)EL3:安全相關,許可權最高
Linux核心中一般只使用EL0和EL1。
AArch64異常向量表中的異常包括:
1)Synchronous exception(同步異常)
2)SError
3)IRQ
4)FIQ
注:SError、IRQ和FIQ屬於非同步異常。
在Linux核心中,在arch/arm64/kernel/entry.S
檔案中定義了異常向量表,內容如下:
/* * Exception vectors. */ .pushsection ".entry.text", "ax" .align 11 ENTRY(vectors) ... ## 省略了部分EL1和EL0的異常向量 kernel_ventry 1, sync // Synchronous EL1h ,對應el1_sync kernel_ventry 1, irq // IRQ EL1h ,對應el1_irq kernel_ventry 1, fiq_invalid // FIQ EL1h ,對應el1_fiq_invalid kernel_ventry 1, error_invalid // Error EL1h ,對應el1_error_invalid ... END(vectors)
二、中斷呼叫流程
選取el1_irq()
函式介紹Linux核心中斷的呼叫流程。
檔案:arch/arm64/kernel/entry.S
,呼叫流程如下:
1、handle_irq()初始化
在DTS
解析階段完成handle_irq()
函式的初始化,流程如下:
of_platform_populate()-> ##platform.c of_platform_bus_create()-> of_platform_device_create_pdata()-> of_device_alloc()-> of_irq_to_resource_table()-> of_irq_to_resource()-> irq_of_parse_and_map()-> irq_create_of_mapping()-> irq_create_fwspec_mapping()-> irq_domain_alloc_irqs()-> irq_domain_alloc_irqs_recursive()-> domain->ops->alloc() ## 對應GICv3的gic_irq_domain_alloc() ::gic_irq_domain_alloc()-> gic_irq_domain_map()
gic_irq_domain_map()
函式中完成了handle_irq()
函式的賦值,具體執行如下:
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { ... /* PPIs */ if (hw < 32) { .. ## 1.賦值PPI的handle_irq = handle_percpu_devid_irq irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_percpu_devid_irq, NULL, NULL); .. } /* SPIs */ if (hw >= 32 && hw < gic_data.irq_nr) { ## 2.賦值SPI的handle_irq = handle_fasteoi_irq irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); ... } /* LPIs */ if (hw >= 8192 && hw < GIC_ID_NR) { ... ## 3.賦值LPI的handle_irq = handle_fasteoi_irq irq_domain_set_info(d, irq, hw, chip, d->host_data, handle_fasteoi_irq, NULL, NULL); } return 0; }
2、handle_irq()實現
以共享外設中斷SPI
的中斷處理函式handle_fasteoi_irq()
為例,繼續跟蹤中斷的執行過程。
handle_fasteoi_irq()-> ## kernel/irq/chip.c handle_irq_event()-> handle_irq_event_percpu()->
handle_irq_event_percpu()
函式會呼叫已經註冊的中斷處理函式,同時喚醒irq_thread
執行緒。
irqreturn_t handle_irq_event_percpu(struct irq_desc *desc) { ... while (action) { ... trace_irq_handler_entry(irq, action); ## 1. 執行已經註冊的對應的中斷處理函式 ---- (重點) res = action->handler(irq, action->dev_id); trace_irq_handler_exit(irq, action, res); ... switch (res) { case IRQ_WAKE_THREAD: ## 2.喚醒irq_thread執行緒 __irq_wake_thread(desc, action); ... default: break; } ... } ... }
3、中斷處理執行緒
在使用request_threaded_irq()
函式申請中斷時,會建立一個irq_thread
執行緒,呼叫流程如下:
request_threaded_irq()-> ## kernel/irq/manage.c __setup_irq()-> setup_irq_thread()-> kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name) ## 建立了一個irq_thread執行緒
irq_thread
執行緒平時在睡眠狀態,等待handle_irq_event_percpu()
函式喚醒,進一步執行已註冊的中斷處理執行緒函式。
static int irq_thread(void *data) { ... if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD, &action->thread_flags)) handler_fn = irq_forced_thread_fn; else handler_fn = irq_thread_fn; ## 1. 賦值中斷處理執行緒 init_task_work(&on_exit_work, irq_thread_dtor); task_work_add(current, &on_exit_work, false); irq_thread_check_affinity(desc, action); while (!irq_wait_for_interrupt(action)) { ... action_ret = handler_fn(desc, action); ## 2.執行已註冊的對應的中斷處理執行緒函式 ---- (重點) ... } ... }
三、應用舉例
使用DRM
框架中HDMI
中斷驗證中斷呼叫流程。
檔案:drivers\gpu\drm\bridge\synopsys\dw-hdmi.c
int dw_hdmi_bind(struct device *dev, struct device *master, void *data, struct drm_encoder *encoder, struct resource *iores, int irq, const struct dw_hdmi_plat_data *plat_data) { ... ## 申請中斷,並傳入中斷處理函式dw_hdmi_hardirq() 和 中斷處理執行緒函式dw_hdmi_irq() ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq, dw_hdmi_irq, IRQF_SHARED, dev_name(dev), hdmi); ... }
在中斷處理函式dw_hdmi_hardirq()
和中斷處理執行緒函式dw_hdmi_irq
中增加dump_stack()
呼叫(注:僅限於除錯驗證)。
插入HDMI
線,系統啟動後,顯示中斷呼叫流程的日誌如下:
[ 7.980327] Exception stack(0xffffffc0796979d0 to 0xffffffc079697b00) [ 8.013527] Hardware name: ROCK PI 4A 2 (DT) [ 8.013911] Call trace: [ 8.014140] [<ffffff80080888d8>] dump_backtrace+0x0/0x220 [ 8.014621] [<ffffff8008088b1c>] show_stack+0x24/0x30 [ 8.015078] [<ffffff800856ebec>] dump_stack+0x98/0xc0 [ 8.015526] [<ffffff80086c1fa8>] dw_hdmi_hardirq+0xf8/0xfc ## 2.中斷處理函式 [ 8.016018] [<ffffff80080f1664>] handle_irq_event_percpu+0xf4/0x1f0 [ 8.016578] [<ffffff80080f17b0>] handle_irq_event+0x50/0x80 [ 8.017071] [<ffffff80080f4fd4>] handle_fasteoi_irq+0xcc/0x134 [ 8.017586] [<ffffff80080f0b60>] generic_handle_irq+0x2c/0x44 [ 8.018100] [<ffffff80080f0edc>] __handle_domain_irq+0xb4/0xb8 [ 8.018615] [<ffffff8008080e70>] gic_handle_irq+0xc8/0x180 [ 8.019104] Exception stack(0xffffffc079697b40 to 0xffffffc079697c70) [ 8.019676] 7b40: 0000000000000001 00000000000068c1 0000000000000000 ffffffc079697c88 [ 8.020373] 7b60: 0000000000000000 0000000000000000 ffffffc077fd3b50 ffffffc077fd3b78 [ 8.021069] 7b80: ffffffc077fd3b80 0000000000000000 ffffff800921ee78 00000000ffffffff [ 8.021766] 7ba0: ffffffc0783ad1d0 ffffff8009d30020 ffffff8009d30020 0000000000000000 [ 8.022462] 7bc0: 0000000000000028 0000000000007fff 0000000000000003 ffffffc077f908e0 [ 8.023159] 7be0: 00000000000068c1 0000000002400040 ffffffc077f908d8 ffffff80093b20f5 [ 8.023855] 7c00: ffffff80093a7740 00000000000068c1 0000000000000001 0000000000000008 [ 8.024552] 7c20: ffffff80092102c8 ffffffc079697c70 ffffff800857402c ffffffc079697c70 [ 8.025249] 7c40: ffffff8008573fe4 0000000020000045 ffffffc079697c70 ffffff80081732d8 [ 8.025944] 7c60: ffffffffffffffff ffffffc079413800 [ 8.026377] [<ffffff80080827b4>] el1_irq+0xb4/0x140 ## 1.異常處理入口 [ 8.026813] [<ffffff8008573fe4>] __radix_tree_lookup+0x70/0xa4 [ 8.027329] [<ffffff80081737dc>] find_get_entry+0x2c/0xbc [ 8.027811] [<ffffff800817394c>] pagecache_get_page+0x54/0x1b8 [ 8.028326] [<ffffff8008483360>] btrfs_test_extent_io+0x8c/0x4b4 [ 8.028863] [<ffffff80091396cc>] init_btrfs_fs+0xe4/0x168 [ 8.029344] [<ffffff80080831cc>] do_one_initcall+0x18c/0x194 [ 8.029848] [<ffffff8009110e10>] kernel_init_freeable+0x228/0x22c [ 8.030385] [<ffffff8008c7519c>] kernel_init+0x18/0x100 [ 8.030854] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
和
[ 8.065185] CPU: 0 PID: 97 Comm: irq/55-ff940000 Not tainted 4.4.154-00037-gdee50b698ce8-dirty #72 [ 8.065978] Hardware name: ROCK PI 4A 2 (DT) [ 8.066364] Call trace: [ 8.066603] [<ffffff80080888d8>] dump_backtrace+0x0/0x220 [ 8.067086] [<ffffff8008088b1c>] show_stack+0x24/0x30 [ 8.067546] [<ffffff800856ebec>] dump_stack+0x98/0xc0 [ 8.068007] [<ffffff80086c59f0>] dw_hdmi_irq+0x1e8/0x228 ## 2. 中斷處理執行緒函式 [ 8.068490] [<ffffff80080f2550>] irq_thread_fn+0x30/0x54 [ 8.068960] [<ffffff80080f28ac>] irq_thread+0x17c/0x1b0 ## 1. irq_thread 執行緒 [ 8.069430] [<ffffff80080ba310>] kthread+0xe0/0xf0 [ 8.069864] [<ffffff8008082ef0>] ret_from_fork+0x10/0x20
- 使用Ubuntu製作ext4檔案系統
- Linux核心__setup()巨集介紹
- Linux核心notifier機制
- Linux Kernel Makefiles編譯標誌
- Linux Kernel Makefiles賦值
- 基於Ubuntu 18.04 安裝perf工具
- Linux核心睡眠喚醒流程
- GDB常用除錯命令(一)
- Linux核心睡眠喚醒狀態
- Linux核心睡眠喚醒除錯
- Linux核心編譯失敗
- Linux DRM那些事-HDMI介面DTS配置
- Linux DRM那些事-HDMI介面EDID獲取
- 顯示技術之HDMI介面介紹
- Linux核心中斷之中斷呼叫流程
- Linux核心中斷之中斷初始化
- Linux核心中斷之中斷申請介面
- Linux核心中斷之獲取中斷號
- Linux核心程式入口地址
- GNU Binutils之objcopy命令