diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index 7333b3c7c1ce..30c7a1a1327d 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -1678,13 +1678,39 @@ sccnkbdunlock(sc_softc_t *sc, struct sc_cnstate *sp) static void sccnscrlock(sc_softc_t *sc, struct sc_cnstate *sp) { - SC_VIDEO_LOCK(sc); + int retries; + + /** + * Locking method: + * - if kdb_active and video_mtx is not owned by anyone, then lock + * by kdb remaining active + * - if !kdb_active, try to acquire video_mtx without blocking or + * recursing; if we get it then it works normally. + * Note that video_mtx is especially unusable if we already own it, + * since then it is protecting something and syscons is not reentrant + * enough to ignore the protection even in the kdb_active case. + */ + if (kdb_active) { + sp->kdb_locked = sc->video_mtx.mtx_lock == MTX_UNOWNED || panicstr; + sp->mtx_locked = FALSE; + } else { + sp->kdb_locked = FALSE; + for (retries = 0; retries < 1000; retries++) { + sp->mtx_locked = mtx_trylock_spin_flags(&sc->video_mtx, + MTX_QUIET) != 0 || panicstr; + if (sp->mtx_locked) + break; + DELAY(1); + } + } } static void sccnscrunlock(sc_softc_t *sc, struct sc_cnstate *sp) { - SC_VIDEO_UNLOCK(sc); + if (sp->mtx_locked) + mtx_unlock_spin(&sc->video_mtx); + sp->mtx_locked = sp->kdb_locked = FALSE; } static void @@ -1721,6 +1747,8 @@ over_keyboard: ; /* The screen is opened iff locking it succeeds. */ sccnscrlock(sc, sp); + if (!sp->kdb_locked && !sp->mtx_locked) + return; sp->scr_opened = TRUE; /* The screen switch is optional. */ @@ -1797,6 +1825,10 @@ sc_cnungrab(struct consdev *cp) atomic_add_int(&sc->grab_level, -1); } +static char sc_cnputc_log[0x1000]; +static u_int sc_cnputc_loghead; +static u_int sc_cnputc_logtail; + static void sc_cnputc(struct consdev *cd, int c) { @@ -1808,12 +1840,28 @@ sc_cnputc(struct consdev *cd, int c) struct tty *tp; #endif #endif /* !SC_NO_HISTORY */ + u_int head; int s; /* assert(sc_console != NULL) */ sccnopen(scp->sc, &st, 0); + /* + * Log the output. + * + * In the unlocked case, the logging is intentionally only + * perfectly atomic for the indexes. + */ + head = atomic_fetchadd_int(&sc_cnputc_loghead, 1); + sc_cnputc_log[head % sizeof(sc_cnputc_log)] = c; + + /* + * If we couldn't open, return to defer output. + */ + if (!st.scr_opened) + return; + #ifndef SC_NO_HISTORY if (scp == scp->sc->cur_scp && scp->status & SLKED) { scp->status &= ~SLKED; @@ -1841,8 +1889,14 @@ sc_cnputc(struct consdev *cd, int c) } #endif /* !SC_NO_HISTORY */ - buf[0] = c; - sc_puts(scp, buf, 1, 1); + /* Play any output still in the log (our char may already be done). */ + while (sc_cnputc_logtail != atomic_load_acq_int(&sc_cnputc_loghead)) { + buf[0] = sc_cnputc_log[sc_cnputc_logtail++ % sizeof(sc_cnputc_log)]; + if (atomic_load_acq_int(&sc_cnputc_loghead) - sc_cnputc_logtail >= + sizeof(sc_cnputc_log)) + continue; + sc_puts(scp, buf, 1, 1); + } s = spltty(); /* block sckbdevent and scrn_timer */ sccnupdate(scp); @@ -1883,9 +1937,11 @@ sc_cngetc_locked(struct sc_cnstate *sp) * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ - sc_touch_scrn_saver(); + if (sp->scr_opened) + sc_touch_scrn_saver(); scp = sc_console->sc->cur_scp; /* XXX */ - sccnupdate(scp); + if (sp->scr_opened) + sccnupdate(scp); if (fkeycp < fkey.len) return fkey.str[fkeycp++]; diff --git a/sys/dev/syscons/syscons.h b/sys/dev/syscons/syscons.h index 8d8e5ca85f27..3454700c9475 100644 --- a/sys/dev/syscons/syscons.h +++ b/sys/dev/syscons/syscons.h @@ -191,6 +191,8 @@ struct tty; struct sc_cnstate { u_char kbd_locked; + u_char kdb_locked; + u_char mtx_locked; u_char kbd_opened; u_char scr_opened; };