/** * enum irqreturn * @IRQ_NONE interrupt was not from this device or was not handled * @IRQ_HANDLED interrupt was handled by this device * @IRQ_WAKE_THREAD handler requests to wake the handler thread */ enum irqreturn { IRQ_NONE = (0 << 0), IRQ_HANDLED = (1 << 0), IRQ_WAKE_THREAD = (1 << 1), };
/** * struct irqaction - per interrupt action descriptor * @handler: interrupt handler function * @name: name of the device * @dev_id: cookie to identify the device * @percpu_dev_id: cookie to identify the device * @next: pointer to the next irqaction for shared interrupts * @irq: interrupt number * @flags: flags (see IRQF_* above) * @thread_fn: interrupt handler function for threaded interrupts * @thread: thread pointer for threaded interrupts * @secondary: pointer to secondary irqaction (force threading) * @thread_flags: flags related to @thread * @thread_mask: bitmask for keeping track of @thread activity * @dir: pointer to the proc/irq/NN/name entry */ struct irqaction { irq_handler_t handler; void *dev_id; void __percpu *percpu_dev_id; struct irqaction *next; irq_handler_t thread_fn; struct task_struct *thread; struct irqaction *secondary; unsigned int irq; unsigned int flags; unsigned long thread_flags; unsigned long thread_mask; const char *name; struct proc_dir_entry *dir; };
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; ...... new->irq = irq; ...... /* * Create a handler thread when a thread function is supplied * and the interrupt does not nest into another interrupt * thread. */ if (new->thread_fn && !nested) { ret = setup_irq_thread(new, irq, false); } ...... old_ptr = &desc->action; old = *old_ptr; if (old) { /* add new interrupt at end of irq queue */ do { thread_mask |= old->thread_mask; old_ptr = &old->next; old = *old_ptr; } while (old); } ...... *old_ptr = new; ...... if (new->thread) wake_up_process(new->thread); ...... }
/* * Linux IRQ vector layout. * * There are 256 IDT entries (per CPU - each entry is 8 bytes) which can * be defined by Linux. They are used as a jump table by the CPU when a * given vector is triggered - by a CPU-external, CPU-internal or * software-triggered event. * * Linux sets the kernel code address each entry jumps to early during * bootup, and never changes them. This is the general layout of the * IDT entries: * * Vectors 0 ... 31 : system traps and exceptions - hardcoded events * Vectors 32 ... 127 : device interrupts * Vector 128 : legacy int80 syscall interface * Vectors 129 ... INVALIDATE_TLB_VECTOR_START-1 except 204 : device interrupts * Vectors INVALIDATE_TLB_VECTOR_START ... 255 : special interrupts * * 64-bit x86 has per CPU IDT tables, 32-bit has one shared IDT table. * * This file enumerates the exact layout of them: */ #define FIRST_EXTERNAL_VECTOR 0x20 #define IA32_SYSCALL_VECTOR 0x80 #define NR_VECTORS 256 #define FIRST_SYSTEM_VECTOR NR_VECTORS
/* * Set the IDT descriptor to a fixed read-only location, so that the * "sidt" instruction will not leak the location of the kernel, and * to defend the IDT against arbitrary memory write vulnerabilities. * It will be reloaded in cpu_init() */ __set_fixmap(FIX_RO_IDT, __pa_symbol(idt_table), PAGE_KERNEL_RO); idt_descr.address = fix_to_virt(FIX_RO_IDT); ......
我们回到trap_init中,当前32个中断都用set_intr_gate设置完毕。在中断向量表idt_table中填完了之后,接下来的for循环,for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++),将前32个中断都在used_vectors中标记为1,表示这些都设置过中断处理函数了。
ENTRY(irq_entries_start) vector=FIRST_EXTERNAL_VECTOR .rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR) pushl $(~vector+0x80) /* Note: always in signed byte range */ vector=vector+1 jmp common_interrupt /* 会调用到do_IRQ */ .align 8 .endr END(irq_entries_start)
common_interrupt: ASM_CLAC addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */ interrupt do_IRQ /* 0(%rsp): old RSP */ ret_from_intr: ...... /* Interrupt came from user space */ GLOBAL(retint_user) ...... /* Returning to kernel space */ retint_kernel: ......
/* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific * handlers). */ __visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); struct irq_desc * desc; /* high bit used in ret_from_ code */ unsigned vector = ~regs->orig_ax; ...... desc = __this_cpu_read(vector_irq[vector]); if (!handle_irq(desc, regs)) { ...... } ...... set_irq_regs(old_regs); return 1; }
static int __assign_irq_vector(int irq, struct apic_chip_data *d, const struct cpumask *mask, struct irq_data *irqdata) { static int current_vector = FIRST_EXTERNAL_VECTOR + VECTOR_OFFSET_START; static int current_offset = VECTOR_OFFSET_START % 16; int cpu, vector; ...... while (cpu < nr_cpu_ids) { int new_cpu, offset; ...... vector = current_vector; offset = current_offset; next: vector += 16; if (vector >= first_system_vector) { offset = (offset + 1) % 16; vector = FIRST_EXTERNAL_VECTOR + offset; }
/* If the search wrapped around, try the next cpu */ if (unlikely(current_vector == vector)) goto next_cpu;
if (test_bit(vector, used_vectors)) goto next;
...... /* Found one! */ current_vector = vector; current_offset = offset; /* Schedule the old vector for cleanup on all cpus */ if (d->cfg.vector) cpumask_copy(d->old_domain, d->domain); for_each_cpu(new_cpu, vector_searchmask) per_cpu(vector_irq, new_cpu)[vector] = irq_to_desc(irq); goto update;