Re: Nested interrupts problem, not SA_INTERRUPT

Richard Black (Richard.Black@cl.cam.ac.uk)
Thu, 13 Jul 1995 11:26:09 +0100


Yesterday I mailed the list with this question (which I had previously posted to
comp.os.linux.development.system).

> I have written a device driver which is using an interrupt (5 actually). I am
> not using the SA_INTERRUPT flag. However when my interface is generating a very
> heavy interrupt load I observe the system clock falling behind wall clock time.
>
> My reading of the code in irq.c and irq.h is that with no flags it should be
> possible for the clock interrupt handler to interrupt my devices handler
> preventing this slippage.
>
> I have checked the interrupt enable flag when inside my device driver and it
> does appear to be set.

This morning I realised that what was happening was that the clock interrupt was
being interrupted by my interrupt, and so further (nested) clock interrupts were
disabled for that reason.

I have modified the linux timer and interrupt code to do the "hardclock thing"
found in other versions of Unix - specifically to ensure that no other
interrupts are allowed to interrupt the timer interrupt. This solved the problem.
The diffs are included below:

-----
Richard Black (usual disclaimers)
University of Cambridge
Computer Laboratory
Cambridge
United Kingdom

===================================================================
RCS file: RCS/signal.h,v
retrieving revision 1.1
diff -c3 -r1.1 signal.h
*** 1.1 1995/07/13 07:32:22
--- signal.h 1995/07/13 07:35:53
***************
*** 57,62 ****
--- 57,70 ----
#define SA_INTERRUPT 0x20000000
#define SA_NOMASK 0x40000000
#define SA_ONESHOT 0x80000000
+ /*
+ * Richard.Black@cl.cam.ac.uk 1995/07/13
+ *
+ * SA_INTERRUPT is NOT a NO-OP as stated above. It is used by
+ * arch/i385/kernel/irq.c. Added SA_HARDCLOCK (kernel interrupt flag
+ * like SA_INTERRUPT) for ensuring that clock ticks do not get lost.
+ */
+ #define SA_HARDCLOCK 0x04000000

#define SIG_BLOCK 0 /* for blocking signals */
#define SIG_UNBLOCK 1 /* for unblocking signals */
===================================================================
RCS file: RCS/irq.h,v
retrieving revision 1.1
diff -c3 -r1.1 irq.h
*** 1.1 1995/07/13 07:29:54
--- irq.h 1995/07/13 08:09:15
***************
*** 120,130 ****
--- 120,132 ----

#define IRQ_NAME2(nr) nr##_interrupt(void)
#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
+ #define HARDCLOCK_IRQ_NAME(nr) IRQ_NAME2(hardclock_IRQ##nr)
#define FAST_IRQ_NAME(nr) IRQ_NAME2(fast_IRQ##nr)
#define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr)

