CVSROOT: /cvs Module name: src Changes by: d...@cvs.openbsd.org 2016/10/03 05:54:29
Modified files: sys/kern : kern_timeout.c Log message: avoid holding timeout_mutex while interacting with the scheduler. as noted by haesbaert, this is necessary to avoid deadlocks because the scheduler can call back into the timeout subsystem while its holding its own locks. this happened in two places. firstly, in softclock() it would take timeout_mutex to find pending work. if that pending work needs a process context, it would queue the work for the thread and call wakeup, which enters the scheduler locks. if another cpu is trying to tsleep (or msleep) with a timeout specified, the sleep code would be holding the sched lock and call timeout_add, which takes timeout_mutex. this is solved by deferring the wakeup to after timeout_mutex is left. this also has the benefit of mitigating the number of wakeups done per softclock tick. secondly, the timeout worker thread takes timeout_mutex and calls msleep when there's no work to do (ie, the queue is empty). msleep will take the sched locks. again, if another cpu does a tsleep with a timeout, you get a deadlock. to solve this im using sleep_setup and sleep_finish to sleep on an empty queue, which is safe to do outside the lock as it is comparisons of the queue head pointers, not derefs of the contents of the queue. as long as the sleeps and wakeups are ordered correctly with the enqueue and dequeue operations under the mutex, this all works. you can think of the queue as a single descriptor ring, and the wakeup as an interrupt. the second deadlock was identified by guenther@ ok tedu@ mpi@