linux 中断流程,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,调用流程如下:

39cf524b3e0f

image.png

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] [] dump_backtrace+0x0/0x220

[ 8.014621] [] show_stack+0x24/0x30

[ 8.015078] [] dump_stack+0x98/0xc0

[ 8.015526] [] dw_hdmi_hardirq+0xf8/0xfc ## 2.中断处理函数

[ 8.016018] [] handle_irq_event_percpu+0xf4/0x1f0

[ 8.016578] [] handle_irq_event+0x50/0x80

[ 8.017071] [] handle_fasteoi_irq+0xcc/0x134

[ 8.017586] [] generic_handle_irq+0x2c/0x44

[ 8.018100] [] __handle_domain_irq+0xb4/0xb8

[ 8.018615] [] 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] [] el1_irq+0xb4/0x140 ## 1.异常处理入口

[ 8.026813] [] __radix_tree_lookup+0x70/0xa4

[ 8.027329] [] find_get_entry+0x2c/0xbc

[ 8.027811] [] pagecache_get_page+0x54/0x1b8

[ 8.028326] [] btrfs_test_extent_io+0x8c/0x4b4

[ 8.028863] [] init_btrfs_fs+0xe4/0x168

[ 8.029344] [] do_one_initcall+0x18c/0x194

[ 8.029848] [] kernel_init_freeable+0x228/0x22c

[ 8.030385] [] kernel_init+0x18/0x100

[ 8.030854] [] 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] [] dump_backtrace+0x0/0x220

[ 8.067086] [] show_stack+0x24/0x30

[ 8.067546] [] dump_stack+0x98/0xc0

[ 8.068007] [] dw_hdmi_irq+0x1e8/0x228 ## 2\. 中断处理线程函数

[ 8.068490] [] irq_thread_fn+0x30/0x54

[ 8.068960] [] irq_thread+0x17c/0x1b0 ## 1\. irq_thread 线程

[ 8.069430] [] kthread+0xe0/0xf0

[ 8.069864] [] ret_from_fork+0x10/0x20

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值