中断处理过程
来源:互联网 发布:现货交易软件开发 编辑:程序博客网 时间:2024/06/09 14:43
本文的初衷是搞清楚:当中断发生后,硬件会关闭中断,但何时会打开?
为这个问题,从中断发生到从中断返回遍历了一遍。
1.异常矢量表
/*****************************************************************/
entry-armv.S
.globl __vectors_start
__vectors_start:
ARM( swi SYS_ERROR0 )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
2.中断处理的整体框架
以中断异常为例:vector_irq是宏vector_stub irq, IRQ_MODE, 4展开后的结果。宏vector_stub irq, IRQ_MODE, 4 展开后就是这个样子:
vector_irq:
.if \correction[4]/*根据中断模式修改 lr*/
sub lr, lr, #\correction[4]
.endif
@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
/*当发生异常时,CPU会根据异常的类型进入某个工作模式,但是很快vector_stub宏又会
*强制CPU进行管理模式,在管理模式下进行后续处理,这种方法简化了程序的设计,使得
*异常发生后的工作模式要么是用户模式,要么是管理模式
*/
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
movs pc, lr @ branch to handler in SVC mode
.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
/*根据异常模式【SVC or user只有这两种,非user都按照svc处理】*/
假设进入svc模式:
__irq_svc:
svc_entry
irq_handler
svc_exit r5 @ return from exception
__irq_svc分为3部分:
svc_entry保存寄存器的值,最后的结果就是对变量pt_regs赋值
struct pt_regs {
unsigned long uregs[18];
};
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]
BLANK();
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
BLANK();
先说下svc_exit,就是从中断返回,没有做其他事情
svc_exit r5 @ return from exception
.macro svc_exit, rpsr
msr spsr_cxsf, \rpsr
clrex @ clear the exclusive monitor
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.endm
3.中断处理框架的irq_handle
arch/arm/kernel/setup.c
这里引入一个简单的抽象层 handle_arch_irq,被平台具体的 interrupt controller赋值
handle_arch_irq = mdesc->handle_irq;
DT_MACHINE_START(TL7689_PAD_TEST,"NUFRONT-TL7689-PAD-AURORA")
.map_io = tl7689_map_io,
.init_irq = of_pad_test_gic_init,
.timer = &tl7689_timer,
.handle_irq = gic_handle_irq,
.init_machine = tl7689_dt_init,
.init_early = tl7689_init_early,
.dt_compat = tl7689_dt_board_compat,
MACHINE_END
最后看下macro irq_handler
.macro irq_handlerldr r1, =handle_arch_irq
mov r0, sp
adr lr, BSYM(9997f)/*这里是设置返回地址到lr*/
ldr pc, [r1]
9997:
.endm
3.1 irq_handle的C入口点
找到中断处理的入口点:gic_handle_irqcrash> handle_arch_irq
handle_arch_irq = $1 =
{<text variable, no debug info>} 0xc074284c <handle_arch_irq>
crash> rd 0xc074284c
c074284c: c0008458
crash> dis c0008458
0xc0008458 <gic_handle_irq>: mov r12, sp
arch/arm/common/gic.c
asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & ~0x1c00;
if (likely(irqnr > 15 && irqnr < 1021)) {
irqnr = irq_find_mapping(gic->domain, irqnr);
handle_IRQ(irqnr, regs);
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
}
arch/arm/kernel/irq.c
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq);
}
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
kernel/irq/irqdesc.c
int generic_handle_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
generic_handle_irq_desc(irq, desc);
return 0;
}
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq, desc);
}
是因为CONFIG_MULTI_IRQ_HANDLER开关没有使用arch_irq_handler_default
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr r1, =handle_arch_irq
mov r0, sp
adr lr, BSYM(9997f)
ldr pc, [r1]
#else
arch_irq_handler_default
#endif
9997:
.endm
/*
* Interrupt handling. Preserves r7, r8, r9
*/
.macro arch_irq_handler_default
get_irqnr_preamble r6, lr
1: get_irqnr_and_base r0, r2, r6, lr
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, BSYM(1b)
bne asm_do_IRQ
/*函数asm_do_IRQ的两个参数是在汇编里得到的, 而gic_handle_irq只有一个参数
*中断号是在函数本身得到的,但最终调用的都是handle_IRQ(irqnr, regs);
*/
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
handle_IRQ(irq, regs);
}
3.2 每个中断号对应中断处理函数的框架
void handle_IRQ(unsigned int irq, struct pt_regs *regs){
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq);
}
/* AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
/*到开始进入gic_handle_irq为止,中断是关闭的,何时打开的呀?接着看 handle_IRQ*/
handle_IRQ -> generic_handle_irq(irq) -> generic_handle_irq_desc(irq, desc)
-> desc->handle_irq(irq, desc);
没个中断号对应的不同的handle:以handle_level_irq为例。
3.2.1 中断号具体的中断处理函数
/***********************************************************************************/void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
raw_spin_lock(&desc->lock);
/*调用具体的chip相关的函数mask and ack*/
mask_ack_irq(desc);
/*如果另一个CPU在执行该中断即状态为IRQD_IRQ_INPROGRESS
*则让另一个中断处理该中断,直接退出该中断处理
*/
if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
if (!irq_check_poll(desc))
goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
/*
* If its disabled or no action available
* keep it masked and get out of here
*/
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
goto out_unlock;
/*处理该中断*/
handle_irq_event(desc);
cond_unmask_irq(desc);
out_unlock:
raw_spin_unlock(&desc->lock);
}
3.2.1.1 调用注册的中断处理函数
/**这里关联到irqaction
*/
irqreturn_t handle_irq_event(struct irq_desc *desc)
{
struct irqaction *action = desc->action;
irqreturn_t ret;
desc->istate &= ~IRQS_PENDING;
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
raw_spin_unlock(&desc->lock);
ret = handle_irq_event_percpu(desc, action);
raw_spin_lock(&desc->lock);
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
return ret;
}
irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
irqreturn_t retval = IRQ_NONE;
do{
action->handler(irq, action->dev_id);
switch (res) {
case IRQ_WAKE_THREAD:
irq_wake_thread(desc, action);
/* Fall through to add to randomness */
case IRQ_HANDLED:
random |= action->flags;
break;
default:
break;
action = action->next;
}while(action);
if (random & IRQF_SAMPLE_RANDOM)/*中断源作为随机数的种子*/
add_interrupt_randomness(irq);
}
/*到这里完成了注册的中断函数的调用,是在关中断的条件下完成的,中断处理还有一部分*/
3.2.2 irq_exit
/*
* Exit an interrupt context. Process softirqs if needed and possible:
*/
void irq_exit(void)
{
account_system_vtime(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
rcu_irq_exit();
sched_preempt_enable_no_resched();
}
看看进入softirq的条件:!in_interrupt()
#define in_interrupt() (irq_count())
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
/*preempt_count是thread_info的一个成员*/
#define preempt_count() (current_thread_info()->preempt_count)
看看进入softirq的条件:就是没有未处理完的 HARDIRQ and SOFTIRQ,并且软件中断有pending 位;
这里能看出串行的意思:如果当前有 softirq就退出,但是它的action 会在__do_softirq被调用.
另外一个有关串行的地方:tasklet,如果发现tasklet_struct的flag为TASKLET_STATE_RUN【Tasklet is running (SMP only)】
这次就不执行,加入到队尾,下次再尝试执行。 同一个tasklet不会发生竞争。
不同tasklet就可能发生竞争:虽然!in_interrupt()保证,不同tasklet在同一个cpu上是串行的,但是不同的tasklet可以在不同的CPU上运行。
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __this_cpu_read(tasklet_vec.head);
__this_cpu_write(tasklet_vec.head, NULL);
__this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
static inline void invoke_softirq(void)
{
if (!force_irqthreads) {
do_softirq();
}
}
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
if (in_interrupt())
return;
local_irq_save(flags);
pending = local_softirq_pending();
if (pending)
__do_softirq();
local_irq_restore(flags);
}
/*在这里终于看到了开中断的地方:local_irq_enable();
*在处理softirq_action的前后:local_irq_enable() and local_irq_disable();
*/
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
pending = local_softirq_pending();
__local_bh_disable((unsigned long)__builtin_return_address(0),
SOFTIRQ_OFFSET);
restart:
local_irq_enable();
h = softirq_vec;
do {
if (pending & 1) {
unsigned int vec_nr = h - softirq_vec;
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(vec_nr);
trace_softirq_entry(vec_nr);
h->action(h);
trace_softirq_exit(vec_nr);
}
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;
if (pending)
wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current);
__local_bh_enable(SOFTIRQ_OFFSET);
}
crash> softirq_vec
softirq_vec = $3 =
{{
action = 0xc0030280 <tasklet_hi_action>
}, {
action = 0xc00367dc <run_timer_softirq>
}, {
action = 0xc041a874 <net_tx_action>
}, {
action = 0xc041d69c <net_rx_action>
}, {
action = 0xc01f0e0c <blk_done_softirq>
}, {
action = 0xc01f1534 <blk_iopoll_softirq>
}, {
action = 0xc00303a8 <tasklet_action>
}, {
action = 0xc005cf54 <run_rebalance_domains>
}, {
action = 0xc004cb5c <run_hrtimer_softirq>
}, {
action = 0xc0087068 <rcu_process_callbacks>
}
}
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __this_cpu_read(tasklet_vec.head);
__this_cpu_write(tasklet_vec.head, NULL);
__this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
函数的主要功能是遍历tasklet_vec
crash> tasklet_vec
PER-CPU DATA TYPE:
struct tasklet_head tasklet_vec;
4. 开关中断的汇编代码
local_irq_enable()/ local_irq_disable()对应的汇编代码
使用中断内在函数生成用于更改当前抢先优先级的 CPSIE 或 CPSID 指令(请参阅Table 2.10)。 例如,在使用 __disable_irq 内在函数时,编译器会生成 CPSID i 指令,该指令将 PRIMASK 设置为 1。 这将把执行优先级提升为 0,并阻止具有可配置优先级的异常进入。 请参阅《ARMv7-M 体系结构参考手册》。
Table 2.10. 中断内在函数
内在函数 操作码 PRIMASK FAULTMASK
__enable_irq CPSIE i 0
__disable_irq CPSID i 1
__enable_fiq CPSIE f 0
__disable_fiq CPSID f 1
static inline void arch_local_irq_enable(void)
{
asm volatile(
" cpsie i @ arch_local_irq_enable"
:
:
: "memory", "cc");
}
5. 如果没有进入softirq,怎样打开中断的?
是在退出中断时,打开的【恢复就是打开啊,原来总是开中断的】
svc_exit r5 @ return from exception
.macro svc_exit, rpsr
msr spsr_cxsf, \rpsr
clrex @ clear the exclusive monitor
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.endm
6 有关的函数类型和关系
有两个函数类型: irq_flow_handle and irq_handle
irq_flow_handle是框架型的,会调有上层的用户注册的 irq_handle,和ack等操作。
typedef void (*irq_flow_handler_t)(unsigned int irq, struct irq_desc *desc);
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
crash> irq_handler_t
typedef enum irqreturn {IRQ_NONE, IRQ_HANDLED, IRQ_WAKE_THREAD} (*)(int, void *)
irq_flow_handle是具体的interrput controller chip相关的
crash> irq_desc | grep handle_irq
handle_irq = 0xc007cfa0 <handle_bad_irq>,
handle_irq = 0xc007f9c0 <handle_percpu_devid_irq>,
handle_irq = 0xc007fed8 <handle_level_irq>,
handle_irq = 0xc007fc3c <handle_edge_irq>,
handle_irq = 0xc00251b0 <tl7689_gpio_irq_dispatch>,
handle_irq = 0xc007fdb4 <handle_fasteoi_irq>,
kernel/irq/chip.c
handle_xxx_yyy
kernel/irq/handle.c
void handle_bad_irq(unsigned int irq, struct irq_desc *desc)
{
print_irq_desc(irq, desc);
kstat_incr_irqs_this_cpu(irq, desc);
ack_bad_irq(irq);
}
- 中断及中断处理过程
- 中断及中断处理过程
- 中断及中断处理过程
- 中断及中断处理过程
- WinCE中断处理过程
- ARM 中断处理过程
- 中断处理调用过程
- ARM中断处理过程
- Linux中断处理过程
- Linux中断处理过程
- 中断的处理过程
- Nucleus中断处理过程!!!!
- 中断处理过程
- 中断的处理过程
- 中断处理过程
- ARM中断处理过程
- 中断处理过程
- 中断处理过程
- snprintf/_snprintf 在不同平台间函数差异
- Html锚点连接_邮箱地址链接_实例
- Qt窗口部件
- 疯狂java .....~~~
- 面向切面编程AOP
- 中断处理过程
- c# 中运用js实现百分比数据加载提示
- SVN简单流程---以公司的使用方法为例
- Latex笔记
- 性能测试(并发负载压力)测试分析-简要篇 (转载)
- CUDA: GPU的异构程序开发流程
- IOS使用内置地图,定位应用开发
- jQuery 文本编辑器插件 HtmlBox 使用
- Android 网络请求通用的get与post方式