Linux内核中断

定义

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线程处理软中断 */
	}
	...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值