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:
parent
e6a971bc07
commit
cf45120117
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
Loading…
Reference in New Issue