linux的(de)中斷子(zǐ)系統簡介(彙編和(hé / huò)hardirq部分)_ARM平台(S
發表時(shí)間:2020-10-19
發布人(rén):融晨科技
浏覽次數:45
2011年9月份時(shí)刻做的(de)标記, 當時(shí)浏覽中斷子(zǐ)體系的(de)代碼後做的(de)一個(gè)PPT, 内核版本不(bù)記得了(le/liǎo), 硬件平台是(shì)samsung 的(de)S5PV210.
這(zhè)部分主如果針對彙編和(hé / huò)hard irq的(de)部分, 在(zài)hard irq處理後的(de)softirq的(de)處理, 以(yǐ)及下半部的(de)處理(tasklet/workqueue)都沒有涉及.
Agenda
?Interrupts in ARM
?Important structs
?External interrupt resources in S5PV210
?Code flow
?Kernel API
?Interrupts in ARM
ARM CPU CORE 中隻有兩根中斷引腳, 分别是(shì)IRQ和(hé / huò)FIQ.
?IRQ
–Why chip can handle so many IRQS? ===> VIC
?FIQ
?Important structs
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
irq_desc is a global struct describing all the interrupt lines in the system.
struct irq_desc { <span style="color:#FF0000;"> unsigned int irq;</span> struct timer_rand_state *timer_rand_state; unsigned int *kstat_irqs; #ifdef CONFIG_INTR_REMAP struct irq_2_iommu *irq_2_iommu; #endif <span style="color:#FF0000;"> irq_flow_handler_t handle_irq; //high level irq-events handle struct irq_chip *chip;</span> struct msi_desc *msi_desc; void *handler_data; void *chip_data; <span style="color:#FF0000;"> struct irqaction *action; /* IRQ action list */</span> 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> { <span style="color:#FF0000;"> irq_handler_t handler; // handler assigned by request_irq</span> 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; };之(zhī)間的(de)關系圖:
[img]http://img.blog.csdn.net/20150104101704954?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFja2pvbmVzXzAwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
?External interrupt resources in S5PV210
[img]http://img.blog.csdn.net/20150104101748946?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFja2pvbmVzXzAwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
?Code flow
起首可以(yǐ)查看體系中有哪些有效的(de)中斷以(yǐ)及相幹的(de)信息.
# cat /proc/interrupts //隻顯示有響應的(de)action的(de)IRQ的(de)信息 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: 0
起首是(shì)初始化的(de)過程, IRQ init sequence:
start_kernel setup_arch early_trap_init early_irq_init //沒做什麽事 init_IRQ s5pv210_init_irq s5p_init_irq vic_init(irq_nr直接是(shì)大(dà)年夜32開端的(de), 前面的(de)今朝看起來(lái)至少留給了(le/liǎo)timer和(hé / huò)UART) s3c_init_vic_timer_irq s3c_init_uart_irqs個(gè)中的(de)early_trap_init, 根本的(de)思路就(jiù)是(shì), 對于(yú)有MMU的(de)體系, 異常向量的(de)虛拟地(dì / de)址被映射到(dào)0xFFFF0000, 所以(yǐ), 真正的(de)7個(gè)異常向量(__vectors_start~__vectors_end)是(shì)被拷貝到(dào)這(zhè)個(gè)0xFFFF0000開端的(de)處所了(le/liǎo). 接着, 異常處理代碼塊(__stubs_start~__stubs_end)被拷貝到(dào)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. */ <span style="color:#FF0000;"> 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);</span> /* * 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); }
接下來(lái), 中斷産生後, 起首我們看到(dào)的(de)是(shì)前面注冊的(de)那些vectors. 異常向量表的(de)寫法重要(yào / yāo)應用跳轉指令B來(lái)進行. 因爲(wéi / wèi)異常向量表和(hé / huò)異常處理代碼塊之(zhī)間沒有跨越B指令請求的(de)2^24=32MB, 僅僅相差0x200. 然則因爲(wéi / wèi)vertor_swi不(bù)是(shì)在(zài)這(zhè)個(gè)文件中定義的(de), 所以(yǐ), 隻能用LDR指令來(lái)進行處理了(le/liǎo).
__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人(rén)口 W(b) vector_fiq + stubs_offset //FIQ .globl __vectors_end __vectors_end:
@@@ 中斷處理法度榜樣的(de) stub vector_irq: @ 調劑 LR_irq sub lr, lr, #4 @ 保存 R0, LR_irq(中斷之(zhī)前的(de) PC, 斷點), SPSR_irq(中斷之(zhī)前的(de) CPSR) 到(dào) irq模式的(de)┞坊中 stmia sp, {r0, lr} @ save r0, lr mrs lr, spsr str lr, [sp, #8] @ save spsr @ SPSR 設置爲(wéi / wèi) SVC模式 mrs r0, cpsr eor r0, r0, #(\mode ^ SVC_MODE) msr spsr_cxsf, r0 @ 根據中斷前的(de)模式跳轉到(dào)響應的(de)處理法度榜樣 @ lr是(shì)中斷剛開端時(shí)的(de) SPSR,即被中斷代碼的(de) CPSR,其低 4位表示中斷之(zhī)前的(de)模式 and lr, lr, #0x0f mov r0, sp ldr lr, [pc, lr, lsl #2] @ 跳轉到(dào)響應模式的(de)處理法度榜樣,模式變爲(wéi / wèi) SVC(SPSR 拷貝到(dào) CPSR ) movs pc, lr @ 跳轉表,必須緊跟 ldr lr,[pc,lr,lsl #2]和(hé / huò) 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)
user mode的(de)處理
@@@ USR模式中斷人(rén)口 __irq_usr: @ 在(zài)内核棧中産生 include/asm-arm/ptrace.h中 pt_regs 定義的(de)┞坊幀構造 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 @ 把被中斷義務的(de) 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 ?@ 輪回調用 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 ? bne <span style="color:#FF0000;">asm_do_IRQ</span> ? ?#ifdef CONFIG_PREEMPT ? ldr r0, [tsk, #TI_PREEMPT] ? str r8, [tsk, #TI_PREEMPT] ? teq r0, r7 ? strne r0, [r0, -r0] ?#endif ?@ 返回到(dào) user 模式 ? mov why, #0 ? <span style="color:#FF0000;">b ret_to_user</span>
svc mode的(de)處理
@@@ SVC模式中斷人(rén)口 __irq_svc: @ 在(zài)内核棧中産生 include/asm-arm/ptrace.h中 pt_regs 定義的(de)┞坊幀構造 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} @ 把被中斷義務的(de) 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 bne asm_do_IRQ @ 如不(bù)雅須要(yào / yāo)調劑,調用 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 @ 返回到(dào)内核空間 ldr r0, [sp, #S_PSR] @ irqs are already disabled msr spsr_cxsf, r0 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
可以(yǐ)看到(dào), 都調用了(le/liǎo) asm_do_IRQ
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); <span style="color:#FF0000;">irq_enter();</span> /* * 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;">generic_handle_irq(irq);</span> } /* AT91 specific workaround */ irq_finish(irq); irq_exit(); 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 <span style="color:#FF0000;">desc->handle_irq(irq, desc); // high level handle, take handle_level_irq for example</span> #else if (likely(desc->handle_irq)) desc->handle_irq(irq, desc); else __do_IRQ(irq); #endif }
然落後入 handle_level_irq
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); action_ret = <span style="color:#FF0000;">handle_IRQ_event(irq, action);</span> 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); }然後是(shì)handle_IRQ_event, 這(zhè)邊調用了(le/liǎo)request_irq時(shí)刻注冊的(de)那個(gè)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); ret = <span style="color:#FF0000;">action->handler(irq, action->dev_id); // handle registered by request_irq</span> 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; }以(yǐ)圖表示的(de)話, 就(jiù)是(shì):
[img]http://img.blog.csdn.net/20150104110240871?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFja2pvbmVzXzAwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
?Kernel API
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) 參數: irq---中斷通道(dào)号,取值範圍爲(wéi / wèi) 0~NR_IRQS – 1 handler---中斷處理法度榜樣,原型爲(wéi / wèi) irq_return_t isr_func(int irq, void *dev_id) irq_flags---标記位 dev_name---名稱,将會顯示在(zài)/proc/interrupts中 dev_id---區分共享同一個(gè)中斷通道(dào)的(de)不(bù)合的(de)處理法度榜樣 void free_irq(unsigned int irq, void *dev_id) 參數: irq---中斷通道(dào)号,取值範圍爲(wéi / wèi) 0~NR_IRQS – 1 dev_id---區分共享同一個(gè)中斷通道(dào)的(de)不(bù)合的(de)處理法度榜樣時(shí)才須要(yào / yāo)用到(dào).
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) 設置指定通道(dào)的(de)觸發類型
Ryan: PPT完成于(yú)2011.9.15, blog完成于(yú)2015.1.4