linux内核中断分析

来源:互联网 发布:汕头美工助理招聘信息 编辑:程序博客网 时间:2024/05/22 07:40
知识要点
一、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 intstatus;/* 中断线状态*/
unsigned intdepth;/* nested irq disables */
unsigned intwake_depth;/* nested wake enables */
unsigned intirq_count;/* For detecting broken IRQs */    
unsigned longlast_unhandled;/* Aging timer for unhandled count */
unsigned intirqs_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 <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_handlerentry_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内核修炼之道》






0 0
原创粉丝点击