240 likes | 692 Views
제 42 강 : Interrupt(III) Functions for Interrupt Handling. irq_desc[ ]. ISR. timer. IRQ 1. Network. IRQ 2. action. g1(). g2(). IRQ 3. SCSI. action. IRQ m. f1(). f2(). Functions for Interrupt Handling. functions for interrupt. IRQ m CPU i.
E N D
제42강 : Interrupt(III) Functions for Interrupt Handling irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() Functions for Interrupt Handling
IRQm CPUi • CPUi updates irq_desc[m].status • IRQ_PENDING CPU i CPU k (2) PIC selects CPUi based on counter irq_desc[ ] APIC timer IRQ1 (4) CPUi determines which CPU should execute device specific ISR for IRQm? case A: No CPU is working on IRQm Let CPUi handle IRQ m case B: Other CPUk already working on IRQm i.e. status == INPROGRESS Let CPUk handle this IRQ m Let CPUk handle all IRQmsequentially because IRQ m data accesses have to be serialized anyway So let CPUk handle all IRQ m serially. . . . tty IRQ2 action g1() g1() IRQ3 SCSI dev #2 action IRQm f1() f1() dev #1 • Interrupt line IRQm • requests interrupt IRQ_DISABLED - at PIC IRQ_WAITING - interrupt not been raised IRQ_PENDING - Ack but not yet serviced by kernel IRQ_INPROGRESS - handler being executed
asmlinkage unsigned int do_IRQ(struct pt_regs regs) { int irq = regs.orig_eax & 0xff; irq_desc_t *desc = irq_desc + irq; struct irqaction * action; unsigned int status; irq_enter(); kstat_this_cpu.irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); status|= IRQ_PENDING; /* signal arrived. Just acked */ action = NULL; if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status|= IRQ_INPROGRESS;/* we’re handling it */ } desc->status = status; if (unlikely(!action)) goto out; for (;;) { irqreturn_t action_ret; spin_unlock(&desc->lock); action_ret = handle_IRQ_event(irq, ®s, action); spin_lock(&desc->lock); if (!noirqdebug) note_interrupt(irq, desc, action_ret); if (likely(!(desc->status & IRQ_PENDING))) break; desc->status &= ~IRQ_PENDING; } desc->status &= ~IRQ_INPROGRESS; out: desc->handler->end(irq); spin_unlock(&desc->lock); irq_exit(); return 1; } int handle_IRQ_event( ….) { int status = 1; int retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { status |= actionflags; retval |= actionhandler(irq, action->dev_id, regs); action = actionnext; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); return retval; }
irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() asmlinkage unsigned int do_IRQ(struct pt_regs regs) /* each irq is assigned a vector number */ { int irq = regs.orig_eax & 0xff; /* get irq vector on stack, put it into local variable */ irq_desc_t *desc = irq_desc + irq; /* pointer to array irq_desc[IRQm]*/ struct irqaction * action; unsigned int status; irq_enter(); kstat_this_cpu.irqs[irq]++; spin_lock(&desc->lock); /* wait if irq_desc[IRQm] is locked -- other CPU is accessing */ desc->handler->ack(irq); status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); /* dev not waiting any more*/ status |= IRQ_PENDING; /* Ack’ed. Need to handle it. */ action = NULL; /* initial value for action == NULL */ if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { /* Any CPU working for this IRQ?*/ action = desc->action; /* If yes, assign action pointer */ status &= ~IRQ_PENDING; /* no more in PENDING state */ status |= IRQ_INPROGRESS; /* we are handling it. Set the bit */ } descstatus = status; if (unlikely(!action)) goto out; /* If nothing has been assigned to action, do nothing */ I can proceed now I will register this request in global table PIC can go back to work & handle next signal
Three cases[Situation: IRQm interrupts CPUi ] CPU[i] desc[IRQm] • No CPU is currently working on IRQm now CPUi starts processing IRQm • Other CPUk is already running handler for IRQm CPUi does nothing (action NIL) for IRQm CPUi just changes state of IRQm to “PENDING” (new arrival & Ack’ed) (3) CPUk is completing handler for IRQm. Before exiting ISR, CPUk re-examines state of IRQm If it is back to PENDING, new signal arrived from IRQm while CPUk was handling previous request from IRQm
CPU[i] irq_desc[IRQm] Case (1)No CPU is working on IRQm CPUi starts handler for IRQm
irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() asmlinkage unsigned int do_IRQ(struct pt_regs regs) { int irq = regs.orig_eax & 0xff; /* get irq vector on stack, put it into local variable */ irq_desc_t *desc = irq_desc + irq; struct irqaction * action; unsigned int status; irq_enter(); kstat_this_cpu.irqs[irq]++; spin_lock(&desclock); deschandleack(irq); status = descstatus &~(IRQ_REPLAY | IRQ_WAITING); /* dev not waiting any more*/ status |= IRQ_PENDING; /* Need to handle it. Set PENDING*/ action = NULL; /* initial value for action == NULL */ if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { /* must I handle it? */ /* Not in progress */ action = descaction; /* no CPU is handling IRQm */ status &= ~IRQ_PENDING; /* no more in PENDING state */ status |= IRQ_INPROGRESS; /* I am handling it. Set the bit */ } descstatus = status; if (unlikely(!action)) goto out; /* If nothing has been assigned to action, do nothing */ this IRQm is not in progress? IRQ_DISABLED - interrupt not allowed (masked or not) IRQ_WAITING - (interrupt allowed but) interrupt not been raised IRQ_PENDING - (interrupt raised and Ack’ed). but notserviced by kernel yet IRQ_INPROGRESS – kernel is executing ISR
irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() for (;;) { irqreturn_t action_ret; spin_unlock(&desc->lock); action_ret = handle_IRQ_event(irq, ®s, action); /* call handler */ spin_lock(&desc->lock); if (!noirqdebug) note_interrupt(irq, desc, action_ret); if (likely(!(desc->status & IRQ_PENDING)))/*Re-check PENDING. I cleared before*/ break; /* It remains in reset state. Exit do_IRQ() */ desc->status &= ~IRQ_PENDING; /* New arrival. I handle it. Still INPROGRESS */ } desc->status &= ~IRQ_INPROGRESS; /* No longer INPROGRESS */ out: desc->handler->end(irq); spin_unlock(&desc->lock); irq_exit(); return 1; } IRQ_DISABLED - interrupt not allowed (masked or not) IRQ_WAITING - (interrupt allowed but) interrupt not been raised IRQ_PENDING - (interrupt raised and Ack’ed). but notserviced by kernel yet IRQ_INPROGRESS – kernel is executing ISR
inthandle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action) { int status = 1; /* Force the "do bottom halves" bit */ int retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { status |= actionflags; retval |= actionhandler(irq, action->dev_id, regs); action = actionnext; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); return retval; } device specific operation irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2()
CPU[i] irq_desc[IRQm] Case (2)Other CPUk is already working for IRQmI (CPUi ) will not handle IRQm I (CPUi) just mark “new arrival from IRQm” “IRQm ‘PENDING’state”
irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() asmlinkage unsigned int do_IRQ(struct pt_regs regs) { int irq = regs.orig_eax & 0xff; irq_desc_t *desc = irq_desc + irq; struct irqaction * action; unsigned int status; irq_enter(); kstat_this_cpu.irqs[irq]++; spin_lock(&desclock); deschandlerack(irq); status = descstatus & ~(IRQ_REPLAY | IRQ_WAITING); /* dev not waiting any more*/ status |= IRQ_PENDING; /* Need to handle it. Just Ack’ed */ action = NULL; /* initial value for action == NULL */ if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { /* must I handle it? */ action = desc->action; status &= ~IRQ_PENDING; status |= IRQ_INPROGRESS; } descstatus = status; if (unlikely(!action)) goto out; /* If nothing has been assigned to action, do nothing */ Other CPU is already working on this IRQm IRQ_DISABLED - interrupt not allowed (masked or not) IRQ_WAITING - (interrupt allowed but) interrupt not been raised IRQ_PENDING - (interrupt raised and Ack’ed). but notserviced by kernel yet IRQ_INPROGRESS – kernel is executing ISR
irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() for (;;) { irqreturn_t action_ret; spin_unlock(&desc->lock); action_ret = handle_IRQ_event(irq, ®s, action); /* call handler */ spin_lock(&desc->lock); if (!noirqdebug) note_interrupt(irq, desc, action_ret); if (likely(!(desc->status & IRQ_PENDING)))/*Re-check PENDING. I cleared before*/ break; /* It remains in reset state. Exit do_IRQ() */ desc->status &= ~IRQ_PENDING; /* New arrival. I handle it. Still INPROGRESS */ } desc->status &= ~IRQ_INPROGRESS; /* No longer INPROGRESS */ out: desc->handler->end(irq); spin_unlock(&desc->lock); irq_exit(); return 1; }
CPU[i] irq_desc[IRQm] Case (3) CPUk exits handler for IRQm. Before exiting handler, re-examines state of IRQm If IRQm is PENDING state, new request arrived from a device on IRQm line
irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action) { int status = 1; /* Force the "do bottom halves" bit */ int retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { status |= action->flags; retval |= action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); return retval; } device specific operation Returns to do_IRQ( )
/* This applies to any hw interrupts that allow a second instance of the same irq to arrive while we are in do_IRQ or in the handler. But code here only handles second_instance of the irq, not the third or fourth. So it is mostly useful for irq hardware that does not mask cleanly in an SMP environment. */ for (;;) { irqreturn_t action_ret; spin_unlock(&desc->lock); action_ret =handle_IRQ_event(irq, ®s, action); spin_lock(&desc->lock); if (!noirqdebug) note_interrupt(irq, desc, action_ret); if (likely(!(desc->status & IRQ_PENDING)))/*Re-check PENDING. I cleared before*/ break; /* It still remains in reset state. Exit do_IRQ() */ desc->status &= ~IRQ_PENDING; /* New arrival. I will handle this one too. */ } desc->status &= ~IRQ_INPROGRESS; /* No longer INPROGRESS */ out: /* The ->end() handler has to deal with interrupts which got * disabled while the handler was running. */ desc->handler->end(irq); spin_unlock(&desc->lock); irq_exit(); return 1; } irq_desc[ ] ISR timer IRQ1 Network IRQ2 action g1() g2() IRQ3 SCSI action IRQm f1() f2() IRQ_DISABLED - interrupt not allowed (masked or not) IRQ_WAITING - (interrupt allowed but) interrupt not been raised IRQ_PENDING - (interrupt raised and Ack’ed). but notserviced by kernel yet IRQ_INPROGRESS – kernel is executing ISR new arrival at IRQm no new arrival exit for() loop
State of IRQm PENDING WAITING INPROGRESS interrupt signal from device acknowledged but no CPU is running ISR for this IRQ yet no signal from device a CPU starts running ISR for this IRQ devices device PIC PIC selects CPUi PIC INTR CPUi CPUi runs do_IRQ( ) ack(irq): PIC back to work marks this IRQ PENDING A CPU must be assigned CPU is assigned to this irq starts processing device new arrivals? more handler? IRQ_DISABLED - interrupt not allowed (masked or not) IRQ_WAITING - (interrupt allowed but) interrupt not been raised IRQ_PENDING - (interrupt raised and Ack’ed). but notserviced by kernel yet IRQ_INPROGRESS - ISR is executing ……