--- //depot/vendor/freebsd/src/sys/kern/kern_mutex.c 2006/01/27 22:44:24 +++ //depot/projects/smpng/sys/kern/kern_mutex.c 2006/02/24 15:08:55 @@ -86,8 +91,9 @@ */ #define mtx_unowned(m) ((m)->mtx_lock == MTX_UNOWNED) -#define mtx_owner(m) (mtx_unowned((m)) ? NULL \ - : (struct thread *)((m)->mtx_lock & MTX_FLAGMASK)) +#define mtx_rawowner(m) ((struct thread *)((m)->mtx_lock & MTX_FLAGMASK)) + +#define mtx_owner(m) (mtx_unowned((m)) ? NULL : mtx_rawowner((m))) #ifdef DDB static void db_show_mtx(struct lock_object *lock); @@ -470,6 +476,15 @@ return; } + /* + * If we have already panic'd and this is the thread that called + * panic(), then don't block on any mutexes but silently succeed. + * Otherwise, the kernel will deadlock since the scheduler isn't + * going to run the thread that holds the lock we need. + */ + if (panicstr != NULL && curthread->td_flags & TDF_INPANIC) + return; + if (LOCK_LOG_TEST(&m->mtx_object, opts)) CTR4(KTR_LOCK, "_mtx_lock_sleep: %s contested (lock=%p) at %s:%d", @@ -536,7 +551,8 @@ if (m != &Giant && TD_IS_RUNNING(owner)) { #endif turnstile_release(&m->mtx_object); - while (mtx_owner(m) == owner && TD_IS_RUNNING(owner)) { + while (mtx_rawowner(m) == owner && + TD_IS_RUNNING(owner)) { cpu_spinwait(); } continue; @@ -592,35 +608,42 @@ _mtx_lock_spin(struct mtx *m, uintptr_t tid, int opts, const char *file, int line) { - int i = 0; + int i = 0, idlespin = 0; + struct thread *td; if (LOCK_LOG_TEST(&m->mtx_object, opts)) CTR1(KTR_LOCK, "_mtx_lock_spin: %p spinning", m); + /* It's ok for the idle loop to spin forever on sched_lock. */ + if (m == &sched_lock && curthread == PCPU_GET(idlethread)) + idlespin = 1; for (;;) { if (_obtain_lock(m, tid)) break; /* Give interrupts a chance while we spin. */ spinlock_exit(); while (m->mtx_lock != MTX_UNOWNED) { if (i++ < 10000000) { cpu_spinwait(); continue; } - if (i < 60000000) + if (i < 60000000 || kdb_active || panicstr != NULL || + idlespin) DELAY(1); - else if (!kdb_active && !panicstr) { - printf("spin lock %s held by %p for > 5 seconds\n", - m->mtx_object.lo_name, (void *)m->mtx_lock); + else { + td = mtx_owner(m); + printf( + "spin lock %p (%s) held by %p (tid %d) too long\n", + m, m->mtx_object.lo_name, mtx_rawowner(m), + td ? td->td_tid : -1); #ifdef WITNESS - witness_display_spinlock(&m->mtx_object, - mtx_owner(m)); + witness_display_spinlock(&m->mtx_object, td); #endif panic("spin lock held too long"); } cpu_spinwait(); } spinlock_enter(); } @@ -653,6 +688,15 @@ return; } + /* + * If we failed to unlock this lock and we are a thread that has + * called panic(), it may be due to the bypass in _mtx_lock_sleep() + * above. In that case, just return and leave the lock alone to + * avoid changing the state. + */ + if (panicstr != NULL && curthread->td_flags & TDF_INPANIC) + return; + turnstile_lock(&m->mtx_object); ts = turnstile_lookup(&m->mtx_object); if (LOCK_LOG_TEST(&m->mtx_object, opts)) --- //depot/vendor/freebsd/src/sys/sys/proc.h 2006/02/22 19:00:49 +++ //depot/projects/smpng/sys/sys/proc.h 2006/02/24 20:11:28 @@ -792,7 +792,8 @@ } while (0) #define _PHOLD(p) do { \ PROC_LOCK_ASSERT((p), MA_OWNED); \ - KASSERT(!((p)->p_flag & P_WEXIT), ("PHOLD of exiting process"));\ + KASSERT(!((p)->p_flag & P_WEXIT) || (p) == curproc, \ + ("PHOLD of exiting process")); \ (p)->p_lock++; \ if (((p)->p_sflag & PS_INMEM) == 0) \ faultin((p)); \