Date: Wed, 24 Aug 2016 18:59:24 +0000 (UTC) From: Bruce Evans <bde@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r304773 - head/sys/dev/syscons Message-ID: <201608241859.u7OIxOEj067818@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: bde Date: Wed Aug 24 18:59:24 2016 New Revision: 304773 URL: https://svnweb.freebsd.org/changeset/base/304773 Log: 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. Modified: head/sys/dev/syscons/syscons.c head/sys/dev/syscons/syscons.h Modified: head/sys/dev/syscons/syscons.c ============================================================================== --- head/sys/dev/syscons/syscons.c Wed Aug 24 17:45:11 2016 (r304772) +++ head/sys/dev/syscons/syscons.c Wed Aug 24 18:59:24 2016 (r304773) @@ -79,8 +79,6 @@ __FBSDID("$FreeBSD$"); #include <dev/fb/splashreg.h> #include <dev/syscons/syscons.h> -struct sc_cnstate; /* not used yet */ - #define COLD 0 #define WARM 1 @@ -1655,7 +1653,13 @@ sccnopen(sc_softc_t *sc, struct sc_cnsta { 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_cnsta /* 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 */ Modified: head/sys/dev/syscons/syscons.h ============================================================================== --- head/sys/dev/syscons/syscons.h Wed Aug 24 17:45:11 2016 (r304772) +++ head/sys/dev/syscons/syscons.h Wed Aug 24 18:59:24 2016 (r304773) @@ -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;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201608241859.u7OIxOEj067818>