September 2011 Time to make notes, then read interrupt subsystem code after doing a PPT, kernel version do not remember, hardware platform is Samsung's s5pv210.
This part is mainly for the assembly and hard IRQ parts, the processing of SOFTIRQ after hard IRQ processing, and the processing of the lower half (tasklet/workqueue) are not involved.
Agenda
? Interrupts in ARM? Important structs? External Interrupt resources in s5pv210? Code flow? Kernel API
? Interrupts in ARM
There are only two interrupt pins in the ARM CPU CORE, IRQ and Fiq, respectively.
? 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_L Ock_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 {<strong>unsigned intirq;</strong>struct timer_rand_state *timer_rand_state;unsigned int *kstat_irqs, #ifdef config_intr_remapstruct irq_2_iommu *irq_2_iommu; #endif <strong>irq_flow_handler_thand Le_irq;//high level irq-events handlestruct irq_chip*chip;</strong>struct Msi_desc*msi_desc;void*handler_data ; void*chip_data;<strong>struct irqaction*action;/* IRQ action list */</strong>unsigned intstatus;/* IRQ Status */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 ; raw_spinlock_tlock; #ifdef config_smpcpumask_var_taffinity;const struct cpumask*affinity_hint;unsigned intnode;# Ifdef config_generic_pending_irqcpumask_var_tpending_mask; #endif #endifatomic_tthreads_active;wait_queue_head_t Wait_for_threads, #ifdef config_proc_fsstruct proc_dir_entry*dir; #endifconst 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_methodvoid (*release) (unsigned int IRQ, void *dev_id); #endif/* * for Compatibility,->typename is copied into->name. * would 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;};
Diagram of the relationship between:
? External Interrupt Resources in s5pv210
? Code Flow
You can start by looking at the effective interrupts and related information in your system.
# cat/proc/interrupts//Show only IRQ information with corresponding action CPU0IRQ_NR countdesc->chip->name action->name 16 : S3c-uart s5pv210-uart 18:59 s3c-uart s5pv210-uart 33:1s5p_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 s 5p_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-pl3 30.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 9 0:52662 VIC mmc0 92:268 Vic mmc1 93:0 Vic s3c-csis 97:2582 Vic S3CFB, s3cfb102 : 0 Vic s3c-fimc1103:0 Vic s3c-fimc2105:0 Vic s3c-g2d106:7 Vic pvrsrvkm107:0 Vic s5p-tvout108:0 Vic s5p-tvout109:0 Vic s3c2440-i2c.1110:0 Vic s3c-mfc111:0 Vic s5p-tvout130:13 VIC mmc2170:0 S5p-eint bq27520_interr:0
The first is the initialization process, IRQ init sequence:
Start_kernel setup_arch early_trap_init early_irq_init//didn't do anything Init_irq S5PV210_INIT_IRQ S5p_init_irq Vic_init (IRQ_NR is directly starting from 32, the front now looks at least left to the timer and UART) S3c_init_vic_timer_irqs3c_init_uart_irqs
Early_trap_init, the basic idea is that for a system with the MMU, the virtual address of the anomaly vector is mapped to 0xffff0000, so the real 7 anomaly vectors (__vectors_start~__vectors_end) It was copied to the place where the 0xffff0000 began. The exception-handling code block (__stubs_start~__stubs_end) is then copied to the 0xffff0200.
void __init early_trap_init (void) {unsigned long vectors = config_vectors_base; 0xffff0000extern 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 * is 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, Kuse R_SZ); </strong>/* * Copy signal return handlers into the vector page, and * Set Sigreturn to is 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);}
Next, after the break occurs, we first see those vectors that were previously registered. The exception vector table is mainly used in the notation of jump instruction B. Because there is no 2^24=32mb between the exception vector table and the exception handling code block that exceeds the requirements of the B Directive, only the difference is 0x200. However, because VERTOR_SWI is not defined in this file, it can only be handled with LDR directives.
__vectors_start:arm (SWISYS_ERROR0) thumb (svc#0) thumb (NOP) W (b) Vector_und + STUBS_OFFSETW (LDR) PC. Lcvswi + STUBS_OFFSETW (b) Vector_pabt + stubs_offset //prefetch ABORTW (b) Vector_dabt + stubs_offset //Data ABORTW (b) VECTOR_ADDREXCPTN + Stubs_offset //W (b) VECTOR_IRQ + STUBS_OFFSET//IRQ inlet W (b) Vector_fiq + Stubs_offset //fiq.globl__vectors_end__vectors_end:
@@@ 中断 Handler stub VECTOR_IRQ: @ Adjust LR_IRQ sub LR, LR, #4 @ save R0, Lr_irq (interrupt before PC, breakpoint), SPSR_IRQ (before interrupt CPSR) to IR Q-mode stack Stmia sp, {r0, LR} @ Save R0, LR Mrs LR, SPSR str lr, [sp, #8] @ save SPSR @ spsr set to Svc mode Mrs R 0, CPSR Eor r0, R0, # (\mode ^ svc_mode) msr spsr_cxsf, r0@ jump to the appropriate handler according to the pre-interrupt mode @ LR is the SPSR at the beginning of the interrupt, the CPSR of the interrupted code, and its low 4-bit representation Interrupt mode and LR, LR, #0x0f mov r0, SP ldr LR, [PC, LR, LSL #2] @ jump to the corresponding mode handler, the mode becomes SVC (SPSR copy to CPSR) Movs pc, LR @ Jump table, must follow Ldr LR,[PC,LR,LSL #2] and movs PC,LR two instructions (ARM pipelining??) . Long __irq_usr @ 0 (usr). Long __irq_invalid @ 1 (FIQ). Long __irq_invalid @ 2 (IRQ). Lo ng __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)
handling of User mode
@@@ usr mode interrupt entry __irq_usr: @ Generate include/asm-arm/ptrace.h pt_regs-defined stack frame structure in the kernel stack 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 is now ready to Fil L in the remaining blanks on the stack: @ r2-lr_<exception>, already fixed up for correct Return/restart @ r 3-spsr_<exception> @ r4-orig_r0 (see Pt_regs definition in ptrace.h) @ Also, separately save sp_usr and L R_usr stmia r0, {r2-r4} stmdb r0, {sp, lr}^ @ Clear FP to mark the first stack frame ZERO_FP @ put interrupted task preemp T_count added 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>[email protected] Loop call ASM_DO_IRQ () </div><div>?1: get_irqnr_and_base R0, R6, R5, LR </div><div>? movne R1, sp </ Div><div>[email protected] 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& nbsp R0, [R0,-r0] </div><div>? #endif </div><div>[email protected] return to user mode </div><div>? mov Why, #0 </div><div>?<strong><span style= "color:# FF0000; " > b ret_to_user</span></strong></div></div>
Treatment of SVC mode
@@@ svc Mode Interrupt entry __irq_svc: @ Generate include/asm-arm/ptrace.h Pt_regs defined stack frame structure in the kernel stack 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 Avoidanc E 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 is now ready to fill in the RE Maining blanks on the stack: @ r0-sp_svc @ r1-lr_svc @ r2-lr_<exception>, already fixed up for Correc T Return/restart @ r3-spsr_<exception> @ r4-orig_r0 (see Pt_regs definition in ptrace.h) Stmia R5, {R 0-R4} @ Increase the preempt_count of the interrupted task by 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 @ loop call ASM-DO_IRQ () 1:get_irqnr_an D_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> @ If scheduling is required, call svc_preempt for kernel preemption #ifdef config_preempt ld R 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,-R 0] @ Bug () #endif @ return to kernel space Ldr r0, [sp, #S_PSR] @ IRQs is already disabled MSR spsr_cxsf, R0 Ldmia sp, {R 0-pc}^ @ load r0-pc, CPSR
As you can see, both call the ASM_DO_IRQ
asmlinkage void __exception ASM_DO_IRQ (unsigned int IRQ, struct pt_regs *regs) {struct Pt_regs *old_regs = Set_irq_regs (reg s); <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); Handle, take HANDLE_LEVEL_IRQ for example</strong> #elseif (likely (DESC->HANDLE_IRQ)) desc-> HANDLE_IRQ (IRQ, DESC); Else__do_irq (IRQ); #endif}
And then into 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); <strong>action_ret = Handle_irq_event (IRQ, Action), </strong>if (!noirqdebug) note_interrupt (IRQ, DESC, Action_ret); Raw_spin_lock ( &desc->lock);d esc->status &= ~irq_inprogress;if ( Desc->status & (irq_disabled | Irq_oneshot)) UNMASK_IRQ (DESC, IRQ); Out_unlock:raw_spin_unlock (&desc->lock);}
Then there is the handle_irq_event, which calls the REQUEST_IRQ when the handle is registered.
irqreturn_t handle_irq_event (unsigned int IRQ, struct irqaction *action) {irqreturn_t ret, retval = irq_none;unsigned int s Tatus = 0;do {trace_irq_handler_entry (IRQ, action); <strong>ret = Action->handler (IRQ, action->dev_id);//< C0/>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;}
In the picture, that is:
? Kernel API
int REQUEST_IRQ (unsigned int IRQ, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) parameter : IRQ---Interrupt channel number, value range is 0~nr_irqs–1 handler---Interrupt handler, prototype is irq_return_t isr_func (int irq, void *dev_id) irq_flags---flag bit dev_na The Me---name, which will be displayed in/proc/interrupts dev_id---distinguish between different handlers that share the same interrupt channel void FREE_IRQ (unsigned int irq, void *dev_id) Parameters: IRQ---Interrupt channel number with a value range of 0~nr_irqs–1dev_id---is required to distinguish between different handlers that share the same interrupt channel.
int set_irq_chip (unsigned int IRQ, struct irq_chip *chip) set chip int set_irq_chip_data (unsigned int irq, void *data) Set ch ip_data int set_irq_handle (unsigned int IRQ, irq_flow_handler_t handle) set HANDLE_IRQ int set_irq_data (unsigned int IRQ, V OID *data) Set Handler_data int set_irq_type (unsigned int IRQ, unsigned int type) to set the trigger type for the specified channel
Ryan:ppt completed in 2011.9.15, blog completed in 2015.1.4
Introduction to the interrupt Subsystem for Linux (assembly and hard IRQ section) _arm Platform (s5pv210)