diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 2001-11-12 20:28:21 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 2001-11-12 20:28:21 +0000 |
commit | 7ece4483fdd0ce93cdf7aa0d9e12cfebf7572485 (patch) | |
tree | 618636a54223bbdfac0b9635c3ef5be81f4ec7de /sys/arch/i386 | |
parent | a5a01c0181d2982008980b62763f2f94d0aae0c0 (diff) |
Redo the ICU interrupt handling system to better emulate an APIC system.
This is done by representing the current blockings of interrupts with
a priority level instead of an interrupt mask. This makes it possible to
share implementations for spl* functions/macros between both ICU
and APIC (a must when going MP) systems. In this process, assign
soft interrupts their own levels to match the way things will be on
APIC systems where they actually will be real interrupts and not just
bits in a pending mask as they are now.
Heavily inspired by Bill Studenmunds SMP work in NetBSD
Diffstat (limited to 'sys/arch/i386')
-rw-r--r-- | sys/arch/i386/i386/autoconf.c | 7 | ||||
-rw-r--r-- | sys/arch/i386/i386/locore.s | 4 | ||||
-rw-r--r-- | sys/arch/i386/include/intr.h | 115 | ||||
-rw-r--r-- | sys/arch/i386/isa/icu.h | 5 | ||||
-rw-r--r-- | sys/arch/i386/isa/icu.s | 20 | ||||
-rw-r--r-- | sys/arch/i386/isa/isa_machdep.c | 53 | ||||
-rw-r--r-- | sys/arch/i386/isa/npx.c | 6 | ||||
-rw-r--r-- | sys/arch/i386/isa/vector.s | 14 |
8 files changed, 135 insertions, 89 deletions
diff --git a/sys/arch/i386/i386/autoconf.c b/sys/arch/i386/i386/autoconf.c index 149bcb3cec2..77bc9cbafe7 100644 --- a/sys/arch/i386/i386/autoconf.c +++ b/sys/arch/i386/i386/autoconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: autoconf.c,v 1.36 2001/06/25 00:43:11 mickey Exp $ */ +/* $OpenBSD: autoconf.c,v 1.37 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: autoconf.c,v 1.20 1996/05/03 19:41:56 christos Exp $ */ /*- @@ -87,9 +87,8 @@ cpu_configure() if (config_rootfound("mainbus", NULL) == NULL) panic("cpu_configure: mainbus not configured"); - printf("biomask %x netmask %x ttymask %x\n", - (u_short)imask[IPL_BIO], (u_short)imask[IPL_NET], - (u_short)imask[IPL_TTY]); + printf("biomask %x netmask %x ttymask %x\n", (u_short)IMASK(IPL_BIO), + (u_short)IMASK(IPL_NET), (u_short)IMASK(IPL_TTY)); spl0(); diff --git a/sys/arch/i386/i386/locore.s b/sys/arch/i386/i386/locore.s index 9880a04a6be..cdb3d479c00 100644 --- a/sys/arch/i386/i386/locore.s +++ b/sys/arch/i386/i386/locore.s @@ -1,4 +1,4 @@ -/* $OpenBSD: locore.s,v 1.59 2001/10/24 04:04:30 mickey Exp $ */ +/* $OpenBSD: locore.s,v 1.60 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: locore.s,v 1.145 1996/05/03 19:41:19 christos Exp $ */ /*- @@ -1653,7 +1653,7 @@ ENTRY(cpu_switch) */ movl $0,_C_LABEL(curproc) - movl $0,_C_LABEL(cpl) # spl0() + movl $IPL_NONE,_C_LABEL(cpl) # spl0() call _C_LABEL(Xspllower) # process pending interrupts switch_search: diff --git a/sys/arch/i386/include/intr.h b/sys/arch/i386/include/intr.h index 30ebfd44ed5..ca821fea28d 100644 --- a/sys/arch/i386/include/intr.h +++ b/sys/arch/i386/include/intr.h @@ -1,4 +1,4 @@ -/* $OpenBSD: intr.h,v 1.7 2001/06/24 17:05:36 miod Exp $ */ +/* $OpenBSD: intr.h,v 1.8 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: intr.h,v 1.5 1996/05/13 06:11:28 mycroft Exp $ */ /* @@ -33,19 +33,54 @@ #ifndef _I386_INTR_H_ #define _I386_INTR_H_ -/* Interrupt priority `levels'; not mutually exclusive. */ -#define IPL_NONE 0 /* nothing */ -#define IPL_BIO 1 /* block I/O */ -#define IPL_NET 2 /* network */ -#define IPL_TTY 3 /* terminal */ -#define IPL_IMP 4 /* memory allocation */ -#define IPL_AUDIO 5 /* audio */ -#define IPL_CLOCK 6 /* clock */ -#define IPL_HIGH 7 /* everything */ +/* + * Intel APICs (advanced programmable interrupt controllers) have + * bytesized priority registers where the upper nibble is the actual + * interrupt priority level (a.k.a. IPL). Interrupt vectors are + * closely tied to these levels as interrupts whose vectors' upper + * nibble is lower than or equal to the current level are blocked. + * Not all 256 possible vectors are available for interrupts in + * APIC systems, only + * + * For systems where instead the older ICU (interrupt controlling + * unit, a.k.a. PIC or 82C59) is used, the IPL is not directly useful, + * since the interrupt blocking is handled via interrupt masks instead + * of levels. However the IPL is easily used as an offset into arrays + * of masks. + */ +#define IPLSHIFT 4 /* The upper nibble of vectors is the IPL. */ +#define NIPL 16 /* Four bits of information gives as much. */ +#define IPL(level) ((level) >> IPLSHIFT) /* Extract the IPL. */ +/* XXX Maybe this IDTVECOFF definition should be elsewhere? */ +#define IDTVECOFF 0x20 /* The lower 32 IDT vectors are reserved. */ -#ifndef _LOCORE -int imask[IPL_HIGH+1]; -#endif +/* + * This macro is only defined for 0 <= x < 14, i.e. there are fourteen + * distinct priority levels available for interrupts. + */ +#define MAKEIPL(priority) (IDTVECOFF + ((priority) << IPLSHIFT)) + +/* + * Interrupt priority levels. + * XXX We are somewhat sloppy about what we mean by IPLs, sometimes + * XXX we refer to the eight-bit value suitable for storing into APICs' + * XXX priority registers, other times about the four-bit entity found + * XXX in the former values' upper nibble, which can be used as offsets + * XXX in various arrays of our implementation. We are hoping that + * XXX the context will provide enough information to not make this + * XXX sloppy naming a real problem. + */ +#define IPL_NONE 0 /* nothing */ +#define IPL_SOFTCLOCK MAKEIPL(0) /* timeouts */ +#define IPL_SOFTNET MAKEIPL(1) /* protocol stacks */ +#define IPL_BIO MAKEIPL(2) /* block I/O */ +#define IPL_NET MAKEIPL(3) /* network */ +#define IPL_SOFTTTY MAKEIPL(4) /* delayed terminal handling */ +#define IPL_TTY MAKEIPL(5) /* terminal */ +#define IPL_IMP MAKEIPL(6) /* memory allocation */ +#define IPL_AUDIO MAKEIPL(7) /* audio */ +#define IPL_CLOCK MAKEIPL(8) /* clock */ +#define IPL_HIGH MAKEIPL(9) /* everything */ /* Interrupt sharing types. */ #define IST_NONE 0 /* none */ @@ -55,16 +90,19 @@ int imask[IPL_HIGH+1]; /* Soft interrupt masks. */ #define SIR_CLOCK 31 -#define SIR_CLOCKMASK ((1 << SIR_CLOCK)) #define SIR_NET 30 -#define SIR_NETMASK ((1 << SIR_NET) | SIR_CLOCKMASK) #define SIR_TTY 29 -#define SIR_TTYMASK ((1 << SIR_TTY) | SIR_CLOCKMASK) -#define SIR_ALLMASK (SIR_CLOCKMASK | SIR_NETMASK | SIR_TTYMASK) #ifndef _LOCORE -volatile int cpl, ipending, astpending; +volatile int cpl; /* Current interrupt priority level. */ +volatile int ipending; /* Interrupts pending. */ +volatile int astpending;/* Asynchronous software traps (softints) pending. */ +int imask[NIPL]; /* Bitmasks telling what interrupts are blocked. */ +int iunmask[NIPL]; /* Bitmasks telling what interrupts are accepted. */ + +#define IMASK(level) imask[IPL(level)] +#define IUNMASK(level) iunmask[IPL(level)] extern void Xspllower __P((void)); @@ -74,7 +112,7 @@ static __inline void splx __P((int)); static __inline void softintr __P((int)); /* - * Add a mask to cpl, and return the old value of cpl. + * Raise current interrupt priority level, and return the old one. */ static __inline int splraise(ncpl) @@ -82,21 +120,21 @@ splraise(ncpl) { register int ocpl = cpl; - cpl = ocpl | ncpl; + if (ncpl > ocpl) + cpl = ncpl; return (ocpl); } /* - * Restore a value to cpl (unmasking interrupts). If any unmasked + * Restore an old interrupt priority level. If any thereby unmasked * interrupts are pending, call Xspllower() to process them. */ static __inline void splx(ncpl) register int ncpl; { - cpl = ncpl; - if (ipending & ~ncpl) + if (ipending & IUNMASK(ncpl)) Xspllower(); } @@ -110,20 +148,18 @@ spllower(ncpl) { register int ocpl = cpl; - cpl = ncpl; - if (ipending & ~ncpl) - Xspllower(); + splx(ncpl); return (ocpl); } /* * Hardware interrupt masks */ -#define splbio() splraise(imask[IPL_BIO]) -#define splnet() splraise(imask[IPL_NET]) -#define spltty() splraise(imask[IPL_TTY]) -#define splaudio() splraise(imask[IPL_AUDIO]) -#define splclock() splraise(imask[IPL_CLOCK]) +#define splbio() splraise(IPL_BIO) +#define splnet() splraise(IPL_NET) +#define spltty() splraise(IPL_TTY) +#define splaudio() splraise(IPL_AUDIO) +#define splclock() splraise(IPL_CLOCK) #define splstatclock() splhigh() /* @@ -132,18 +168,18 @@ spllower(ncpl) * NOTE: spllowersoftclock() is used by hardclock() to lower the priority from * clock to softclock before it calls softclock(). */ -#define spllowersoftclock() spllower(SIR_CLOCKMASK) -#define splsoftclock() splraise(SIR_CLOCKMASK) -#define splsoftnet() splraise(SIR_NETMASK) -#define splsofttty() splraise(SIR_TTYMASK) +#define spllowersoftclock() spllower(IPL_SOFTCLOCK) +#define splsoftclock() splraise(IPL_SOFTCLOCK) +#define splsoftnet() splraise(IPL_SOFTNET) +#define splsofttty() splraise(IPL_SOFTTTY) /* * Miscellaneous */ -#define splimp() splraise(imask[IPL_IMP]) -#define splvm() splraise(imask[IPL_IMP]) -#define splhigh() splraise(imask[IPL_HIGH]) -#define spl0() spllower(0) +#define splimp() splraise(IPL_IMP) +#define splvm() splraise(IPL_IMP) +#define splhigh() splraise(IPL_HIGH) +#define spl0() spllower(IPL_NONE) /* * Software interrupt registration @@ -154,7 +190,6 @@ static __inline void softintr(mask) register int mask; { - __asm __volatile("orl %0,_ipending" : : "ir" (mask)); } diff --git a/sys/arch/i386/isa/icu.h b/sys/arch/i386/isa/icu.h index a2fff6d231c..5414eeaaf4a 100644 --- a/sys/arch/i386/isa/icu.h +++ b/sys/arch/i386/isa/icu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: icu.h,v 1.4 1999/01/13 07:26:00 niklas Exp $ */ +/* $OpenBSD: icu.h,v 1.5 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: icu.h,v 1.19 1996/02/01 22:31:21 mycroft Exp $ */ /*- @@ -65,6 +65,9 @@ extern unsigned imen; /* interrupt mask enable */ /* * Interrupt Control offset into Interrupt descriptor table (IDT) + * XXX ICU_OFFSET is actually a property of our architecture not of the ICU + * XXX and therefore ought to use the architecture manifest constant IDTVECOFF + * XXX for its definition instead. */ #define ICU_OFFSET 32 /* 0-31 are processor exceptions */ #define ICU_LEN 16 /* 32-47 are ISA interrupts */ diff --git a/sys/arch/i386/isa/icu.s b/sys/arch/i386/isa/icu.s index a21c5ce112d..f348a10b0b9 100644 --- a/sys/arch/i386/isa/icu.s +++ b/sys/arch/i386/isa/icu.s @@ -1,4 +1,4 @@ -/* $OpenBSD: icu.s,v 1.15 2001/11/08 20:01:52 mickey Exp $ */ +/* $OpenBSD: icu.s,v 1.16 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: icu.s,v 1.45 1996/01/07 03:59:34 mycroft Exp $ */ /*- @@ -45,7 +45,7 @@ _C_LABEL(imen): ALIGN_TEXT _C_LABEL(splhigh): - movl $-1,%eax + movl $IPL_HIGH,%eax xchgl %eax,_C_LABEL(cpl) ret @@ -72,8 +72,9 @@ IDTVEC(spllower) pushl %edi movl _C_LABEL(cpl),%ebx # save priority movl $1f,%esi # address to resume loop at -1: movl %ebx,%eax - notl %eax +1: movl %ebx,%eax # get cpl + shrl $4,%eax # find its mask. + movl _C_LABEL(iunmask)(,%eax,4),%eax andl _C_LABEL(ipending),%eax jz 2f bsfl %eax,%eax @@ -97,8 +98,9 @@ IDTVEC(doreti) popl %ebx # get previous priority movl %ebx,_C_LABEL(cpl) movl $1f,%esi # address to resume loop at -1: movl %ebx,%eax - notl %eax +1: movl %ebx,%eax # get cpl + shrl $4,%eax # find its mask + movl _C_LABEL(iunmask)(,%eax,4),%eax andl _C_LABEL(ipending),%eax jz 2f bsfl %eax,%eax # slow, but not worth optimizing @@ -132,7 +134,7 @@ IDTVEC(doreti) IDTVEC(softtty) #if NPCCOM > 0 - leal SIR_TTYMASK(%ebx),%eax + movl $IPL_SOFTTTY,%eax movl %eax,_C_LABEL(cpl) call _C_LABEL(comsoft) movl %ebx,_C_LABEL(cpl) @@ -147,7 +149,7 @@ IDTVEC(softtty) 1: IDTVEC(softnet) - leal SIR_NETMASK(%ebx),%eax + movl $IPL_SOFTNET,%eax movl %eax,_C_LABEL(cpl) xorl %edi,%edi xchgl _C_LABEL(netisr),%edi @@ -157,7 +159,7 @@ IDTVEC(softnet) #undef DONETISR IDTVEC(softclock) - leal SIR_CLOCKMASK(%ebx),%eax + movl $IPL_SOFTCLOCK,%eax movl %eax,_C_LABEL(cpl) call _C_LABEL(softclock) movl %ebx,_C_LABEL(cpl) diff --git a/sys/arch/i386/isa/isa_machdep.c b/sys/arch/i386/isa/isa_machdep.c index bf93ba716a3..b57340a2977 100644 --- a/sys/arch/i386/isa/isa_machdep.c +++ b/sys/arch/i386/isa/isa_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: isa_machdep.c,v 1.40 2001/11/06 19:53:14 miod Exp $ */ +/* $OpenBSD: isa_machdep.c,v 1.41 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: isa_machdep.c,v 1.22 1997/06/12 23:57:32 thorpej Exp $ */ #define ISA_DMA_STATS @@ -129,6 +129,7 @@ #define _I386_BUS_DMA_PRIVATE #include <machine/bus.h> +#include <machine/intr.h> #include <machine/pio.h> #include <machine/cpufunc.h> @@ -221,8 +222,8 @@ isa_defaultirq() /* icu vectors */ for (i = 0; i < ICU_LEN; i++) - setgate(&idt[ICU_OFFSET + i], IDTVEC(intr)[i], 0, SDT_SYS386IGT, - SEL_KPL, GICODE_SEL); + setgate(&idt[ICU_OFFSET + i], IDTVEC(intr)[i], 0, + SDT_SYS386IGT, SEL_KPL, GICODE_SEL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ @@ -260,13 +261,13 @@ isa_defaultirq() int isa_nmi() { - /* This is historic garbage; these ports are not readable */ log(LOG_CRIT, "No-maskable interrupt, may be parity error\n"); return(0); } -u_long intrstray[ICU_LEN] = {0}; +u_long intrstray[ICU_LEN]; + /* * Caught a stray interrupt, notify */ @@ -287,6 +288,7 @@ isa_strayintr(irq) int fastvec; int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN]; +int ilevel[ICU_LEN]; struct intrhand *intrhand[ICU_LEN]; /* @@ -305,45 +307,45 @@ intr_calculatemasks() for (irq = 0; irq < ICU_LEN; irq++) { register int levels = 0; for (q = intrhand[irq]; q; q = q->ih_next) - levels |= 1 << q->ih_level; + levels |= 1 << IPL(q->ih_level); intrlevel[irq] = levels; } /* Then figure out which IRQs use each level. */ - for (level = 0; level < 5; level++) { + for (level = 0; level < NIPL; level++) { register int irqs = 0; for (irq = 0; irq < ICU_LEN; irq++) if (intrlevel[irq] & (1 << level)) irqs |= 1 << irq; - imask[level] = irqs | SIR_ALLMASK; + imask[level] = irqs; } /* - * There are tty, network and disk drivers that use free() at interrupt - * time, so imp > (tty | net | bio). + * Initialize soft interrupt masks to block themselves. */ - imask[IPL_IMP] |= imask[IPL_TTY] | imask[IPL_NET] | imask[IPL_BIO]; - imask[IPL_AUDIO] |= imask[IPL_IMP]; + IMASK(IPL_SOFTCLOCK) |= 1 << SIR_CLOCK; + IMASK(IPL_SOFTNET) |= 1 << SIR_NET; + IMASK(IPL_SOFTTTY) |= 1 << SIR_TTY; /* * Enforce a hierarchy that gives slow devices a better chance at not * dropping data. */ - imask[IPL_TTY] |= imask[IPL_NET] | imask[IPL_BIO]; - imask[IPL_NET] |= imask[IPL_BIO]; - - /* - * These are pseudo-levels. - */ - imask[IPL_NONE] = 0x00000000; - imask[IPL_HIGH] = 0xffffffff; + for (level = 0; level < NIPL - 1; level++) + imask[level + 1] |= imask[level]; /* And eventually calculate the complete masks. */ for (irq = 0; irq < ICU_LEN; irq++) { register int irqs = 1 << irq; - for (q = intrhand[irq]; q; q = q->ih_next) - irqs |= imask[q->ih_level]; - intrmask[irq] = irqs | SIR_ALLMASK; + int level = IPL_NONE; + + for (q = intrhand[irq]; q; q = q->ih_next) { + irqs |= IMASK(q->ih_level); + if (q->ih_level > level) + level = q->ih_level; + } + intrmask[irq] = irqs; + ilevel[irq] = level; } /* Lastly, determine which IRQs are actually in use. */ @@ -357,13 +359,16 @@ intr_calculatemasks() imen = ~irqs; SET_ICUS(); } + + /* For speed of splx, provide the inverse of the interrupt masks. */ + for (irq = 0; irq < ICU_LEN; irq++) + iunmask[irq] = ~imask[irq]; } int fakeintr(arg) void *arg; { - return 0; } diff --git a/sys/arch/i386/isa/npx.c b/sys/arch/i386/isa/npx.c index a446ab4efc9..fe04652777f 100644 --- a/sys/arch/i386/isa/npx.c +++ b/sys/arch/i386/isa/npx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: npx.c,v 1.20 2001/11/06 19:53:14 miod Exp $ */ +/* $OpenBSD: npx.c,v 1.21 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: npx.c,v 1.57 1996/05/12 23:12:24 mycroft Exp $ */ #if 0 @@ -537,7 +537,7 @@ npxdna(p) } #ifdef DIAGNOSTIC - if (cpl != 0 || npx_nointr != 0) + if (cpl != IPL_NONE || npx_nointr != 0) panic("npxdna: masked"); #endif @@ -612,7 +612,7 @@ npxsave() { #ifdef DIAGNOSTIC - if (cpl != 0 || npx_nointr != 0) + if (cpl != IPL_NONE || npx_nointr != 0) panic("npxsave: masked"); #endif iprintf(("Fork")); diff --git a/sys/arch/i386/isa/vector.s b/sys/arch/i386/isa/vector.s index 21a403b2a8d..61c78232624 100644 --- a/sys/arch/i386/isa/vector.s +++ b/sys/arch/i386/isa/vector.s @@ -1,4 +1,4 @@ -/* $OpenBSD: vector.s,v 1.11 2001/05/05 23:25:52 art Exp $ */ +/* $OpenBSD: vector.s,v 1.12 2001/11/12 20:28:20 niklas Exp $ */ /* $NetBSD: vector.s,v 1.32 1996/01/07 21:29:47 mycroft Exp $ */ /* @@ -218,13 +218,15 @@ _Xintr/**/irq_num/**/: ;\ MASK(irq_num, icu) /* mask it in hardware */ ;\ ack(irq_num) /* and allow other intrs */ ;\ incl MY_COUNT+V_INTR /* statistical info */ ;\ - testb $IRQ_BIT(irq_num),_cpl + IRQ_BYTE(irq_num) ;\ - jnz _Xhold/**/irq_num /* currently masked; hold it */ ;\ + movl _C_LABEL(ilevel) + (irq_num) * 4, %eax ;\ + movzbl _C_LABEL(cpl),%ebx ;\ + cmpl %eax,%ebx ;\ + jae _C_LABEL(Xhold/**/irq_num)/* currently masked; hold it */;\ _Xresume/**/irq_num/**/: ;\ - movl _cpl,%eax /* cpl to restore on exit */ ;\ + movzbl _C_LABEL(cpl),%eax /* cpl to restore on exit */ ;\ pushl %eax ;\ - orl _intrmask + (irq_num) * 4,%eax ;\ - movl %eax,_cpl /* add in this intr's mask */ ;\ + movl _C_LABEL(ilevel) + (irq_num) * 4,%eax ;\ + movl %eax,_C_LABEL(cpl) /* block enough for this irq */ ;\ sti /* safe to take intrs now */ ;\ movl _intrhand + (irq_num) * 4,%ebx /* head of chain */ ;\ testl %ebx,%ebx ;\ |