Allow sleeping while clearing a sleep timeout
authorvisa <visa@openbsd.org>
Sun, 12 Jun 2022 10:36:04 +0000 (10:36 +0000)
committervisa <visa@openbsd.org>
Sun, 12 Jun 2022 10:36:04 +0000 (10:36 +0000)
Since sys/kern/kern_timeout.c r1.84, timeout_barrier() has used sleeping
with soft-interrupt-driven timeouts. Adjust the sleep machinery so that
the timeout clearing can block in sleep_finish().

This adds one step of recursion inside sleep_finish(). However, the
sleep queue handling does not recurse because sleep_finish() completes
it before calling timeout_del_barrier().

This fixes the following panic:

panic: kernel diagnostic assertion "(p->p_flag & P_TIMEOUT) == 0" failed: file "sys/kern/kern_synch.c", line 373
Stopped at db_enter+0x10:  popq    %rbp
db_enter() at db_enter+0x10
panic() at panic+0xbf
__assert() at __assert+0x25
sleep_setup() at sleep_setup+0x1d8
cond_wait() at cond_wait+0x46
timeout_barrier() at timeout_barrier+0x109
timeout_del_barrier() at timeout_del_barrier+0xa2
sleep_finish() at sleep_finish+0x16d
tsleep() at tsleep+0xb2
sys_nanosleep() at sys_nanosleep+0x12d
syscall() at syscall+0x374

OK mpi@ dlg@

sys/kern/kern_synch.c

index 5f3e8be..917b604 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_synch.c,v 1.187 2022/05/13 15:32:00 claudio Exp $        */
+/*     $OpenBSD: kern_synch.c,v 1.188 2022/06/12 10:36:04 visa Exp $   */
 /*     $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */
 
 /*
@@ -370,8 +370,8 @@ sleep_setup(struct sleep_state *sls, const volatile void *ident, int prio,
        p->p_slppri = prio & PRIMASK;
        TAILQ_INSERT_TAIL(&slpque[LOOKUP(ident)], p, p_runq);
 
-       KASSERT((p->p_flag & P_TIMEOUT) == 0);
        if (timo) {
+               KASSERT((p->p_flag & P_TIMEOUT) == 0);
                sls->sls_timeout = 1;
                timeout_add(&p->p_sleep_to, timo);
        }
@@ -432,13 +432,12 @@ sleep_finish(struct sleep_state *sls, int do_sleep)
 
        if (sls->sls_timeout) {
                if (p->p_flag & P_TIMEOUT) {
-                       atomic_clearbits_int(&p->p_flag, P_TIMEOUT);
                        error1 = EWOULDBLOCK;
                } else {
-                       /* This must not sleep. */
+                       /* This can sleep. It must not use timeouts. */
                        timeout_del_barrier(&p->p_sleep_to);
-                       KASSERT((p->p_flag & P_TIMEOUT) == 0);
                }
+               atomic_clearbits_int(&p->p_flag, P_TIMEOUT);
        }
 
        /* Check if thread was woken up because of a unwind or signal */