zl程序教程

您现在的位置是:首页 >  系统

当前栏目

linux kernel中的中断处理流程

Linux流程 处理 中断 Kernel
2023-09-27 14:26:31 时间


★★★ 友情链接 : 个人博客导读首页—点击此处 ★★★

1、linux kernel arm32的中断函数处理流程

我们从irq_handler宏函数看起

(1)、irq_handler宏

注意CONFIG_MULTI_IRQ_HANDLER宏表示"允许每台机器在运行时指定它自己的IRQ处理程序",当前默认是不开启的.
所以走else的arch_irq_handler_default逻辑

(arch/arm/kernel/entry-armV.S)
/*
 * Interrupt handling.
 */
	.macro	irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
	ldr	r1, =handle_arch_irq
	mov	r0, sp
	badr	lr, 9997f
	ldr	pc, [r1]
#else
	arch_irq_handler_default
#endif
9997:
	.endm
(2)、arch_irq_handler_default 宏

arch_irq_handler_default 宏 调用了asm_do_IRQ或do_IPI

(arch/arm/include/asr/entry-macro-multi.S)
/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
	.macro	arch_irq_handler_default
	get_irqnr_preamble r6, lr
1:	get_irqnr_and_base r0, r2, r6, lr
	movne	r1, sp
	@
	@ routine called with r0 = irq number, r1 = struct pt_regs *
	@
	badrne	lr, 1b
	bne	asm_do_IRQ

#ifdef CONFIG_SMP
	/*
	 * XXX
	 *
	 * this macro assumes that irqstat (r2) and base (r6) are
	 * preserved from get_irqnr_and_base above
	 */
	ALT_SMP(test_for_ipi r0, r2, r6, lr)
	ALT_UP_B(9997f)
	movne	r1, sp
	badrne	lr, 1b
	bne	do_IPI
#endif
9997:
	.endm
(3)、调用了asm_do_IRQ

asm_do_IRQ()—>handle_IRQ()—>__handle_domain_irq()—>generic_handle_irq()—>调用用户request_irq注册的中断处理函数

void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
	__handle_domain_irq(NULL, irq, false, regs);
}
/*
 * asm_do_IRQ is the interface to be used from assembly code.
 */
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
	handle_IRQ(irq, regs);
}
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
			bool lookup, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);
	unsigned int irq = hwirq;
	int ret = 0;

	irq_enter();

#ifdef CONFIG_IRQ_DOMAIN
	if (lookup)
		irq = irq_find_mapping(domain, hwirq);
#endif

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(!irq || irq >= nr_irqs)) {
		ack_bad_irq(irq);
		ret = -EINVAL;
	} else {
		generic_handle_irq(irq);
	}

	irq_exit();
	set_irq_regs(old_regs);
	return ret;
}
(kernel/irq/irqdesc.c)
int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	if (!desc)
		return -EINVAL;
	generic_handle_irq_desc(desc);
	return 0;
}
EXPORT_SYMBOL_GPL(generic_handle_irq);
(kernel/include/linux/irqdesc.h)
static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
	desc->handle_irq(desc);  // 这里真正调用到,用户request_irq注册的中断处理函数
}

2、linux kernel arm64的中断函数处理流程

我们依然从irq_handler宏函数看起

(1)、irq_handler宏 调用handle_arch_irq
(arch/arm/kernel/entry.S)
/*
 * Interrupt handling.
 */
	.macro	irq_handler
	ldr_l	x1, handle_arch_irq
	mov	x0, sp
	irq_stack_entry
	blr	x1
	irq_stack_exit
	.endm
(2)、handle_arch_irq

handle_arch_irq是在set_handle_irq()设置的handle地址

void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
	if (handle_arch_irq)
		return;

	handle_arch_irq = handle_irq;
}
(3)、gic_handle_irq()

在gic_of_init初始化的时候,将gic_handle_irq()地址赋给了handle_arch_irq

static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
......
	set_handle_irq(gic_handle_irq);
......
}

进入gic中的gic_handle_irq处理函数
在这里会调用handle_domain_irq函数或者handle_IPI函数

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
	u32 irqnr;

	do {
		irqnr = gic_read_iar();

		if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
			int err;

			if (static_key_true(&supports_deactivate))
				gic_write_eoir(irqnr);

			err = handle_domain_irq(gic_data.domain, irqnr, regs);
			if (err) {
				WARN_ONCE(true, "Unexpected interrupt received!\n");
				if (static_key_true(&supports_deactivate)) {
					if (irqnr < 8192)
						gic_write_dir(irqnr);
				} else {
					gic_write_eoir(irqnr);
				}
			}
			continue;
		}
		if (irqnr < 16) {
			gic_write_eoir(irqnr);
			if (static_key_true(&supports_deactivate))
				gic_write_dir(irqnr);
#ifdef CONFIG_SMP
			/*
			 * Unlike GICv2, we don't need an smp_rmb() here.
			 * The control dependency from gic_read_iar to
			 * the ISB in gic_write_eoir is enough to ensure
			 * that any shared data read by handle_IPI will
			 * be read after the ACK.
			 */
			handle_IPI(irqnr, regs);
#else
			WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
			continue;
		}
	} while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}
(4)、handle_domain_irq()---->__handle_domain_irq()---->generic_handle_irq()---->generic_handle_irq_desc()
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
			bool lookup, struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);
	unsigned int irq = hwirq;
	int ret = 0;

	irq_enter();

#ifdef CONFIG_IRQ_DOMAIN
	if (lookup)
		irq = irq_find_mapping(domain, hwirq);
#endif

	/*
	 * Some hardware gives randomly wrong interrupts.  Rather
	 * than crashing, do something sensible.
	 */
	if (unlikely(!irq || irq >= nr_irqs)) {
		ack_bad_irq(irq);
		ret = -EINVAL;
	} else {
		generic_handle_irq(irq);
	}

	irq_exit();
	set_irq_regs(old_regs);
	return ret;
}
int generic_handle_irq(unsigned int irq)
{
	struct irq_desc *desc = irq_to_desc(irq);

	if (!desc)
		return -EINVAL;
	generic_handle_irq_desc(desc);
	return 0;
}
EXPORT_SYMBOL_GPL(generic_handle_irq);

static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
	desc->handle_irq(desc);
}