2011年9月份时候做的笔记, 当时阅读中断子系统的代码后做的一个PPT, 内核版本不记得了, 硬件平台是samsung 的S5PV210.
这部分主要是针对汇编和hard irq的部分, 在hard irq处理后的softirq的处理, 以及下半部的处理(tasklet/workqueue)都没有涉及.
Agenda
struct irq_desc <strong>irq_desc</strong>[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
}; //NR_IRQS = 393 in R70
struct irq_desc {
<strong> unsigned int irq;</strong>
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
<strong> irq_flow_handler_t handle_irq; //high level irq-events handle
struct irq_chip *chip;</strong>
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
<strong> struct irqaction *action; /* IRQ action list */</strong>
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;
raw_spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
const struct cpumask *affinity_hint;
unsigned int node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;struct <strong>irq_chip</strong> {
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;
};struct <strong>irqaction</strong> {
<strong> irq_handler_t handler; // handler assigned by request_irq</strong>
unsigned long flags;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
};之间的关系图:?External interrupt resources in S5PV210
# cat /proc/interrupts //只显示有相应的action的IRQ的信息
CPU0
IRQ_NR count desc->chip->name action->name
16: 43 s3c-uart s5pv210-uart
18: 59 s3c-uart s5pv210-uart
33: 1 s5p_vic_eint mmc1
36: 0 s5p_vic_eint a700_ts
37: 1 s5p_vic_eint aic3254 headset irq
38: 0 s5p_vic_eint keypad
39: 0 s5p_vic_eint keypad
40: 0 s5p_vic_eint keypad
41: 0 s5p_vic_eint keypad
42: 0 s5p_vic_eint keypad
43: 0 s5p_vic_eint keypad
45: 1 s5p_vic_eint hpd
46: 1 s5p_vic_eint USB wak up
50: 0 VIC s3c-pl330.0
51: 0 VIC s3c-pl330.1
52: 0 VIC s3c-pl330.2
58: 0 VIC System timer
59: 0 VIC s3c2410-wdt
61: 14772 VIC rtc-tick
78: 220 VIC s3c2440-i2c.0
83: 27985 VIC s3c2440-i2c.2
88: 1 VIC s3c-udc
90: 52662 VIC mmc0
92: 268 VIC mmc1
93: 0 VIC s3c-csis
97: 2582 VIC s3cfb, s3cfb
102: 0 VIC s3c-fimc1
103: 0 VIC s3c-fimc2
105: 0 VIC s3c-g2d
106: 747 VIC pvrsrvkm
107: 0 VIC s5p-tvout
108: 0 VIC s5p-tvout
109: 0 VIC s3c2440-i2c.1
110: 0 VIC s3c-mfc
111: 0 VIC s5p-tvout
130: 13 VIC mmc2
170: 0 s5p-eint Bq27520_INT
Err: 0start_kernel
setup_arch
early_trap_init
early_irq_init //没做什么事
init_IRQ
s5pv210_init_irq
s5p_init_irq
vic_init(irq_nr直接是从32开始的, 前面的目前看起来至少留给了timer和UART)
s3c_init_vic_timer_irq
s3c_init_uart_irqs其中的early_trap_init, 基本的思路就是, 对于有MMU的系统, 异常向量的虚拟地址被映射到0xFFFF0000, 所以, 真正的7个异常向量(__vectors_start~__vectors_end)是被拷贝到这个0xFFFF0000开始的地方了. 接着, 异常处理代码块(__stubs_start~__stubs_end)被拷贝到0xFFFF0200处.
void __init early_trap_init(void)
{
unsigned long vectors = CONFIG_VECTORS_BASE; //0xFFFF0000
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
<strong> memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);</strong>
/*
* Copy signal return handlers into the vector page, and
* set sigreturn to be a pointer to these.
*/
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes));
memcpy((void *)KERN_RESTART_CODE, syscall_restart_code, sizeof(syscall_restart_code));
flush_icache_range(vectors, vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}__vectors_start: ARM( swi SYS_ERROR0 ) THUMB( svc #0 ) THUMB( nop ) W(b) vector_und + stubs_offset W(ldr) pc, .LCvswi + stubs_offset W(b) vector_pabt + stubs_offset // prefetch abort W(b) vector_dabt + stubs_offset // data abort W(b) vector_addrexcptn + stubs_offset // W(b) vector_irq + stubs_offset //IRQ入口 W(b) vector_fiq + stubs_offset //FIQ .globl __vectors_end __vectors_end:
@@@ 中断处理程序的 stub
vector_irq:
@ 调整 LR_irq
sub lr, lr, #4
@ 保存 R0, LR_irq(中断之前的 PC, 断点), SPSR_irq(中断之前的 CPSR) 到 irq模式的栈中
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@ SPSR 设置为 SVC模式
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE)
msr spsr_cxsf, r0
@ 根据中断前的模式跳转到相应的处理程序
@ lr是中断刚开始时的 SPSR,即被中断代码的 CPSR,其低 4位表示中断之前的模式
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
@ 跳转到相应模式的处理程序,模式变为 SVC(SPSR 拷贝到 CPSR )
movs pc, lr
@ 跳转表,必须紧跟 ldr lr,[pc,lr,lsl #2]和 movs pc,lr 两条指令(ARM 流水线机制??)
.long __irq_usr @ 0 (USR)
.long __irq_invalid @ 1 (FIQ)
.long __irq_invalid @ 2 (IRQ)
.long __irq_svc @ 3 (SVC)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6 (ABT)
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b (UND)
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f (SYS)
@@@ USR模式中断入口
__irq_usr:
@ 在内核栈中产生 include/asm-arm/ptrace.h中 pt_regs 定义的栈帧结构
sub sp, sp, #S_FRAME_SIZE
stmib sp, {r1 - r12}
ldmia r0, {r1 - r3}
add r0, sp, #S_PC @ here for interlock avoidance
mov r4, #-1 @ "" "" "" ""
str r1, [sp] @ save the "real" r0 copied
@ from the exception stack
@ We are now ready to fill in the remaining blanks on the stack:
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@ Also, separately save sp_usr and lr_usr
stmia r0, {r2 - r4}
stmdb r0, {sp, lr}^
@ Clear FP to mark the first stack frame
zero_fp
@ 把被中断任务的 preempt_count 增加 1
get_thread_info tsk
#ifdef CONFIG_PREEMPT
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
add r7, r8, #1 @ increment it
str r7, [tsk, #TI_PREEMPT]
#endif
<div><div>?@ 循环调用 asm_do_IRQ()
</div><div>?1: get_irqnr_and_base r0, r6, r5, lr
</div><div>? movne r1, sp
</div><div>?@ routine called with r0 = irq number, r1 = struct pt_regs *
</div><div>? adrne lr, 1b
</div><div>? <span style="color:red;"><strong>bne asm_do_IRQ</strong></span>
</div><div>?
</div><div>?#ifdef CONFIG_PREEMPT
</div><div>? ldr r0, [tsk, #TI_PREEMPT]
</div><div>? str r8, [tsk, #TI_PREEMPT]
</div><div>? teq r0, r7
</div><div>? strne r0, [r0, -r0]
</div><div>?#endif
</div><div>?@ 返回到 user 模式
</div><div>? mov why, #0
</div><div>?<strong><span style="color:#FF0000;"> b ret_to_user</span></strong>
</div></div>@@@ SVC模式中断入口
__irq_svc:
@ 在内核栈中产生 include/asm-arm/ptrace.h中 pt_regs 定义的栈帧结构
sub sp, sp, #S_FRAME_SIZE
tst sp, #4
bicne sp, sp, #4
stmib sp, {r1 - r12}
ldmia r0, {r1 - r3}
add r5, sp, #S_SP @ here for interlock avoidance
mov r4, #-1 @ "" "" "" ""
add r0, sp, #S_FRAME_SIZE @ "" "" "" ""
addne r0, r0, #4
str r1, [sp] @ save the "real" r0 copied from the exception stack
mov r1, lr
@ We are now ready to fill in the remaining blanks on the stack:
@ r0 - sp_svc
@ r1 - lr_svc
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
stmia r5, {r0 - r4}
@ 把被中断任务的 preempt_count 增加 1
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
add r7, r8, #1 @ increment it
str r7, [tsk, #TI_PREEMPT]
#endif
@ 循环调用 asm-do_IRQ()
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
@ routine called with r0 = irq number, r1 = struct pt_regs *
adrne lr, 1b
<span style="color:#FF0000;"><strong> bne asm_do_IRQ </strong></span>
@ 如果需要调度,调用 svc_preempt进行内核抢占
#ifdef CONFIG_PREEMPT
ldr r0, [tsk, #TI_FLAGS] @ get flags
tst r0, #_TIF_NEED_RESCHED
blne svc_preempt
preempt_return:
ldr r0, [tsk, #TI_PREEMPT] @ read preempt value
str r8, [tsk, #TI_PREEMPT] @ restore preempt count
teq r0, r7
strne r0, [r0, -r0] @ bug()
#endif
@ 返回到内核空间
ldr r0, [sp, #S_PSR] @ irqs are already disabled
msr spsr_cxsf, r0
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsrasmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
<strong> irq_enter();</strong>
/*
* 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 {
<span style="color:#FF0000;"><strong> generic_handle_irq(irq);</strong></span>
}
/* AT91 specific workaround */
irq_finish(irq);
<strong> irq_exit();</strong>
set_irq_regs(old_regs);
}static inline void generic_handle_irq(unsigned int irq)
{
generic_handle_irq_desc(irq, irq_to_desc(irq));
}
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
<strong> desc->handle_irq(irq, desc); // high level handle, take handle_level_irq for example</strong>
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
else
__do_IRQ(irq);
#endif
}Void handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret;
raw_spin_lock(&desc->lock);
mask_ack_irq(desc, irq);
if (unlikely(desc->status & IRQ_INPROGRESS))
goto out_unlock;
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq, desc);
action = desc->action;
if (unlikely(!action || (desc->status & IRQ_DISABLED)))
goto out_unlock;
desc->status |= IRQ_INPROGRESS;
raw_spin_unlock(&desc->lock);
<strong> action_ret = handle_IRQ_event(irq, action);</strong>
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
raw_spin_lock(&desc->lock);
desc->status &= ~IRQ_INPROGRESS;
if (!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT)))
unmask_irq(desc, irq);
out_unlock:
raw_spin_unlock(&desc->lock);
}然后是handle_IRQ_event, 这边调用了request_irq时候注册的那个handle.irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0;
do {
trace_irq_handler_entry(irq, action);
<strong> ret = action->handler(irq, action->dev_id); // handle registered by request_irq</strong>
trace_irq_handler_exit(irq, action, ret);
switch (ret) {
case IRQ_WAKE_THREAD:
…
/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break;
default:
break;
}
retval |= ret;
action = action->next;
} while (action);
if (status & IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();
return retval;
}以图表示的话, 就是:int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) 参数: irq---中断通道号,取值范围为 0~NR_IRQS – 1 handler---中断处理程序,原型为 irq_return_t isr_func(int irq, void *dev_id) irq_flags---标志位 dev_name---名称,将会显示在/proc/interrupts中 dev_id---区分共享同一个中断通道的不同的处理程序 void free_irq(unsigned int irq, void *dev_id) 参数: irq---中断通道号,取值范围为 0~NR_IRQS – 1 dev_id---区分共享同一个中断通道的不同的处理程序时才需要用到.
int set_irq_chip(unsigned int irq, struct irq_chip *chip) 设置 chip int set_irq_chip_data(unsigned int irq, void *data) 设置 chip_data int set_irq_handle(unsigned int irq, irq_flow_handler_t handle) 设置 handle_irq int set_irq_data(unsigned int irq, void *data) 设置 handler_data int set_irq_type(unsigned int irq, unsigned int type) 设置指定通道的触发类型
linux的中断子系统简介(汇编和hard irq部分)_ARM平台(S5PV210)
原文:http://blog.csdn.net/jackjones_008/article/details/42387241