kernel - Fix syscons's visual bell
authorMatthew Dillon <dillon@apollo.backplane.com>
Sat, 17 Feb 2018 19:29:49 +0000 (11:29 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sat, 17 Feb 2018 19:32:01 +0000 (11:32 -0800)
* The visual bell code was completely broken.

* Refactor the callout, and use the new callout_init_lk facility
  to deal with races.

Reported-by: snurg
sys/dev/misc/syscons/syscons.c

index c26c6ce..bd3e1c7 100644 (file)
@@ -157,11 +157,10 @@ TUNABLE_INT("kern.kms_columns", &desired_cols);
 
 static int     debugger;
 static cdev_t  cctl_dev;
-#if 0
 static timeout_t blink_screen_callout;
-#endif
 static  void   sc_blink_screen(scr_stat *scp);
 static struct mtx      syscons_mtx = MTX_INITIALIZER("syscons");
+static struct lock     syscons_blink_lk = LOCK_INITIALIZER("sblnk", 0, 0);
 
 /* prototypes */
 static int scvidprobe(int unit, int flags, int cons);
@@ -3564,7 +3563,7 @@ init_scp(sc_softc_t *sc, int vty, scr_stat *scp)
     scp->status = 0;
     scp->mode = sc->initial_mode;
     scp->fbi_generation = sc->fbi_generation;
-    callout_init_mp(&scp->blink_screen_ch);
+    callout_init_lk(&scp->blink_screen_ch, &syscons_blink_lk);
     if (scp->sc->fbi == NULL) {
        lwkt_gettoken(&tty_token);
        (*vidsw[sc->adapter]->get_info)(sc->adp, scp->mode, &info);
@@ -4264,7 +4263,9 @@ sc_bell(scr_stat *scp, int pitch, int duration)
        scp->sc->blink_in_progress = 3;
        if (scp != scp->sc->cur_scp)
            scp->sc->blink_in_progress += 2;
+       lockmgr(&syscons_blink_lk, LK_EXCLUSIVE);
        sc_blink_screen(scp->sc->cur_scp);
+       lockmgr(&syscons_blink_lk, LK_RELEASE);
     } else if (duration != 0 && pitch != 0) {
        if (scp != scp->sc->cur_scp)
            pitch *= 2;
@@ -4273,8 +4274,8 @@ sc_bell(scr_stat *scp, int pitch, int duration)
 }
 
 /*
- * Two versions of blink_screen(), one called from the console path
- * with the syscons locked, and one called from a timer callout.
+ * Blink the screen, called with the syscons_mtx and syscons_blink_lk
+ * held.  Can be called directly or via the callout.
  */
 static void
 sc_blink_screen(scr_stat *scp)
@@ -4288,40 +4289,29 @@ sc_blink_screen(scr_stat *scp)
        (*scp->rndr->draw)(scp, 0, scp->xsize*scp->ysize,
                           scp->sc->blink_in_progress & 1);
        scp->sc->blink_in_progress--;
+       callout_reset(&scp->blink_screen_ch, hz / 10,
+                     blink_screen_callout, scp);
     }
 }
 
-#if 0
+/*
+ * Screen blink callout.  Note that there is a lock order
+ * reversal between syscons_blink_lk and the syscons lock.
+ * Acquire the syscons lock non-blocking.
+ */
 static void
 blink_screen_callout(void *arg)
 {
     scr_stat *scp = arg;
-    struct tty *tp;
 
-    if (ISGRAPHSC(scp) || (scp->sc->blink_in_progress <= 1)) {
-       syscons_lock();
-       scp->sc->blink_in_progress = 0;
-       mark_all(scp);
+    if (syscons_lock_nonblock() == 0) {
+       sc_blink_screen(scp);
        syscons_unlock();
-       tp = VIRTUAL_TTY(scp->sc, scp->index);
-       if (ISTTYOPEN(tp))
-           scstart(tp);
-       if (scp->sc->delayed_next_scr) {
-           syscons_lock();
-           sc_switch_scr(scp->sc, scp->sc->delayed_next_scr - 1);
-           syscons_unlock();
-       }
     } else {
-       syscons_lock();
-       (*scp->rndr->draw)(scp, 0, scp->xsize*scp->ysize, 
-                          scp->sc->blink_in_progress & 1);
-       scp->sc->blink_in_progress--;
-       syscons_unlock();
        callout_reset(&scp->blink_screen_ch, hz / 10,
                      blink_screen_callout, scp);
     }
 }
-#endif
 
 /*
  * Allocate active keyboard. Try to allocate "kbdmux" keyboard first, and,