中断产生流程

发布时间:2024年01月06日

?中断产生流程

中断向量表

entry.S (arch\arm64\kernel)

ENTRY(vectors)
?? ?kernel_ventry?? ?1, sync_invalid?? ??? ??? ?// Synchronous EL1t
?? ?kernel_ventry?? ?1, irq_invalid?? ??? ??? ?// IRQ EL1t
?? ?kernel_ventry?? ?1, fiq_invalid?? ??? ??? ?// FIQ EL1t
?? ?kernel_ventry?? ?1, error_invalid?? ??? ?// Error EL1t

?? ?kernel_ventry?? ?1, sync?? ??? ??? ??? ?// Synchronous EL1h
?? ?kernel_ventry?? ?1, irq?? ??? ??? ??? ?// IRQ EL1h
?? ?kernel_ventry?? ?1, fiq_invalid?? ??? ??? ?// FIQ EL1h
?? ?kernel_ventry?? ?1, error_invalid?? ??? ?// Error EL1h

?? ?kernel_ventry?? ?0, sync?? ??? ??? ??? ?// Synchronous 64-bit EL0
?? ?kernel_ventry?? ?0, irq?? ??? ??? ??? ?// IRQ 64-bit EL0
?? ?kernel_ventry?? ?0, fiq_invalid?? ??? ??? ?// FIQ 64-bit EL0
?? ?kernel_ventry?? ?0, error_invalid?? ??? ?// Error 64-bit EL0

#ifdef CONFIG_COMPAT
?? ?kernel_ventry?? ?0, sync_compat, 32?? ??? ?// Synchronous 32-bit EL0
?? ?kernel_ventry?? ?0, irq_compat, 32?? ??? ?// IRQ 32-bit EL0
?? ?kernel_ventry?? ?0, fiq_invalid_compat, 32?? ?// FIQ 32-bit EL0
?? ?kernel_ventry?? ?0, error_invalid_compat, 32?? ?// Error 32-bit EL0
#else
?? ?kernel_ventry?? ?0, sync_invalid, 32?? ??? ?// Synchronous 32-bit EL0
?? ?kernel_ventry?? ?0, irq_invalid, 32?? ??? ?// IRQ 32-bit EL0
?? ?kernel_ventry?? ?0, fiq_invalid, 32?? ??? ?// FIQ 32-bit EL0
?? ?kernel_ventry?? ?0, error_invalid, 32?? ??? ?// Error 32-bit EL0
#endif
END(vectors)

以el1_irq为例

el1_irq:
?? ?kernel_entry 1
?? ?enable_dbg
#ifdef CONFIG_TRACE_IRQFLAGS
?? ?bl?? ?trace_hardirqs_off
#endif

?? ?irq_handler

#ifdef CONFIG_PREEMPT
?? ?ldr?? ?w24, [tsk, #TSK_TI_PREEMPT]?? ?// get preempt count
?? ?cbnz?? ?w24, 1f?? ??? ??? ??? ?// preempt count != 0
?? ?ldr?? ?x0, [tsk, #TSK_TI_FLAGS]?? ?// get flags
?? ?tbz?? ?x0, #TIF_NEED_RESCHED, 1f?? ?// needs rescheduling?
?? ?bl?? ?el1_preempt
1:
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
?? ?bl?? ?trace_hardirqs_on
#endif
?? ?kernel_exit 1
ENDPROC(el1_irq)

从上kernel_entry 保存现场,?irq_handler是中断处理函数,kernel_exit是恢复现场

中间CONFIG_PREEMPT宏包含一段抢占调度的代码,下面介绍一下irq_handler函数

?? ?.macro?? ?irq_handler
?? ?ldr_l?? ?x1, handle_arch_irq
?? ?mov?? ?x0, sp
?? ?irq_stack_entry? //栈指针切换,切换到中断栈空间
?? ?blr?? ?x1? ? ? ? ? ? ? //跳到handle_arch_irq函数执行
?? ?irq_stack_exit? //栈指针切换,退出中断栈空间
?? ?.endm

handle_arch_irq是通过set_handle_irq赋值的

路径:entry.S (kernel4.14\arch\arm64\kernel)

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

?? ?handle_arch_irq = handle_irq;
}

此set_handle_irq函数是在gic_init_bases函数中调用的?set_handle_irq(gic_handle_irq);

gic_handle_irq函数路径:Irq-gic-v3.c (kernel4.14\drivers\irqchip)

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);
?? ??? ??? ?else
?? ??? ??? ??? ?isb();

?? ??? ??? ?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);
}

static inline int handle_domain_irq(struct irq_domain *domain,
?? ??? ??? ??? ? ? ?unsigned int hwirq, struct pt_regs *regs)
{
?? ?return __handle_domain_irq(domain, hwirq, true, 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;
}‘

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(desc);
?? ?return 0;
}

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

desc->handle_irq通过gic_irq_domain_map初始化的,根据不同的中断号走不同的路径

