aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/syscons/syscons.c
diff options
context:
space:
mode:
authorBruce Evans <bde@FreeBSD.org>2016-09-01 19:18:26 +0000
committerBruce Evans <bde@FreeBSD.org>2016-09-01 19:18:26 +0000
commit90adad104b4f1c3fb8644bdb843d0c641085787b (patch)
tree4a84834273ff851daca6220e7df058352d9520b9 /sys/dev/syscons/syscons.c
parentc543b519bec71972147950ec5751c6e542ba3030 (diff)
Notes
Diffstat (limited to 'sys/dev/syscons/syscons.c')
-rw-r--r--sys/dev/syscons/syscons.c68
1 files changed, 62 insertions, 6 deletions
diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c
index 7333b3c7c1cee..30c7a1a1327d0 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++];