第三十四期-ARM Linux内核的中断(4)

作者:罗宇哲,中国科学院软件研究所智能软件研究中心

上一期中我们介绍了ARM Linux内核中外设中断处理的部分流程,这一期我们将继续介绍ARM Linux内核的外设中断处理流程中与中断描述符相关的部分。

一、ARM Linux内核的中断描述符

上一期我们提到ARM Linux内核处理外设中断时会通过调用Linux中断号对应的中断描述符里的handle_irq()函数来处理相关中断。中断描述符在ARM Linux中由结构体irq_desc来表示,其代码在openEuler源码仓库的/openeuler/kernel/blob/kernel-4.19/include/linux/irqdesc.h文件中可以找到:

struct irq_desc {

……

irq_flow_handler_t handle_irq;

……

struct irqaction *action; /* IRQ action list */

……

} ____cacheline_internodealigned_in_smp;

该结构体中定义了两层中断处理函数[1]:

  • 第一层中断处理函数是handle_irq,irq_flow_handler_t是一个函数指针;
  • 第二层中断处理函数保存在结构体irqaction组成的链表里,每一个链表元素都保存了一个设备驱动注册的中断处理函数。因为多个硬件设备可能共享同一个硬件中断号,所以irqaction组成的链表里可能有多个元素。

irq_flow_handler_t的定义代码可以在openEuler源码仓库的/openeuler/kernel/blob/kernel-4.19/include/linux/irqhandler.h文件中可以找到:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xnymW0im-1590748034937)(media/6413b47c3fa0c1278858b12aa1c3f8ad.png)]
该函数指针指向的函数输入为指向结构体irq_desc的指针类型,输出为void类型。我们在openEuler源码仓库的/openeuler/kernel/blob/kernel-4.19/include/linux/irq.h文件中可以找到多个符合irq_flow_handler_t类型的替换函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kBkbYWDQ-1590748034946)(media/3981de40916abf39605ea03b40f2597e.png)]
当硬件中断号小于16时中断为软件产生的中断(SGI),中断处理程序调用handle_IPI()函数处理;当硬件中断号大于或等于16而小于32时中断为私有外设中断(PPI),handle_irq()函数被设置为handle_percpu_devid_irq();当硬件中断号大于等于32但小于gic_data.irq_nr(该值在irq-gic-v3.c文件中的gic_init_bases()函数中初始化,其最大值为1020,表示GICv3最多支持1020个中断源(SGI+PPI+SPI))时中断为共享外设中断,handle_irq()被设置为handle_fasteoi_irq();当硬件中断号大于等于8192但小于GIC_ID_NR时中断为局部特定外设中断(LPI),handle_irq()被设置为handle_fasteoi_irq()。有关中断类型的知识可以在第二十八期找到。设置handle_irq()的代码可以在openEuler源码仓库的/openeuler/kernel/blob/kernel-4.19/drivers/irqchip/irq-gic-v3.c文件中可以找到:

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,

irq_hw_number_t hw)

{

struct irq_chip *chip = &gic_chip;

if (static_branch_likely(&supports_deactivate_key))

chip = &gic_eoimode1_chip;

/* SGIs are private to the core kernel */

if (hw < 16)

return -EPERM;

/* Nothing here */

if (hw >= gic_data.irq_nr && hw < 8192)

return -EPERM;

/* Off limits */

if (hw >= GIC_ID_NR)

return -EPERM;

/* PPIs */

if (hw < 32) {

irq_set_percpu_devid(irq);

irq_domain_set_info(d, irq, hw, chip, d->host_data,

handle_percpu_devid_irq, NULL, NULL);

irq_set_status_flags(irq, IRQ_NOAUTOEN);

}

/* SPIs */

if (hw >= 32 && hw < gic_data.irq_nr) {

irq_domain_set_info(d, irq, hw, chip, d->host_data,

handle_fasteoi_irq, NULL, NULL);

irq_set_probe(irq);

irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));

}

/* LPIs */

if (hw >= 8192 && hw < GIC_ID_NR) {

if (!gic_dist_supports_lpis())

return -EPERM;

irq_domain_set_info(d, irq, hw, chip, d->host_data,

handle_fasteoi_irq, NULL, NULL);

}

return 0;

}

handle_percpu_devid_irq()和handle_fasteoi_irq()两个函数最终都会调用中断描述符中irqaction结构体里面的handler()函数(action->handler())从而调用中断处理函数。其中handle_fasteoi_irq()->handle_irq_event()->__handle_irq_event_percpu()还会遍历中断处理描述符action的链表,调用所有中断处理描述符里的中断处理函数handler(),__handle_irq_event_percpu()的代码可以在openEuler源码仓库的/openeuler/kernel/blob/kernel-4.19/kernel/irq/handle.c文件中可以找到:

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)

{

……

struct irqaction *action;

……

for_each_action_of_desc(desc, action) {

……

res = action->handler(irq, action->dev_id);

……

}

return retval;

}

irqaction结构体的代码在openEuler源码仓库的/openeuler/kernel/blob/kernel-4.19/include/linux/interrupt.h文件中可以找到:

/**

* struct irqaction - per interrupt action descriptor

* @handler: interrupt handler function

* @name: name of the device

* @dev_id: cookie to identify the device

* @percpu_dev_id: cookie to identify the device

* @next: pointer to the next irqaction for shared interrupts

* @irq: interrupt number

* @flags: flags (see IRQF_* above)

* @thread_fn: interrupt handler function for threaded interrupts

* @thread: thread pointer for threaded interrupts

* @secondary: pointer to secondary irqaction (force threading)

* @thread_flags: flags related to @thread

* @thread_mask: bitmask for keeping track of @thread activity

* @dir: pointer to the proc/irq/NN/name entry

*/

struct irqaction {

irq_handler_t handler;

void *dev_id;

void __percpu *percpu_dev_id;

struct irqaction *next;

irq_handler_t thread_fn;

struct task_struct *thread;

struct irqaction *secondary;

unsigned int irq;

unsigned int flags;

unsigned long thread_flags;

unsigned long thread_mask;

const char *name;

struct proc_dir_entry *dir;

} ____cacheline_internodealigned_in_smp;

该结构体通过next指针构成了链表,从而可以保存多个设备驱动注册的处理函数并使多个设备共享同一个硬件中断号。中断中驱动有关的知识我们将在设备驱动一章详细介绍。

二、结语

本期我们介绍了中断描述符及与其相关的重要函数,下一期我们将介绍软件产生的中断(SGI)的处理流程。

参考文献
[1] 《Linux内核深度解析》,余华兵著,2019

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值