gic_irq_domain_map’如下:

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_key_true(&supports_deactivate))
?? ??? ?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 *///PPI(Private Peripheral Interrupts)是指专用私有中断,通常用于CPU本地时钟等应用场景
?? ?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 */ //共享中断源允许多个处理器共享同一个中断源。SPI 中断主要用于处理外部设备引发的中断事件,如网络接口卡、硬盘控制器、串口控制器等外设的中断。
?? ?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);
?? ?}
?? ?/* LPIs *///特定于位置的外设中断(lpi)总是基于消息的,可以来自外设,也可以来自PCle根复核。
?? ?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;
}

?

通过gic_irq_domain_map函数可以看出desc->handle_irq(desc);是handle_percpu_devid_irq和handle_fasteoi_irq二选一

handle_percpu_devid_irq如下:

void handle_percpu_devid_irq(struct irq_desc *desc)
{
?? ?struct irq_chip *chip = irq_desc_get_chip(desc);
?? ?struct irqaction *action = desc->action;
?? ?unsigned int irq = irq_desc_get_irq(desc);
?? ?irqreturn_t res;

?? ?/*
?? ? * PER CPU interrupts are not serialized. Do not touch
?? ? * desc->tot_count.
?? ? */
?? ?__kstat_incr_irqs_this_cpu(desc);

?? ?if (chip->irq_ack)
?? ??? ?chip->irq_ack(&desc->irq_data);

?? ?if (likely(action)) {
?? ??? ?trace_irq_handler_entry(irq, action);
?? ??? ?res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));//此处调用的就是注册中断时传递的中断处理函数
?? ??? ?trace_irq_handler_exit(irq, action, res);
?? ?} else {
?? ??? ?unsigned int cpu = smp_processor_id();
?? ??? ?bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);

?? ??? ?if (enabled)
?? ??? ??? ?irq_percpu_disable(desc, cpu);

?? ??? ?pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n",
?? ??? ??? ? ? ?enabled ? " and unmasked" : "", irq, cpu);
?? ?}

?? ?if (chip->irq_eoi)
?? ??? ?chip->irq_eoi(&desc->irq_data);
}

handle_fasteoi_irq如下:

void handle_fasteoi_irq(struct irq_desc *desc)
{
?? ?struct irq_chip *chip = desc->irq_data.chip;

?? ?raw_spin_lock(&desc->lock);

?? ?if (!irq_may_run(desc))
?? ??? ?goto out;

?? ?desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

?? ?/*
?? ? * If its disabled or no action available
?? ? * then mask it and get out of here:
?? ? */
?? ?if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
?? ??? ?desc->istate |= IRQS_PENDING;
?? ??? ?mask_irq(desc);
?? ??? ?goto out;
?? ?}

?? ?kstat_incr_irqs_this_cpu(desc);
?? ?if (desc->istate & IRQS_ONESHOT)
?? ??? ?mask_irq(desc);

?? ?preflow_handler(desc);
?? ?handle_irq_event(desc);

?? ?cond_unmask_eoi_irq(desc, chip);

?? ?raw_spin_unlock(&desc->lock);
?? ?return;
out:
?? ?if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
?? ??? ?chip->irq_eoi(&desc->irq_data);
?? ?raw_spin_unlock(&desc->lock);
}

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
?? ?irqreturn_t ret;

?? ?desc->istate &= ~IRQS_PENDING;
?? ?irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);//设置中断状态
?? ?raw_spin_unlock(&desc->lock);

?? ?ret = handle_irq_event_percpu(desc);

?? ?raw_spin_lock(&desc->lock);
?? ?irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);//清除中断状态
?? ?return ret;
}
?

irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
{
?? ?irqreturn_t retval;
?? ?unsigned int flags = 0;

?? ?retval = __handle_irq_event_percpu(desc, &flags);

?? ?add_interrupt_randomness(desc->irq_data.irq, flags);

?? ?if (!noirqdebug)
?? ??? ?note_interrupt(desc, retval);
?? ?return retval;
}

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
?? ?irqreturn_t retval = IRQ_NONE;
?? ?unsigned int irq = desc->irq_data.irq;
?? ?struct irqaction *action;

?? ?for_each_action_of_desc(desc, action) {//处理中断描述中的每一个action
?? ??? ?irqreturn_t res;

?? ??? ?trace_irq_handler_entry(irq, action);
?? ??? ?res = action->handler(irq, action->dev_id);//此处调用的就是注册中断时传递的中断处理函数
?? ??? ?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);//此处中断线程化唤醒线程

?? ??? ??? ?/* Fall through to add to randomness */
?? ??? ?case IRQ_HANDLED:
?? ??? ??? ?*flags |= action->flags;
?? ??? ??? ?break;

?? ??? ?default:
?? ??? ??? ?break;
?? ??? ?}

?? ??? ?retval |= res;
?? ?}

?? ?return retval;
}
?

handle_IPI后续再写。

文章来源:https://blog.csdn.net/weixin_44740132/article/details/135423355
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。