1. 中断入口
要处理中断,必须为handle_arch_irq
赋值。
ARM64自己定义handle_arch_irq
的实现。
1/// arch/arm64/kernel/irq.c
2void (*handle_arch_irq)(struct pt_regs *) __ro_after_init = default_handle_irq;
3void (*handle_arch_fiq)(struct pt_regs *) __ro_after_init = default_handle_fiq;
4
5int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
6{
7 if (handle_arch_irq != default_handle_irq)
8 return -EBUSY;
9
10 handle_arch_irq = handle_irq;
11 pr_info("Root IRQ handler: %ps\n", handle_irq);
12 return 0;
13}
14
15int __init set_handle_fiq(void (*handle_fiq)(struct pt_regs *))
16{
17 if (handle_arch_fiq != default_handle_fiq)
18 return -EBUSY;
19
20 handle_arch_fiq = handle_fiq;
21 pr_info("Root FIQ handler: %ps\n", handle_fiq);
22 return 0;
23}
ARM使能CONFIG_GENERIC_IRQ_MULTI_HANDLER
。
1/// kernel/irq/handle.c
2#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
3void (*handle_arch_irq)(struct pt_regs *) __ro_after_init;
4#endif
5/// ... ...
6#ifdef CONFIG_GENERIC_IRQ_MULTI_HANDLER
7int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
8{
9 if (handle_arch_irq)
10 return -EBUSY;
11
12 handle_arch_irq = handle_irq;
13 return 0;
14}
15
16/**
17 * generic_handle_arch_irq - root irq handler for architectures which do no
18 * entry accounting themselves
19 * @regs: Register file coming from the low-level handling code
20 */
21asmlinkage void noinstr generic_handle_arch_irq(struct pt_regs *regs)
22{
23 struct pt_regs *old_regs;
24
25 irq_enter();
26 old_regs = set_irq_regs(regs);
27 handle_arch_irq(regs);
28 set_irq_regs(old_regs);
29 irq_exit();
30}
31#endif
1.1. arm32
arm32的中断入口分别在__irq_svc
和__irq_usr
中,其内部会使用irq_handler
来调用generic_handle_arch_irq
,最终调用到handle_arch_irq
。
1/// arch/arm/kernel/entry-armv.S
2/*
3 * Interrupt handling.
4 */
5 .macro irq_handler, from_user:req
6 mov r1, sp
7 ldr_this_cpu r2, irq_stack_ptr, r2, r3
8 .if \from_user == 0
9 @
10 @ If we took the interrupt while running in the kernel, we may already
11 @ be using the IRQ stack, so revert to the original value in that case.
12 @
13 subs r3, r2, r1 @ SP above bottom of IRQ stack?
14 rsbscs r3, r3, #THREAD_SIZE @ ... and below the top?
15#ifdef CONFIG_VMAP_STACK
16 ldr_va r3, high_memory, cc @ End of the linear region
17 cmpcc r3, r1 @ Stack pointer was below it?
18#endif
19 bcc 0f @ If not, switch to the IRQ stack
20 mov r0, r1
21 /// 直接调用generic_handle_arch_irq
22 bl generic_handle_arch_irq
23 b 1f
240:
25 .endif
26
27 /// 将generic_handle_arch_irq作为call_with_stack的参数
28 /// 在call_with_stack内调用
29 mov_l r0, generic_handle_arch_irq
30 bl call_with_stack
311:
32 .endm
1.2. arm64
handle_arch_irq
作为参数,最终由do_interrupt_handler
调用。
2. gic的中断处理函数
2.1. 实际的处理函数
1# cat /proc/interrupts
2 CPU0 CPU1
3 11: 2598 2172 GICv3 27 Level arch_timer
4 13: 176 0 GICv3 33 Level uart-pl011
5 16: 563 0 GICv3 75 Edge virtio0
6 17: 0 0 GICv3 79 Edge virtio4
7 18: 0 0 GICv3 34 Level rtc-pl031
8 19: 0 0 GICv3 23 Level arm-pmu
9 20: 3 0 GICv3 76 Edge virtio1
10 21: 82 0 GICv3 77 Edge virtio2
11 22: 33 0 GICv3 78 Edge virtio3
12 23: 0 0 9030000.pl061 3 Edge GPIO Key Poweroff
13IPI0: 56 84 Rescheduling interrupts
14IPI1: 503 797 Function call interrupts
15IPI2: 0 0 CPU stop interrupts
16IPI3: 0 0 CPU stop (for crash dump) interrupts
17IPI4: 0 0 Timer broadcast interrupts
18IPI5: 0 0 IRQ work interrupts
19IPI6: 0 0 CPU wake-up interrupts
20Err: 0
21# cd /sys/kernel/debug/irq/irqs
22# grep -w handler * | sort -n
231:handler: handle_percpu_devid_irq
242:handler: handle_percpu_devid_irq
253:handler: handle_percpu_devid_irq
264:handler: handle_percpu_devid_irq
275:handler: handle_percpu_devid_irq
286:handler: handle_percpu_devid_irq
297:handler: handle_percpu_devid_irq
308:handler: handle_percpu_devid_irq
319:handler: handle_percpu_devid_irq
3210:handler: handle_percpu_devid_irq
3311:handler: handle_percpu_devid_irq
3412:handler: handle_percpu_devid_irq
3513:handler: handle_fasteoi_irq
3614:handler: pl061_irq_handler
3715:handler: handle_fasteoi_irq
3816:handler: handle_fasteoi_irq
3917:handler: handle_fasteoi_irq
4018:handler: handle_fasteoi_irq
4119:handler: handle_percpu_devid_irq
4220:handler: handle_fasteoi_irq
4321:handler: handle_fasteoi_irq
4422:handler: handle_fasteoi_irq
4523:handler: handle_edge_irq
这里重点关注handle_fasteoi_irq
,其最后会调用__handle_irq_event_percpu
。
在__handle_irq_event_percpu
中,如果handler
返回IRQ_WAKE_THREAD
,则会唤醒中断处理线程。
1irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc)
2{
3 irqreturn_t retval = IRQ_NONE;
4 unsigned int irq = desc->irq_data.irq;
5 struct irqaction *action;
6
7 record_irq_time(desc);
8
9 for_each_action_of_desc(desc, action) {
10 irqreturn_t res;
11
12 /*
13 * If this IRQ would be threaded under force_irqthreads, mark it so.
14 */
15 if (irq_settings_can_thread(desc) &&
16 !(action->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)))
17 lockdep_hardirq_threaded();
18
19 trace_irq_handler_entry(irq, action);
20 res = action->handler(irq, action->dev_id);
21 trace_irq_handler_exit(irq, action, res);
22
23 if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pS enabled interrupts\n",
24 irq, action->handler))
25 local_irq_disable();
26
27 switch (res) {
28 case IRQ_WAKE_THREAD:
29 /*
30 * Catch drivers which return WAKE_THREAD but
31 * did not set up a thread function
32 */
33 if (unlikely(!action->thread_fn)) {
34 warn_no_thread(irq, action);
35 break;
36 }
37
38 __irq_wake_thread(desc, action);
39 break;
40
41 default:
42 break;
43 }
44
45 retval |= res;
46 }
47
48 return retval;
49}
3. 中断线程化
之前提到,在__setup_irq
申请中断时,可以在指定的handler
或着通过强制中断线程化来使用irq_default_primary_handler
中返回IRQ_WAKE_THREAD
来唤醒中断线程。
中断线程服务函数irq_thread
:
1/// kernel/irq/manage.c
2/*
3 * Interrupt handler thread
4 */
5static int irq_thread(void *data)
6{
7 struct callback_head on_exit_work;
8 struct irqaction *action = data;
9 struct irq_desc *desc = irq_to_desc(action->irq);
10 irqreturn_t (*handler_fn)(struct irq_desc *desc,
11 struct irqaction *action);
12
13 irq_thread_set_ready(desc, action);
14
15 sched_set_fifo(current);
16
17 if (force_irqthreads() && test_bit(IRQTF_FORCED_THREAD,
18 &action->thread_flags))
19 handler_fn = irq_forced_thread_fn;
20 else
21 handler_fn = irq_thread_fn;
22
23 init_task_work(&on_exit_work, irq_thread_dtor);
24 task_work_add(current, &on_exit_work, TWA_NONE);
25
26 irq_thread_check_affinity(desc, action);
27
28 while (!irq_wait_for_interrupt(action)) {
29 irqreturn_t action_ret;
30
31 irq_thread_check_affinity(desc, action);
32
33 action_ret = handler_fn(desc, action);
34 if (action_ret == IRQ_WAKE_THREAD)
35 irq_wake_secondary(desc, action);
36
37 wake_threads_waitq(desc);
38 }
39
40 /*
41 * This is the regular exit path. __free_irq() is stopping the
42 * thread via kthread_stop() after calling
43 * synchronize_hardirq(). So neither IRQTF_RUNTHREAD nor the
44 * oneshot mask bit can be set.
45 */
46 task_work_cancel(current, irq_thread_dtor);
47 return 0;
48}