NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: kern/37915: vt100 + wscons + tty vmlocking changes = panic



On Wed, Jan 30, 2008 at 01:30:00AM +0000, dholland%eecs.harvard.edu@localhost 
wrote:
 > The problem is that ttwrite() holds tty_lock while calling ttstart();
 > this makes its way down to wsemul_vt100_handle_csi(), which for
 > several sequences that report stuff back calls wsdisplay_emulinput(),
 > which calls ttyinput(), which tries to get tty_lock again.

Making a small and noninvasive patch for this (as ad@ requested) turns
out to be not so trivial, since it turns out that spin mutexes don't
track who's holding them. (And for a while I thought tty_lock was
being released and reacquired somewhere inside ttinput, which would
have made it just about impossible, but that turns out to have been a
red herring.)

Thus, while the following patch could be considered a workaround, I
can't with a straight face call it a fix.

Index: tty.c
===================================================================
RCS file: /cvsroot/src/sys/kern/tty.c,v
retrieving revision 1.228
diff -u -p -r1.228 tty.c
--- tty.c       19 Nov 2008 18:36:07 -0000      1.228
+++ tty.c       2 Jan 2009 09:01:35 -0000
@@ -202,6 +202,10 @@ kmutex_t tty_lock;
 krwlock_t ttcompat_lock;
 int (*ttcompatvec)(struct tty *, u_long, void *, int, struct lwp *);
 
+/* XXX: names the cpu that's currently doing ttstart(). See PR 37915. */
+static struct cpu_info *in_ttstart_hack_cpu;
+static unsigned in_ttstart_hack_depth;
+
 uint64_t tk_cancc;
 uint64_t tk_nin;
 uint64_t tk_nout;
@@ -702,11 +706,20 @@ ttyinput_wlock(int c, struct tty *tp)
  *
  * XXX - this is a hack, all drivers must changed to acquire the
  *      lock before calling linesw->l_rint()
+ *
+ * XXX - it is a bigger hack; wscons sometimes already has the lock.
+ *      We record which cpu is in ttstart (and how many times) to
+ *      avoid re-entering the lock when we come back here via wscons.
+ *      This is a hack for a specific known case, not a general fix.
+ *      Note that we're also misusing mutex_owned, but the specific
+ *      usage ought to work and a real fix is planned for later.
+ *      PR 37915.
  */
 int
 ttyinput(int c, struct tty *tp)
 {
        int error;
+       int dolock = 1;
 
        /*
         * Unless the receiver is enabled, drop incoming data.
@@ -714,9 +727,16 @@ ttyinput(int c, struct tty *tp)
        if (!ISSET(tp->t_cflag, CREAD))
                return (0);
 
-       mutex_spin_enter(&tty_lock);
+       /* The bigger hack. */
+       if (mutex_owned(&tty_lock) && in_ttstart_hack_cpu == curcpu()) {
+               dolock = 0;
+       }
+
+       if (dolock)
+               mutex_spin_enter(&tty_lock);
        error = ttyinput_wlock(c, tp);
-       mutex_spin_exit(&tty_lock);
+       if (dolock)
+               mutex_spin_exit(&tty_lock);
 
        return (error);
 }
@@ -1551,9 +1571,39 @@ ttrstrt(void *tp_arg)
 int
 ttstart(struct tty *tp)
 {
+       struct cpu_info *mycpu;
+
+       mycpu = curcpu();
+       KASSERT(mutex_owned(&tty_lock));
+
+       /*
+        * Track who's in ttstart. See comment above ttyinput(), and
+        * PR 37915. If someone else was still in ttstart, overwrite
+        * them. This appears to be necessary because something
+        * releases and reacquires tty_lock inside ->t_oproc; I'm
+        * hoping it doesn't call ttyinput() again after that.
+        */
+       if (in_ttstart_hack_cpu == mycpu) {
+               in_ttstart_hack_depth++;
+       } else if (in_ttstart_hack_cpu == NULL) {
+               in_ttstart_hack_cpu = mycpu;
+               in_ttstart_hack_depth = 1;
+       } else {
+               panic("ttstart: unexpected reentrant call "
+                     "(tty_lock released inside t_oproc?)\n");
+       }
 
        if (tp->t_oproc != NULL)        /* XXX: Kludge for pty. */
                (*tp->t_oproc)(tp);
+
+       KASSERT(mycpu == curcpu());
+       KASSERT(in_ttstart_hack_cpu == mycpu);
+       KASSERT(in_ttstart_hack_depth > 0);
+       in_ttstart_hack_depth--;
+       if (in_ttstart_hack_depth == 0) {
+               in_ttstart_hack_cpu = NULL;
+       }
+
        return (0);
 }
 


-- 
David A. Holland
dholland%netbsd.org@localhost


Home | Main Index | Thread Index | Old Index