#define BUILD_IRQ(chip,nr,mask) \
asmlinkage void IRQ_NAME(nr); \
+ asmlinkage void HARDCLOCK_IRQ_NAME(nr); \
asmlinkage void FAST_IRQ_NAME(nr); \
asmlinkage void BAD_IRQ_NAME(nr); \
__asm__( \
***************
*** 136,141 ****
--- 138,158 ----
"incl _intr_count\n\t"\
"sti\n\t" \
"movl %esp,%ebx\n\t" \
+ "pushl %ebx\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call _do_IRQ\n\t" \
+ "addl $8,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ "decl _intr_count\n\t" \
+ "jmp ret_from_sys_call\n" \
+ "\n.align 4\n" \
+ "_hardclock_IRQ" #nr "_interrupt:\n\t" \
+ "pushl $-"#nr"-2\n\t" \
+ SAVE_ALL \
+ ACK_##chip(mask) \
+ "incl _intr_count\n\t"\
+ "movl %esp,%ebx\n\t" \
"pushl %ebx\n\t" \
"pushl $" #nr "\n\t" \
"call _do_IRQ\n\t" \
===================================================================
RCS file: RCS/irq.c,v
retrieving revision 1.1
diff -c3 -r1.1 irq.c
*** 1.1 1995/07/13 07:28:31
--- irq.c 1995/07/13 07:42:57
***************
*** 107,114 ****
BUILD_IRQ(SECOND,15,0x80)

/*
! * Pointers to the low-level handlers: first the general ones, then the
! * fast ones, then the bad ones.
*/
static void (*interrupt[16])(void) = {
IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt,
--- 107,114 ----
BUILD_IRQ(SECOND,15,0x80)

/*
! * Pointers to the low-level handlers: first the general ones, then
! * the hardclock ones, then the fast ones, then the bad ones.
*/
static void (*interrupt[16])(void) = {
IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt,
***************
*** 117,122 ****
--- 117,133 ----
IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt
};

+ static void (*hardclock_interrupt[16])(void) = {
+ hardclock_IRQ0_interrupt, hardclock_IRQ1_interrupt,
+ hardclock_IRQ2_interrupt, hardclock_IRQ3_interrupt,
+ hardclock_IRQ4_interrupt, hardclock_IRQ5_interrupt,
+ hardclock_IRQ6_interrupt, hardclock_IRQ7_interrupt,
+ hardclock_IRQ8_interrupt, hardclock_IRQ9_interrupt,
+ hardclock_IRQ10_interrupt, hardclock_IRQ11_interrupt,
+ hardclock_IRQ12_interrupt, hardclock_IRQ13_interrupt,
+ hardclock_IRQ14_interrupt, hardclock_IRQ15_interrupt
+ };
+
static void (*fast_interrupt[16])(void) = {
fast_IRQ0_interrupt, fast_IRQ1_interrupt,
fast_IRQ2_interrupt, fast_IRQ3_interrupt,
***************
*** 170,176 ****
continue;
len += sprintf(buf+len, "%2d: %8d %c %s\n",
i, kstat.interrupts[i],
! (action->flags & SA_INTERRUPT) ? '+' : ' ',
action->name);
}
return len;
--- 181,189 ----
continue;
len += sprintf(buf+len, "%2d: %8d %c %s\n",
i, kstat.interrupts[i],
! (action->flags & SA_INTERRUPT) ?
! ((action->flags & SA_HARDCLOCK) ? '*' : '+')
! : ' ',
action->name);
}
return len;
***************
*** 226,233 ****
action->mask = 0;
action->name = devname;
if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
! if (action->flags & SA_INTERRUPT)
set_intr_gate(0x20+irq,fast_interrupt[irq]);
else
set_intr_gate(0x20+irq,interrupt[irq]);
}
--- 239,250 ----
action->mask = 0;
action->name = devname;
if (!(action->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
! if (action->flags & SA_INTERRUPT) {
! if (action->flags & SA_HARDCLOCK)
! set_intr_gate(0x20+irq,hardclock_interrupt[irq]);
! else
set_intr_gate(0x20+irq,fast_interrupt[irq]);
+ }
else
set_intr_gate(0x20+irq,interrupt[irq]);
}
===================================================================
RCS file: RCS/sched.c,v
retrieving revision 1.1
diff -c3 -r1.1 sched.c
*** 1.1 1995/07/13 06:36:27
--- sched.c 1995/07/13 08:33:32
***************
*** 663,669 ****
continue;
mark_bh(TIMER_BH);
}
! cli();
itimer_ticks++;
if (itimer_ticks > itimer_next)
need_resched = 1;
--- 663,669 ----
continue;
mark_bh(TIMER_BH);
}
! /* RJB cli(); */
itimer_ticks++;
if (itimer_ticks > itimer_next)
need_resched = 1;
***************
*** 671,677 ****
mark_bh(TIMER_BH);
if (tq_timer != &tq_last)
mark_bh(TQUEUE_BH);
! sti();
}

asmlinkage int sys_alarm(long seconds)
--- 671,677 ----
mark_bh(TIMER_BH);
if (tq_timer != &tq_last)
mark_bh(TQUEUE_BH);
! /* RJB sti(); */
}

asmlinkage int sys_alarm(long seconds)
***************
*** 781,787 ****
bh_base[TIMER_BH].routine = timer_bh;
bh_base[TQUEUE_BH].routine = tqueue_bh;
bh_base[IMMEDIATE_BH].routine = immediate_bh;
! if (request_irq(TIMER_IRQ, do_timer, 0, "timer") != 0)
panic("Could not allocate timer IRQ!");
enable_bh(TIMER_BH);
enable_bh(TQUEUE_BH);
--- 781,787 ----
bh_base[TIMER_BH].routine = timer_bh;
bh_base[TQUEUE_BH].routine = tqueue_bh;
bh_base[IMMEDIATE_BH].routine = immediate_bh;
! if (request_irq(TIMER_IRQ, do_timer, SA_INTERRUPT | SA_HARDCLOCK, "timer") != 0)
panic("Could not allocate timer IRQ!");
enable_bh(TIMER_BH);
enable_bh(TQUEUE_BH);

======================================================================

End.