一、struct irq_chip、struct irq_desc[]、struct irqaction三者之间的关系
二、Linux内核中中断的初始化流程、中断的注册流程、中断的执行流程
三、多核cpu的中断亲和力和中断负载均衡
四、中断的上半部和下半部
一、struct irq_chip、struct irq_desc[]、struct irqaction三者之间的关
include /linux/irq.h
主要的三个数据结构
struct irq_chip :中断控制器描述符, CPU所对应的一个具体的中断控制器,如早期intel对应的中断控制器为8259a,ioapic_chip。 一个cpu可以有多个irq_chip,即多个中断控制器
struct irq_desc : 中断描述符数组,每一个IRQ对应自己的struct irq_desc对象,共同组成一个
struct irqaction : 中断服务程序描述符,该IRQ对应的一系列中断程序
如图所示为该三个结构体关系,一个中断控制器(irq_chip)对应着一个中断描述符数组(irq_desc),每一个成员都是一个中断号,每一个中断号下面都有具体的中断服务程序(irqaction)链表
1、/*中断描述符*/ ------>一个IRQ对应自己的struct irq_desc对象,多个irq_desc组成irq_desc[ ]数组
struct irq_desc {/*中断描述符*/
unsigned intirq; /* 该irq_desc具体的中断号 */
irq_flow_handler_thandle_irq;/*该irq线公共中断服务程序*/struct irq_chip*chip;/*该中断线所属的中断控制器*/
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction*action;/*该中断服务程序,区别于公共中断服务程序,这里指向的是中断服务程序的队列头*/
unsigned int status; /* 中断线状态*/
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;
const char *name;
} ____cacheline_internodealigned_in_smp;
struct irq_desc {
struct irq_chip *chip; /*该中断线所属的中断控制器*/
}
struct irq_chip {
const char *name; /*中断控制器名称*/
unsigned int (*startup)(unsigned int irq);
void (*shutdown)(unsigned int irq);
void (*enable)(unsigned int irq);
void (*disable)(unsigned int irq);
void (*ack)(unsigned int irq);
void (*mask)(unsigned int irq);
void (*mask_ack)(unsigned int irq);
void (*unmask)(unsigned int irq);
void (*eoi)(unsigned int irq);
void (*end)(unsigned int irq);
int (*set_affinity)(unsigned int irq,
const struct cpumask *dest);
int (*retrigger)(unsigned int irq);
int (*set_type)(unsigned int irq, unsigned int flow_type);
int (*set_wake)(unsigned int irq, unsigned int on);
void (*bus_lock)(unsigned int irq);
void (*bus_sync_unlock)(unsigned int irq);
/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHOD
void (*release)(unsigned int irq, void *dev_id);
#endif
/*
* For compatibility, ->typename is copied into ->name.
* Will disappear.
*/
const char *typename;
};
3、/*中断服务程序描述符*/-------> 一个中断服务程序结构体声明
struct irq_desc {
struct irqaction *action; /*中断服务程序,区别于公共中断服务程序,这里指向的是中断服务程序的队列头*/
}
struct irqaction {
irq_handler_t handler;/*中断服务程序*/
unsigned long flags; /*IRQ 中断处理标志*/
const char *name; /*设备名*/
void *dev_id;
struct irqaction *next;/*指向该IRQ向中断请求队列下一个irqaction对象*/
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};
共享同一个IRQ先的多个irqaction对象组成的队列,即所谓的中断请求队列。当该IRQ线上产生中断时,请求队列中的中断服务程序将被依次执行
二、Linux内核中中断的初始化流程、中断的注册流程、中断的执行流程1、中断子系统的初始化 内核代码linux 2.6.30.4
start_kernel
{
trap_init /* arm 为空函数 */
early_irq_init /* irq_desc[NR_IRQS]数组基本初始化 */
init_IRQ /* irq_desc[NR_IRQS] 中断描述符数组初始化 */
{
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
init_arch_irq(); /* 在setup_arch中 init_arch_irq = mdesc->init_irq;*/
--> s3c24xx_init_irq 以s3c24400为例
{
set_irq_chip(irqno, &s3c_irq_chip); /* 根据 irqno 关联对应的irq_desc[irqno]和irq_chip */
set_irq_handler(irqno, handle_edge_irq); /* 设置irq_dest[irqno]公共中断函数 */
set_irq_flags(irqno, IRQF_VALID); /* 设置irq_dest[irqno] flags */
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); /* 设置irq_dest[irqno]公共中断函数 */
}
}
}
void __init setup_arch(char **cmdline_p)
{
mdesc = setup_machine(machine_arch_type); /*从machine_arch_type 段中获取 machine_desc结构体 */
init_arch_irq = mdesc->init_irq; /* 其中 mdesc结构体在具体的架构中定义 */
}
#define MACHINE_START(_type,_name) \ ----> arch/arm/include/asm/mach/arch.h
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
MACHINE_START(S3C2440, "SMDK2440") -----> mach-smdk2440.c/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq= s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
2、中断服务的注册 内核代码linux 2.6.30.4
request_irq
-->request_threaded_irq
{
desc = irq_to_desc(irq); /* 根据irq号取出其对应的irq_desc */
__setup_irq(irq, desc, action); /* 将struct irqaction *action 添加到desc->action链表中*/
}
3、内核中中断的执行过程 内核代码linux 2.6.30.4
entry_armv.S
.long __irq_usr @ 0 (USR_26 / USR_32) 用户模式下中断
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long__irq_svc@ 3 (SVC_26 / SVC_32)内核模式下中断
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
__irq_svc: entry_armv.S
irq_handler
ENDPROC(__irq_svc)
.macro irq_handler entry_armv.S
bne asm_do_IRQ
asm_do_IRQ ----> irq.c
--->generic_handle_irq(irq);
----->generic_handle_irq_desc
------->__do_IRQ(irq);
{
struct irq_desc *desc = irq_to_desc(irq);
action_ret = handle_IRQ_event(irq, desc->action); /* 依次执行 irq对应的desc->action的handler服务例程 */
}
handle_IRQ_event(unsigned int irq, struct irqaction *action) /* 依次执行 irq对应的desc->action的handler服务例程 */
{
do {
ret = action->handler(irq, action->dev_id);
action = action->next;
} while (action);
}
又比如hisi芯片的IRQ执行流程
vector_stub irq, IRQ_MODE, 4 ----> entry-armv.S.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long__irq_svc@ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
__irq_svc: ----> entry-armv.S
irq_handler
ENDPROC(__irq_svc)
#ifdef CONFIG_MULTI_IRQ_HANDLER ----> entry-armv.S
ldr r1, = handle_arch_irq /* 在 init_IRQ中赋值, handle_arch_irq*/
mov r0, sp
adr lr, BSYM(9997f)
ldr pc, [r1]
#else
arch_irq_handler_default
#endif
==========handle_arch_irq赋值流程,handle_arch_irq=gic_handle_irq=============
void __init init_IRQ(void)
{
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init();
else
machine_desc->init_irq();
}
hi3536_gic_init_irq
gic_init_bases
-->set_handle_irq(gic_handle_irq);
{
if (handle_arch_irq)
return;
handle_arch_irq = handle_irq;
}
MACHINE_START(HI3536, "hi3536") ----> arch/arm/mach-hi3536/core.c
.atag_offset = 0x100,
.map_io = hi3536_map_io,
.init_early = hi3536_init_early,
.init_irq = hi3536_gic_init_irq,
#ifdef CONFIG_HI3536_SYSCNT
.init_time = arch_timer_init,
#else
.init_time = hi3536_timer_init,
#endif
.init_machine = hi3536_init,
.smp = smp_ops(hi3536_smp_ops),
.reserve = hi3536_reserve,
.restart = hi3536_restart,
MACHINE_END
gic_handle_irq
--->handle_IRQ
----->generic_handle_irq
{
struct irq_desc *desc = irq_to_desc(irq);
generic_handle_irq_desc(irq, desc);
{
desc->handle_irq(irq, desc);/*这里根据具体的handle_irq来执行,若irq<32,则使用 handle_percpu_devid_irq,否则使用handle_fasteoi_irq*/
handle_fasteoi_irq/* 以irq大于32的handle_fasteoi_irq为例 */
{
handle_irq_event(desc);
--->handle_irq_event_percpu/* 依次执行irq_desc上action上的服务例程 */
{
do {
ret = action->handler(irq, action->dev_id);
action = action->next;
} while (action);
}
}
}
}
static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
{
if (hw < 32) /*中断号小于32 */
irq_set_chip_and_handler(irq, &gic_chip, handle_percpu_devid_irq);/*赋值desc->handle_irq*/
else /* 中断号大于32 */
irq_set_chip_and_handler(irq, &gic_chip,handle_fasteoi_irq);/*赋值desc->handle_irq*/
return 0;
}
三、多核cpu的中断亲和力和中断负载均衡
1、处理器间中断
在多核cpu中,中断可以通过 处理器间中断(Inter-Processor Interrupt) 传递到其他核上.
2、中断亲和力和中断负载均衡
利用中断亲和可以来做中断的负载均衡,将负载绑定到负载较轻的cpu上,更好的优化性能.
linux中断上半部(top half) ------> 不可中断
上半部的功能是"登记中断",当一个中断发生时,它进行相应地硬件读写后并 把中断例程的下半部挂到该设备的下半部执行 工作队列/tasklet中去
linux中断的下半部(botttom half) -----> 可中断
具体的下半部几种方式
几种下半部方式 :
1、软中断
2、tasklet
3、工作队列
当前下半部看到使用使用工作队和tasklet比较多
参考资料
Linux 2.6.30.4源码
linux 3.10.y源码
《linux内核修炼之道》