中断发生后会调到__irq_svc:
.align 5__irq_svc:
svc_entry
irq_handler
----
svc_exit r5 @ return from exception
UNWIND(.fnend )
ENDPROC(__irq_svc)
看配置文件中定义了CONFIG_MULTI_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
因使用了多个macro,看汇编会清楚些。
crash> dis -l __irq_svc
../arch/arm/kernel/entry-armv.S0xc000df00 <__irq_svc>: sub sp, sp, #68 ; 0x44
0xc000df04 <__irq_svc+4>: tst sp, #4
0xc000df08 <__irq_svc+8>: subeq sp, sp, #4
0xc000df0c <__irq_svc+12>: stm sp, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}
0xc000df10 <__irq_svc+16>: ldm r0, {r3, r4, r5}
0xc000df14 <__irq_svc+20>: add r7, sp, #48 ; 0x30
0xc000df18 <__irq_svc+24>: mvn r6, #0
0xc000df1c <__irq_svc+28>: add r2, sp, #68 ; 0x44
0xc000df20 <__irq_svc+32>: addeq r2, r2, #4
0xc000df24 <__irq_svc+36>: push {r3} ; (str r3, [sp, #-4]!)
0xc000df28 <__irq_svc+40>: mov r3, lr
0xc000df2c <__irq_svc+44>: stm r7, {r2, r3, r4, r5, r6}
0xc000df30 <__irq_svc+48>: ldr r1, [pc, #52] ; 0xc000df6c
0xc000df34 <__irq_svc+52>: mov r0, sp
0xc000df38 <__irq_svc+56>: add lr, pc, #0
0xc000df3c <__irq_svc+60>: ldr pc, [r1]
0xc000df40 <__irq_svc+64>: lsr r9, sp, #13
0xc000df44 <__irq_svc+68>: lsl r9, r9, #13
0xc000df48 <__irq_svc+72>: ldr r8, [r9, #4]
0xc000df4c <__irq_svc+76>: ldr r0, [r9]
0xc000df50 <__irq_svc+80>: teq r8, #0
0xc000df54 <__irq_svc+84>: movne r0, #0
0xc000df58 <__irq_svc+88>: tst r0, #2
0xc000df5c <__irq_svc+92>: blne 0xc000df70 <svc_preempt>
0xc000df60 <__irq_svc+96>: msr SPSR_fsxc, r5
0xc000df64 <__irq_svc+100>: clrex
0xc000df68 <__irq_svc+104>: ldm sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc}^
0xc000df6c <__irq_svc+108>: ldrshtgt sp, [r3], #-236 ; 0xffffff14
看其中的ldr pc, [r1],可以看到最终PC为c0008458
0xc000df30 <__irq_svc+48>: ldr r1, [pc, #52] ; 0xc000df6c
crash> rd 0xc000df6c
c000df6c: c073defc
r1: c073defc
0xc000df3c <__irq_svc+60>: ldr pc, [r1]
crash> rd c073defc
c073defc: c0008458
找到调用的函数gic_handle_irq
crash> dis -l c0008458/home/wenshuai/code/kernel3.4/linux_kernel/arch/arm/common/gic.c: 288
0xc0008458 <gic_handle_irq>: mov r12, sp
asmlinkage 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);
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
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);
}
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);
}
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
从中断号到irq_desc
以中断号为索引,从全局变量数组struct irq_desc irq_desc[NR_IRQS]中得到对应的 irq_desc,
这个全局变量是哪里赋值的?
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;
}
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}
crash> irq_desc | grep "handle_irq"
handle_irq = 0xc007cbd8 <handle_bad_irq>,
handle_irq = 0xc007cbd8 <handle_bad_irq>,
handle_irq = 0xc007f608 <handle_percpu_devid_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc0026244 <tl7689_gpio_irq_dispatch>,
handle_irq = 0xc007f9fc <handle_fasteoi_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc0026244 <tl7689_gpio_irq_dispatch>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc007f884 <handle_edge_irq>,
handle_irq = 0xc007f884 <handle_edge_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc007fb20 <handle_level_irq>,
handle_irq = 0xc0026244 <tl7689_gpio_irq_dispatch>,
handle_irq = 0xc007f9fc <handle_fasteoi_irq>,
handle_irq = 0xc007fc30 <handle_simple_irq>.
从irq_desc到irqaction
一个硬件中断上可以挂载多个action,当然要通过参数把各个action区别开,每个irq_desc 有一个指向irqaction的单向链表。
-> handle_irq_event(desc);
handle_irq_event(struct irq_desc *desc)
{
struct irqaction *action = desc->action;
handle_irq_event_percpu(desc, action);
}
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
-> action->handler(irq, action->dev_id);
中断共享
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action){
irqreturn_t retval = IRQ_NONE;
unsigned int random = 0, irq = desc->irq_data.irq;
do {
irqreturn_t res;
trace_irq_handler_entry(irq, action);
res = action->handler(irq, action->dev_id);
trace_irq_handler_exit(irq, action, res);
action = action->next;
} while (action);
}
这些中断处理函数处理完后,会回到
0xc000df68 <__irq_svc+104>: ldm sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc}^
也就是中断前的位置;
如果在中断处理过程中,发生了调度,处理完回到这里即可,这里中断使用的是当前进程的栈【这和编译选项中的8K的内核栈有关】。
原理上是可以的,只是中断处理函数的处理时间过长,影响了I/O的性能而已。
中断处理相关数据结构
有两个数据结构和中断处理相关:irq_desc and irqaction.两者的关系是 irq_desc通过struct irqaction *action找到对应的 irqaction list
struct irq_desc {
struct irq_data irq_data;
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
irq_flow_handler_t handle_irq;
struct irqaction *action;
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth;
unsigned int wake_depth;
unsigned int irq_count;
unsigned long last_unhandled;
unsigned int irqs_unhandled;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
struct proc_dir_entry *dir;
struct module *owner;
const char *name;
}
struct irqaction {
irq_handler_t handler;
unsigned long flags;
void *dev_id;
void *percpu_dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
}
irq_desc相关的操作
通过函数irq_set_chip_and_handler对irq_desc的 chip and handle赋值
arch/arm/common/gic.c
gic_irq_domain_map ->
irq_set_chip_and_handler(irq, &gic_chip, handle_percpu_devid_irq);
irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq);
kernel/irq/chip.c
static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle)
{
irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}
void
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
irq_flow_handler_t handle, const char *name)
{
irq_set_chip(irq, chip);
__irq_set_handler(irq, handle, 0, name);
}
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!chip)
chip = &no_irq_chip;
desc->irq_data.chip = chip;
}
void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
unsigned long flags;
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
desc->handle_irq = handle;
desc->name = name;
}
irqaction相关的操作
对应的函数是request_irq and request_threaded_irq. request_irq 是 request_threaded_irq的特例。
irqaction的创建和赋值在函数request_threaded_irq中实现,另外request_threaded_irq还创建了一个 调度属性为FIFO[RT]的内核thread,
去处理中断的后半部。
static inline int __must_checkre
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);
}
kernel/irq/manager.c
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;
desc = irq_to_desc(irq);
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
__setup_irq(irq, desc, action);
}
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
struct irqaction *old, **old_ptr;
if (new->thread_fn && !nested) {
struct task_struct *t;
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
/*
* We keep the reference to the task struct even if
* the thread dies to avoid that the interrupt code
* references an already freed task_struct.
*/
get_task_struct(t);
new->thread = t;
}
---
if (new->thread)
wake_up_process(new->thread);
}