Module Name: src
Committed By: skrll
Date: Sat Sep 19 07:39:51 UTC 2015
Modified Files:
src/sys/dev/usb [nick-nhusb]: ucom.c
Log Message:
Make ucom(4) MP safe
To generate a diff of this commit:
cvs rdiff -u -r1.108.2.7 -r1.108.2.8 src/sys/dev/usb/ucom.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/usb/ucom.c
diff -u src/sys/dev/usb/ucom.c:1.108.2.7 src/sys/dev/usb/ucom.c:1.108.2.8
--- src/sys/dev/usb/ucom.c:1.108.2.7 Thu Jul 23 07:36:33 2015
+++ src/sys/dev/usb/ucom.c Sat Sep 19 07:39:51 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: ucom.c,v 1.108.2.7 2015/07/23 07:36:33 skrll Exp $ */
+/* $NetBSD: ucom.c,v 1.108.2.8 2015/09/19 07:39:51 skrll Exp $ */
/*
* Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ucom.c,v 1.108.2.7 2015/07/23 07:36:33 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ucom.c,v 1.108.2.8 2015/09/19 07:39:51 skrll Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -179,7 +179,11 @@ struct ucom_softc {
struct pps_state sc_pps_state; /* pps state */
- krndsource_t sc_rndsource; /* random source */
+ krndsource_t sc_rndsource; /* random source */
+
+ kmutex_t *sc_lock;
+ kcondvar_t sc_opencv;
+ kcondvar_t sc_detachcv;
};
dev_type_open(ucomopen);
@@ -203,7 +207,7 @@ const struct cdevsw ucom_cdevsw = {
.d_mmap = nommap,
.d_kqfilter = ttykqfilter,
.d_discard = nodiscard,
- .d_flag = D_TTY
+ .d_flag = D_TTY | D_MPSAFE
};
static void ucom_cleanup(struct ucom_softc *);
@@ -278,7 +282,10 @@ ucom_attach(device_t parent, device_t se
sc->sc_refcnt = 0;
sc->sc_dying = 0;
- sc->sc_si = softint_establish(SOFTINT_NET, ucom_softintr, sc);
+ sc->sc_si = softint_establish(SOFTINT_USB, ucom_softintr, sc);
+ sc->sc_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTUSB);
+ cv_init(&sc->sc_opencv, "ucomopen");
+ cv_init(&sc->sc_detachcv, "ucomdtch");
tp = tty_alloc();
tp->t_oproc = ucomstart;
@@ -303,14 +310,17 @@ ucom_detach(device_t self, int flags)
struct ucom_softc *sc = device_private(self);
struct tty *tp = sc->sc_tty;
int maj, mn;
- int s, i;
+ int i;
UCOMHIST_FUNC(); UCOMHIST_CALLED();
DPRINTF("sc=%p flags=%d tp=%p", sc, flags, tp, 0);
DPRINTF("... pipe=%d,%d",sc->sc_bulkin_no, sc->sc_bulkout_no, 0, 0);
+ mutex_enter(sc->sc_lock);
sc->sc_dying = 1;
+ mutex_exit(sc->sc_lock);
+
pmf_device_deregister(self);
if (sc->sc_bulkin_pipe != NULL)
@@ -318,8 +328,8 @@ ucom_detach(device_t self, int flags)
if (sc->sc_bulkout_pipe != NULL)
usbd_abort_pipe(sc->sc_bulkout_pipe);
- s = splusb();
- if (--sc->sc_refcnt >= 0) {
+ mutex_enter(sc->sc_lock);
+ while (sc->sc_refcnt > 0) {
/* Wake up anyone waiting */
if (tp != NULL) {
mutex_spin_enter(&tty_lock);
@@ -329,11 +339,11 @@ ucom_detach(device_t self, int flags)
mutex_spin_exit(&tty_lock);
}
/* Wait for processes to go away. */
- usb_detach_waitold(sc->sc_dev);
+ usb_detach_wait(sc->sc_dev, &sc->sc_detachcv, sc->sc_lock);
}
softint_disestablish(sc->sc_si);
- splx(s);
+ mutex_exit(sc->sc_lock);
/* locate the major number */
maj = cdevsw_lookup_major(&ucom_cdevsw);
@@ -365,6 +375,10 @@ ucom_detach(device_t self, int flags)
/* Detach the random source */
rnd_detach_source(&sc->sc_rndsource);
+ mutex_destroy(sc->sc_lock);
+ cv_destroy(&sc->sc_opencv);
+ cv_destroy(&sc->sc_detachcv);
+
return 0;
}
@@ -379,7 +393,9 @@ ucom_activate(device_t self, enum devact
switch (act) {
case DVACT_DEACTIVATE:
+ mutex_enter(sc->sc_lock);
sc->sc_dying = 1;
+ mutex_exit(sc->sc_lock);
return 0;
default:
return EOPNOTSUPP;
@@ -393,13 +409,15 @@ ucom_shutdown(struct ucom_softc *sc)
UCOMHIST_FUNC(); UCOMHIST_CALLED();
+ KASSERT(mutex_owned(sc->sc_lock));
/*
* Hang up if necessary. Wait a bit, so the other side has time to
* notice even if we immediately open the port again.
*/
if (ISSET(tp->t_cflag, HUPCL)) {
ucom_dtr(sc, 0);
- (void)tsleep(sc, TTIPRI, ttclos, hz);
+ /* XXX will only timeout */
+ (void) kpause(ttclos, false, hz, sc->sc_lock);
}
}
@@ -411,7 +429,7 @@ ucomopen(dev_t dev, int flag, int mode,
struct ucom_softc *sc = device_lookup_private(&ucom_cd, unit);
struct ucom_buffer *ub;
struct tty *tp;
- int s, i;
+ int i;
int error;
UCOMHIST_FUNC(); UCOMHIST_CALLED();
@@ -419,31 +437,38 @@ ucomopen(dev_t dev, int flag, int mode,
if (sc == NULL)
return ENXIO;
- if (sc->sc_dying)
+ mutex_enter(sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(sc->sc_lock);
return EIO;
+ }
- if (!device_is_active(sc->sc_dev))
+ if (!device_is_active(sc->sc_dev)) {
+ mutex_exit(sc->sc_lock);
return ENXIO;
+ }
tp = sc->sc_tty;
DPRINTF("unit=%d, tp=%p\n", unit, tp, 0, 0);
- if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
+ if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp)) {
+ mutex_exit(sc->sc_lock);
return EBUSY;
-
- s = spltty();
+ }
/*
* Do the following iff this is a first open.
*/
- while (sc->sc_opening)
- tsleep(&sc->sc_opening, PRIBIO, "ucomop", 0);
+ while (sc->sc_opening) {
+ error = cv_wait_sig(&sc->sc_opencv, sc->sc_lock);
- if (sc->sc_dying) {
- splx(s);
- return EIO;
+ if (error) {
+ mutex_exit(sc->sc_lock);
+ return error;
+ }
}
+
sc->sc_opening = 1;
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
@@ -457,8 +482,8 @@ ucomopen(dev_t dev, int flag, int mode,
if (error) {
ucom_cleanup(sc);
sc->sc_opening = 0;
- wakeup(&sc->sc_opening);
- splx(s);
+ cv_signal(&sc->sc_opencv);
+ mutex_exit(sc->sc_lock);
return error;
}
}
@@ -577,8 +602,8 @@ ucomopen(dev_t dev, int flag, int mode,
}
sc->sc_opening = 0;
- wakeup(&sc->sc_opening);
- splx(s);
+ cv_signal(&sc->sc_opencv);
+ mutex_exit(sc->sc_lock);
error = ttyopen(tp, UCOMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
if (error)
@@ -614,13 +639,15 @@ fail_1:
usbd_close_pipe(sc->sc_bulkin_pipe);
sc->sc_bulkin_pipe = NULL;
fail_0:
+ mutex_enter(sc->sc_lock);
sc->sc_opening = 0;
- wakeup(&sc->sc_opening);
- splx(s);
+ cv_signal(&sc->sc_opencv);
+ mutex_exit(sc->sc_lock);
+
return error;
bad:
- s = spltty();
+ mutex_spin_enter(&tty_lock);
CLR(tp->t_state, TS_BUSY);
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
/*
@@ -629,7 +656,7 @@ bad:
*/
ucom_cleanup(sc);
}
- splx(s);
+ mutex_spin_exit(&tty_lock);
return error;
}
@@ -639,7 +666,6 @@ ucomclose(dev_t dev, int flag, int mode,
{
struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev));
struct tty *tp;
- int s;
UCOMHIST_FUNC(); UCOMHIST_CALLED();
@@ -648,12 +674,13 @@ ucomclose(dev_t dev, int flag, int mode,
if (sc == NULL)
return 0;
+ mutex_enter(sc->sc_lock);
tp = sc->sc_tty;
- if (!ISSET(tp->t_state, TS_ISOPEN))
- return 0;
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ goto out;
+ }
- s = spltty();
sc->sc_refcnt++;
(*tp->t_linesw->l_close)(tp, flag);
@@ -672,8 +699,10 @@ ucomclose(dev_t dev, int flag, int mode,
sc->sc_methods->ucom_close(sc->sc_parent, sc->sc_portno);
if (--sc->sc_refcnt < 0)
- usb_detach_wakeupold(sc->sc_dev);
- splx(s);
+ usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv);
+
+out:
+ mutex_exit(sc->sc_lock);
return 0;
}
@@ -687,15 +716,26 @@ ucomread(dev_t dev, struct uio *uio, int
UCOMHIST_FUNC(); UCOMHIST_CALLED();
- if (sc == NULL || sc->sc_dying)
+ if (sc == NULL)
+ return EIO;
+
+ mutex_enter(sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(sc->sc_lock);
return EIO;
+ }
tp = sc->sc_tty;
sc->sc_refcnt++;
+ mutex_exit(sc->sc_lock);
error = ((*tp->t_linesw->l_read)(tp, uio, flag));
+ mutex_enter(sc->sc_lock);
+
if (--sc->sc_refcnt < 0)
- usb_detach_wakeupold(sc->sc_dev);
+ usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv);
+ mutex_exit(sc->sc_lock);
+
return error;
}
@@ -706,15 +746,25 @@ ucomwrite(dev_t dev, struct uio *uio, in
struct tty *tp;
int error;
- if (sc == NULL || sc->sc_dying)
+ if (sc == NULL)
+ return EIO;
+
+ mutex_enter(sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(sc->sc_lock);
return EIO;
+ }
tp = sc->sc_tty;
sc->sc_refcnt++;
+ mutex_exit(sc->sc_lock);
error = ((*tp->t_linesw->l_write)(tp, uio, flag));
+ mutex_enter(sc->sc_lock);
if (--sc->sc_refcnt < 0)
- usb_detach_wakeupold(sc->sc_dev);
+ usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv);
+ mutex_exit(sc->sc_lock);
+
return error;
}
@@ -726,15 +776,24 @@ ucompoll(dev_t dev, int events, struct l
int revents;
sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev));
- if (sc == NULL || sc->sc_dying)
+ if (sc == NULL)
return POLLHUP;
+ mutex_enter(sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(sc->sc_lock);
+ return POLLHUP;
+ }
tp = sc->sc_tty;
sc->sc_refcnt++;
+ mutex_exit(sc->sc_lock);
revents = ((*tp->t_linesw->l_poll)(tp, events, l));
+ mutex_enter(sc->sc_lock);
if (--sc->sc_refcnt < 0)
- usb_detach_wakeupold(sc->sc_dev);
+ usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv);
+ mutex_exit(sc->sc_lock);
+
return revents;
}
@@ -752,13 +811,20 @@ ucomioctl(dev_t dev, u_long cmd, void *d
struct ucom_softc *sc = device_lookup_private(&ucom_cd, UCOMUNIT(dev));
int error;
- if (sc == NULL || sc->sc_dying)
+ if (sc == NULL)
return EIO;
+ mutex_enter(sc->sc_lock);
+ if (sc->sc_dying) {
+ mutex_exit(sc->sc_lock);
+ return EIO;
+ }
+
sc->sc_refcnt++;
error = ucom_do_ioctl(sc, cmd, data, flag, l);
if (--sc->sc_refcnt < 0)
- usb_detach_wakeupold(sc->sc_dev);
+ usb_detach_broadcast(sc->sc_dev, &sc->sc_detachcv);
+ mutex_exit(sc->sc_lock);
return error;
}
@@ -768,7 +834,6 @@ ucom_do_ioctl(struct ucom_softc *sc, u_l
{
struct tty *tp = sc->sc_tty;
int error;
- int s;
UCOMHIST_FUNC(); UCOMHIST_CALLED();
@@ -792,7 +857,7 @@ ucom_do_ioctl(struct ucom_softc *sc, u_l
error = 0;
DPRINTF("our cmd=0x%08lx", cmd, 0, 0, 0);
- s = spltty();
+ //mutex_enter(&tty_lock);
switch (cmd) {
case TIOCSBRK:
@@ -852,7 +917,7 @@ ucom_do_ioctl(struct ucom_softc *sc, u_l
break;
}
- splx(s);
+ //mutex_exit(&tty_lock);
return error;
}
@@ -1066,15 +1131,15 @@ ucomhwiflow(struct tty *tp, int block)
if (sc == NULL)
return 0;
+ mutex_enter(sc->sc_lock);
old = sc->sc_rx_stopped;
sc->sc_rx_stopped = (u_char)block;
if (old && !block) {
- int s = splusb();
sc->sc_rx_unblock = 1;
softint_schedule(sc->sc_si);
- splx(s);
}
+ mutex_exit(sc->sc_lock);
return 1;
}
@@ -1085,14 +1150,18 @@ ucomstart(struct tty *tp)
struct ucom_softc *sc = device_lookup_private(&ucom_cd,
UCOMUNIT(tp->t_dev));
struct ucom_buffer *ub;
- int s;
u_char *data;
int cnt;
- if (sc == NULL || sc->sc_dying)
+ if (sc == NULL)
return;
- s = spltty();
+ KASSERT(sc->sc_lock);
+ KASSERT(mutex_owned(&tty_lock));
+ if (sc->sc_dying) {
+ return;
+ }
+
if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
goto out;
if (sc->sc_tx_stopped)
@@ -1113,6 +1182,7 @@ ucomstart(struct tty *tp)
SET(tp->t_state, TS_BUSY);
goto out;
}
+
SIMPLEQ_REMOVE_HEAD(&sc->sc_obuff_free, ub_link);
if (SIMPLEQ_FIRST(&sc->sc_obuff_free) == NULL)
@@ -1135,24 +1205,26 @@ ucomstart(struct tty *tp)
softint_schedule(sc->sc_si);
out:
- splx(s);
+ return;
}
void
ucomstop(struct tty *tp, int flag)
{
#if 0
- /*struct ucom_softc *sc =
- device_lookup_private(&ucom_cd, UCOMUNIT(tp->t_dev));*/
- int s;
+ struct ucom_softc *sc =
+ device_lookup_private(&ucom_cd, UCOMUNIT(tp->t_dev));
- s = spltty();
+ mutex_enter(sc->sc_lock);
+ mutex_spin_enter(&tty_lock);
if (ISSET(tp->t_state, TS_BUSY)) {
+ /* obuff_full -> obuff_free? */
/* sc->sc_tx_stopped = 1; */
if (!ISSET(tp->t_state, TS_TTSTOP))
SET(tp->t_state, TS_FLUSH);
}
- splx(s);
+ mutex_spin_exit(&tty_lock);
+ mutex_exit(sc->sc_lock);
#endif
}
@@ -1163,6 +1235,8 @@ ucom_write_status(struct ucom_softc *sc,
struct tty *tp = sc->sc_tty;
uint32_t cc = ub->ub_len;
+ KASSERT(mutex_owned(sc->sc_lock));
+
switch (err) {
case USBD_IN_PROGRESS:
ub->ub_index = ub->ub_len;
@@ -1193,17 +1267,20 @@ ucom_write_status(struct ucom_softc *sc,
if ((ub = SIMPLEQ_FIRST(&sc->sc_obuff_full)) != NULL)
ucom_submit_write(sc, ub);
+ mutex_spin_enter(&tty_lock);
(*tp->t_linesw->l_start)(tp);
+ mutex_spin_exit(&tty_lock);
}
break;
}
}
-/* Call at spltty() */
static void
ucom_submit_write(struct ucom_softc *sc, struct ucom_buffer *ub)
{
+ KASSERT(mutex_owned(sc->sc_lock));
+
usbd_setup_xfer(ub->ub_xfer, sc->sc_bulkout_pipe,
(void *)sc, ub->ub_data, ub->ub_len,
0, USBD_NO_TIMEOUT, ucomwritecb);
@@ -1215,13 +1292,11 @@ static void
ucomwritecb(struct usbd_xfer *xfer, void *p, usbd_status status)
{
struct ucom_softc *sc = (struct ucom_softc *)p;
- int s;
-
- s = spltty();
+ mutex_enter(sc->sc_lock);
ucom_write_status(sc, SIMPLEQ_FIRST(&sc->sc_obuff_full), status);
+ mutex_exit(sc->sc_lock);
- splx(s);
}
static void
@@ -1230,12 +1305,15 @@ ucom_softintr(void *arg)
struct ucom_softc *sc = arg;
struct tty *tp = sc->sc_tty;
struct ucom_buffer *ub;
- int s;
- if (!ISSET(tp->t_state, TS_ISOPEN))
+ mutex_enter(sc->sc_lock);
+ mutex_enter(&tty_lock);
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ mutex_exit(&tty_lock);
+ mutex_exit(sc->sc_lock);
return;
-
- s = spltty();
+ }
+ mutex_exit(&tty_lock);
ub = SIMPLEQ_FIRST(&sc->sc_obuff_full);
@@ -1245,7 +1323,7 @@ ucom_softintr(void *arg)
if (sc->sc_rx_unblock)
ucom_read_complete(sc);
- splx(s);
+ mutex_exit(sc->sc_lock);
}
static void
@@ -1254,7 +1332,8 @@ ucom_read_complete(struct ucom_softc *sc
int (*rint)(int, struct tty *);
struct ucom_buffer *ub;
struct tty *tp;
- int s;
+
+ KASSERT(mutex_owned(sc->sc_lock));
tp = sc->sc_tty;
rint = tp->t_linesw->l_rint;
@@ -1262,8 +1341,7 @@ ucom_read_complete(struct ucom_softc *sc
while (ub != NULL && !sc->sc_rx_stopped) {
- s = spltty();
-
+ /* XXX ttyinput takes tty_lock */
while (ub->ub_index < ub->ub_len && !sc->sc_rx_stopped) {
/* Give characters to tty layer. */
if ((*rint)(ub->ub_data[ub->ub_index], tp) == -1) {
@@ -1273,8 +1351,6 @@ ucom_read_complete(struct ucom_softc *sc
ub->ub_index++;
}
- splx(s);
-
if (ub->ub_index == ub->ub_len) {
SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_full, ub_link);
@@ -1315,10 +1391,10 @@ ucomreadcb(struct usbd_xfer *xfer, void
struct ucom_buffer *ub;
uint32_t cc;
u_char *cp;
- int s;
UCOMHIST_FUNC(); UCOMHIST_CALLED();
+ mutex_enter(sc->sc_lock);
ub = SIMPLEQ_FIRST(&sc->sc_ibuff_empty);
SIMPLEQ_REMOVE_HEAD(&sc->sc_ibuff_empty, ub_link);
@@ -1327,25 +1403,26 @@ ucomreadcb(struct usbd_xfer *xfer, void
DPRINTF("dying", 0, 0, 0, 0);
ub->ub_index = ub->ub_len = 0;
/* Send something to wake upper layer */
- s = spltty();
if (status != USBD_CANCELLED) {
(tp->t_linesw->l_rint)('\n', tp);
mutex_spin_enter(&tty_lock); /* XXX */
ttwakeup(tp);
mutex_spin_exit(&tty_lock); /* XXX */
}
- splx(s);
+ mutex_exit(sc->sc_lock);
return;
}
if (status == USBD_STALLED) {
usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe);
ucomsubmitread(sc, ub);
+ mutex_exit(sc->sc_lock);
return;
}
if (status != USBD_NORMAL_COMPLETION) {
printf("ucomreadcb: wonky status=%s\n", usbd_errstr(status));
+ mutex_exit(sc->sc_lock);
return;
}
@@ -1364,6 +1441,7 @@ ucomreadcb(struct usbd_xfer *xfer, void
if (sc->sc_opening) {
ucomsubmitread(sc, ub);
+ mutex_exit(sc->sc_lock);
return;
}
@@ -1379,6 +1457,7 @@ ucomreadcb(struct usbd_xfer *xfer, void
SIMPLEQ_INSERT_TAIL(&sc->sc_ibuff_full, ub, ub_link);
ucom_read_complete(sc);
+ mutex_exit(sc->sc_lock);
}
static void
@@ -1390,16 +1469,24 @@ ucom_cleanup(struct ucom_softc *sc)
DPRINTF("closing pipes", 0, 0, 0, 0);
+ KASSERT(mutex_owned(sc->sc_lock));
+
ucom_shutdown(sc);
if (sc->sc_bulkin_pipe != NULL) {
- usbd_abort_pipe(sc->sc_bulkin_pipe);
- usbd_close_pipe(sc->sc_bulkin_pipe);
+ struct usbd_pipe *bulkin_pipe = sc->sc_bulkin_pipe;
sc->sc_bulkin_pipe = NULL;
+ mutex_exit(sc->sc_lock);
+ usbd_abort_pipe(bulkin_pipe);
+ usbd_close_pipe(bulkin_pipe);
+ mutex_enter(sc->sc_lock);
}
if (sc->sc_bulkout_pipe != NULL) {
- usbd_abort_pipe(sc->sc_bulkout_pipe);
- usbd_close_pipe(sc->sc_bulkout_pipe);
+ struct usbd_pipe *bulkout_pipe = sc->sc_bulkout_pipe;
sc->sc_bulkout_pipe = NULL;
+ mutex_exit(sc->sc_lock);
+ usbd_abort_pipe(bulkout_pipe);
+ usbd_close_pipe(bulkout_pipe);
+ mutex_enter(sc->sc_lock);
}
for (ub = &sc->sc_ibuff[0]; ub != &sc->sc_ibuff[UCOM_IN_BUFFS]; ub++) {
if (ub->ub_xfer != NULL) {