summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_kthread.c
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2010-01-24 15:07:00 +0000
committerattilio <attilio@FreeBSD.org>2010-01-24 15:07:00 +0000
commita4281bb8848a39a697bf8c77c4e958baf048bdea (patch)
treeb9469e81eedd64257cbfcad33ebdccc86a58a8d2 /sys/kern/kern_kthread.c
parent62a013da482cd3be2013620de2512c32e56adb68 (diff)
downloadFreeBSD-src-a4281bb8848a39a697bf8c77c4e958baf048bdea.zip
FreeBSD-src-a4281bb8848a39a697bf8c77c4e958baf048bdea.tar.gz
- Fix the kthread_{suspend, resume, suspend_check}() locking.
In the current code, the locking is completely broken and may lead easilly to deadlocks. Fix it by using the proc_mtx, linked to the suspending thread, as lock for the operation. Keep using the thread_lock for setting and reading the flag even if it is not entirely necessary (atomic ops may do it as well, but this way the code is more readable). - Fix a deadlock within kthread_suspend(). The suspender should not sleep on a different channel wrt the suspended thread, or, otherwise, the awaker should wakeup both. Uniform the interface to what the kproc_* counterparts do (sleeping on the same channel). - Change the kthread_suspend_check() prototype. kthread_suspend_check() always assumes curthread and must only refer to it, so skip the thread pointer as it may be easilly mistaken. If curthread is not a kthread, the system will panic. In collabouration with: jhb Tested by: Giovanni Trematerra <giovanni dot trematerra at gmail dot com> MFC: 2 weeks
Diffstat (limited to 'sys/kern/kern_kthread.c')
-rw-r--r--sys/kern/kern_kthread.c70
1 files changed, 52 insertions, 18 deletions
diff --git a/sys/kern/kern_kthread.c b/sys/kern/kern_kthread.c
index 3c5248e..5809d14 100644
--- a/sys/kern/kern_kthread.c
+++ b/sys/kern/kern_kthread.c
@@ -335,34 +335,55 @@ kthread_exit(void)
int
kthread_suspend(struct thread *td, int timo)
{
- if ((td->td_pflags & TDP_KTHREAD) == 0) {
+ struct proc *p;
+
+ p = td->td_proc;
+
+ /*
+ * td_pflags should not be ready by any other thread different by
+ * curthread, but as long as this flag is invariant during the
+ * thread lifetime, it is ok to check for it now.
+ */
+ if ((td->td_pflags & TDP_KTHREAD) == 0)
return (EINVAL);
- }
+
+ /*
+ * The caller of the primitive should have already checked that the
+ * thread is up and running, thus not being blocked by other
+ * conditions.
+ */
+ PROC_LOCK(p);
thread_lock(td);
td->td_flags |= TDF_KTH_SUSP;
thread_unlock(td);
- /*
- * If it's stopped for some other reason,
- * kick it to notice our request
- * or we'll end up timing out
- */
- wakeup(td); /* traditional place for kernel threads to sleep on */ /* XXX ?? */
- return (tsleep(&td->td_flags, PPAUSE | PDROP, "suspkt", timo));
+ return (msleep(&td->td_flags, &p->p_mtx, PPAUSE | PDROP, "suspkt",
+ timo));
}
/*
- * let the kthread it can keep going again.
+ * Resume a thread previously put asleep with kthread_suspend().
*/
int
kthread_resume(struct thread *td)
{
- if ((td->td_pflags & TDP_KTHREAD) == 0) {
+ struct proc *p;
+
+ p = td->td_proc;
+
+ /*
+ * td_pflags should not be ready by any other thread different by
+ * curthread, but as long as this flag is invariant during the
+ * thread lifetime, it is ok to check for it now.
+ */
+ if ((td->td_pflags & TDP_KTHREAD) == 0)
return (EINVAL);
- }
+
+ PROC_LOCK(p);
thread_lock(td);
td->td_flags &= ~TDF_KTH_SUSP;
thread_unlock(td);
- wakeup(&td->td_name);
+ wakeup(&td->td_flags);
+ PROC_UNLOCK(p);
return (0);
}
@@ -371,15 +392,28 @@ kthread_resume(struct thread *td)
* and notify the caller that is has happened.
*/
void
-kthread_suspend_check(struct thread *td)
+kthread_suspend_check()
{
+ struct proc *p;
+ struct thread *td;
+
+ td = curthread;
+ p = td->td_proc;
+
+ if ((td->td_pflags & TDP_KTHREAD) == 0)
+ panic("%s: curthread is not a valid kthread", __func__);
+
+ /*
+ * As long as the double-lock protection is used when accessing the
+ * TDF_KTH_SUSP flag, synchronizing the read operation via proc mutex
+ * is fine.
+ */
+ PROC_LOCK(p);
while (td->td_flags & TDF_KTH_SUSP) {
- /*
- * let the caller know we got the message then sleep
- */
wakeup(&td->td_flags);
- tsleep(&td->td_name, PPAUSE, "ktsusp", 0);
+ msleep(&td->td_flags, &p->p_mtx, PPAUSE, "ktsusp", 0);
}
+ PROC_UNLOCK(p);
}
int
OpenPOWER on IntegriCloud