在计算机的发展过程中,外设的速度长期是低于CPU速度的,CPU 不能长期处于等待外设的状态,因此提供了外设通知CPU的方式--中断。
中断架构分为三部分:
1、中断源,一般由外设产生,称之为 peripheral,是中断发送方;
2、中断控制器, 一般称之为 GIC Controller,负责对中断进行优先级,屏蔽等管理,并根据一定规则路由到CPU;
3、CPU,这是中断的接收方 ,负责处理中断;
struct irq_chip {
const char *name;
void (*irq_enable)(struct irq_data *data);
void (*irq_disable)(struct irq_data *data);
void (*irq_mask)(struct irq_data *data);
void (*irq_unmask)(struct irq_data *data);
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
...
};
linux 使用 struct irq_chip 来描述中断控制器,它提供了对于中断的 enable, disable, mask, unmask等操作。
struct irq_desc {
const char *name;
unsigned int depth;
struct irqaction *action;
struct irq_data irq_data;
struct cpumask *percpu_enabled;
...
};
对于每个中断,linux 使用 irq_desc 来描述,它提供了中断 name, 处理函数, action以及目标cpu.
struct irq_data {
struct irq_chip *chip;
struct irq_domain *domain;
unsigned int irq;
unsigned long hwirq;
...
};
irq_chip"结构体中的每个函数指针,都会携带一个指向struct irq_data的指针作为参数,可见这个"irq_data"与中断控制必定关系密切。也就是说,在"irq_desc"的定义中,与中断控制器紧密联系的这部分被单独提取出来,构成了"irq_data"结构体.
在启动过程中:
在 irq domain 创建好之后, irq 和 hwirq 映射关系是空的。因此需要提供映射的方式。
linux 目前大多采用 radix-tree 方式进行映射
首先,每个 hwirq 的信息描述通过 dts 进行描述
其次,每个 irq 需要对应一个 struct irq_desc
最后,建立 hwirq 到 irq 的映射
/* arm64/kernel/entry-common.c */
void __el1_irq(void)
{
enter_from_kernel_mode(regs);
irq_enter_rcu();
do_interrupt_handler(regs, handler);
irq_exit_rcu();
}
/* kernel/softirq.c */
void irq_enter_rcu(void) {
__irq_enter_raw();
}
#define __irq_enter_raw() \
do { \
preempt_count_add(HARDIRQ_OFFSET); \
lockdep_hardirq_enter(); \
} while (0)
在进入 hardirq 时, hardirq count 加1.
2.退出中断
/* kernel/softirq.c */
void irq_exit_rcu(void)
{
__irq_exit_rcu();
/* must be last! */
lockdep_hardirq_exit();
}
static inline void __irq_exit_rcu(void)
{
...
preempt_count_sub(HARDIRQ_OFFSET);
}
在退出 hardirq 时,hardirq count 减1.
hardirq context 解决的是当前是否在中断中,但是中断的竞争的解决是通过 irq_enable,irq_disable 解决的。
static inline void softirq_handle_begin(void)
{
__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
}
2. 退出 softirq
static inline void softirq_handle_end(void)
{
__local_bh_enable(SOFTIRQ_OFFSET);
}
对于当前 cpu, 当触发softirq 时,只需要更新 SOFTIRQ_OFFSET, 也就是表示是否在中断上下文中。
https://zhuanlan.zhihu.com/p/83709066
中断子系统 - 蜗窝科技