--- a/sys/dev/misc/syscons/syscons.c +++ b/sys/dev/misc/syscons/syscons.c @@ -3534,11 +3534,27 @@ sc_clean_up(scr_stat *scp, int need_unlock) { #if NSPLASH > 0 int error; #endif /* NSPLASH */ lwkt_gettoken(&vga_token); if (scp->sc->flags & SC_SCRN_BLANKED) { sc_touch_scrn_saver(); #if NSPLASH > 0 + /* + * DF-5195: we may be called with vga_token held by our caller + * (scioctl, sc_set_text_mode, the KDSETMODE/VT_ACTIVATE branches). + * The wake path of wait_scrn_saver_stop() -- scrn_timer -> + * stop_scrn_saver() -> restore_scrn_saver_mode(TRUE) -> set_mode() -- + * must itself lwkt_gettoken(&vga_token) (see set_mode()), so sleeping + * here with the token held deadlocks the console subsystem. Drop OUR + * reference (acquired just above; tokens are released in strict LIFO + * order and ours is the innermost) around the sleep and re-acquire it + * so every caller keeps its held-on-return invariant. + */ + lwkt_reltoken(&vga_token); if ((error = wait_scrn_saver_stop(scp->sc, need_unlock))) { + lwkt_gettoken(&vga_token); lwkt_reltoken(&vga_token); return error; } + lwkt_gettoken(&vga_token); #endif /* NSPLASH */ } scp->status |= MOUSE_HIDDEN;