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.
This commit is contained in:
Bruce Evans 2016-08-24 18:59:24 +00:00
parent 2078bf6ef0
commit e866ca565f
2 changed files with 55 additions and 12 deletions

View File

@ -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_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 @@ scgetc(sc_softc_t *sc, u_int flags, struct sc_cnstate *sp)
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 */

View File

@ -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;