From 6a0ce57d103400aff5aad56d579536a89e231968 Mon Sep 17 00:00:00 2001 From: Attilio Rao Date: Tue, 26 Jun 2007 21:42:01 +0000 Subject: [PATCH] Fix an old standing LOR between callout_lock and sleepqueues chain (which could lead to a deadlock). - sleepq_set_timeout acquires callout_lock (via callout_reset()) only with sleepq chain lock held - msleep_spin in _callout_stop_safe lock the sleepqueue chain with callout_lock held In order to solve this don't use msleep_spin in _callout_stop_safe() but use directly sleepqueues as inline msleep_spin code. Rearrange the wakeup path in order to have it consistent too. Reported by: kris (via stress2 test suite) Tested by: Timothy Redaelli Reviewed by: jhb Approved by: jeff (mentor) Approved by: re --- sys/kern/kern_timeout.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/sys/kern/kern_timeout.c b/sys/kern/kern_timeout.c index e3441c2d59d5..2e67ece1f9ed 100644 --- a/sys/kern/kern_timeout.c +++ b/sys/kern/kern_timeout.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include static int avg_depth; @@ -308,8 +309,10 @@ softclock(void *dummy) * There is someone waiting * for the callout to complete. */ - wakeup(&callout_wait); callout_wait = 0; + mtx_unlock_spin(&callout_lock); + wakeup(&callout_wait); + mtx_lock_spin(&callout_lock); } steps = 0; c = nextsoftcheck; @@ -529,9 +532,38 @@ _callout_stop_safe(c, safe) * finish. */ while (c == curr_callout) { + + /* + * Use direct calls to sleepqueue interface + * instead of cv/msleep in order to avoid + * a LOR between callout_lock and sleepqueue + * chain spinlocks. This piece of code + * emulates a msleep_spin() call actually. + */ + mtx_unlock_spin(&callout_lock); + sleepq_lock(&callout_wait); + + /* + * Check again the state of curr_callout + * because curthread could have lost the + * race previously won. + */ + mtx_lock_spin(&callout_lock); + if (c != curr_callout) { + sleepq_release(&callout_wait); + break; + } callout_wait = 1; - msleep_spin(&callout_wait, &callout_lock, - "codrain", 0); + DROP_GIANT(); + mtx_unlock_spin(&callout_lock); + sleepq_add(&callout_wait, + &callout_lock.lock_object, "codrain", + SLEEPQ_SLEEP, 0); + sleepq_wait(&callout_wait); + + /* Reacquire locks previously released. */ + PICKUP_GIANT(); + mtx_lock_spin(&callout_lock); } } else if (use_mtx && !curr_cancelled) { /*