Linux中断子系统框架流程详解(基于Kernel 3.16,arm,设备树)

这里主要讲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。。。

Linux中断子系统框架流程详解(基于Kernel 3.16,arm,设备树)_第1张图片

进入gic中断控制器的初始化函数gic_of_init:

Linux中断子系统框架流程详解(基于Kernel 3.16,arm,设备树)_第2张图片

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。

至此,硬件中断产生时从中断向量入口开始的处理流程分析结束。

你可能感兴趣的:(Linux中断子系统框架详解)