![ebd506f51bc3af6d88e774ae0bba17ba.png](https://img-blog.csdnimg.cn/img_convert/ebd506f51bc3af6d88e774ae0bba17ba.png)
作者:罗宇哲,中国科学院软件研究所智能软件研究中心
上一期中我们介绍了ARM Linux内核中SGI的处理流程,这一期我们将介绍ARM Linux内核的与中断相关的常用函数。
一、ARM Linux内核中与中断相关的常用函数
在ARM Linux内核源码中,常见的与中断有关的函数包括中断处理程序注册函数、中断释放函数、开中断函数和关中断函数。
中断处理程序注册函数用于注册设备所用的中断处理函数,主要有request_irq()和request_threaded_irq()。request_irq()是通过调用request_threaded_irq()实现的,其源代码可以在openEuler源码仓库中的openeuler/kernel/blob/kernel-4.19/include/linux/interrupt.h文件中找到:
![73bc10b683493f6d4afecaebd9676478.png](https://img-blog.csdnimg.cn/img_convert/73bc10b683493f6d4afecaebd9676478.png)
它将request_threaded_irq()的thread_fn参数设为NULL,表示不使用中断线程化。中断线程化是使用内核线程来处理中断,可以减少系统关中断的时间,增加系统的实时性[1]。函数request_threaded_irq()用于注册线程化的中断,其代码可以在openEuler源码仓库的openeuler/kernel/blob/kernel-4.19/kernel/irq/manage.c文件中找到:
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
if (irq == IRQ_NOTCONNECTED)
return -ENOTCONN;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*
* Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
* it cannot be set along with IRQF_NO_SUSPEND.
*/
if (((irqflags & IRQF_SHARED) && !dev_id) ||
(!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
retval = irq_chip_pm_get(&desc->irq_data);
if (retval < 0) {
kfree(action);
return retval;
}
retval = __setup_irq(irq, desc, action);
if (retval) {
irq_chip_pm_put(&desc->irq_data);
kfree(action->secondary);
kfree(action);
}
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (irqflags & IRQF_SHARED)) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
该函数参数列表中各参数的意义为:
- irq:Linux中断号;
- handler:当IRQ发生时被调用的函数;
- thread_fn:IRQ处理线程调用的函数,如果是NULL就不创建线程;
- irqflags:中断类型标志符;
- devname:设备的ascii名称;
- dev_id:传递给handler函数的cookie。
该函数分配中断资源并使能中断处理。当handler参数为NULL时,若thread_fn参数不为NULL,则将handler参数设置为irq_default_primary_handler。该函数设置了irqaction结构体的参数,通过调用__setup_irq()->setup_irq_thread()函数设置处理中断的线程的优先级和调度策略,然后在关中断的情况下调用中断处理函数handler()。如果irqaction结构体重被注册的中断处理函数执行后返回的是IRQ_WAKE_THREAD那么将唤醒中断处理线程并运行thread_fn。
在上面的函数中包含了关中断的函数(local_irq_disable()和local_irq_save())和开中断的函数(local_irq_enable()和local_irq_restore())。local_irq_save(flags)函数先把中断状态保存在其参数flags中然后禁止中断,local_irq_restore()则恢复本处理器的中断状态[1]。local_irq_disable()最终调用arch_local_irq_disable()函数,arch_local_irq_disable()函数通过汇编指令设置DAIF标志位来屏蔽中断;而local_irq_enable()函数最终调用arch_local_irq_enable()函数,arch_local_irq_enable()函数通过汇编指令清除DAIF标志位来使能中断。arch_local_irq_disable()函数和arch_local_irq_enable()函数在openEuler源码仓库的openeuler/kernel/blob/kernel-4.19/arch/arm64/include/asm/irqflags.h文件中可以找到:
![d802250eff20b53c8fab101fb80d90d3.png](https://img-blog.csdnimg.cn/img_convert/d802250eff20b53c8fab101fb80d90d3.png)
![32f69a50897f9573521c7bafab930fa7.png](https://img-blog.csdnimg.cn/img_convert/32f69a50897f9573521c7bafab930fa7.png)
最后我们介绍一下中断释放函数free_irq(),该函数用于移除被注册的中断处理函数。如果Linux中断号对应的中断不再被任何设备使用那么该中断被disable。该函数有两个参数:
- irq: 待释放的中断的Linux中断号;
- dev_id:待释放的设备标识。
该函数的代码可以在openEuler源码仓库的openeuler/kernel/blob/kernel-4.19/kernel/irq/manage.c文件中找到:
![b95a473ad26c27f5aab710fc48556b6f.png](https://img-blog.csdnimg.cn/img_convert/b95a473ad26c27f5aab710fc48556b6f.png)
二、结语
本期我们介绍了与中断相关的几个常用函数,下一期我们将介绍ARM Linux内核中的底半机制。
参考文献
[1] 《Linux内核深度解析》,余华兵著,2019