这里主要讲3点,1. 中断的初始化详细过程;2. request_irq、request_threaded_irq等中断函数的注册;3. 硬件中断产生时从中断向量入口开始的处理流程。写博客不易,如若转载,请注明出处。
一、中断控制器的初始化详细过程
从main.c (init) 中的 asmlinkage __visible void __init start_kernel(void)开始,看函数init_IRQ();该函数即中断控制器的初始化入口。
asmlinkage __visible void __init start_kernel(void)
{
。。。
setup_arch(&command_line);
。。。
/* init some links before init_ISA_irqs() */
early_irq_init();
init_IRQ();
。。。
/* Do the rest non-__init'ed, we're now alive */
rest_init();
}
void __init init_IRQ(void)
{
int ret;
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init(); -------进入这个函数
else
machine_desc->init_irq();
if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
(machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
outer_cache.write_sec = machine_desc->l2c_write_sec;
ret = l2x0_of_init(machine_desc->l2c_aux_val,
machine_desc->l2c_aux_mask);
if (ret)
pr_err("L2C: failed to init: %d\n", ret);
}
}
这里假设有设备树,machine_desc->init_irq未经初始化。进入irqchip_init():
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
}
__irqchip_of_table是哪来的呢? 见 Irq-gic.c (drivers\irqchip) 的最底部:
IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
IRQCHIP_DECLARE的定义如下:
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
#define OF_DECLARE_2(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
#ifdef CONFIG_OF
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#else
。。。。。
#endif
# define __section(S) __attribute__ ((__section__(#S)))
接着去看 vmlinux.lds (arch\arm\kernel),关键字搜索:_irqchip_of_table,可以看到里面有这么一段:
__irqchip_of_table = .; *(__irqchip_of_table) *(__irqchip_of_table_end) . = ALIGN(8);
至此,就知道了__irqchip_of_table存的是一个struct of_device_id的数组,里面放着中断控制器驱动的匹配字段(compat)和初始化函数入口(fn)。
回到void __init irqchip_init(void),进入函数of_irq_init: 【void __init of_irq_init(const struct of_device_id *matches)】
void __init of_irq_init(const struct of_device_id *matches)
{
struct device_node *np, *parent = NULL;
struct intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);
for_each_matching_node(np, matches) { ----找出匹配irq-gic驱动的中断控制器结点,并指明自己的父中断控制器结点,将自己结点加入intc_desc_list
if (!of_find_property(np, "interrupt-controller", NULL) ||
!of_device_is_available(np))
continue;
/*
* Here, we allocate and populate an intc_desc with the node
* pointer, interrupt-parent device_node etc.
*/
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (WARN_ON(!desc))
goto err;
desc->dev = np;
desc->interrupt_parent = of_irq_find_parent(np);
if (desc->interrupt_parent == np)
desc->interrupt_parent = NULL;
list_add_tail(&desc->list, &intc_desc_list);
}
/*
* The root irq controller is the one without an interrupt-parent.
* That one goes first, followed by the controllers that reference it,
* followed by the ones that reference the 2nd level controllers, etc.
*/
while (!list_empty(&intc_desc_list)) { ----通过建立的中断控制器树,从root结点开始执行irq_init_cb,接着是level1、level2、level3。。。
/*
* Process all controllers with the current 'parent'.
* First pass will be looking for NULL as the parent.
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
const struct of_device_id *match;
int ret;
of_irq_init_cb_t irq_init_cb;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
match = of_match_node(matches, desc->dev);
if (WARN(!match->data,
"of_irq_init: no init function for %s\n",
match->compatible)) {
kfree(desc);
continue;
}
pr_debug("of_irq_init: init %s @ %p, parent %p\n",
match->compatible,
desc->dev, desc->interrupt_parent);
irq_init_cb = (of_irq_init_cb_t)match->data; ----这里是IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);中的gic_of_init函数
ret = irq_init_cb(desc->dev, desc->interrupt_parent);
if (ret) {
kfree(desc);
continue;
}
/*
* This one is now set up; add it to the parent list so
* its children can get processed in a subsequent pass.
*/
list_add_tail(&desc->list, &intc_parent_list);
}
/* Get the next pending parent that might have children */
desc = list_first_entry_or_null(&intc_parent_list,
typeof(*desc), list);
if (!desc) {
pr_err("of_irq_init: children remain, but no parents\n");
break;
}
list_del(&desc->list);
parent = desc->dev;
kfree(desc);
}
list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
list_del(&desc->list);
kfree(desc);
}
。。。
}
以上初始化,从root中断控制器结点开始执行irq_init_cb,接着是level1 controller1、level1 controller2、level1 controller3、level2 controller1、level2 controller2、level2 controller3、level2 controller4、level2 controller5、level3。。。
进入gic中断控制器的初始化函数gic_of_init:
of_iomap是用来获取设备树资源并做ioremap,返回ioremap得到的虚拟地址,用于设备资源的访问。这里gic_cnt=0,percpu_offset=0,继续走进函数 gic_init_bases:
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node *node)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data *gic;
int gic_irqs, irq_base, i;
int nr_routable_irqs;
BUG_ON(gic_nr >= MAX_GIC_NR);
gic = &gic_data[gic_nr];
。。。
{ /* Normal, sane GIC... */
WARN(percpu_offset,
"GIC_NON_BANKED not enabled, ignoring %08x offset!",
percpu_offset);
gic->dist_base.common_base = dist_base;
gic->cpu_base.common_base = cpu_base;
gic_set_base_accessor(gic, gic_get_common_base);
}
/*
* Initialize the CPU interface map to all CPUs.
* It will be refined as each CPU probes its ID.
*/
for (i = 0; i < NR_GIC_CPU_IF; i++)
gic_cpu_map[i] = 0xff;
/*
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too.
*/
if (gic_nr == 0 && (irq_start & 31) > 0) {
hwirq_base = 16;
if (irq_start != -1)
irq_start = (irq_start & ~31) + 16;
} else {
hwirq_base = 32;
}
/*
* Find out how many interrupts are supported.
* The GIC only supports up to 1020 interrupt sources.
*/
gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
gic_irqs = (gic_irqs + 1) * 32;
if (gic_irqs > 1020)
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;
gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
if (of_property_read_u32(node, "arm,routable-irqs",
&nr_routable_irqs)) {
irq_base = irq_alloc_descs(irq_start, 16, gic_irqs,
numa_node_id()); ----用于分配数量为gic_irqs的struct irq_desc数据结构,返回request_irq可用的irq base地址
if (IS_ERR_VALUE(irq_base)) {
WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
irq_start);
irq_base = irq_start;
}
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic); ----进入这个函数,gic_irqs是该中断控制器支持的中断数量;irq_base ~ irq_base+gic_irqs-1 是request_irq(irq, 。。。)时对应的中断号;hwirq_base ~ hwirq_base+gic_irqs-1 是中断控制器连接外设中断线定义的映射关系
} else {
gic->domain = irq_domain_add_linear(node, nr_routable_irqs,
&gic_irq_domain_ops,
gic);
}
if (WARN_ON(!gic->domain))
return;
if (gic_nr == 0) {
#ifdef CONFIG_SMP
set_smp_cross_call(gic_raise_softirq);
register_cpu_notifier(&gic_cpu_notifier);
#endif
set_handle_irq(gic_handle_irq);
}
gic_chip.flags |= gic_arch_extn.flags;
gic_dist_init(gic);
gic_cpu_init(gic);
gic_pm_init(gic);
}
irq_domain_add_legacy(node, gic_irqs, irq_base, hwirq_base, &gic_irq_domain_ops, gic); 这个函数,gic_irqs是该中断控制器支持的中断数量;irq_base ~ irq_base+gic_irqs-1 是request_irq(irq, 。。。)时对应的中断号;hwirq_base ~ hwirq_base+gic_irqs-1 是中断控制器连接外设中断线定义的映射关系【至于为什么hwirq_base从16开始,可百度arm控制器 GIC的SGI/PPI/SPI相关内容,0~15用于内部命令下发出发的中断,即通过SGI产生的中断】。这里绑定了irq domain的操作方法gic_irq_domain_ops。该函数中做了irq_base ~ irq_base+gic_irqs-1 和 hwirq_base ~ hwirq_base+gic_irqs-1之间的映射关系。
关于gic_irq_domain_ops,初始化如下:
static const struct irq_domain_ops gic_irq_domain_ops = {
.map = gic_irq_domain_map,
.unmap = gic_irq_domain_unmap,
.xlate = gic_irq_domain_xlate,
};
主要函数gic_irq_domain_map:
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
irq_hw_number_t hw)
{
if (hw < 32) {
irq_set_percpu_devid(irq);
irq_set_chip_and_handler(irq, &gic_chip,
handle_percpu_devid_irq);
set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
} else {
irq_set_chip_and_handler(irq, &gic_chip,
handle_fasteoi_irq); -----handle_fasteoi_irq这个函数将会在中断产生时执行gic_handle_irq调用到,各个irq对应的struct irq_desc里的每个handle_irq成员都初始化为handle_fasteoi_irq(从irq_set_chip_and_handler的内部调用情况可以找到)。是一个重要函数
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
gic_routable_irq_domain_ops->map(d, irq, hw);
}
irq_set_chip_data(irq, d->host_data);
return 0;
}
回过头来,看看irq_domain_add_legacy函数的定义,如下:
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
irq_hw_number_t first_hwirq,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *domain;
domain = __irq_domain_add(of_node, first_hwirq + size,
first_hwirq + size, 0, ops, host_data); ----这里进行irq domain的初始化和添加
if (!domain)
return NULL;
irq_domain_associate_many(domain, first_irq, first_hwirq, size);
return domain;
}
继续看函数irq_domain_associate_many-->irq_domain_associate:
void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
irq_hw_number_t hwirq_base, int count)
{
int i;
pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
for (i = 0; i < count; i++) {
irq_domain_associate(domain, irq_base + i, hwirq_base + i);
}
}
int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
irq_hw_number_t hwirq)
{
。。。
if (domain->ops->map) {
ret = domain->ops->map(domain, virq, hwirq);
。。。
}
/* irq 和 hwirq 关系的映射,就是在这里进行的。有2种方法,一种线性映射,另一种用红黑树关联 */
if (hwirq < domain->revmap_size) {
domain->linear_revmap[hwirq] = virq;
} else {
mutex_lock(&revmap_trees_mutex);
radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
mutex_unlock(&revmap_trees_mutex);
}
。。。
return 0;
}
irq和hwirq的关系映射,就是在irq_domain_associate函数中进行的。中断执行时,通过获取中断控制器的寄存器信息获知产生中断的hwirq,再由映射关系获取到irq,最终执行request_irq的irq绑定的irq_handler。
二、request_irq、request_threaded_irq等中断函数的注册
request_threaded_irq、request_irq中主要做一件事:分配struct irqaction及初始化(赋值irq、handler、thread_fun等),并挂载到struct desc的desc->action下。struct irqaction中handler、thread_fun,正是中断处理时由struct desc struct desc-->handle_irq回调到的函数。request_irq、request_threaded_irq函数如下:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
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;
。。。
desc = irq_to_desc(irq);
。。。
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;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc);
if (retval)
kfree(action);
。。。
return retval;
}
request_threaded_irq、request_irq中主要做一件事:分配struct irqaction并初始化,通过__setup_irq挂载到struct desc的desc->action下。挂载情况,见__setup_irq函数:
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
struct irqaction *old, **old_ptr;
unsigned long flags, thread_mask = 0;
int ret, nested, shared = 0;
cpumask_var_t mask;
。。。
old_ptr = &desc->action;
old = *old_ptr;
if (old) {
。。。
do {
/*
* Or all existing action->thread_mask bits,
* so we can find the next zero bit for this
* new action.
*/
thread_mask |= old->thread_mask;
old_ptr = &old->next;
old = *old_ptr;
} while (old);
shared = 1;
}
。。。
new->irq = irq;
*old_ptr = new;
。。。
return 0;
。。。
}
三、硬件中断产生时从中断向量入口开始的处理流程
这里就以arm为例,中断向量入口在 entry-armv.S (arch\arm\kernel),当有中断产生时,CPU的PC指针跳转的中断表如下:
__vectors_start:
W(b) vector_rst
W(b) vector_und
W(ldr) pc, __vectors_start + 0x1000
W(b) vector_pabt
W(b) vector_dabt
W(b) vector_addrexcptn
W(b) vector_irq
W(b) vector_fiq
.data
.globl cr_alignment
cr_alignment:
.space 4
W(b)宏展开实际上就是 b 或 b.w 汇编指令,可以通过nm vmlinux查到__vectors_start的地址:
$ nm vmlinux|grep __vectors_start
00000000 t __vectors_start
当外部中断产生时,PC指针自动跳转到“W(b) vector_irq”行,执行命令 b vector_irq。vector_irq找半天也找不到,可以看本entry-armv.S中的宏定义:
vector_stub irq, IRQ_MODE, 4
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
@
@ Save r0, lr_ (parent PC) and spsr_
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
ENDPROC(vector_\name)
vector_irq即等同于以上的 vector_\name处的入口。
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
以上汇编执行结束后,将使pc指向下面的 .long __irq_usr(中断产生时处于用户态),或者 .long __irq_svc(中断产生时处于内核态),跳转到 __irq_usr(中断产生时处于用户态),或者 __irq_svc 入口。
vector_rst:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
b vector_und
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
__irq_usr、__irq_svc入口如下:
.align 5
__irq_usr:
usr_entry
kuser_cmpxchg_check
irq_handler
get_thread_info tsk
mov why, #0
b ret_to_user_from_irq
UNWIND(.fnend )
ENDPROC(__irq_usr)
.align 5
__irq_svc:
svc_entry
irq_handler
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
ldr r0, [tsk, #TI_FLAGS] @ get flags
teq r8, #0 @ if preempt count != 0
movne r0, #0 @ force flags to 0
tst r0, #_TIF_NEED_RESCHED
blne svc_preempt
#endif
svc_exit r5, irq = 1 @ return from exception
UNWIND(.fnend )
ENDPROC(__irq_svc)
执行irq_handler,入口如下:
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr r1, =handle_arch_irq
mov r0, sp
adr lr, BSYM(9997f)
ldr pc, [r1]
#else
arch_irq_handler_default
#endif
9997:
.endm
这里pc = [r1], r1 = handle_arch_irq。handle_arch_irq出处如下:
__vectors_start:
W(b) vector_rst
W(b) vector_und
W(ldr) pc, __vectors_start + 0x1000
W(b) vector_pabt
W(b) vector_dabt
W(b) vector_addrexcptn
W(b) vector_irq
W(b) vector_fiq
.data
.globl cr_alignment
cr_alignment:
.space 4
#ifdef CONFIG_MULTI_IRQ_HANDLER
.globl handle_arch_irq -----在这里
handle_arch_irq:
.space 4
#endif
handle_arch_irq只留出了4个字节的空白空间,该空间由实际的中断处理函数地址填入。见中断控制器的初始化详细过程start_kernel-->init_IRQ-->irqchip_init-->of_irq_init(__irqchip_of_table) -->irq_init_cb = (of_irq_init_cb_t)match->data;ret = irq_init_cb(desc->dev, desc->interrupt_parent);这里irq_init_cb执行的是irq-gic.c (drivers\irqchip)里的gic_of_init --> gic_init_bases -->set_handle_irq(gic_handle_irq); set_handle_irq函数实现如下:
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
if (handle_arch_irq)
return;
handle_arch_irq = handle_irq;
}
由此,handle_arch_irq被填入处理函数:gic_handle_irq。
gic_handle_irq函数实现如下:
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); ----读取INTACK中断控制器寄存器,获知hwirq,即可知道哪个外设产生的中断
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr); ----这里参数传入的irqnr是hwirq,通过该函数映射为request_irq()的irq号
handle_IRQ(irqnr, regs); ----外部设备中断进入这里
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
}
这里有个重要的步骤,通过irqnr = irq_find_mapping(gic->domain, irqnr); 将传入的irqnr(hwirq),转换为request_irq()用的irq号返回。
接下来进入handle_IRQ:
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq); ----进入这里
}
irq_exit();
set_irq_regs(old_regs);
}
进入generic_handle_irq:
int generic_handle_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
generic_handle_irq_desc(irq, desc);
return 0;
}
进入generic_handle_irq_desc:
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}
desc->handle_irq(irq, desc);回调的是中断控制器初始化过程中的handle_fasteoi_irq。具体见gic_init_bases-->irq_domain_add_legacy(node, gic_irqs, irq_base, hwirq_base, &gic_irq_domain_ops, gic);的gic_irq_domain_ops --> .map = gic_irq_domain_map --> irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq);
handle_fasteoi_irq的实现如下:
void
handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
{
struct irq_chip *chip = desc->irq_data.chip;
raw_spin_lock(&desc->lock);
。。。
handle_irq_event(desc);
。。。
raw_spin_unlock(&desc->lock);
return;
。。。
}
进入handle_irq_event:
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
struct irqaction *action = desc->action;
irqreturn_t ret;
。。。
ret = handle_irq_event_percpu(desc, action);
。。。
return ret;
}
进入handle_irq_event_percpu:
irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
irqreturn_t retval = IRQ_NONE;
unsigned int flags = 0, irq = desc->irq_data.irq;
do {
irqreturn_t res;
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id); ----这里调用的就是request_irq、request_threaded_irq注册的handler
trace_irq_handler_exit(irq, action, res);
if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
irq, action->handler))
local_irq_disable();
switch (res) {
case IRQ_WAKE_THREAD:
/*
* Catch drivers which return WAKE_THREAD but
* did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq, action);
break;
}
__irq_wake_thread(desc, action); ----这里调用的就是用来唤醒request_threaded_irq注册的内核线程,执行thread_fn
/* Fall through to add to randomness */
case IRQ_HANDLED:
flags |= action->flags;
break;
default:
break;
}
retval |= res;
action = action->next;
} while (action);
add_interrupt_randomness(irq, flags);
if (!noirqdebug)
note_interrupt(irq, desc, retval);
return retval;
}
action->handler(irq, action->dev_id); 执行的就是request_irq、request_threaded_irq注册的handler;
__irq_wake_thread(desc, action); 唤醒request_threaded_irq注册的内核线程,执行thread_fn。
至此,硬件中断产生时从中断向量入口开始的处理流程分析结束。