之前在ARM平台上分析过中断处理的详细步骤, 写了个PPT, 但是后来找不到了. 正好前一阵子看mips下的中断的时候, 随手写了点东西, 还是记录到blog上来吧, 方便查找.
这里面的每一个文字都是我自己码出来的, 转载的兄弟请注明出处.
1. 重要的结构体介绍
struct irq_desc {
unsigned int irq;
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ 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;
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
包含重要的 irq_chip 的成员 chip, 和irqaction的成员 action, 以及irq_flow_handler_t类型的函数指针handle_irq;
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);
void (*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);
/* 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;
};
struct irqaction {
irq_handler_t handler;
unsigned long flags;
cpumask_t mask;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
};
这个handler就是通过request_irq去注册的handler.
2. 重要的变量和函数等
全局数组struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp;
3. mips/concerto的中断的初始化
kernel的流程: (缩进对齐表示是被上一级函数调用的函数, 个人习惯用法)
start_kernel
early_irq_init //初始化上面的全局的irq_desc
init_IRQ
arch_init_irq
mips本身的中断:
0-8是mips本身的, 8-NR_IRQS是PIC的.
如果mips支持超线程, 则0-1的初始化是
if (cpu_has_mipsmt)
for (i = irq_base; i < irq_base + 2; i++)
set_irq_chip(i, &mips_mt_cpu_irq_controller);
2-7的初始化是
for (i = irq_base + 2; i < irq_base + 8; i++)
set_irq_chip_and_handler(i, &mips_cpu_irq_controller,
handle_percpu_irq);
mips_cpu_irq_controller就是irq_chip类型的struct.
handle_percpu_irq 是以及 irq_flow_handler_t 类型的函数.
8-NR_IRQS的初始化是
for (irq = CONCERTO_PIC_IRQ_BASE; irq < NR_IRQS; irq++) {
concerto_hard_disable_pic_irq(irq - CONCERTO_PIC_IRQ_BASE);
set_irq_chip_and_handler(irq, &concerto_pic_irq_type, handle_simple_irq);
}
常见的 irq_flow_handler_t 类型的handler有:
handle_simple_irq
handle_level_irq
handle_fasteoi_irq
handle_edge_irq
handle_percpu_irq
不管是哪种 irq_desc 中注册的 irq_flow_handler_t, 都是调用的 handle_IRQ_event, 在 handle_IRQ_event 中再调用 request_irq 中注册的各自的handler.
4. 中断的大致流程
do_IRQ
irq_enter
generic_handle_irq(irq)
generic_handle_irq_desc
desc->handle_irq(irq, desc); //比如前面的 handle_simple_irq
handle_IRQ_event(irq, action);
action->handler(irq, action->dev_id); //就是request_irq的注册的handler
irq_exit
invoke_softirq //当前没在处理hard irq和soft irq, 同时有pending的soft irq
__do_softirq //最多restart 10次, MAX_SOFTIRQ_RESTART, 还有的话就去唤醒softirq线程 wakeup_softirqd
h->action(h); //比如 blk_done_softirq
ksoftirqd //如果需要
5. softirq
系统中有
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
open_softirq去注册h->action(h), 而 raise_softirq 去开始使能并执行对应的softirq.
更具体的介绍在后面的文章中, 这个是大致的处理框架.