From b402c63383c29b53787bd1b33d0ea96d6d8a734e Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 2 Jul 2007 01:37:11 +0000 Subject: [PATCH] Implement an architecture function cpu_mplock_contested() which is called by the LWKT thread scheduler when the only thread(s) it can schedule need the MP lock and the scheduler was unable to acquire the MP lock. On real systems this function just executes the cpu 'pause' instruction, and on virtual systems this functions sleeps for a millisecond. Use umtx_sleep() instead of sigpause() in the virtual kernel's idle loop to interlock threads scheduled via a signal with the idle loop sleep. This fixes a race condition that caused the vkernel to stop scheduling (but there may be more issues, stay tuned). --- sys/kern/lwkt_thread.c | 9 +++++--- sys/platform/pc32/i386/machdep.c | 13 ++++++++++- sys/platform/vkernel/i386/cpu_regs.c | 33 +++++++++++++++++++--------- sys/sys/systm.h | 3 ++- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/sys/kern/lwkt_thread.c b/sys/kern/lwkt_thread.c index 4287663bee..811a3112ce 100644 --- a/sys/kern/lwkt_thread.c +++ b/sys/kern/lwkt_thread.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.108 2007/05/24 05:51:27 dillon Exp $ + * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.109 2007/07/02 01:37:08 dillon Exp $ */ /* @@ -679,6 +679,7 @@ again: nq = bsrl(rqmask); } if (ntd == NULL) { + cpu_mplock_contested(); ntd = &gd->gd_idlethread; ntd->td_flags |= TDF_IDLE_NOHLT; goto using_idle_thread; @@ -720,10 +721,12 @@ using_idle_thread: */ if (ntd->td_mpcount) { mpheld = MP_LOCK_HELD(); - if (gd->gd_trap_nesting_level == 0 && panicstr == NULL) + if (gd->gd_trap_nesting_level == 0 && panicstr == NULL) { panic("Idle thread %p was holding the BGL!", ntd); - else if (mpheld == 0) + } else if (mpheld == 0) { + cpu_mplock_contested(); goto again; + } } #endif } diff --git a/sys/platform/pc32/i386/machdep.c b/sys/platform/pc32/i386/machdep.c index e63e84345b..f650aa77ab 100644 --- a/sys/platform/pc32/i386/machdep.c +++ b/sys/platform/pc32/i386/machdep.c @@ -36,7 +36,7 @@ * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $ - * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.125 2007/07/01 01:11:38 dillon Exp $ + * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.126 2007/07/02 01:37:09 dillon Exp $ */ #include "use_apm.h" @@ -948,6 +948,17 @@ cpu_idle(void) } } +/* + * This routine is called when the only runnable threads require + * the MP lock, and the scheduler couldn't get it. On a real cpu + * we let the scheduler spin. + */ +void +cpu_mplock_contested(void) +{ + cpu_pause(); +} + /* * Clear registers on exec */ diff --git a/sys/platform/vkernel/i386/cpu_regs.c b/sys/platform/vkernel/i386/cpu_regs.c index f6da94d967..e77ce087cf 100644 --- a/sys/platform/vkernel/i386/cpu_regs.c +++ b/sys/platform/vkernel/i386/cpu_regs.c @@ -37,7 +37,7 @@ * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $ - * $DragonFly: src/sys/platform/vkernel/i386/cpu_regs.c,v 1.18 2007/07/01 02:51:43 dillon Exp $ + * $DragonFly: src/sys/platform/vkernel/i386/cpu_regs.c,v 1.19 2007/07/02 01:37:10 dillon Exp $ */ #include "use_ether.h" @@ -107,6 +107,7 @@ #include #include #include +#include /* umtx_* functions */ extern void dblfault_handler (void); @@ -679,6 +680,7 @@ void cpu_idle(void) { struct thread *td = curthread; + struct mdglobaldata *gd = mdcpu; crit_exit(); KKASSERT(td->td_pri < TDPRI_CRIT); @@ -689,19 +691,14 @@ cpu_idle(void) lwkt_switch(); /* - * If we are going to halt call splz unconditionally after - * CLIing to catch any interrupt races. Note that we are - * at SPL0 and interrupts are enabled. - * - * We must poll our mailbox signals prior to calling - * sigpause() in order to properly interlock with them. + * The idle loop halts only if no threads are scheduleable + * and no signals have occured. */ if (cpu_idle_hlt && !lwkt_runnable() && (td->td_flags & TDF_IDLE_NOHLT) == 0) { splz(); - if (!lwkt_runnable()) { - sigpause(0); - } + if (!lwkt_runnable()) + umtx_sleep(&gd->mi.gd_runqmask, 0, 0); #ifdef SMP else { __asm __volatile("pause"); @@ -722,6 +719,22 @@ cpu_idle(void) } } +#ifdef SMP + +/* + * Called by the LWKT switch core with a critical section held if the only + * schedulable thread needs the MP lock and we couldn't get it. On + * a real cpu we just spin in the scheduler. In the virtual kernel + * we sleep for a bit. + */ +void +cpu_mplock_contested(void) +{ + usleep(1000); +} + +#endif + /* * Clear registers on exec */ diff --git a/sys/sys/systm.h b/sys/sys/systm.h index 79f5622a08..6ac300bdac 100644 --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -37,7 +37,7 @@ * * @(#)systm.h 8.7 (Berkeley) 3/29/95 * $FreeBSD: src/sys/sys/systm.h,v 1.111.2.18 2002/12/17 18:04:02 sam Exp $ - * $DragonFly: src/sys/sys/systm.h,v 1.71 2007/05/24 20:51:19 dillon Exp $ + * $DragonFly: src/sys/sys/systm.h,v 1.72 2007/07/02 01:37:11 dillon Exp $ */ #ifndef _SYS_SYSTM_H_ @@ -146,6 +146,7 @@ void *phashinit (int count, struct malloc_type *type, u_long *nentries); int cpu_sanitize_frame (struct trapframe *); int cpu_sanitize_tls (struct savetls *); +void cpu_mplock_contested(void); void cpu_halt (void); void cpu_reset (void); void cpu_boot (int); -- 2.41.0