linux内核中断分析

知识要点
一、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对应的一系列中断程序

  
linux内核中断分析_第1张图片




      如图所示为该三个结构体关系,一个中断控制器(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;


2、/*中断控制器描述符*/ --->CPU所对应的一个具体的中断控制器,如早期intel对应的中断控制器为8259a etc..
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 */
.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)


.macro irq_handler
#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

====================================================
所以 handle_arch_irq=gic_handle_irq

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内核修炼之道》






你可能感兴趣的:(linux内核)