From b68b7282f20103df07102bcf0af8de583f7880cf Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Sun, 29 Jun 2003 05:29:31 +0000 Subject: [PATCH] Implement interrupt thread preemption + minor cleanup. --- sys/cpu/i386/include/cpu.h | 17 +---------------- sys/i386/include/cpu.h | 17 +---------------- sys/kern/kern_clock.c | 15 ++++----------- sys/kern/kern_intr.c | 23 ++++++++++++++++++----- sys/kern/lwkt_thread.c | 35 +++++++++++++++++++++++++++++++++-- sys/sys/thread.h | 4 ++-- 6 files changed, 59 insertions(+), 52 deletions(-) diff --git a/sys/cpu/i386/include/cpu.h b/sys/cpu/i386/include/cpu.h index a240e74626..888abb8e5c 100644 --- a/sys/cpu/i386/include/cpu.h +++ b/sys/cpu/i386/include/cpu.h @@ -35,7 +35,7 @@ * * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 * $FreeBSD: src/sys/i386/include/cpu.h,v 1.43.2.2 2001/06/15 09:37:57 scottl Exp $ - * $DragonFly: src/sys/cpu/i386/include/cpu.h,v 1.5 2003/06/29 03:28:43 dillon Exp $ + * $DragonFly: src/sys/cpu/i386/include/cpu.h,v 1.6 2003/06/29 05:29:30 dillon Exp $ */ #ifndef _MACHINE_CPU_H_ @@ -62,21 +62,6 @@ ((ISPL((framep)->cf_cs) == SEL_UPL) || (framep->cf_eflags & PSL_VM)) #define CLKF_INTR(framep) (mycpu->gd_intr_nesting_level >= 2) -#if 0 -/* - * XXX splsoftclock() is very broken and barely worth fixing. It doesn't - * turn off the clock bit in imen or in the icu. (This is not a serious - * problem at 100 Hz but it is serious at 16000 Hz for pcaudio. softclock() - * can take more than 62.5 usec so clock interrupts are lost.) It doesn't - * check for pending interrupts being unmasked. clkintr() and Xintr0() - * assume that the ipl is high when hardclock() returns. Our SWI_CLOCK - * handling is efficient enough that little is gained by calling - * softclock() directly. - */ -#define CLKF_BASEPRI(framep) ((framep)->cf_ppl == 0) -#else -#define CLKF_BASEPRI(framep) (0) -#endif #define CLKF_PC(framep) ((framep)->cf_eip) /* diff --git a/sys/i386/include/cpu.h b/sys/i386/include/cpu.h index 700e916ce4..55ab0594f3 100644 --- a/sys/i386/include/cpu.h +++ b/sys/i386/include/cpu.h @@ -35,7 +35,7 @@ * * from: @(#)cpu.h 5.4 (Berkeley) 5/9/91 * $FreeBSD: src/sys/i386/include/cpu.h,v 1.43.2.2 2001/06/15 09:37:57 scottl Exp $ - * $DragonFly: src/sys/i386/include/Attic/cpu.h,v 1.5 2003/06/29 03:28:43 dillon Exp $ + * $DragonFly: src/sys/i386/include/Attic/cpu.h,v 1.6 2003/06/29 05:29:30 dillon Exp $ */ #ifndef _MACHINE_CPU_H_ @@ -62,21 +62,6 @@ ((ISPL((framep)->cf_cs) == SEL_UPL) || (framep->cf_eflags & PSL_VM)) #define CLKF_INTR(framep) (mycpu->gd_intr_nesting_level >= 2) -#if 0 -/* - * XXX splsoftclock() is very broken and barely worth fixing. It doesn't - * turn off the clock bit in imen or in the icu. (This is not a serious - * problem at 100 Hz but it is serious at 16000 Hz for pcaudio. softclock() - * can take more than 62.5 usec so clock interrupts are lost.) It doesn't - * check for pending interrupts being unmasked. clkintr() and Xintr0() - * assume that the ipl is high when hardclock() returns. Our SWI_CLOCK - * handling is efficient enough that little is gained by calling - * softclock() directly. - */ -#define CLKF_BASEPRI(framep) ((framep)->cf_ppl == 0) -#else -#define CLKF_BASEPRI(framep) (0) -#endif #define CLKF_PC(framep) ((framep)->cf_eip) /* diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index a1fb47f8fc..6fe2f2b090 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -38,7 +38,7 @@ * * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 * $FreeBSD: src/sys/kern/kern_clock.c,v 1.105.2.10 2002/10/17 13:19:40 maxim Exp $ - * $DragonFly: src/sys/kern/kern_clock.c,v 1.4 2003/06/28 04:16:04 dillon Exp $ + * $DragonFly: src/sys/kern/kern_clock.c,v 1.5 2003/06/29 05:29:31 dillon Exp $ */ #include "opt_ntp.h" @@ -254,17 +254,10 @@ hardclock(frame) * relatively high clock interrupt priority any longer than necessary. */ if (TAILQ_FIRST(&callwheel[ticks & callwheelmask]) != NULL) { - if (CLKF_BASEPRI(frame)) { - /* - * Save the overhead of a software interrupt; - * it will happen as soon as we return, so do it now. - */ - (void)splsoftclock(); - softclock(); - } else - setsoftclock(); - } else if (softticks + 1 == ticks) + setsoftclock(); + } else if (softticks + 1 == ticks) { ++softticks; + } } /* diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index 3db8f477b8..249c91ff86 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -24,7 +24,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/kern/kern_intr.c,v 1.24.2.1 2001/10/14 20:05:50 luigi Exp $ - * $DragonFly: src/sys/kern/kern_intr.c,v 1.3 2003/06/29 03:28:44 dillon Exp $ + * $DragonFly: src/sys/kern/kern_intr.c,v 1.4 2003/06/29 05:29:31 dillon Exp $ * */ @@ -139,7 +139,8 @@ unregister_int(int intr, inthand2_t handler) } /* - * Dispatch an interrupt. + * Dispatch an interrupt. If there's nothing to do we have a stray + * interrupt and can just return, leaving the interrupt masked. */ void sched_ithd(int intr) @@ -147,15 +148,20 @@ sched_ithd(int intr) thread_t td; if ((td = ithreads[intr]) != NULL) { - if (intlists[intr] == NULL) + if (intlists[intr] == NULL) { printf("sched_ithd: stray interrupt %d\n", intr); - else + } else { lwkt_schedule(td); + lwkt_preempt(td, intr); + } } else { printf("sched_ithd: stray interrupt %d\n", intr); } } +/* + * Interrupt threads run this as their main loop. + */ static void ithread_handler(void *arg) { @@ -171,7 +177,14 @@ ithread_handler(void *arg) rec->handler(rec->argument); } ithread_done(intr); - KKASSERT(curthread->td_pri == TDPRI_CRIT); + + /* + * temporary sanity check. If we preempted another thread we + * are placed in another critical section by that thread which + * will be released when we block and resume the original thread. + */ + KKASSERT(curthread->td_pri == TDPRI_CRIT || + (curthread->td_preempted && curthread->td_pri == 2 * TDPRI_CRIT)); } crit_exit(); /* not reached */ } diff --git a/sys/kern/lwkt_thread.c b/sys/kern/lwkt_thread.c index ba645e7b05..385678a468 100644 --- a/sys/kern/lwkt_thread.c +++ b/sys/kern/lwkt_thread.c @@ -27,7 +27,7 @@ * thread scheduler, which means that generally speaking we only need * to use a critical section to prevent hicups. * - * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.9 2003/06/29 03:28:44 dillon Exp $ + * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.10 2003/06/29 05:29:31 dillon Exp $ */ #include @@ -206,7 +206,7 @@ lwkt_switch(void) thread_t td = curthread; thread_t ntd; - if (mycpu->gd_intr_nesting_level) + if (mycpu->gd_intr_nesting_level && td->td_preempted == NULL) panic("lwkt_switch: cannot switch from within an interrupt\n"); crit_enter(); @@ -216,6 +216,7 @@ lwkt_switch(void) * thread. */ td->td_preempted = NULL; + td->td_pri -= TDPRI_CRIT; ntd->td_flags &= ~TDF_PREEMPTED; } else if ((ntd = TAILQ_FIRST(&mycpu->gd_tdrunq)) != NULL) { TAILQ_REMOVE(&mycpu->gd_tdrunq, ntd, td_threadq); @@ -228,6 +229,36 @@ lwkt_switch(void) crit_exit(); } +/* + * The target thread preempts the current thread. The target thread + * structure must be stable and preempt-safe (e.g. an interrupt thread). + * When the target thread blocks the current thread will be resumed. + * + * XXX the target runs in a critical section so it does not open the original + * thread up to additional interrupts that the original thread believes it + * is blocking. + * + * Normal kernel threads should not preempt other normal kernel threads + * as it breaks the assumptions kernel threads are allowed to make. Note + * that preemption does not mess around with the current thread's RUNQ + * state. + */ +void +lwkt_preempt(struct thread *ntd, int id) +{ + struct thread *td = curthread; + + crit_enter(); + if (ntd->td_preempted == NULL) { + ntd->td_preempted = curthread; + td->td_flags |= TDF_PREEMPTED; + ntd->td_pri += TDPRI_CRIT; + while (td->td_flags & TDF_PREEMPTED) + ntd->td_switch(ntd); + } + crit_exit_noyield(); +} + /* * Yield our thread while higher priority threads are pending. This is * typically called when we leave a critical section but it can be safely diff --git a/sys/sys/thread.h b/sys/sys/thread.h index 1b9f3b3d77..d18648f657 100644 --- a/sys/sys/thread.h +++ b/sys/sys/thread.h @@ -4,7 +4,7 @@ * Implements the architecture independant portion of the LWKT * subsystem. * - * $DragonFly: src/sys/sys/thread.h,v 1.13 2003/06/29 03:28:46 dillon Exp $ + * $DragonFly: src/sys/sys/thread.h,v 1.14 2003/06/29 05:29:31 dillon Exp $ */ #ifndef _SYS_THREAD_H_ @@ -198,7 +198,7 @@ extern void lwkt_free_thread(struct thread *td); extern void lwkt_init_wait(struct lwkt_wait *w); extern void lwkt_gdinit(struct globaldata *gd); extern void lwkt_switch(void); -extern void lwkt_preempt(void); +extern void lwkt_preempt(thread_t ntd, int id); extern void lwkt_schedule(thread_t td); extern void lwkt_schedule_self(void); extern void lwkt_deschedule(thread_t td); -- 2.41.0