定义
CSAPP
异常分为4类:中断(interrupt),陷阱(trap)、故障(fault)、终止(abort)
第1个是异步的,后3个是同步的
Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A: System Programming Guide, Part 1
irq_desc
struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
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),
}
};
start_kernel
start_kernel()位于linux-4.20/init/main.c中,相当于main()
start_kernel
init_IRQ
time_init
late_time_init = x86_late_time_init;
late_time_init // x86_late_time_init
x86_init.irqs.intr_mode_init(); // apic_intr_mode_init
apic_bsp_setup
setup_local_APIC
setup_IO_APIC
init_IRQ():初始化硬中断
init_IRQ位于linux-4.20/arch/x86/kernel/irqinit.c中
init_IRQ() -> native_init_IRQ() -> init_ISA_irqs() -> irq_set_chip_and_handler() -> irq_set_chip_and_handler_name() -> __irq_set_handler() -> __irq_do_set_handler()
void __init init_IRQ(void)
{
...
x86_init.irqs.intr_init(); /* native_init_IRQ() */
}
struct x86_init_ops x86_init __initdata = {
...
.irqs = {
.pre_vector_init = init_ISA_irqs,
.intr_init = native_init_IRQ,
.trap_init = x86_init_noop,
.intr_mode_init = apic_intr_mode_init
},
...
};
void __init native_init_IRQ(void)
{
...
x86_init.irqs.pre_vector_init(); /* init_ISA_irqs() */
idt_setup_apic_and_irq_gates();
...
}
void __init init_ISA_irqs(void)
{
...
for (i = 0; i < nr_legacy_irqs(); i++) /* 遍历每个中断号 */
irq_set_chip_and_handler(i, chip, handle_level_irq); /* handle为handle_level_irq() */
}
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_handler(irq, handle, 0, name);
}
void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
...
__irq_do_set_handler(desc, handle, is_chained, name);
...
}
static void
__irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
int is_chained, const char *name)
{
...
desc->handle_irq = handle; /* 设置desc->handle_irq为handle_level_irq() */
...
}
void __init idt_setup_apic_and_irq_gates(void)
{
...
entry = irq_entries_start + 8 * (i - FIRST_EXTERNAL_VECTOR);
set_intr_gate(i, entry);
...
}
ENTRY(irq_entries_start)
vector=FIRST_EXTERNAL_VECTOR
.rept (FIRST_SYSTEM_VECTOR - FIRST_EXTERNAL_VECTOR)
UNWIND_HINT_IRET_REGS
pushq $(~vector+0x80) /* Note: always in signed byte range */
jmp common_interrupt
.align 8
vector=vector+1
.endr
END(irq_entries_start)
common_interrupt:
addq $-0x80, (%rsp) /* Adjust vector to [-256, -1] range */
call interrupt_entry
UNWIND_HINT_REGS indirect=1
call do_IRQ /* rdi points to pt_regs */
request_irq():注册硬中断
request_irq() -> request_threaded_irq() -> __setup_irq()
对于ixgbe网卡驱动:ixgbe_open() -> ixgbe_request_irq() -> ixgbe_request_msix_irqs() -> request_irq()
static inline int __must_check
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);
}
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); /* 根据irq得到对应的irq_desc */
...
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); /* 分配irqaction */
...
action->handler = handler; /* 设置action->handler为硬中断函数 */
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id; /* 调用request_irq()时传入,作为硬中断函数的参数 */
...
retval = __setup_irq(irq, desc, action); /* 将action插入desc->action队尾 */
...
}
/* 将new插入desc->action队尾 */
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
struct irqaction *old, **old_ptr;
...
new->irq = irq;
...
old_ptr = &desc->action; /* old_ptr指向desc->action队头 */
old = *old_ptr;
if (old) {
...
do {
...
old_ptr = &old->next;
old = *old_ptr;
} while (old); /* old_ptr往右移动,直到desc->action队尾跳出循环 */
...
}
...
*old_ptr = new; /* 将new插入desc->action队尾 */
...
register_irq_proc(irq, desc);
...
register_handler_proc(irq, new);
...
}
open_softirq():注册软中断
net_dev_init() -> open_softirq()
设置全局数组softirq_vec的action为软中断函数
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ, /* 2 */
NET_RX_SOFTIRQ, /* 3 */
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ, /* Unused, but kept as tools rely on the
numbering. Sigh! */
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; /* 软中断向量表 */
static int __init net_dev_init(void)
{
...
open_softirq(NET_TX_SOFTIRQ, net_tx_action); /* 注册发送软中断,软中断函数为net_tx_action() */
open_softirq(NET_RX_SOFTIRQ, net_rx_action); /* 注册接收软中断,软中断函数为net_rx_action() */
...
}
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action; /* 设置softirq_vec[nr].action为软中断函数 */
}
do_IRQ():执行硬中断函数
do_IRQ位于linux-4.20/arch/x86/kernel/irq.c中
do_IRQ() -> handle_irq() -> generic_handle_irq_desc() -> handle_level_irq() -> handle_irq_event() -> handle_irq_event_percpu() -> __handle_irq_event_percpu() -> 硬中断函数
硬中断函数执行完后继续执行exiting_irq()
__visible unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
...
unsigned vector = ~regs->orig_ax;
...
desc = __this_cpu_read(vector_irq[vector]); /* 根据vector得到对应的irq_desc */
...
if (!handle_irq(desc, regs)) {
...
exiting_irq();
...
}
bool handle_irq(struct irq_desc *desc, struct pt_regs *regs)
{
...
generic_handle_irq_desc(desc);
...
}
static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
desc->handle_irq(desc); /* handle_level_irq() */
}
void handle_level_irq(struct irq_desc *desc)
{
...
handle_irq_event(desc);
...
}
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
...
ret = handle_irq_event_percpu(desc);
...
}
irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
{
...
retval = __handle_irq_event_percpu(desc, &flags);
...
}
irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
...
for_each_action_of_desc(desc, action) {
...
/* 硬中断函数
* 对于ixgbe网卡驱动:ixgbe_msix_clean_rings()/ixgbe_intr() */
res = action->handler(irq, action->dev_id);
...
}
...
}
__raise_softirq_irqoff():抛出软中断
对于ixgbe网卡驱动:ixgbe_msix_clean_rings()/ixgbe_intr() -> napi_schedule_irqoff() -> __napi_schedule_irqoff() -> ____napi_schedule() -> __raise_softirq_irqoff()
DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
typedef struct {
u16 __softirq_pending;
...
} ____cacheline_aligned irq_cpustat_t;
#define local_softirq_pending_ref irq_stat.__softirq_pending /* 软中断位图 */
void __raise_softirq_irqoff(unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr); /* 设置软中断位图的第nr位为1 */
}
#define local_softirq_pending() (__this_cpu_read(local_softirq_pending_ref)) /* 读软中断位图 */
#define set_softirq_pending(x) (__this_cpu_write(local_softirq_pending_ref, (x))) /* 写软中断位图为x */
#define or_softirq_pending(x) (__this_cpu_or(local_softirq_pending_ref, (x))) /* 软中断位图和x求或 */
#define __this_cpu_or(pcp, val) \
({ \
__this_cpu_preempt_check("or"); \
raw_cpu_or(pcp, val); \
})
#define raw_cpu_or(pcp, val) __pcpu_size_call(raw_cpu_or_, pcp, val)
#define __pcpu_size_call(stem, variable, ...) \
do { \
__verify_pcpu_ptr(&(variable)); \
switch(sizeof(variable)) { \
case 1: stem##1(variable, __VA_ARGS__);break; \
case 2: stem##2(variable, __VA_ARGS__);break; \
case 4: stem##4(variable, __VA_ARGS__);break; \
case 8: stem##8(variable, __VA_ARGS__);break; \
default: \
__bad_size_call_parameter();break; \
} \
} while (0)
#define raw_cpu_or_2(pcp, val) percpu_to_op("or", (pcp), val) /* percpu_to_op是一个汇编宏 */
exiting_irq():执行软中断函数
exiting_irq() -> irq_exit() -> do_softirq() -> __do_softirq() -> 软中断函数
static inline void exiting_irq(void)
{
irq_exit();
}
void irq_exit(void)
{
...
/* 1、不在一个硬/软中断上下文中,且抢占功能是打开的
* 2、没有软中断等待处理 */
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
...
}
static inline void invoke_softirq(void)
{
if (ksoftirqd_running(local_softirq_pending()))
return;
if (!force_irqthreads) {
...
__do_softirq(); /* 处理软中断 */
...
} else {
wakeup_softirqd(); /* 唤醒ksoftirqd线程处理软中断 */
}
}
static bool ksoftirqd_running(unsigned long pending)
{
struct task_struct *tsk = __this_cpu_read(ksoftirqd);
if (pending & SOFTIRQ_NOW_MASK) /* 有HI_SOFTIRQ/TASKLET_SOFTIRQ软中断等待处理 */
return false;
return tsk && (tsk->state == TASK_RUNNING); /* ksoftirqd线程正在运行 */
}
asmlinkage __visible void __softirq_entry __do_softirq(void)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
...
int max_restart = MAX_SOFTIRQ_RESTART;
struct softirq_action *h;
...
__u32 pending;
int softirq_bit;
...
pending = local_softirq_pending(); /* 读软中断位图 */
...
restart:
...
h = softirq_vec; /* 软中断向量表 */
...
/* ffs(find first set)
* 从最低位开始,返回第一个为1的bit的index */
while ((softirq_bit = ffs(pending))) {
...
h += softirq_bit - 1; /* h指向为1的bit对应的软中断向量 */
...
/* 软中断函数
* 对于网络收发包:net_tx/rx_action() */
h->action(h);
...
h++;
pending >>= softirq_bit;
}
...
pending = local_softirq_pending(); /* 再次读软中断位图 */
if (pending) {
/* 时长没有达到MAX_SOFTIRQ_TIME、不需要重新调度、restart次数没有达到MAX_SOFTIRQ_RESTART */
if (time_before(jiffies, end) && !need_resched() &&
--max_restart)
goto restart;
wakeup_softirqd(); /* 唤醒ksoftirqd线程处理软中断 */
}
...
}