diff options
author | jasone <jasone@FreeBSD.org> | 2000-09-07 01:33:02 +0000 |
---|---|---|
committer | jasone <jasone@FreeBSD.org> | 2000-09-07 01:33:02 +0000 |
commit | 769e0f974d8929599ba599ac496510fffc90ff34 (patch) | |
tree | 9387522900085835de81e7830e570ef3f6b3ea80 /sys/amd64/isa | |
parent | acf1927de02afda4855ec278b1128fd9446405ea (diff) | |
download | FreeBSD-src-769e0f974d8929599ba599ac496510fffc90ff34.zip FreeBSD-src-769e0f974d8929599ba599ac496510fffc90ff34.tar.gz |
Major update to the way synchronization is done in the kernel. Highlights
include:
* Mutual exclusion is used instead of spl*(). See mutex(9). (Note: The
alpha port is still in transition and currently uses both.)
* Per-CPU idle processes.
* Interrupts are run in their own separate kernel threads and can be
preempted (i386 only).
Partially contributed by: BSDi (BSD/OS)
Submissions by (at least): cp, dfr, dillon, grog, jake, jhb, sheldonh
Diffstat (limited to 'sys/amd64/isa')
-rw-r--r-- | sys/amd64/isa/atpic_vector.S | 92 | ||||
-rw-r--r-- | sys/amd64/isa/clock.c | 155 | ||||
-rw-r--r-- | sys/amd64/isa/icu_ipl.S | 57 | ||||
-rw-r--r-- | sys/amd64/isa/icu_ipl.s | 57 | ||||
-rw-r--r-- | sys/amd64/isa/icu_vector.S | 92 | ||||
-rw-r--r-- | sys/amd64/isa/icu_vector.s | 92 | ||||
-rw-r--r-- | sys/amd64/isa/intr_machdep.c | 524 | ||||
-rw-r--r-- | sys/amd64/isa/intr_machdep.h | 50 | ||||
-rw-r--r-- | sys/amd64/isa/ithread.c | 353 | ||||
-rw-r--r-- | sys/amd64/isa/nmi.c | 524 | ||||
-rw-r--r-- | sys/amd64/isa/npx.c | 18 | ||||
-rw-r--r-- | sys/amd64/isa/vector.S | 9 | ||||
-rw-r--r-- | sys/amd64/isa/vector.s | 9 |
13 files changed, 950 insertions, 1082 deletions
diff --git a/sys/amd64/isa/atpic_vector.S b/sys/amd64/isa/atpic_vector.S index e427351..d2b88bf 100644 --- a/sys/amd64/isa/atpic_vector.S +++ b/sys/amd64/isa/atpic_vector.S @@ -53,9 +53,11 @@ IDTVEC(vec_name) ; \ pushl %ecx ; \ pushl %edx ; \ pushl %ds ; \ + pushl %fs ; \ MAYBE_PUSHL_ES ; \ mov $KDSEL,%ax ; \ mov %ax,%ds ; \ + mov %ax,%fs ; \ MAYBE_MOVW_AX_ES ; \ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \ pushl _intr_unit + (irq_num) * 4 ; \ @@ -65,18 +67,21 @@ IDTVEC(vec_name) ; \ incl _cnt+V_INTR ; /* book-keeping can wait */ \ movl _intr_countp + (irq_num) * 4,%eax ; \ incl (%eax) ; \ - movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \ +/* movl _cpl,%eax ; // are we unmasking pending SWIs? / \ notl %eax ; \ - andl _ipending,%eax ; \ - jne 2f ; /* yes, maybe handle them */ \ + andl _spending,$SWI_MASK ; \ + jne 2f ; // yes, maybe handle them */ \ 1: ; \ MEXITCOUNT ; \ MAYBE_POPL_ES ; \ + popl %fs ; \ popl %ds ; \ popl %edx ; \ popl %ecx ; \ popl %eax ; \ iret ; \ + +#if 0 ; \ ALIGN_TEXT ; \ 2: ; \ @@ -88,6 +93,7 @@ IDTVEC(vec_name) ; \ incb _intr_nesting_level ; /* ... really limit it ... */ \ sti ; /* ... to do this as early as possible */ \ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \ + popl %fs ; \ popl %ecx ; /* ... original %ds ... */ \ popl %edx ; \ xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \ @@ -101,11 +107,20 @@ IDTVEC(vec_name) ; \ movl (3+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \ movl %ecx,(3+6)*4(%esp) ; /* ... to fat frame ... */ \ movl (3+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \ - pushl %eax ; \ subl $4,%esp ; /* junk for unit number */ \ MEXITCOUNT ; \ jmp _doreti +#endif +/* + * Slow, threaded interrupts. + * + * XXX Most of the parameters here are obsolete. Fix this when we're + * done. + * XXX we really shouldn't return via doreti if we just schedule the + * interrupt handler and don't run anything. We could just do an + * iret. FIXME. + */ #define INTR(irq_num, vec_name, icu, enable_icus, reg, maybe_extra_ipending) \ .text ; \ SUPERALIGN_TEXT ; \ @@ -116,8 +131,8 @@ IDTVEC(vec_name) ; \ pushl %ds ; /* save our data and extra segments ... */ \ pushl %es ; \ pushl %fs ; \ - mov $KDSEL,%ax ; /* ... and reload with kernel's own ... */ \ - mov %ax,%ds ; /* ... early for obsolete reasons */ \ + mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \ + mov %ax,%ds ; \ mov %ax,%es ; \ mov %ax,%fs ; \ maybe_extra_ipending ; \ @@ -126,43 +141,37 @@ IDTVEC(vec_name) ; \ movb %al,_imen + IRQ_BYTE(irq_num) ; \ outb %al,$icu+ICU_IMR_OFFSET ; \ enable_icus ; \ - movl _cpl,%eax ; \ - testb $IRQ_BIT(irq_num),%reg ; \ - jne 2f ; \ - incb _intr_nesting_level ; \ + incb _intr_nesting_level ; /* XXX do we need this? */ \ __CONCAT(Xresume,irq_num): ; \ FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \ - incl _cnt+V_INTR ; /* tally interrupts */ \ - movl _intr_countp + (irq_num) * 4,%eax ; \ - incl (%eax) ; \ - movl _cpl,%eax ; \ - pushl %eax ; \ - pushl _intr_unit + (irq_num) * 4 ; \ - orl _intr_mask + (irq_num) * 4,%eax ; \ - movl %eax,_cpl ; \ + pushl $irq_num; /* pass the IRQ */ \ sti ; \ - call *_intr_handler + (irq_num) * 4 ; \ - cli ; /* must unmask _imen and icu atomically */ \ - movb _imen + IRQ_BYTE(irq_num),%al ; \ - andb $~IRQ_BIT(irq_num),%al ; \ - movb %al,_imen + IRQ_BYTE(irq_num) ; \ - outb %al,$icu+ICU_IMR_OFFSET ; \ - sti ; /* XXX _doreti repeats the cli/sti */ \ + call _sched_ithd ; \ + addl $4, %esp ; /* discard the parameter */ \ MEXITCOUNT ; \ /* We could usually avoid the following jmp by inlining some of */ \ /* _doreti, but it's probably better to use less cache. */ \ - jmp _doreti ; \ -; \ - ALIGN_TEXT ; \ -2: ; \ - /* XXX skip mcounting here to avoid double count */ \ - orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \ - popl %fs ; \ - popl %es ; \ - popl %ds ; \ - popal ; \ - addl $4+4,%esp ; \ - iret + jmp doreti_next /* and catch up inside doreti */ + +/* + * Reenable the interrupt mask after completing an interrupt. Called + * from ithd_loop. There are two separate functions, one for each + * ICU. + */ + .globl setimask0, setimask1 +setimask0: + cli + movb _imen,%al + outb %al,$IO_ICU1 + ICU_IMR_OFFSET + sti + ret + +setimask1: + cli + movb _imen + 1,%al + outb %al,$IO_ICU2 + ICU_IMR_OFFSET + sti + ret MCOUNT_LABEL(bintr) FAST_INTR(0,fastintr0, ENABLE_ICU1) @@ -181,7 +190,9 @@ MCOUNT_LABEL(bintr) FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2) FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2) FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2) + #define CLKINTR_PENDING movl $1,CNAME(clkintr_pending) +/* Threaded interrupts */ INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING) INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,) INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,) @@ -198,6 +209,7 @@ MCOUNT_LABEL(bintr) INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah,) INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah,) INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah,) + MCOUNT_LABEL(eintr) .data @@ -211,10 +223,4 @@ _ihandlers: /* addresses of interrupt handlers */ .long _swi_null, swi_net, _swi_null, _swi_null .long _swi_vm, _swi_null, _softclock -imasks: /* masks for interrupt handlers */ - .space NHWI*4 /* padding; HWI masks are elsewhere */ - - .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK - .long SWI_VM_MASK, SWI_TQ_MASK, SWI_CLOCK_MASK - .text diff --git a/sys/amd64/isa/clock.c b/sys/amd64/isa/clock.c index 15044ab..724f3c2 100644 --- a/sys/amd64/isa/clock.c +++ b/sys/amd64/isa/clock.c @@ -54,6 +54,7 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> +#include <sys/proc.h> #include <sys/time.h> #include <sys/timetc.h> #include <sys/kernel.h> @@ -93,10 +94,6 @@ #include <i386/isa/mca_machdep.h> #endif -#ifdef SMP -#define disable_intr() CLOCK_DISABLE_INTR() -#define enable_intr() CLOCK_ENABLE_INTR() - #ifdef APIC_IO #include <i386/isa/intr_machdep.h> /* The interrupt triggered by the 8254 (timer) chip */ @@ -104,7 +101,6 @@ int apic_8254_intr; static u_long read_intr_count __P((int vec)); static void setup_8254_mixed_mode __P((void)); #endif -#endif /* SMP */ /* * 32-bit time_t's can't reach leap years before 1904 or after 2036, so we @@ -147,7 +143,9 @@ int tsc_is_broken; int wall_cmos_clock; /* wall CMOS clock assumed if != 0 */ static int beeping = 0; +#if 0 static u_int clk_imask = HWI_MASK | SWI_MASK; +#endif static const u_char daysinmonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; static u_int hardclock_max_count; static u_int32_t i8254_lastcount; @@ -205,8 +203,12 @@ SYSCTL_OPAQUE(_debug, OID_AUTO, i8254_timecounter, CTLFLAG_RD, static void clkintr(struct clockframe frame) { + int intrsave; + if (timecounter->tc_get_timecount == i8254_get_timecount) { + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); if (i8254_ticked) i8254_ticked = 0; else { @@ -214,7 +216,8 @@ clkintr(struct clockframe frame) i8254_lastcount = 0; } clkintr_pending = 0; - enable_intr(); + CLOCK_UNLOCK(); + restore_intr(intrsave); } timer_func(&frame); switch (timer0_state) { @@ -233,14 +236,17 @@ clkintr(struct clockframe frame) break; case ACQUIRE_PENDING: + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); i8254_offset = i8254_get_timecount(NULL); i8254_lastcount = 0; timer0_max_count = TIMER_DIV(new_rate); outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); outb(TIMER_CNTR0, timer0_max_count & 0xff); outb(TIMER_CNTR0, timer0_max_count >> 8); - enable_intr(); + CLOCK_UNLOCK(); + restore_intr(intrsave); timer_func = new_function; timer0_state = ACQUIRED; setdelayed(); @@ -249,7 +255,9 @@ clkintr(struct clockframe frame) case RELEASE_PENDING: if ((timer0_prescaler_count += timer0_max_count) >= hardclock_max_count) { + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); i8254_offset = i8254_get_timecount(NULL); i8254_lastcount = 0; timer0_max_count = hardclock_max_count; @@ -257,7 +265,8 @@ clkintr(struct clockframe frame) TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); outb(TIMER_CNTR0, timer0_max_count & 0xff); outb(TIMER_CNTR0, timer0_max_count >> 8); - enable_intr(); + CLOCK_UNLOCK(); + restore_intr(intrsave); timer0_prescaler_count = 0; timer_func = hardclock; timer0_state = RELEASED; @@ -404,11 +413,11 @@ DB_SHOW_COMMAND(rtc, rtc) static int getit(void) { - u_long ef; - int high, low; + int high, low, intrsave; - ef = read_eflags(); + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); /* Select timer0 and latch counter value. */ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); @@ -417,7 +426,7 @@ getit(void) high = inb(TIMER_CNTR0); CLOCK_UNLOCK(); - write_eflags(ef); + restore_intr(intrsave); return ((high << 8) | low); } @@ -523,6 +532,7 @@ sysbeepstop(void *chan) int sysbeep(int pitch, int period) { + int intrsave; int x = splclock(); if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) @@ -531,10 +541,13 @@ sysbeep(int pitch, int period) splx(x); return (-1); /* XXX Should be EBUSY, but nobody cares anyway. */ } + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); - enable_intr(); + CLOCK_UNLOCK(); + restore_intr(intrsave); if (!beeping) { /* enable counter2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); @@ -683,11 +696,12 @@ fail: static void set_timer_freq(u_int freq, int intr_freq) { - u_long ef; + int intrsave; int new_timer0_max_count; - ef = read_eflags(); + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); timer_freq = freq; new_timer0_max_count = hardclock_max_count = TIMER_DIV(intr_freq); if (new_timer0_max_count != timer0_max_count) { @@ -697,7 +711,7 @@ set_timer_freq(u_int freq, int intr_freq) outb(TIMER_CNTR0, timer0_max_count >> 8); } CLOCK_UNLOCK(); - write_eflags(ef); + restore_intr(intrsave); } /* @@ -711,15 +725,16 @@ set_timer_freq(u_int freq, int intr_freq) void i8254_restore(void) { - u_long ef; + int intrsave; - ef = read_eflags(); + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); outb(TIMER_CNTR0, timer0_max_count & 0xff); outb(TIMER_CNTR0, timer0_max_count >> 8); CLOCK_UNLOCK(); - write_eflags(ef); + restore_intr(intrsave); } /* @@ -979,8 +994,8 @@ cpu_initclocks() { int diag; #ifdef APIC_IO - int apic_8254_trial; - struct intrec *clkdesc; + int apic_8254_trial, num_8254_ticks; + struct intrec *clkdesc, *rtcdesc; #endif /* APIC_IO */ if (statclock_disable) { @@ -1014,14 +1029,15 @@ cpu_initclocks() } else panic("APIC_IO: Cannot route 8254 interrupt to CPU"); } - - clkdesc = inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, - NULL, &clk_imask, INTR_EXCL); - INTREN(1 << apic_8254_intr); - #else /* APIC_IO */ - inthand_add("clk", 0, (inthand2_t *)clkintr, NULL, &clk_imask, + /* + * XXX Check the priority of this interrupt handler. I + * couldn't find anything suitable in the BSD/OS code (grog, + * 19 July 2000). + */ + /* Setup the PIC clk handler. The APIC handler is setup later */ + inthand_add("clk", 0, (inthand2_t *)clkintr, NULL, PI_REALTIME, INTR_EXCL); INTREN(IRQ0); @@ -1032,8 +1048,18 @@ cpu_initclocks() writertc(RTC_STATUSB, RTCSB_24HR); /* Don't bother enabling the statistics clock. */ - if (statclock_disable) + if (statclock_disable) { +#ifdef APIC_IO + /* + * XXX - if statclock is disabled, don't attempt the APIC + * trial. Not sure this is sane for APIC_IO. + */ + inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, NULL, + PI_REALTIME, INTR_EXCL); + INTREN(1 << apic_8254_intr); +#endif /* APIC_IO */ return; + } diag = rtcin(RTC_DIAG); if (diag != 0) printf("RTC BIOS diagnostic error %b\n", diag, RTCDG_BITS); @@ -1041,34 +1067,44 @@ cpu_initclocks() #ifdef APIC_IO if (isa_apic_irq(8) != 8) panic("APIC RTC != 8"); -#endif /* APIC_IO */ - inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, &stat_imask, - INTR_EXCL); - -#ifdef APIC_IO - INTREN(APIC_IRQ8); -#else - INTREN(IRQ8); -#endif /* APIC_IO */ + if (apic_8254_trial) { + /* + * XXX - We use fast interrupts for clk and rtc long enough to + * perform the APIC probe and then revert to exclusive + * interrupts. + */ + clkdesc = inthand_add("clk", apic_8254_intr, + (inthand2_t *)clkintr, NULL, PI_REALTIME, INTR_FAST); + INTREN(1 << apic_8254_intr); - writertc(RTC_STATUSB, rtc_statusb); + rtcdesc = inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, + PI_REALTIME, INTR_FAST); /* XXX */ + INTREN(APIC_IRQ8); + writertc(RTC_STATUSB, rtc_statusb); -#ifdef APIC_IO - if (apic_8254_trial) { - printf("APIC_IO: Testing 8254 interrupt delivery\n"); while (read_intr_count(8) < 6) ; /* nothing */ - if (read_intr_count(apic_8254_intr) < 3) { + num_8254_ticks = read_intr_count(apic_8254_intr); + + /* disable and remove our fake handlers */ + INTRDIS(1 << apic_8254_intr); + inthand_remove(clkdesc); + + writertc(RTC_STATUSA, rtc_statusa); + writertc(RTC_STATUSB, RTCSB_24HR); + + INTRDIS(APIC_IRQ8); + inthand_remove(rtcdesc); + + if (num_8254_ticks < 3) { /* * The MP table is broken. * The 8254 was not connected to the specified pin * on the IO APIC. * Workaround: Limited variant of mixed mode. */ - INTRDIS(1 << apic_8254_intr); - inthand_remove(clkdesc); printf("APIC_IO: Broken MP table detected: " "8254 is not connected to " "IOAPIC #%d intpin %d\n", @@ -1087,13 +1123,27 @@ cpu_initclocks() } apic_8254_intr = apic_irq(0, 0); setup_8254_mixed_mode(); - inthand_add("clk", apic_8254_intr, - (inthand2_t *)clkintr, - NULL, &clk_imask, INTR_EXCL); - INTREN(1 << apic_8254_intr); } } + + /* Finally, setup the real clock handlers */ + inthand_add("clk", apic_8254_intr, (inthand2_t *)clkintr, NULL, + PI_REALTIME, INTR_EXCL); + INTREN(1 << apic_8254_intr); +#endif + + inthand_add("rtc", 8, (inthand2_t *)rtcintr, NULL, PI_REALTIME, + INTR_EXCL); +#ifdef APIC_IO + INTREN(APIC_IRQ8); +#else + INTREN(IRQ8); +#endif + + writertc(RTC_STATUSB, rtc_statusb); + +#ifdef APIC_IO if (apic_int_type(0, 0) != 3 || int_to_apicintpin[apic_8254_intr].ioapic != 0 || int_to_apicintpin[apic_8254_intr].int_pin != 0) @@ -1198,11 +1248,12 @@ static unsigned i8254_get_timecount(struct timecounter *tc) { u_int count; - u_long ef; + int intrsave; u_int high, low; - ef = read_eflags(); + intrsave = save_intr(); disable_intr(); + CLOCK_LOCK(); /* Select timer0 and latch counter value. */ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); @@ -1212,7 +1263,7 @@ i8254_get_timecount(struct timecounter *tc) count = timer0_max_count - ((high << 8) | low); if (count < i8254_lastcount || (!i8254_ticked && (clkintr_pending || - ((count < 20 || (!(ef & PSL_I) && count < timer0_max_count / 2u)) && + ((count < 20 || (!(intrsave & PSL_I) && count < timer0_max_count / 2u)) && #ifdef APIC_IO #define lapic_irr1 ((volatile u_int *)&lapic)[0x210 / 4] /* XXX XXX */ /* XXX this assumes that apic_8254_intr is < 24. */ @@ -1227,7 +1278,7 @@ i8254_get_timecount(struct timecounter *tc) i8254_lastcount = count; count += i8254_offset; CLOCK_UNLOCK(); - write_eflags(ef); + restore_intr(intrsave); return (count); } diff --git a/sys/amd64/isa/icu_ipl.S b/sys/amd64/isa/icu_ipl.S index 3475358..d178d5c 100644 --- a/sys/amd64/isa/icu_ipl.S +++ b/sys/amd64/isa/icu_ipl.S @@ -55,63 +55,6 @@ _imen: .long HWI_MASK SUPERALIGN_TEXT /* - * Interrupt priority mechanism - * -- soft splXX masks with group mechanism (cpl) - * -- h/w masks for currently active or unused interrupts (imen) - * -- ipending = active interrupts currently masked by cpl - */ - -ENTRY(splz) - /* - * The caller has restored cpl and checked that (ipending & ~cpl) - * is nonzero. We have to repeat the check since if there is an - * interrupt while we're looking, _doreti processing for the - * interrupt will handle all the unmasked pending interrupts - * because we restored early. We're repeating the calculation - * of (ipending & ~cpl) anyway so that the caller doesn't have - * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx" - * is undefined when %ecx is 0 so we can't rely on the secondary - * btrl tests. - */ - movl _cpl,%eax -splz_next: - /* - * We don't need any locking here. (ipending & ~cpl) cannot grow - * while we're looking at it - any interrupt will shrink it to 0. - */ - movl %eax,%ecx - notl %ecx - andl _ipending,%ecx - jne splz_unpend - ret - - ALIGN_TEXT -splz_unpend: - bsfl %ecx,%ecx - btrl %ecx,_ipending - jnc splz_next - cmpl $NHWI,%ecx - jae splz_swi - /* - * We would prefer to call the intr handler directly here but that - * doesn't work for badly behaved handlers that want the interrupt - * frame. Also, there's a problem determining the unit number. - * We should change the interface so that the unit number is not - * determined at config time. - */ - jmp *vec(,%ecx,4) - - ALIGN_TEXT -splz_swi: - pushl %eax - orl imasks(,%ecx,4),%eax - movl %eax,_cpl - call *_ihandlers(,%ecx,4) - popl %eax - movl %eax,_cpl - jmp splz_next - -/* * Fake clock interrupt(s) so that they appear to come from our caller instead * of from here, so that system profiling works. * XXX do this more generally (for all vectors; look up the C entry point). diff --git a/sys/amd64/isa/icu_ipl.s b/sys/amd64/isa/icu_ipl.s index 3475358..d178d5c 100644 --- a/sys/amd64/isa/icu_ipl.s +++ b/sys/amd64/isa/icu_ipl.s @@ -55,63 +55,6 @@ _imen: .long HWI_MASK SUPERALIGN_TEXT /* - * Interrupt priority mechanism - * -- soft splXX masks with group mechanism (cpl) - * -- h/w masks for currently active or unused interrupts (imen) - * -- ipending = active interrupts currently masked by cpl - */ - -ENTRY(splz) - /* - * The caller has restored cpl and checked that (ipending & ~cpl) - * is nonzero. We have to repeat the check since if there is an - * interrupt while we're looking, _doreti processing for the - * interrupt will handle all the unmasked pending interrupts - * because we restored early. We're repeating the calculation - * of (ipending & ~cpl) anyway so that the caller doesn't have - * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx" - * is undefined when %ecx is 0 so we can't rely on the secondary - * btrl tests. - */ - movl _cpl,%eax -splz_next: - /* - * We don't need any locking here. (ipending & ~cpl) cannot grow - * while we're looking at it - any interrupt will shrink it to 0. - */ - movl %eax,%ecx - notl %ecx - andl _ipending,%ecx - jne splz_unpend - ret - - ALIGN_TEXT -splz_unpend: - bsfl %ecx,%ecx - btrl %ecx,_ipending - jnc splz_next - cmpl $NHWI,%ecx - jae splz_swi - /* - * We would prefer to call the intr handler directly here but that - * doesn't work for badly behaved handlers that want the interrupt - * frame. Also, there's a problem determining the unit number. - * We should change the interface so that the unit number is not - * determined at config time. - */ - jmp *vec(,%ecx,4) - - ALIGN_TEXT -splz_swi: - pushl %eax - orl imasks(,%ecx,4),%eax - movl %eax,_cpl - call *_ihandlers(,%ecx,4) - popl %eax - movl %eax,_cpl - jmp splz_next - -/* * Fake clock interrupt(s) so that they appear to come from our caller instead * of from here, so that system profiling works. * XXX do this more generally (for all vectors; look up the C entry point). diff --git a/sys/amd64/isa/icu_vector.S b/sys/amd64/isa/icu_vector.S index e427351..d2b88bf 100644 --- a/sys/amd64/isa/icu_vector.S +++ b/sys/amd64/isa/icu_vector.S @@ -53,9 +53,11 @@ IDTVEC(vec_name) ; \ pushl %ecx ; \ pushl %edx ; \ pushl %ds ; \ + pushl %fs ; \ MAYBE_PUSHL_ES ; \ mov $KDSEL,%ax ; \ mov %ax,%ds ; \ + mov %ax,%fs ; \ MAYBE_MOVW_AX_ES ; \ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \ pushl _intr_unit + (irq_num) * 4 ; \ @@ -65,18 +67,21 @@ IDTVEC(vec_name) ; \ incl _cnt+V_INTR ; /* book-keeping can wait */ \ movl _intr_countp + (irq_num) * 4,%eax ; \ incl (%eax) ; \ - movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \ +/* movl _cpl,%eax ; // are we unmasking pending SWIs? / \ notl %eax ; \ - andl _ipending,%eax ; \ - jne 2f ; /* yes, maybe handle them */ \ + andl _spending,$SWI_MASK ; \ + jne 2f ; // yes, maybe handle them */ \ 1: ; \ MEXITCOUNT ; \ MAYBE_POPL_ES ; \ + popl %fs ; \ popl %ds ; \ popl %edx ; \ popl %ecx ; \ popl %eax ; \ iret ; \ + +#if 0 ; \ ALIGN_TEXT ; \ 2: ; \ @@ -88,6 +93,7 @@ IDTVEC(vec_name) ; \ incb _intr_nesting_level ; /* ... really limit it ... */ \ sti ; /* ... to do this as early as possible */ \ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \ + popl %fs ; \ popl %ecx ; /* ... original %ds ... */ \ popl %edx ; \ xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \ @@ -101,11 +107,20 @@ IDTVEC(vec_name) ; \ movl (3+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \ movl %ecx,(3+6)*4(%esp) ; /* ... to fat frame ... */ \ movl (3+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \ - pushl %eax ; \ subl $4,%esp ; /* junk for unit number */ \ MEXITCOUNT ; \ jmp _doreti +#endif +/* + * Slow, threaded interrupts. + * + * XXX Most of the parameters here are obsolete. Fix this when we're + * done. + * XXX we really shouldn't return via doreti if we just schedule the + * interrupt handler and don't run anything. We could just do an + * iret. FIXME. + */ #define INTR(irq_num, vec_name, icu, enable_icus, reg, maybe_extra_ipending) \ .text ; \ SUPERALIGN_TEXT ; \ @@ -116,8 +131,8 @@ IDTVEC(vec_name) ; \ pushl %ds ; /* save our data and extra segments ... */ \ pushl %es ; \ pushl %fs ; \ - mov $KDSEL,%ax ; /* ... and reload with kernel's own ... */ \ - mov %ax,%ds ; /* ... early for obsolete reasons */ \ + mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \ + mov %ax,%ds ; \ mov %ax,%es ; \ mov %ax,%fs ; \ maybe_extra_ipending ; \ @@ -126,43 +141,37 @@ IDTVEC(vec_name) ; \ movb %al,_imen + IRQ_BYTE(irq_num) ; \ outb %al,$icu+ICU_IMR_OFFSET ; \ enable_icus ; \ - movl _cpl,%eax ; \ - testb $IRQ_BIT(irq_num),%reg ; \ - jne 2f ; \ - incb _intr_nesting_level ; \ + incb _intr_nesting_level ; /* XXX do we need this? */ \ __CONCAT(Xresume,irq_num): ; \ FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \ - incl _cnt+V_INTR ; /* tally interrupts */ \ - movl _intr_countp + (irq_num) * 4,%eax ; \ - incl (%eax) ; \ - movl _cpl,%eax ; \ - pushl %eax ; \ - pushl _intr_unit + (irq_num) * 4 ; \ - orl _intr_mask + (irq_num) * 4,%eax ; \ - movl %eax,_cpl ; \ + pushl $irq_num; /* pass the IRQ */ \ sti ; \ - call *_intr_handler + (irq_num) * 4 ; \ - cli ; /* must unmask _imen and icu atomically */ \ - movb _imen + IRQ_BYTE(irq_num),%al ; \ - andb $~IRQ_BIT(irq_num),%al ; \ - movb %al,_imen + IRQ_BYTE(irq_num) ; \ - outb %al,$icu+ICU_IMR_OFFSET ; \ - sti ; /* XXX _doreti repeats the cli/sti */ \ + call _sched_ithd ; \ + addl $4, %esp ; /* discard the parameter */ \ MEXITCOUNT ; \ /* We could usually avoid the following jmp by inlining some of */ \ /* _doreti, but it's probably better to use less cache. */ \ - jmp _doreti ; \ -; \ - ALIGN_TEXT ; \ -2: ; \ - /* XXX skip mcounting here to avoid double count */ \ - orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \ - popl %fs ; \ - popl %es ; \ - popl %ds ; \ - popal ; \ - addl $4+4,%esp ; \ - iret + jmp doreti_next /* and catch up inside doreti */ + +/* + * Reenable the interrupt mask after completing an interrupt. Called + * from ithd_loop. There are two separate functions, one for each + * ICU. + */ + .globl setimask0, setimask1 +setimask0: + cli + movb _imen,%al + outb %al,$IO_ICU1 + ICU_IMR_OFFSET + sti + ret + +setimask1: + cli + movb _imen + 1,%al + outb %al,$IO_ICU2 + ICU_IMR_OFFSET + sti + ret MCOUNT_LABEL(bintr) FAST_INTR(0,fastintr0, ENABLE_ICU1) @@ -181,7 +190,9 @@ MCOUNT_LABEL(bintr) FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2) FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2) FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2) + #define CLKINTR_PENDING movl $1,CNAME(clkintr_pending) +/* Threaded interrupts */ INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING) INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,) INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,) @@ -198,6 +209,7 @@ MCOUNT_LABEL(bintr) INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah,) INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah,) INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah,) + MCOUNT_LABEL(eintr) .data @@ -211,10 +223,4 @@ _ihandlers: /* addresses of interrupt handlers */ .long _swi_null, swi_net, _swi_null, _swi_null .long _swi_vm, _swi_null, _softclock -imasks: /* masks for interrupt handlers */ - .space NHWI*4 /* padding; HWI masks are elsewhere */ - - .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK - .long SWI_VM_MASK, SWI_TQ_MASK, SWI_CLOCK_MASK - .text diff --git a/sys/amd64/isa/icu_vector.s b/sys/amd64/isa/icu_vector.s index e427351..d2b88bf 100644 --- a/sys/amd64/isa/icu_vector.s +++ b/sys/amd64/isa/icu_vector.s @@ -53,9 +53,11 @@ IDTVEC(vec_name) ; \ pushl %ecx ; \ pushl %edx ; \ pushl %ds ; \ + pushl %fs ; \ MAYBE_PUSHL_ES ; \ mov $KDSEL,%ax ; \ mov %ax,%ds ; \ + mov %ax,%fs ; \ MAYBE_MOVW_AX_ES ; \ FAKE_MCOUNT((4+ACTUALLY_PUSHED)*4(%esp)) ; \ pushl _intr_unit + (irq_num) * 4 ; \ @@ -65,18 +67,21 @@ IDTVEC(vec_name) ; \ incl _cnt+V_INTR ; /* book-keeping can wait */ \ movl _intr_countp + (irq_num) * 4,%eax ; \ incl (%eax) ; \ - movl _cpl,%eax ; /* are we unmasking pending HWIs or SWIs? */ \ +/* movl _cpl,%eax ; // are we unmasking pending SWIs? / \ notl %eax ; \ - andl _ipending,%eax ; \ - jne 2f ; /* yes, maybe handle them */ \ + andl _spending,$SWI_MASK ; \ + jne 2f ; // yes, maybe handle them */ \ 1: ; \ MEXITCOUNT ; \ MAYBE_POPL_ES ; \ + popl %fs ; \ popl %ds ; \ popl %edx ; \ popl %ecx ; \ popl %eax ; \ iret ; \ + +#if 0 ; \ ALIGN_TEXT ; \ 2: ; \ @@ -88,6 +93,7 @@ IDTVEC(vec_name) ; \ incb _intr_nesting_level ; /* ... really limit it ... */ \ sti ; /* ... to do this as early as possible */ \ MAYBE_POPL_ES ; /* discard most of thin frame ... */ \ + popl %fs ; \ popl %ecx ; /* ... original %ds ... */ \ popl %edx ; \ xchgl %eax,4(%esp) ; /* orig %eax; save cpl */ \ @@ -101,11 +107,20 @@ IDTVEC(vec_name) ; \ movl (3+8+0)*4(%esp),%ecx ; /* ... %ecx from thin frame ... */ \ movl %ecx,(3+6)*4(%esp) ; /* ... to fat frame ... */ \ movl (3+8+1)*4(%esp),%eax ; /* ... cpl from thin frame */ \ - pushl %eax ; \ subl $4,%esp ; /* junk for unit number */ \ MEXITCOUNT ; \ jmp _doreti +#endif +/* + * Slow, threaded interrupts. + * + * XXX Most of the parameters here are obsolete. Fix this when we're + * done. + * XXX we really shouldn't return via doreti if we just schedule the + * interrupt handler and don't run anything. We could just do an + * iret. FIXME. + */ #define INTR(irq_num, vec_name, icu, enable_icus, reg, maybe_extra_ipending) \ .text ; \ SUPERALIGN_TEXT ; \ @@ -116,8 +131,8 @@ IDTVEC(vec_name) ; \ pushl %ds ; /* save our data and extra segments ... */ \ pushl %es ; \ pushl %fs ; \ - mov $KDSEL,%ax ; /* ... and reload with kernel's own ... */ \ - mov %ax,%ds ; /* ... early for obsolete reasons */ \ + mov $KDSEL,%ax ; /* load kernel ds, es and fs */ \ + mov %ax,%ds ; \ mov %ax,%es ; \ mov %ax,%fs ; \ maybe_extra_ipending ; \ @@ -126,43 +141,37 @@ IDTVEC(vec_name) ; \ movb %al,_imen + IRQ_BYTE(irq_num) ; \ outb %al,$icu+ICU_IMR_OFFSET ; \ enable_icus ; \ - movl _cpl,%eax ; \ - testb $IRQ_BIT(irq_num),%reg ; \ - jne 2f ; \ - incb _intr_nesting_level ; \ + incb _intr_nesting_level ; /* XXX do we need this? */ \ __CONCAT(Xresume,irq_num): ; \ FAKE_MCOUNT(13*4(%esp)) ; /* XXX late to avoid double count */ \ - incl _cnt+V_INTR ; /* tally interrupts */ \ - movl _intr_countp + (irq_num) * 4,%eax ; \ - incl (%eax) ; \ - movl _cpl,%eax ; \ - pushl %eax ; \ - pushl _intr_unit + (irq_num) * 4 ; \ - orl _intr_mask + (irq_num) * 4,%eax ; \ - movl %eax,_cpl ; \ + pushl $irq_num; /* pass the IRQ */ \ sti ; \ - call *_intr_handler + (irq_num) * 4 ; \ - cli ; /* must unmask _imen and icu atomically */ \ - movb _imen + IRQ_BYTE(irq_num),%al ; \ - andb $~IRQ_BIT(irq_num),%al ; \ - movb %al,_imen + IRQ_BYTE(irq_num) ; \ - outb %al,$icu+ICU_IMR_OFFSET ; \ - sti ; /* XXX _doreti repeats the cli/sti */ \ + call _sched_ithd ; \ + addl $4, %esp ; /* discard the parameter */ \ MEXITCOUNT ; \ /* We could usually avoid the following jmp by inlining some of */ \ /* _doreti, but it's probably better to use less cache. */ \ - jmp _doreti ; \ -; \ - ALIGN_TEXT ; \ -2: ; \ - /* XXX skip mcounting here to avoid double count */ \ - orb $IRQ_BIT(irq_num),_ipending + IRQ_BYTE(irq_num) ; \ - popl %fs ; \ - popl %es ; \ - popl %ds ; \ - popal ; \ - addl $4+4,%esp ; \ - iret + jmp doreti_next /* and catch up inside doreti */ + +/* + * Reenable the interrupt mask after completing an interrupt. Called + * from ithd_loop. There are two separate functions, one for each + * ICU. + */ + .globl setimask0, setimask1 +setimask0: + cli + movb _imen,%al + outb %al,$IO_ICU1 + ICU_IMR_OFFSET + sti + ret + +setimask1: + cli + movb _imen + 1,%al + outb %al,$IO_ICU2 + ICU_IMR_OFFSET + sti + ret MCOUNT_LABEL(bintr) FAST_INTR(0,fastintr0, ENABLE_ICU1) @@ -181,7 +190,9 @@ MCOUNT_LABEL(bintr) FAST_INTR(13,fastintr13, ENABLE_ICU1_AND_2) FAST_INTR(14,fastintr14, ENABLE_ICU1_AND_2) FAST_INTR(15,fastintr15, ENABLE_ICU1_AND_2) + #define CLKINTR_PENDING movl $1,CNAME(clkintr_pending) +/* Threaded interrupts */ INTR(0,intr0, IO_ICU1, ENABLE_ICU1, al, CLKINTR_PENDING) INTR(1,intr1, IO_ICU1, ENABLE_ICU1, al,) INTR(2,intr2, IO_ICU1, ENABLE_ICU1, al,) @@ -198,6 +209,7 @@ MCOUNT_LABEL(bintr) INTR(13,intr13, IO_ICU2, ENABLE_ICU1_AND_2, ah,) INTR(14,intr14, IO_ICU2, ENABLE_ICU1_AND_2, ah,) INTR(15,intr15, IO_ICU2, ENABLE_ICU1_AND_2, ah,) + MCOUNT_LABEL(eintr) .data @@ -211,10 +223,4 @@ _ihandlers: /* addresses of interrupt handlers */ .long _swi_null, swi_net, _swi_null, _swi_null .long _swi_vm, _swi_null, _softclock -imasks: /* masks for interrupt handlers */ - .space NHWI*4 /* padding; HWI masks are elsewhere */ - - .long SWI_TTY_MASK, SWI_NET_MASK, SWI_CAMNET_MASK, SWI_CAMBIO_MASK - .long SWI_VM_MASK, SWI_TQ_MASK, SWI_CLOCK_MASK - .text diff --git a/sys/amd64/isa/intr_machdep.c b/sys/amd64/isa/intr_machdep.c index 34a8c22..870760e 100644 --- a/sys/amd64/isa/intr_machdep.c +++ b/sys/amd64/isa/intr_machdep.c @@ -36,12 +36,6 @@ * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * $FreeBSD$ */ -/* - * This file contains an aggregated module marked: - * Copyright (c) 1997, Stefan Esser <se@freebsd.org> - * All rights reserved. - * See the notice for details. - */ #include "opt_auto_eoi.h" @@ -51,11 +45,14 @@ #ifndef SMP #include <machine/lock.h> #endif +#include <sys/proc.h> #include <sys/systm.h> #include <sys/syslog.h> #include <sys/kernel.h> +#include <sys/kthread.h> #include <sys/malloc.h> #include <sys/module.h> +#include <sys/unistd.h> #include <sys/errno.h> #include <sys/interrupt.h> #include <machine/ipl.h> @@ -91,30 +88,14 @@ #include <i386/isa/mca_machdep.h> #endif -/* XXX should be in suitable include files */ -#ifdef PC98 -#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ -#define ICU_SLAVEID 7 -#else -#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ -#define ICU_SLAVEID 2 -#endif - -#ifdef APIC_IO /* - * This is to accommodate "mixed-mode" programming for - * motherboards that don't connect the 8254 to the IO APIC. + * Per-interrupt data. We consider the soft interrupt to be a special + * case, so these arrays have NHWI + NSWI entries, not ICU_LEN. */ -#define AUTO_EOI_1 1 -#endif - -#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) - -u_long *intr_countp[ICU_LEN]; -inthand2_t *intr_handler[ICU_LEN]; -u_int intr_mask[ICU_LEN]; -static u_int* intr_mptr[ICU_LEN]; -void *intr_unit[ICU_LEN]; +u_long *intr_countp[NHWI + NSWI]; /* pointers to interrupt counters */ +inthand2_t *intr_handler[NHWI + NSWI]; /* first level interrupt handler */ +ithd *ithds[NHWI + NSWI]; /* real interrupt handler */ +void *intr_unit[NHWI + NSWI]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), @@ -292,8 +273,9 @@ isa_nmi(cd) } /* - * Fill in default interrupt table (in case of spuruious interrupt - * during configuration of kernel, setup interrupt control unit + * Create a default interrupt table to avoid problems caused by + * spurious interrupts during configuration of kernel, then setup + * interrupt control unit. */ void isa_defaultirq() @@ -364,16 +346,6 @@ isa_strayintr(vcookiep) { int intr = (void **)vcookiep - &intr_unit[0]; - /* DON'T BOTHER FOR NOW! */ - /* for some reason, we get bursts of intr #7, even if not enabled! */ - /* - * Well the reason you got bursts of intr #7 is because someone - * raised an interrupt line and dropped it before the 8259 could - * prioritize it. This is documented in the intel data book. This - * means you have BAD hardware! I have changed this so that only - * the first 5 get logged, then it quits logging them, and puts - * out a special message. rgrimes 3/25/1993 - */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by @@ -405,36 +377,10 @@ isa_irq_pending() } #endif -int -update_intr_masks(void) -{ - int intr, n=0; - u_int mask,*maskptr; - - for (intr=0; intr < ICU_LEN; intr ++) { -#if defined(APIC_IO) - /* no 8259 SLAVE to ignore */ -#else - if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ -#endif /* APIC_IO */ - maskptr = intr_mptr[intr]; - if (!maskptr) - continue; - *maskptr |= SWI_LOW_MASK | (1 << intr); - mask = *maskptr; - if (mask != intr_mask[intr]) { -#if 0 - printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", - intr, intr_mask[intr], mask, maskptr); -#endif - intr_mask[intr]=mask; - n++; - } - - } - return (n); -} - +/* + * Update intrnames array with the specified name. This is used by + * vmstat(8) and the like. + */ static void update_intrname(int intr, char *name) { @@ -485,7 +431,7 @@ found: } int -icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) +icu_setup(int intr, inthand2_t *handler, void *arg, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ @@ -493,7 +439,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; - u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ @@ -506,8 +451,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; - intr_mptr[intr] = maskptr; - intr_mask[intr] = mask | SWI_LOW_MASK | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { @@ -547,11 +490,15 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); - MPINTR_UNLOCK(); write_eflags(ef); return (0); } +/* + * Dissociate an interrupt handler from an IRQ and set the handler to + * the stray interrupt handler. The 'handler' parameter is used only + * for consistency checking. + */ int icu_unset(intr, handler) int intr; @@ -567,8 +514,6 @@ icu_unset(intr, handler) disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; - intr_mptr[intr] = NULL; - intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ @@ -581,353 +526,172 @@ icu_unset(intr, handler) setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ - MPINTR_UNLOCK(); write_eflags(ef); return (0); } -/* The following notice applies beyond this point in the file */ - -/* - * Copyright (c) 1997, Stefan Esser <se@freebsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice unmodified, this list of conditions, and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - * - */ - -typedef struct intrec { - intrmask_t mask; - inthand2_t *handler; - void *argument; - struct intrec *next; - char *name; - int intr; - intrmask_t *maskptr; - int flags; -} intrec; - -static intrec *intreclist_head[ICU_LEN]; - -/* - * The interrupt multiplexer calls each of the handlers in turn. The - * ipl is initially quite low. It is raised as necessary for each call - * and lowered after the call. Thus out of order handling is possible - * even for interrupts of the same type. This is probably no more - * harmful than out of order handling in general (not harmful except - * for real time response which we don't support anyway). - */ -static void -intr_mux(void *arg) -{ - intrec *p; - intrmask_t oldspl; - - for (p = arg; p != NULL; p = p->next) { - oldspl = splq(p->mask); - p->handler(p->argument); - splx(oldspl); - } -} - -static intrec* -find_idesc(unsigned *maskptr, int irq) -{ - intrec *p = intreclist_head[irq]; - - while (p && p->maskptr != maskptr) - p = p->next; - - return (p); -} - -static intrec** -find_pred(intrec *idesc, int irq) +intrec * +inthand_add(const char *name, int irq, inthand2_t handler, void *arg, + int pri, int flags) { - intrec **pp = &intreclist_head[irq]; - intrec *p = *pp; - - while (p != idesc) { - if (p == NULL) - return (NULL); - pp = &p->next; - p = *pp; - } - return (pp); -} - -/* - * Both the low level handler and the shared interrupt multiplexer - * block out further interrupts as set in the handlers "mask", while - * the handler is running. In fact *maskptr should be used for this - * purpose, but since this requires one more pointer dereference on - * each interrupt, we rather bother update "mask" whenever *maskptr - * changes. The function "update_masks" should be called **after** - * all manipulation of the linked list of interrupt handlers hung - * off of intrdec_head[irq] is complete, since the chain of handlers - * will both determine the *maskptr values and the instances of mask - * that are fixed. This function should be called with the irq for - * which a new handler has been add blocked, since the masks may not - * yet know about the use of this irq for a device of a certain class. - */ + ithd *ithd = ithds[irq]; /* descriptor for the IRQ */ + intrec *head; /* chain of handlers for IRQ */ + intrec *idesc; /* descriptor for this handler */ + struct proc *p; /* interrupt thread */ + int errcode = 0; -static void -update_mux_masks(void) -{ - int irq; - for (irq = 0; irq < ICU_LEN; irq++) { - intrec *idesc = intreclist_head[irq]; - while (idesc != NULL) { - if (idesc->maskptr != NULL) { - /* our copy of *maskptr may be stale, refresh */ - idesc->mask = *idesc->maskptr; - } - idesc = idesc->next; + if (name == NULL) /* no name? */ + panic ("anonymous interrupt"); + if (ithd == NULL || ithd->it_ih == NULL) { + /* first handler for this irq. */ + if (ithd == NULL) { + ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK); + if (ithd == NULL) + return (NULL); + bzero(ithd, sizeof(struct ithd)); + ithd->irq = irq; + ithds[irq] = ithd; } - } -} - -static void -update_masks(intrmask_t *maskptr, int irq) -{ - intrmask_t mask = 1 << irq; - - if (maskptr == NULL) - return; - - if (find_idesc(maskptr, irq) == NULL) { - /* no reference to this maskptr was found in this irq's chain */ - if ((*maskptr & mask) == 0) - return; - /* the irq was included in the classes mask, remove it */ - *maskptr &= ~mask; - } else { - /* a reference to this maskptr was found in this irq's chain */ - if ((*maskptr & mask) != 0) - return; - /* put the irq into the classes mask */ - *maskptr |= mask; - } - /* we need to update all values in the intr_mask[irq] array */ - update_intr_masks(); - /* update mask in chains of the interrupt multiplex handler as well */ - update_mux_masks(); -} - -/* - * Add interrupt handler to linked list hung off of intreclist_head[irq] - * and install shared interrupt multiplex handler, if necessary - */ - -static int -add_intrdesc(intrec *idesc) -{ - int irq = idesc->intr; - - intrec *head = intreclist_head[irq]; - - if (head == NULL) { - /* first handler for this irq, just install it */ - if (icu_setup(irq, idesc->handler, idesc->argument, - idesc->maskptr, idesc->flags) != 0) - return (-1); - - update_intrname(irq, idesc->name); - /* keep reference */ - intreclist_head[irq] = idesc; - } else { - if ((idesc->flags & INTR_EXCL) != 0 - || (head->flags & INTR_EXCL) != 0) { + /* + * If we have a fast interrupt, we need to set the + * handler address directly. Do that below. For a + * slow interrupt, we don't need to know more details, + * so do it here because it's tidier. + */ + if ((flags & INTR_FAST) == 0) { /* - * can't append new handler, if either list head or - * new handler do not allow interrupts to be shared + * Only create a kernel thread if we don't already + * have one. */ - if (bootverbose) - printf("\tdevice combination doesn't support " - "shared irq%d\n", irq); - return (-1); - } - if (head->next == NULL) { + if (ithd->it_proc == NULL) { + errcode = kthread_create(ithd_loop, NULL, &p, + RFSTOPPED | RFHIGHPID, "irq%d: %s", irq, + name); + if (errcode) + panic("inthand_add: Can't create " + "interrupt thread"); + p->p_rtprio.type = RTP_PRIO_ITHREAD; + p->p_stat = SWAIT; /* we're idle */ + + /* Put in linkages. */ + ithd->it_proc = p; + p->p_ithd = ithd; + } else + snprintf(ithd->it_proc->p_comm, MAXCOMLEN, + "irq%d: %s", irq, name); + p->p_rtprio.prio = pri; + /* - * second handler for this irq, replace device driver's - * handler by shared interrupt multiplexer function + * The interrupt process must be in place, but + * not necessarily schedulable, before we + * initialize the ICU, since it may cause an + * immediate interrupt. */ - icu_unset(irq, head->handler); - if (icu_setup(irq, intr_mux, head, 0, 0) != 0) - return (-1); - if (bootverbose) - printf("\tusing shared irq%d.\n", irq); - update_intrname(irq, "mux"); + if (icu_setup(irq, &sched_ithd, arg, flags) != 0) + panic("inthand_add: Can't initialize ICU"); } - /* just append to the end of the chain */ - while (head->next != NULL) - head = head->next; - head->next = idesc; - } - update_masks(idesc->maskptr, irq); - return (0); -} - -/* - * Create and activate an interrupt handler descriptor data structure. - * - * The dev_instance pointer is required for resource management, and will - * only be passed through to resource_claim(). - * - * There will be functions that derive a driver and unit name from a - * dev_instance variable, and those functions will be used to maintain the - * interrupt counter label array referenced by systat and vmstat to report - * device interrupt rates (->update_intrlabels). - * - * Add the interrupt handler descriptor data structure created by an - * earlier call of create_intr() to the linked list for its irq and - * adjust the interrupt masks if necessary. - * - * WARNING: This is an internal function and not to be used by device - * drivers. It is subject to change without notice. - */ - -intrec * -inthand_add(const char *name, int irq, inthand2_t handler, void *arg, - intrmask_t *maskptr, int flags) -{ - intrec *idesc; - int errcode = -1; - intrmask_t oldspl; - - if (ICU_LEN > 8 * sizeof *maskptr) { - printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", - ICU_LEN, 8 * sizeof *maskptr); + } else if ((flags & INTR_EXCL) != 0 + || (ithd->it_ih->flags & INTR_EXCL) != 0) { + /* + * We can't append the new handler if either + * list ithd or new handler do not allow + * interrupts to be shared. + */ + if (bootverbose) + printf("\tdevice combination %s and %s " + "doesn't support shared irq%d\n", + ithd->it_ih->name, name, irq); + return(NULL); + } else if (flags & INTR_FAST) { + /* We can only have one fast interrupt by itself. */ + if (bootverbose) + printf("\tCan't add fast interrupt %s" + " to normal interrupt %s on irq%d", + name, ithd->it_ih->name, irq); return (NULL); + } else { /* update p_comm */ + p = ithd->it_proc; + if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) { + strcat(p->p_comm, " "); + strcat(p->p_comm, name); + } else if (strlen(p->p_comm) == MAXCOMLEN) + p->p_comm[MAXCOMLEN - 1] = '+'; + else + strcat(p->p_comm, "+"); } - if ((unsigned)irq >= ICU_LEN) { - printf("create_intr: requested irq%d too high, limit is %d\n", - irq, ICU_LEN -1); + idesc = malloc(sizeof (struct intrec), M_DEVBUF, M_WAITOK); + if (idesc == NULL) return (NULL); - } + bzero(idesc, sizeof (struct intrec)); - idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); - if (idesc == NULL) - return NULL; - bzero(idesc, sizeof *idesc); + idesc->handler = handler; + idesc->argument = arg; + idesc->flags = flags; + idesc->ithd = ithd; - if (name == NULL) - name = "???"; idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); if (idesc->name == NULL) { free(idesc, M_DEVBUF); - return NULL; + return (NULL); } strcpy(idesc->name, name); - idesc->handler = handler; - idesc->argument = arg; - idesc->maskptr = maskptr; - idesc->intr = irq; - idesc->flags = flags; - - /* block this irq */ - oldspl = splq(1 << irq); - - /* add irq to class selected by maskptr */ - errcode = add_intrdesc(idesc); - splx(oldspl); - - if (errcode != 0) { + /* Slow interrupts got set up above. */ + if ((flags & INTR_FAST) + && (icu_setup(irq, idesc->handler, idesc->argument, + idesc->flags) != 0) ) { if (bootverbose) - printf("\tintr_connect(irq%d) failed, result=%d\n", + printf("\tinthand_add(irq%d) failed, result=%d\n", irq, errcode); free(idesc->name, M_DEVBUF); free(idesc, M_DEVBUF); - idesc = NULL; + return NULL; } - + head = ithd->it_ih; /* look at chain of handlers */ + if (head) { + while (head->next != NULL) + head = head->next; /* find the end */ + head->next = idesc; /* hook it in there */ + } else + ithd->it_ih = idesc; /* put it up front */ + update_intrname(irq, idesc->name); return (idesc); } /* - * Deactivate and remove the interrupt handler descriptor data connected - * created by an earlier call of intr_connect() from the linked list and - * adjust theinterrupt masks if necessary. + * Deactivate and remove linked list the interrupt handler descriptor + * data connected created by an earlier call of inthand_add(), then + * adjust the interrupt masks if necessary. * - * Return the memory held by the interrupt handler descriptor data structure - * to the system. Make sure, the handler is not actively used anymore, before. + * Return the memory held by the interrupt handler descriptor data + * structure to the system. First ensure the handler is not actively + * in use. */ int inthand_remove(intrec *idesc) { - intrec **hook, *head; - int irq; - int errcode = 0; - intrmask_t oldspl; + ithd *ithd; /* descriptor for the IRQ */ + intrec *ih; /* chain of handlers */ if (idesc == NULL) return (-1); + ithd = idesc->ithd; + ih = ithd->it_ih; - irq = idesc->intr; - - /* find pointer that keeps the reference to this interrupt descriptor */ - hook = find_pred(idesc, irq); - if (hook == NULL) + if (ih == idesc) /* first in the chain */ + ithd->it_ih = idesc->next; /* unhook it */ + else { + while ((ih != NULL) + && (ih->next != idesc) ) + ih = ih->next; + if (ih->next != idesc) return (-1); - - /* make copy of original list head, the line after may overwrite it */ - head = intreclist_head[irq]; - - /* unlink: make predecessor point to idesc->next instead of to idesc */ - *hook = idesc->next; - - /* now check whether the element we removed was the list head */ - if (idesc == head) { - - oldspl = splq(1 << irq); - - /* check whether the new list head is the only element on list */ - head = intreclist_head[irq]; - if (head != NULL) { - icu_unset(irq, intr_mux); - if (head->next != NULL) { - /* install the multiplex handler with new list head as argument */ - errcode = icu_setup(irq, intr_mux, head, 0, 0); - if (errcode == 0) - update_intrname(irq, NULL); - } else { - /* install the one remaining handler for this irq */ - errcode = icu_setup(irq, head->handler, - head->argument, - head->maskptr, head->flags); - if (errcode == 0) - update_intrname(irq, head->name); + ih->next = ih->next->next; } - } else { - /* revert to old handler, eg: strayintr */ - icu_unset(irq, idesc->handler); - } - splx(oldspl); - } - update_masks(idesc->maskptr, irq); + + if (ithd->it_ih == NULL) /* no handlers left, */ + icu_unset(ithd->irq, idesc->handler); free(idesc, M_DEVBUF); return (0); } diff --git a/sys/amd64/isa/intr_machdep.h b/sys/amd64/isa/intr_machdep.h index 5982295..87c97a3 100644 --- a/sys/amd64/isa/intr_machdep.h +++ b/sys/amd64/isa/intr_machdep.h @@ -98,7 +98,6 @@ #define TPR_BLOCK_XCPUSTOP 0xaf /* */ #define TPR_BLOCK_ALL 0xff /* all INTs */ - #ifdef TEST_TEST1 /* put a 'fake' HWI in top of APIC prio 0x3x, 32 + 31 = 63 = 0x3f */ #define XTEST1_OFFSET (ICU_OFFSET + 31) @@ -145,8 +144,9 @@ extern u_long intrcnt[]; /* counts for for each device and stray */ extern char intrnames[]; /* string table containing device names */ extern u_long *intr_countp[]; /* pointers into intrcnt[] */ extern inthand2_t *intr_handler[]; /* C entry points of intr handlers */ -extern u_int intr_mask[]; /* sets of intrs masked during handling of 1 */ +extern ithd *ithds[]; extern void *intr_unit[]; /* cookies to pass to intr handlers */ +extern ithd softinterrupt; /* soft interrupt thread */ inthand_t IDTVEC(fastintr0), IDTVEC(fastintr1), @@ -190,26 +190,60 @@ inthand_t #endif /** TEST_TEST1 */ #endif /* SMP || APIC_IO */ +#ifdef PC98 +#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ +#define ICU_SLAVEID 7 +#else +#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ +#define ICU_SLAVEID 2 +#endif + +#ifdef APIC_IO +/* + * This is to accommodate "mixed-mode" programming for + * motherboards that don't connect the 8254 to the IO APIC. + */ +#define AUTO_EOI_1 1 +#endif + +#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) + void isa_defaultirq __P((void)); int isa_nmi __P((int cd)); int icu_setup __P((int intr, inthand2_t *func, void *arg, - u_int *maskptr, int flags)); + int flags)); int icu_unset __P((int intr, inthand2_t *handler)); -int update_intr_masks __P((void)); intrmask_t splq __P((intrmask_t mask)); -#define INTR_FAST 0x00000001 /* fast interrupt handler */ -#define INTR_EXCL 0x00010000 /* excl. intr, default is shared */ +/* + * Describe a hardware interrupt handler. These structures are + * accessed via the array intreclist, which contains one pointer per + * hardware interrupt. + * + * Multiple interrupt handlers for a specific IRQ can be chained + * together via the 'next' pointer. + */ +typedef struct intrec { + inthand2_t *handler; /* code address of handler */ + void *argument; /* argument to pass to handler */ + enum intr_type flags; /* flag bits (sys/bus.h) */ + char *name; /* name of handler */ + ithd *ithd; /* handler we're connected to */ + struct intrec *next; /* next handler for this irq */ +} intrec; /* * WARNING: These are internal functions and not to be used by device drivers! * They are subject to change without notice. */ struct intrec *inthand_add(const char *name, int irq, inthand2_t handler, - void *arg, intrmask_t *maskptr, int flags); - + void *arg, int pri, int flags); int inthand_remove(struct intrec *idesc); +void sched_ithd(void *); +void ithd_loop(void *); +void start_softintr(void *); +void intr_soft(void *); #endif /* LOCORE */ diff --git a/sys/amd64/isa/ithread.c b/sys/amd64/isa/ithread.c new file mode 100644 index 0000000..4ceac42 --- /dev/null +++ b/sys/amd64/isa/ithread.c @@ -0,0 +1,353 @@ +/*- + * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Berkeley Software Design Inc's name may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * From BSDI: intr.c,v 1.6.2.5 1999/07/06 19:16:52 cp Exp + * $FreeBSD$ + */ + +/* Interrupt thread code. */ + +#include "opt_auto_eoi.h" + +#include "isa.h" + +#include <sys/param.h> +#include <sys/rtprio.h> /* change this name XXX */ +#ifndef SMP +#include <machine/lock.h> +#endif +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/syslog.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/unistd.h> +#include <sys/errno.h> +#include <sys/interrupt.h> +#include <machine/ipl.h> +#include <machine/md_var.h> +#include <machine/segments.h> +#include <sys/bus.h> + +#if defined(APIC_IO) +#include <machine/smp.h> +#include <machine/smptests.h> /** FAST_HI */ +#include <machine/resource.h> +#endif /* APIC_IO */ +#ifdef PC98 +#include <pc98/pc98/pc98.h> +#include <pc98/pc98/pc98_machdep.h> +#include <pc98/pc98/epsonio.h> +#else +#include <i386/isa/isa.h> +#endif +#include <i386/isa/icu.h> + +#if NISA > 0 +#include <isa/isavar.h> +#endif +#include <i386/isa/intr_machdep.h> +#include <sys/interrupt.h> +#ifdef APIC_IO +#include <machine/clock.h> +#endif + +#include "mca.h" +#if NMCA > 0 +#include <i386/isa/mca_machdep.h> +#endif + +#include <sys/vmmeter.h> +#include <machine/mutex.h> +#include <sys/ktr.h> +#include <machine/cpu.h> +#if 0 +#include <ddb/ddb.h> +#endif + +u_long softintrcnt [NSWI]; + +SYSINIT(start_softintr, SI_SUB_SOFTINTR, SI_ORDER_FIRST, start_softintr, NULL) + +/* + * Schedule a heavyweight interrupt process. This function is called + * from the interrupt handlers Xintr<num>. + */ +void +sched_ithd(void *cookie) +{ + int irq = (int) cookie; /* IRQ we're handling */ + ithd *ir = ithds[irq]; /* and the process that does it */ + + /* This used to be in icu_vector.s */ + /* + * We count software interrupts when we process them. The + * code here follows previous practice, but there's an + * argument for counting hardware interrupts when they're + * processed too. + */ + if (irq < NHWI) /* real interrupt, */ + atomic_add_long(intr_countp[irq], 1); /* one more for this IRQ */ + atomic_add_int(&cnt.v_intr, 1); /* one more global interrupt */ + + CTR3(KTR_INTR, "sched_ithd pid %d(%s) need=%d", + ir->it_proc->p_pid, ir->it_proc->p_comm, ir->it_need); + +#if 0 + /* + * If we are in the debugger, we can't use interrupt threads to + * process interrupts since the threads are scheduled. Instead, + * call the interrupt handlers directly. This should be able to + * go away once we have light-weight interrupt handlers. + */ + if (db_active) { + intrec *ih; /* and our interrupt handler chain */ +#if 0 + membar_unlock(); /* push out "it_need=0" */ +#endif + for (ih = ir->it_ih; ih != NULL; ih = ih->next) { + if ((ih->flags & INTR_MPSAFE) == 0) + mtx_enter(&Giant, MTX_DEF); + ih->handler(ih->argument); + if ((ih->flags & INTR_MPSAFE) == 0) + mtx_exit(&Giant, MTX_DEF); + } + + INTREN (1 << ir->irq); /* reset the mask bit */ + return; + } +#endif + + /* + * Set it_need so that if the thread is already running but close + * to done, it will do another go-round. Then get the sched lock + * and see if the thread is on whichkqs yet. If not, put it on + * there. In any case, kick everyone so that if the new thread + * is higher priority than their current thread, it gets run now. + */ + ir->it_need = 1; + mtx_enter(&sched_lock, MTX_SPIN); + if (ir->it_proc->p_stat == SWAIT) { /* not on run queue */ + CTR1(KTR_INTR, "sched_ithd: setrunqueue %d", + ir->it_proc->p_pid); +/* membar_lock(); */ + ir->it_proc->p_stat = SRUN; + setrunqueue(ir->it_proc); + aston(); + } + else { +if (irq < NHWI && (irq & 7) != 0) + CTR3(KTR_INTR, "sched_ithd %d: it_need %d, state %d", + ir->it_proc->p_pid, + ir->it_need, + ir->it_proc->p_stat ); + } + mtx_exit(&sched_lock, MTX_SPIN); +#if 0 + aston(); /* ??? check priorities first? */ +#else + need_resched(); +#endif +} + +/* + * This is the main code for all interrupt threads. It gets put on + * whichkqs by setrunqueue above. + */ +void +ithd_loop(void *dummy) +{ + ithd *me; /* our thread context */ + intrec *ih; /* and our interrupt handler chain */ + + me = curproc->p_ithd; /* point to myself */ + + /* + * As long as we have interrupts outstanding, go through the + * list of handlers, giving each one a go at it. + */ + for (;;) { + CTR3(KTR_INTR, "ithd_loop pid %d(%s) need=%d", + me->it_proc->p_pid, me->it_proc->p_comm, me->it_need); + while (me->it_need) { + /* + * Service interrupts. If another interrupt + * arrives while we are running, they will set + * it_need to denote that we should make + * another pass. + */ + me->it_need = 0; +#if 0 + membar_unlock(); /* push out "it_need=0" */ +#endif + for (ih = me->it_ih; ih != NULL; ih = ih->next) { + CTR5(KTR_INTR, + "ithd_loop pid %d ih=%p: %p(%p) flg=%x", + me->it_proc->p_pid, (void *)ih, + (void *)ih->handler, ih->argument, + ih->flags); + + if ((ih->flags & INTR_MPSAFE) == 0) + mtx_enter(&Giant, MTX_DEF); + ih->handler(ih->argument); + if ((ih->flags & INTR_MPSAFE) == 0) + mtx_exit(&Giant, MTX_DEF); + } + } + + /* + * Processed all our interrupts. Now get the sched + * lock. This may take a while and it_need may get + * set again, so we have to check it again. + */ + mtx_enter(&sched_lock, MTX_SPIN); + if (!me->it_need) { + + INTREN (1 << me->irq); /* reset the mask bit */ + me->it_proc->p_stat = SWAIT; /* we're idle */ +#ifdef APIC_IO + CTR1(KTR_INTR, "ithd_loop pid %d: done", + me->it_proc->p_pid); +#else + CTR2(KTR_INTR, "ithd_loop pid %d: done, imen=%x", + me->it_proc->p_pid, imen); +#endif + mi_switch(); + CTR1(KTR_INTR, "ithd_loop pid %d: resumed", + me->it_proc->p_pid); + } + mtx_exit(&sched_lock, MTX_SPIN); + } +} + +/* + * Start soft interrupt thread. + */ +void +start_softintr(void *dummy) +{ + int error; + struct proc *p; + ithd *softintr; /* descriptor for the "IRQ" */ + intrec *idesc; /* descriptor for this handler */ + char *name = "sintr"; /* name for idesc */ + int i; + + if (ithds[SOFTINTR]) { /* we already have a thread */ + printf("start_softintr: already running"); + return; + } + /* first handler for this irq. */ + softintr = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK); + if (softintr == NULL) + panic ("Can't create soft interrupt thread"); + bzero(softintr, sizeof(struct ithd)); + softintr->irq = SOFTINTR; + ithds[SOFTINTR] = softintr; + error = kthread_create(intr_soft, NULL, &p, + RFSTOPPED | RFHIGHPID, "softinterrupt"); + if (error) + panic("start_softintr: kthread_create error %d\n", error); + + p->p_rtprio.type = RTP_PRIO_ITHREAD; + p->p_rtprio.prio = PI_SOFT; /* soft interrupt */ + p->p_stat = SWAIT; /* we're idle */ + + /* Put in linkages. */ + softintr->it_proc = p; + p->p_ithd = softintr; /* reverse link */ + + idesc = malloc(sizeof (struct intrec), M_DEVBUF, M_WAITOK); + if (idesc == NULL) + panic ("Can't create soft interrupt thread"); + bzero(idesc, sizeof (struct intrec)); + + idesc->ithd = softintr; + idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); + if (idesc->name == NULL) + panic ("Can't create soft interrupt thread"); + strcpy(idesc->name, name); + for (i = NHWI; i < NHWI + NSWI; i++) + intr_countp[i] = &softintrcnt [i - NHWI]; +} + +/* + * Software interrupt process code. + */ +void +intr_soft(void *dummy) +{ + int i; + ithd *me; /* our thread context */ + + me = curproc->p_ithd; /* point to myself */ + + /* Main loop */ + for (;;) { +#if 0 + CTR3(KTR_INTR, "intr_soft pid %d(%s) need=%d", + me->it_proc->p_pid, me->it_proc->p_comm, + me->it_need); +#endif + + /* + * Service interrupts. If another interrupt arrives + * while we are running, they will set it_need to + * denote that we should make another pass. + */ + me->it_need = 0; + while ((i = ffs(spending))) { + i--; + atomic_add_long(intr_countp[i], 1); + spending &= ~ (1 << i); + mtx_enter(&Giant, MTX_DEF); + (ihandlers[i])(); + mtx_exit(&Giant, MTX_DEF); + } + /* + * Processed all our interrupts. Now get the sched + * lock. This may take a while and it_need may get + * set again, so we have to check it again. + */ + mtx_enter(&sched_lock, MTX_SPIN); + if (!me->it_need) { +#if 0 + CTR1(KTR_INTR, "intr_soft pid %d: done", + me->it_proc->p_pid); +#endif + me->it_proc->p_stat = SWAIT; /* we're idle */ + mi_switch(); +#if 0 + CTR1(KTR_INTR, "intr_soft pid %d: resumed", + me->it_proc->p_pid); +#endif + } + mtx_exit(&sched_lock, MTX_SPIN); + } +} diff --git a/sys/amd64/isa/nmi.c b/sys/amd64/isa/nmi.c index 34a8c22..870760e 100644 --- a/sys/amd64/isa/nmi.c +++ b/sys/amd64/isa/nmi.c @@ -36,12 +36,6 @@ * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * $FreeBSD$ */ -/* - * This file contains an aggregated module marked: - * Copyright (c) 1997, Stefan Esser <se@freebsd.org> - * All rights reserved. - * See the notice for details. - */ #include "opt_auto_eoi.h" @@ -51,11 +45,14 @@ #ifndef SMP #include <machine/lock.h> #endif +#include <sys/proc.h> #include <sys/systm.h> #include <sys/syslog.h> #include <sys/kernel.h> +#include <sys/kthread.h> #include <sys/malloc.h> #include <sys/module.h> +#include <sys/unistd.h> #include <sys/errno.h> #include <sys/interrupt.h> #include <machine/ipl.h> @@ -91,30 +88,14 @@ #include <i386/isa/mca_machdep.h> #endif -/* XXX should be in suitable include files */ -#ifdef PC98 -#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ -#define ICU_SLAVEID 7 -#else -#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ -#define ICU_SLAVEID 2 -#endif - -#ifdef APIC_IO /* - * This is to accommodate "mixed-mode" programming for - * motherboards that don't connect the 8254 to the IO APIC. + * Per-interrupt data. We consider the soft interrupt to be a special + * case, so these arrays have NHWI + NSWI entries, not ICU_LEN. */ -#define AUTO_EOI_1 1 -#endif - -#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) - -u_long *intr_countp[ICU_LEN]; -inthand2_t *intr_handler[ICU_LEN]; -u_int intr_mask[ICU_LEN]; -static u_int* intr_mptr[ICU_LEN]; -void *intr_unit[ICU_LEN]; +u_long *intr_countp[NHWI + NSWI]; /* pointers to interrupt counters */ +inthand2_t *intr_handler[NHWI + NSWI]; /* first level interrupt handler */ +ithd *ithds[NHWI + NSWI]; /* real interrupt handler */ +void *intr_unit[NHWI + NSWI]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), @@ -292,8 +273,9 @@ isa_nmi(cd) } /* - * Fill in default interrupt table (in case of spuruious interrupt - * during configuration of kernel, setup interrupt control unit + * Create a default interrupt table to avoid problems caused by + * spurious interrupts during configuration of kernel, then setup + * interrupt control unit. */ void isa_defaultirq() @@ -364,16 +346,6 @@ isa_strayintr(vcookiep) { int intr = (void **)vcookiep - &intr_unit[0]; - /* DON'T BOTHER FOR NOW! */ - /* for some reason, we get bursts of intr #7, even if not enabled! */ - /* - * Well the reason you got bursts of intr #7 is because someone - * raised an interrupt line and dropped it before the 8259 could - * prioritize it. This is documented in the intel data book. This - * means you have BAD hardware! I have changed this so that only - * the first 5 get logged, then it quits logging them, and puts - * out a special message. rgrimes 3/25/1993 - */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by @@ -405,36 +377,10 @@ isa_irq_pending() } #endif -int -update_intr_masks(void) -{ - int intr, n=0; - u_int mask,*maskptr; - - for (intr=0; intr < ICU_LEN; intr ++) { -#if defined(APIC_IO) - /* no 8259 SLAVE to ignore */ -#else - if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ -#endif /* APIC_IO */ - maskptr = intr_mptr[intr]; - if (!maskptr) - continue; - *maskptr |= SWI_LOW_MASK | (1 << intr); - mask = *maskptr; - if (mask != intr_mask[intr]) { -#if 0 - printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", - intr, intr_mask[intr], mask, maskptr); -#endif - intr_mask[intr]=mask; - n++; - } - - } - return (n); -} - +/* + * Update intrnames array with the specified name. This is used by + * vmstat(8) and the like. + */ static void update_intrname(int intr, char *name) { @@ -485,7 +431,7 @@ found: } int -icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) +icu_setup(int intr, inthand2_t *handler, void *arg, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ @@ -493,7 +439,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; - u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ @@ -506,8 +451,6 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; - intr_mptr[intr] = maskptr; - intr_mask[intr] = mask | SWI_LOW_MASK | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { @@ -547,11 +490,15 @@ icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); - MPINTR_UNLOCK(); write_eflags(ef); return (0); } +/* + * Dissociate an interrupt handler from an IRQ and set the handler to + * the stray interrupt handler. The 'handler' parameter is used only + * for consistency checking. + */ int icu_unset(intr, handler) int intr; @@ -567,8 +514,6 @@ icu_unset(intr, handler) disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; - intr_mptr[intr] = NULL; - intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ @@ -581,353 +526,172 @@ icu_unset(intr, handler) setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ - MPINTR_UNLOCK(); write_eflags(ef); return (0); } -/* The following notice applies beyond this point in the file */ - -/* - * Copyright (c) 1997, Stefan Esser <se@freebsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice unmodified, this list of conditions, and the following - * disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - * - */ - -typedef struct intrec { - intrmask_t mask; - inthand2_t *handler; - void *argument; - struct intrec *next; - char *name; - int intr; - intrmask_t *maskptr; - int flags; -} intrec; - -static intrec *intreclist_head[ICU_LEN]; - -/* - * The interrupt multiplexer calls each of the handlers in turn. The - * ipl is initially quite low. It is raised as necessary for each call - * and lowered after the call. Thus out of order handling is possible - * even for interrupts of the same type. This is probably no more - * harmful than out of order handling in general (not harmful except - * for real time response which we don't support anyway). - */ -static void -intr_mux(void *arg) -{ - intrec *p; - intrmask_t oldspl; - - for (p = arg; p != NULL; p = p->next) { - oldspl = splq(p->mask); - p->handler(p->argument); - splx(oldspl); - } -} - -static intrec* -find_idesc(unsigned *maskptr, int irq) -{ - intrec *p = intreclist_head[irq]; - - while (p && p->maskptr != maskptr) - p = p->next; - - return (p); -} - -static intrec** -find_pred(intrec *idesc, int irq) +intrec * +inthand_add(const char *name, int irq, inthand2_t handler, void *arg, + int pri, int flags) { - intrec **pp = &intreclist_head[irq]; - intrec *p = *pp; - - while (p != idesc) { - if (p == NULL) - return (NULL); - pp = &p->next; - p = *pp; - } - return (pp); -} - -/* - * Both the low level handler and the shared interrupt multiplexer - * block out further interrupts as set in the handlers "mask", while - * the handler is running. In fact *maskptr should be used for this - * purpose, but since this requires one more pointer dereference on - * each interrupt, we rather bother update "mask" whenever *maskptr - * changes. The function "update_masks" should be called **after** - * all manipulation of the linked list of interrupt handlers hung - * off of intrdec_head[irq] is complete, since the chain of handlers - * will both determine the *maskptr values and the instances of mask - * that are fixed. This function should be called with the irq for - * which a new handler has been add blocked, since the masks may not - * yet know about the use of this irq for a device of a certain class. - */ + ithd *ithd = ithds[irq]; /* descriptor for the IRQ */ + intrec *head; /* chain of handlers for IRQ */ + intrec *idesc; /* descriptor for this handler */ + struct proc *p; /* interrupt thread */ + int errcode = 0; -static void -update_mux_masks(void) -{ - int irq; - for (irq = 0; irq < ICU_LEN; irq++) { - intrec *idesc = intreclist_head[irq]; - while (idesc != NULL) { - if (idesc->maskptr != NULL) { - /* our copy of *maskptr may be stale, refresh */ - idesc->mask = *idesc->maskptr; - } - idesc = idesc->next; + if (name == NULL) /* no name? */ + panic ("anonymous interrupt"); + if (ithd == NULL || ithd->it_ih == NULL) { + /* first handler for this irq. */ + if (ithd == NULL) { + ithd = malloc(sizeof (struct ithd), M_DEVBUF, M_WAITOK); + if (ithd == NULL) + return (NULL); + bzero(ithd, sizeof(struct ithd)); + ithd->irq = irq; + ithds[irq] = ithd; } - } -} - -static void -update_masks(intrmask_t *maskptr, int irq) -{ - intrmask_t mask = 1 << irq; - - if (maskptr == NULL) - return; - - if (find_idesc(maskptr, irq) == NULL) { - /* no reference to this maskptr was found in this irq's chain */ - if ((*maskptr & mask) == 0) - return; - /* the irq was included in the classes mask, remove it */ - *maskptr &= ~mask; - } else { - /* a reference to this maskptr was found in this irq's chain */ - if ((*maskptr & mask) != 0) - return; - /* put the irq into the classes mask */ - *maskptr |= mask; - } - /* we need to update all values in the intr_mask[irq] array */ - update_intr_masks(); - /* update mask in chains of the interrupt multiplex handler as well */ - update_mux_masks(); -} - -/* - * Add interrupt handler to linked list hung off of intreclist_head[irq] - * and install shared interrupt multiplex handler, if necessary - */ - -static int -add_intrdesc(intrec *idesc) -{ - int irq = idesc->intr; - - intrec *head = intreclist_head[irq]; - - if (head == NULL) { - /* first handler for this irq, just install it */ - if (icu_setup(irq, idesc->handler, idesc->argument, - idesc->maskptr, idesc->flags) != 0) - return (-1); - - update_intrname(irq, idesc->name); - /* keep reference */ - intreclist_head[irq] = idesc; - } else { - if ((idesc->flags & INTR_EXCL) != 0 - || (head->flags & INTR_EXCL) != 0) { + /* + * If we have a fast interrupt, we need to set the + * handler address directly. Do that below. For a + * slow interrupt, we don't need to know more details, + * so do it here because it's tidier. + */ + if ((flags & INTR_FAST) == 0) { /* - * can't append new handler, if either list head or - * new handler do not allow interrupts to be shared + * Only create a kernel thread if we don't already + * have one. */ - if (bootverbose) - printf("\tdevice combination doesn't support " - "shared irq%d\n", irq); - return (-1); - } - if (head->next == NULL) { + if (ithd->it_proc == NULL) { + errcode = kthread_create(ithd_loop, NULL, &p, + RFSTOPPED | RFHIGHPID, "irq%d: %s", irq, + name); + if (errcode) + panic("inthand_add: Can't create " + "interrupt thread"); + p->p_rtprio.type = RTP_PRIO_ITHREAD; + p->p_stat = SWAIT; /* we're idle */ + + /* Put in linkages. */ + ithd->it_proc = p; + p->p_ithd = ithd; + } else + snprintf(ithd->it_proc->p_comm, MAXCOMLEN, + "irq%d: %s", irq, name); + p->p_rtprio.prio = pri; + /* - * second handler for this irq, replace device driver's - * handler by shared interrupt multiplexer function + * The interrupt process must be in place, but + * not necessarily schedulable, before we + * initialize the ICU, since it may cause an + * immediate interrupt. */ - icu_unset(irq, head->handler); - if (icu_setup(irq, intr_mux, head, 0, 0) != 0) - return (-1); - if (bootverbose) - printf("\tusing shared irq%d.\n", irq); - update_intrname(irq, "mux"); + if (icu_setup(irq, &sched_ithd, arg, flags) != 0) + panic("inthand_add: Can't initialize ICU"); } - /* just append to the end of the chain */ - while (head->next != NULL) - head = head->next; - head->next = idesc; - } - update_masks(idesc->maskptr, irq); - return (0); -} - -/* - * Create and activate an interrupt handler descriptor data structure. - * - * The dev_instance pointer is required for resource management, and will - * only be passed through to resource_claim(). - * - * There will be functions that derive a driver and unit name from a - * dev_instance variable, and those functions will be used to maintain the - * interrupt counter label array referenced by systat and vmstat to report - * device interrupt rates (->update_intrlabels). - * - * Add the interrupt handler descriptor data structure created by an - * earlier call of create_intr() to the linked list for its irq and - * adjust the interrupt masks if necessary. - * - * WARNING: This is an internal function and not to be used by device - * drivers. It is subject to change without notice. - */ - -intrec * -inthand_add(const char *name, int irq, inthand2_t handler, void *arg, - intrmask_t *maskptr, int flags) -{ - intrec *idesc; - int errcode = -1; - intrmask_t oldspl; - - if (ICU_LEN > 8 * sizeof *maskptr) { - printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", - ICU_LEN, 8 * sizeof *maskptr); + } else if ((flags & INTR_EXCL) != 0 + || (ithd->it_ih->flags & INTR_EXCL) != 0) { + /* + * We can't append the new handler if either + * list ithd or new handler do not allow + * interrupts to be shared. + */ + if (bootverbose) + printf("\tdevice combination %s and %s " + "doesn't support shared irq%d\n", + ithd->it_ih->name, name, irq); + return(NULL); + } else if (flags & INTR_FAST) { + /* We can only have one fast interrupt by itself. */ + if (bootverbose) + printf("\tCan't add fast interrupt %s" + " to normal interrupt %s on irq%d", + name, ithd->it_ih->name, irq); return (NULL); + } else { /* update p_comm */ + p = ithd->it_proc; + if (strlen(p->p_comm) + strlen(name) < MAXCOMLEN) { + strcat(p->p_comm, " "); + strcat(p->p_comm, name); + } else if (strlen(p->p_comm) == MAXCOMLEN) + p->p_comm[MAXCOMLEN - 1] = '+'; + else + strcat(p->p_comm, "+"); } - if ((unsigned)irq >= ICU_LEN) { - printf("create_intr: requested irq%d too high, limit is %d\n", - irq, ICU_LEN -1); + idesc = malloc(sizeof (struct intrec), M_DEVBUF, M_WAITOK); + if (idesc == NULL) return (NULL); - } + bzero(idesc, sizeof (struct intrec)); - idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); - if (idesc == NULL) - return NULL; - bzero(idesc, sizeof *idesc); + idesc->handler = handler; + idesc->argument = arg; + idesc->flags = flags; + idesc->ithd = ithd; - if (name == NULL) - name = "???"; idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); if (idesc->name == NULL) { free(idesc, M_DEVBUF); - return NULL; + return (NULL); } strcpy(idesc->name, name); - idesc->handler = handler; - idesc->argument = arg; - idesc->maskptr = maskptr; - idesc->intr = irq; - idesc->flags = flags; - - /* block this irq */ - oldspl = splq(1 << irq); - - /* add irq to class selected by maskptr */ - errcode = add_intrdesc(idesc); - splx(oldspl); - - if (errcode != 0) { + /* Slow interrupts got set up above. */ + if ((flags & INTR_FAST) + && (icu_setup(irq, idesc->handler, idesc->argument, + idesc->flags) != 0) ) { if (bootverbose) - printf("\tintr_connect(irq%d) failed, result=%d\n", + printf("\tinthand_add(irq%d) failed, result=%d\n", irq, errcode); free(idesc->name, M_DEVBUF); free(idesc, M_DEVBUF); - idesc = NULL; + return NULL; } - + head = ithd->it_ih; /* look at chain of handlers */ + if (head) { + while (head->next != NULL) + head = head->next; /* find the end */ + head->next = idesc; /* hook it in there */ + } else + ithd->it_ih = idesc; /* put it up front */ + update_intrname(irq, idesc->name); return (idesc); } /* - * Deactivate and remove the interrupt handler descriptor data connected - * created by an earlier call of intr_connect() from the linked list and - * adjust theinterrupt masks if necessary. + * Deactivate and remove linked list the interrupt handler descriptor + * data connected created by an earlier call of inthand_add(), then + * adjust the interrupt masks if necessary. * - * Return the memory held by the interrupt handler descriptor data structure - * to the system. Make sure, the handler is not actively used anymore, before. + * Return the memory held by the interrupt handler descriptor data + * structure to the system. First ensure the handler is not actively + * in use. */ int inthand_remove(intrec *idesc) { - intrec **hook, *head; - int irq; - int errcode = 0; - intrmask_t oldspl; + ithd *ithd; /* descriptor for the IRQ */ + intrec *ih; /* chain of handlers */ if (idesc == NULL) return (-1); + ithd = idesc->ithd; + ih = ithd->it_ih; - irq = idesc->intr; - - /* find pointer that keeps the reference to this interrupt descriptor */ - hook = find_pred(idesc, irq); - if (hook == NULL) + if (ih == idesc) /* first in the chain */ + ithd->it_ih = idesc->next; /* unhook it */ + else { + while ((ih != NULL) + && (ih->next != idesc) ) + ih = ih->next; + if (ih->next != idesc) return (-1); - - /* make copy of original list head, the line after may overwrite it */ - head = intreclist_head[irq]; - - /* unlink: make predecessor point to idesc->next instead of to idesc */ - *hook = idesc->next; - - /* now check whether the element we removed was the list head */ - if (idesc == head) { - - oldspl = splq(1 << irq); - - /* check whether the new list head is the only element on list */ - head = intreclist_head[irq]; - if (head != NULL) { - icu_unset(irq, intr_mux); - if (head->next != NULL) { - /* install the multiplex handler with new list head as argument */ - errcode = icu_setup(irq, intr_mux, head, 0, 0); - if (errcode == 0) - update_intrname(irq, NULL); - } else { - /* install the one remaining handler for this irq */ - errcode = icu_setup(irq, head->handler, - head->argument, - head->maskptr, head->flags); - if (errcode == 0) - update_intrname(irq, head->name); + ih->next = ih->next->next; } - } else { - /* revert to old handler, eg: strayintr */ - icu_unset(irq, idesc->handler); - } - splx(oldspl); - } - update_masks(idesc->maskptr, irq); + + if (ithd->it_ih == NULL) /* no handlers left, */ + icu_unset(ithd->irq, idesc->handler); free(idesc, M_DEVBUF); return (0); } diff --git a/sys/amd64/isa/npx.c b/sys/amd64/isa/npx.c index 637853e..8610e35 100644 --- a/sys/amd64/isa/npx.c +++ b/sys/amd64/isa/npx.c @@ -245,6 +245,12 @@ npx_probe(dev) setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); npx_idt_probeintr = idt[npx_intrno]; + + /* + * XXX This looks highly bogus, but it appears that npc_probe1 + * needs interrupts enabled. Does this make any difference + * here? + */ enable_intr(); result = npx_probe1(dev); disable_intr(); @@ -797,7 +803,7 @@ npxdna() /* * Record new context early in case frstor causes an IRQ13. */ - npxproc = curproc; + PCPU_SET(npxproc, CURPROC); curpcb->pcb_savefpu.sv_ex_sw = 0; /* * The following frstor may cause an IRQ13 when the state being @@ -834,16 +840,18 @@ npxsave(addr) fnsave(addr); /* fnop(); */ start_emulating(); - npxproc = NULL; + PCPU_SET(npxproc, NULL); #else /* SMP */ + int intrstate; u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; + intrstate = save_intr(); disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); @@ -851,12 +859,12 @@ npxsave(addr) outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); idt[npx_intrno] = npx_idt_probeintr; - enable_intr(); + write_eflags(intrstate); stop_emulating(); fnsave(addr); fnop(); start_emulating(); - npxproc = NULL; + PCPU_SET(npxproc, NULL); disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); @@ -866,7 +874,7 @@ npxsave(addr) (icu2_mask & ~(npx0_imask >> 8)) | (old_icu2_mask & (npx0_imask >> 8))); idt[npx_intrno] = save_idt_npxintr; - enable_intr(); /* back to usual state */ + restore_intr(intrstate); /* back to previous state */ #endif /* SMP */ } diff --git a/sys/amd64/isa/vector.S b/sys/amd64/isa/vector.S index 5447a90..79f2320 100644 --- a/sys/amd64/isa/vector.S +++ b/sys/amd64/isa/vector.S @@ -16,9 +16,10 @@ #include <i386/isa/isa.h> #endif +#define FAST_INTR_HANDLER_USES_ES 1 #ifdef FAST_INTR_HANDLER_USES_ES #define ACTUALLY_PUSHED 1 -#define MAYBE_MOVW_AX_ES movl %ax,%es +#define MAYBE_MOVW_AX_ES movw %ax,%es #define MAYBE_POPL_ES popl %es #define MAYBE_PUSHL_ES pushl %es #else @@ -36,11 +37,6 @@ .data ALIGN_DATA - .globl _intr_nesting_level -_intr_nesting_level: - .byte 0 - .space 3 - /* * Interrupt counters and names for export to vmstat(8) and friends. * @@ -58,7 +54,6 @@ _eintrcnt: _intrnames: .space NR_INTRNAMES * 16 _eintrnames: - .text /* diff --git a/sys/amd64/isa/vector.s b/sys/amd64/isa/vector.s index 5447a90..79f2320 100644 --- a/sys/amd64/isa/vector.s +++ b/sys/amd64/isa/vector.s @@ -16,9 +16,10 @@ #include <i386/isa/isa.h> #endif +#define FAST_INTR_HANDLER_USES_ES 1 #ifdef FAST_INTR_HANDLER_USES_ES #define ACTUALLY_PUSHED 1 -#define MAYBE_MOVW_AX_ES movl %ax,%es +#define MAYBE_MOVW_AX_ES movw %ax,%es #define MAYBE_POPL_ES popl %es #define MAYBE_PUSHL_ES pushl %es #else @@ -36,11 +37,6 @@ .data ALIGN_DATA - .globl _intr_nesting_level -_intr_nesting_level: - .byte 0 - .space 3 - /* * Interrupt counters and names for export to vmstat(8) and friends. * @@ -58,7 +54,6 @@ _eintrcnt: _intrnames: .space NR_INTRNAMES * 16 _eintrnames: - .text /* |