From e866ca565f902f8f70555f9ffd12a6e40930fa91 Mon Sep 17 00:00:00 2001 From: Bruce Evans Date: Wed, 24 Aug 2016 18:59:24 +0000 Subject: [PATCH] Flesh out the state and flags args to sccnopen(). Set state flags to indicate (potentially partial) success of the open. Use these to decide what to close in sccnclose(). Only grab/ungrab use open/close so far. Add a per-sc variable to count successful keyboard opens and use this instead of the grab count to decide if the keyboad state has been switched. Start fixing the locking by using atomic ops for the most important counter -- the grab level one. Other racy counting will eventually be fixed by normal mutex or kdb locking in most cases. Use a 2-entry per-sc stack of states for grabbing. 2 is just enough to debug grabbing, e.g., for gets(). gets() grabs once and might not be able to do a full (or any) state switch. ddb grabs again and has a better chance of doing a full state switch and needs a place to stack the previous state. For more than 3 levels, grabbing just changes the count. Console drivers should try to switch on every i/o in case lower levels of nesting failed to switch but the current level succeeds, but then the switch (back) must be completed on every i/o and this flaps the state unless the switch is null. The main point of grabbing is to make it null quite often. Syscons grabbing also does a carefully chosen screen focus that is not done on every i/o. Add a large comment about grabbing. Restore some small lost comments. --- sys/dev/syscons/syscons.c | 59 +++++++++++++++++++++++++++++++-------- sys/dev/syscons/syscons.h | 8 ++++++ 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index 9a1f23864f1..4c9e881eb13 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -79,8 +79,6 @@ __FBSDID("$FreeBSD$"); #include #include -struct sc_cnstate; /* not used yet */ - #define COLD 0 #define WARM 1 @@ -1655,7 +1653,13 @@ sccnopen(sc_softc_t *sc, struct sc_cnstate *sp, int flags) { int kbd_mode; - if (sc->kbd == NULL) + /* assert(sc_console_unit >= 0) */ + + sp->kbd_opened = FALSE; + sp->scr_opened = FALSE; + + /* Opening the keyboard is optional. */ + if (!(flags & 1) || sc->kbd == NULL) goto over_keyboard; /* @@ -1667,48 +1671,79 @@ sccnopen(sc_softc_t *sc, struct sc_cnstate *sp, int flags) /* Switch the keyboard to console mode (K_XLATE, polled) on all scp's. */ kbd_mode = K_XLATE; (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&kbd_mode); + sc->kbd_open_level++; kbdd_poll(sc->kbd, TRUE); + + sp->kbd_opened = TRUE; over_keyboard: ; + /* The screen is opened iff locking it succeeds. */ + sp->scr_opened = TRUE; + + /* The screen switch is optional. */ + if (!(flags & 2)) + return; + + /* try to switch to the kernel console screen */ if (!cold && sc->cur_scp->index != sc_console->index && sc->cur_scp->smode.mode == VT_AUTO && sc_console->smode.mode == VT_AUTO) sc_switch_scr(sc, sc_console->index); - } static void sccnclose(sc_softc_t *sc, struct sc_cnstate *sp) { - if (sc->kbd == NULL) + sp->scr_opened = FALSE; + + if (!sp->kbd_opened) return; /* Restore keyboard mode (for the current, possibly-changed scp). */ kbdd_poll(sc->kbd, FALSE); - (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); + if (--sc->kbd_open_level == 0) + (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&sc->cur_scp->kbd_mode); kbdd_disable(sc->kbd); + sp->kbd_opened = FALSE; } +/* + * Grabbing switches the screen and keyboard focus to sc_console and the + * keyboard mode to (K_XLATE, polled). Only switching to polled mode is + * essential (for preventing the interrupt handler from eating input + * between polls). Focus is part of the UI, and the other switches are + * work just was well when they are done on every entry and exit. + * + * Screen switches while grabbed are supported, and to maintain focus for + * this ungrabbing and closing only restore the polling state and then + * the keyboard mode if on the original screen. + */ + static void sc_cngrab(struct consdev *cp) { sc_softc_t *sc; + int lev; sc = sc_console->sc; - if (sc->grab_level++ == 0) - sccnopen(sc, NULL, 0); + lev = atomic_fetchadd_int(&sc->grab_level, 1); + if (lev >= 0 || lev < 2) + sccnopen(sc, &sc->grab_state[lev], 1 | 2); } static void sc_cnungrab(struct consdev *cp) { sc_softc_t *sc; + int lev; sc = sc_console->sc; - if (--sc->grab_level == 0) - sccnclose(sc, NULL); + lev = atomic_load_acq_int(&sc->grab_level) - 1; + if (lev >= 0 || lev < 2) + sccnclose(sc, &sc->grab_state[lev]); + atomic_add_int(&sc->grab_level, -1); } static void @@ -2681,7 +2716,7 @@ exchange_scr(sc_softc_t *sc) sc_set_border(scp, scp->border); /* set up the keyboard for the new screen */ - if (sc->grab_level == 0 && sc->old_scp->kbd_mode != scp->kbd_mode) + if (sc->kbd_open_level == 0 && sc->old_scp->kbd_mode != scp->kbd_mode) (void)kbdd_ioctl(sc->kbd, KDSKBMODE, (caddr_t)&scp->kbd_mode); update_kbd_state(scp, scp->status, LOCK_MASK); @@ -3426,7 +3461,7 @@ next_code: if (!(flags & SCGETC_CN)) random_harvest_queue(&c, sizeof(c), 1, RANDOM_KEYBOARD); - if (sc->grab_level == 0 && scp->kbd_mode != K_XLATE) + if (sc->kbd_open_level == 0 && scp->kbd_mode != K_XLATE) return KEYCHAR(c); /* if scroll-lock pressed allow history browsing */ diff --git a/sys/dev/syscons/syscons.h b/sys/dev/syscons/syscons.h index e0aa0beb432..6521a272600 100644 --- a/sys/dev/syscons/syscons.h +++ b/sys/dev/syscons/syscons.h @@ -188,6 +188,11 @@ struct video_adapter; struct scr_stat; struct tty; +struct sc_cnstate { + u_char kbd_opened; + u_char scr_opened; +}; + typedef struct sc_softc { int unit; /* unit # */ int config; /* configuration flags */ @@ -231,6 +236,9 @@ typedef struct sc_softc { char write_in_progress; char blink_in_progress; int grab_level; + /* 2 is just enough for kdb to grab for stepping normal grabbing: */ + struct sc_cnstate grab_state[2]; + int kbd_open_level; struct mtx scr_lock; /* mutex for sc_puts() */ struct mtx video_mtx;