Avoid deadlock in tty code if a terminal emulation responds to

type/status/etc inquiries. (PR kern/37915)
This is clearly a design problem in tty, but we need a cheap fix now.
The problem is that ttyinput() tries to pull a spinlock which
is already held on calls to t_oproc.
The workaround is based on the fact that within wscons code, the
wsdisplay_emulinput() function is only called directly from
wsdisplaystart(). So we can be sure that the tty lock is held,
and use an inofficial entry point in ttc.c which avoids the locking.
These ate certainly more assumptions than needed by the fix
proposed in the PR, but it doesn't affect (and slow down) other
tty drivers.
This commit is contained in:
drochner 2009-01-22 20:40:20 +00:00
parent e6a971bc07
commit cf45120117
3 changed files with 35 additions and 7 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: wsdisplay.c,v 1.125 2009/01/15 04:22:11 yamt Exp $ */ /* $NetBSD: wsdisplay.c,v 1.126 2009/01/22 20:40:20 drochner Exp $ */
/* /*
* Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved.
@ -31,7 +31,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: wsdisplay.c,v 1.125 2009/01/15 04:22:11 yamt Exp $"); __KERNEL_RCSID(0, "$NetBSD: wsdisplay.c,v 1.126 2009/01/22 20:40:20 drochner Exp $");
#include "opt_wsdisplay_compat.h" #include "opt_wsdisplay_compat.h"
#include "opt_wsmsgattrs.h" #include "opt_wsmsgattrs.h"
@ -95,6 +95,11 @@ struct wsscreen {
#endif #endif
struct wsdisplay_softc *sc; struct wsdisplay_softc *sc;
#ifdef DIAGNOSTIC
/* XXX this is to support a hack in emulinput, see comment below */
int scr_in_ttyoutput;
#endif
}; };
struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int,
@ -1524,6 +1529,10 @@ wsdisplaystart(struct tty *tp)
tp->t_state |= TS_BUSY; tp->t_state |= TS_BUSY;
splx(s); splx(s);
#ifdef DIAGNOSTIC
scr->scr_in_ttyoutput = 1;
#endif
/* /*
* Drain output from ring buffer. * Drain output from ring buffer.
* The output will normally be in one contiguous chunk, but when the * The output will normally be in one contiguous chunk, but when the
@ -1554,6 +1563,10 @@ wsdisplaystart(struct tty *tp)
ndflush(&tp->t_outq, n); ndflush(&tp->t_outq, n);
} }
#ifdef DIAGNOSTIC
scr->scr_in_ttyoutput = 0;
#endif
s = spltty(); s = spltty();
tp->t_state &= ~TS_BUSY; tp->t_state &= ~TS_BUSY;
/* Come back if there's more to do */ /* Come back if there's more to do */
@ -1610,6 +1623,7 @@ wsdisplay_emulinput(void *v, const u_char *data, u_int count)
{ {
struct wsscreen *scr = v; struct wsscreen *scr = v;
struct tty *tp; struct tty *tp;
int (*ifcn)(int, struct tty *);
if (v == NULL) /* console, before real attach */ if (v == NULL) /* console, before real attach */
return; return;
@ -1620,8 +1634,21 @@ wsdisplay_emulinput(void *v, const u_char *data, u_int count)
return; return;
tp = scr->scr_tty; tp = scr->scr_tty;
/*
* XXX bad hack to work around locking problems in tty.c:
* ttyinput() will try to lock again, causing deadlock.
* We assume that wsdisplay_emulinput() can only be called
* from within wsdisplaystart(), and thus the tty lock
* is already held. Use an entry point which doesn't lock.
*/
KASSERT(scr->scr_in_ttyoutput);
ifcn = tp->t_linesw->l_rint;
if (ifcn == ttyinput)
ifcn = ttyinput_wlock;
while (count-- > 0) while (count-- > 0)
(*tp->t_linesw->l_rint)(*data++, tp); (*ifcn)(*data++, tp);
} }
/* /*

View File

@ -1,4 +1,4 @@
/* $NetBSD: tty.c,v 1.229 2009/01/22 14:38:35 yamt Exp $ */ /* $NetBSD: tty.c,v 1.230 2009/01/22 20:40:20 drochner Exp $ */
/*- /*-
* Copyright (c) 2008 The NetBSD Foundation, Inc. * Copyright (c) 2008 The NetBSD Foundation, Inc.
@ -63,7 +63,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tty.c,v 1.229 2009/01/22 14:38:35 yamt Exp $"); __KERNEL_RCSID(0, "$NetBSD: tty.c,v 1.230 2009/01/22 20:40:20 drochner Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -374,7 +374,7 @@ ttyclose(struct tty *tp)
* ttyinput() helper. * ttyinput() helper.
* Call with the tty lock held. * Call with the tty lock held.
*/ */
static int /* XXX static */ int
ttyinput_wlock(int c, struct tty *tp) ttyinput_wlock(int c, struct tty *tp)
{ {
int iflag, lflag, i, error; int iflag, lflag, i, error;

View File

@ -1,4 +1,4 @@
/* $NetBSD: tty.h,v 1.85 2009/01/22 14:38:34 yamt Exp $ */ /* $NetBSD: tty.h,v 1.86 2009/01/22 20:40:20 drochner Exp $ */
/*- /*-
* Copyright (c) 2008 The NetBSD Foundation, Inc. * Copyright (c) 2008 The NetBSD Foundation, Inc.
@ -271,6 +271,7 @@ void ttyflush(struct tty *, int);
void ttygetinfo(struct tty *, int, char *, size_t); void ttygetinfo(struct tty *, int, char *, size_t);
void ttyputinfo(struct tty *, char *); void ttyputinfo(struct tty *, char *);
int ttyinput(int, struct tty *); int ttyinput(int, struct tty *);
int ttyinput_wlock(int, struct tty *); /* XXX see wsdisplay.c */
int ttylclose(struct tty *, int); int ttylclose(struct tty *, int);
int ttylopen(dev_t, struct tty *); int ttylopen(dev_t, struct tty *);
int ttykqfilter(dev_t, struct knote *); int ttykqfilter(dev_t, struct knote *);