summaryrefslogtreecommitdiff
path: root/sys/dev/usb
diff options
context:
space:
mode:
authorriastradh <riastradh@NetBSD.org>2021-09-07 10:43:11 +0000
committerriastradh <riastradh@NetBSD.org>2021-09-07 10:43:11 +0000
commita1c4e40175eba9ddea9053fbfca3aa1e930d0af2 (patch)
tree9fd8b52e9372da383a9d004abc8104b6e0e764a0 /sys/dev/usb
parent55ccc9335b1605af432f90b3cff7d3e4cce03fd3 (diff)
ugen(4): Ensure we close pipes on detach.
Calling vdevgone has the effect of VOP_REVOKE -> spec_node_revoke -> VOP_CLOSE -> spec_close -> cdev_close -> ugenclose, but: (a) the flags passed to VOP_CLOSE don't have FREAD or FWRITE, and (b) ugenclose has no effect when sc_dying is set anyway. So create another path into the close logic, ugen_do_close. We have to do this in detach _after_ the references have drained because we may free buffers that other users are still using while the reference count is nonzero.
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/ugen.c64
1 files changed, 43 insertions, 21 deletions
diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c
index f361a132342..dc32ca906a9 100644
--- a/sys/dev/usb/ugen.c
+++ b/sys/dev/usb/ugen.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ugen.c,v 1.162 2021/09/07 10:42:59 riastradh Exp $ */
+/* $NetBSD: ugen.c,v 1.163 2021/09/07 10:43:11 riastradh Exp $ */
/*
* Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.162 2021/09/07 10:42:59 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.163 2021/09/07 10:43:11 riastradh Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@@ -690,30 +690,25 @@ out: if (error && opened)
return error;
}
-static int
-ugenclose(dev_t dev, int flag, int mode, struct lwp *l)
+static void
+ugen_do_close(struct ugen_softc *sc, int flag, int endpt)
{
- int endpt = UGENENDPOINT(dev);
- struct ugen_softc *sc;
struct ugen_endpoint *sce;
int dir;
int i;
- int error;
KASSERT(KERNEL_LOCKED_P()); /* sc_is_open */
- if ((sc = ugenif_acquire(UGENUNIT(dev))) == NULL)
- return ENXIO;
-
- DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
- flag, mode, UGENUNIT(dev), endpt));
-
- KASSERT(sc->sc_is_open[endpt]);
+ if (!sc->sc_is_open[endpt]) {
+ KASSERT(sc->sc_endpoints[endpt][IN].pipeh == NULL);
+ KASSERT(sc->sc_endpoints[endpt][OUT].pipeh == NULL);
+ KASSERT(sc->sc_endpoints[endpt][IN].ibuf == NULL);
+ KASSERT(sc->sc_endpoints[endpt][OUT].ibuf == NULL);
+ return;
+ }
if (endpt == USB_CONTROL_ENDPOINT) {
DPRINTFN(5, ("ugenclose: close control\n"));
- sc->sc_is_open[endpt] = 0;
- error = 0;
goto out;
}
@@ -758,11 +753,31 @@ ugenclose(dev_t dev, int flag, int mode, struct lwp *l)
sce->ibuf = NULL;
}
}
- sc->sc_is_open[endpt] = 0;
- error = 0;
-out: ugenif_release(sc);
- return error;
+out: sc->sc_is_open[endpt] = 0;
+}
+
+static int
+ugenclose(dev_t dev, int flag, int mode, struct lwp *l)
+{
+ int endpt = UGENENDPOINT(dev);
+ struct ugen_softc *sc;
+
+ DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n",
+ flag, mode, UGENUNIT(dev), endpt));
+
+ KASSERT(KERNEL_LOCKED_P()); /* ugen_do_close */
+
+ if ((sc = ugenif_acquire(UGENUNIT(dev))) == NULL)
+ return ENXIO;
+
+ KASSERT(sc->sc_is_open[endpt]);
+ ugen_do_close(sc, flag, endpt);
+ KASSERT(!sc->sc_is_open[endpt]);
+
+ ugenif_release(sc);
+
+ return 0;
}
Static int
@@ -1214,10 +1229,17 @@ ugen_detach(device_t self, int flags)
/* locate the major number */
maj = cdevsw_lookup_major(&ugen_cdevsw);
- /* Nuke the vnodes for any open instances (calls close). */
+ /*
+ * Nuke the vnodes for any open instances (calls ugenclose, but
+ * with no effect because we already set sc_dying).
+ */
mn = sc->sc_unit * USB_MAX_ENDPOINTS;
vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR);
+ /* Actually close any lingering pipes. */
+ for (i = 0; i < USB_MAX_ENDPOINTS; i++)
+ ugen_do_close(sc, FREAD|FWRITE, i);
+
usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev);
ugenif_put_unit(sc);