Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/usb usb(4): Tighten interface locking and pipe refer...



details:   https://anonhg.NetBSD.org/src/rev/cf679e8b884a
branches:  trunk
changeset: 379633:cf679e8b884a
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sun Jun 13 00:13:24 2021 +0000

description:
usb(4): Tighten interface locking and pipe references.

- Just use a reference count, not a list of pipes.

- Take the reference in usbd_open_pipe*, before we even look up the
  endpoint by address; the endpoint is not stable until we hold the
  interface and prevent usbd_set_interface.

- Make opening pipes just fail if usbd_set_interface is in progress.
  => No need to block -- might block for a while, and this is
     essentially a driver error rather than a legitimate reason to
     block.
  => This should maybe be a kassert, but it's not clear that ugen(4)
     doesn't have a user-triggerable path to that kassert, so let's
     keep it as a graceful failure for now until someone can audit
     ugen(4) and make an informed decision.

- No need for a separate interface pipe lock; just use the bus lock.

This is a little bit longer than before, but makes the bracketed
nature of the references a little clearer and introduces more
kasserts to detect mistakes with internal API usage.

diffstat:

 sys/dev/usb/usb_subr.c |  146 ++++++++++++++++++++++++++++++++++++++++--------
 sys/dev/usb/usbdi.c    |   65 +++++++++++++++------
 sys/dev/usb/usbdivar.h |   11 ++-
 3 files changed, 174 insertions(+), 48 deletions(-)

diffs (truncated from 415 to 300 lines):

diff -r fe6e3b043b7b -r cf679e8b884a sys/dev/usb/usb_subr.c
--- a/sys/dev/usb/usb_subr.c    Sun Jun 13 00:11:57 2021 +0000
+++ b/sys/dev/usb/usb_subr.c    Sun Jun 13 00:13:24 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: usb_subr.c,v 1.260 2021/06/12 15:49:45 riastradh Exp $ */
+/*     $NetBSD: usb_subr.c,v 1.261 2021/06/13 00:13:24 riastradh Exp $ */
 /*     $FreeBSD: src/sys/dev/usb/usb_subr.c,v 1.18 1999/11/17 22:33:47 n_hibma Exp $   */
 
 /*
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: usb_subr.c,v 1.260 2021/06/12 15:49:45 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: usb_subr.c,v 1.261 2021/06/13 00:13:24 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -415,8 +415,7 @@ usbd_iface_init(struct usbd_device *dev,
        ifc->ui_index = 0;
        ifc->ui_altindex = 0;
        ifc->ui_endpoints = NULL;
-       LIST_INIT(&ifc->ui_pipes);
-       mutex_init(&ifc->ui_pipelock, MUTEX_DEFAULT, IPL_NONE);
+       ifc->ui_busy = 0;
 }
 
 static void
@@ -429,9 +428,100 @@ usbd_iface_fini(struct usbd_device *dev,
        KASSERT(ifc->ui_index == 0);
        KASSERT(ifc->ui_altindex == 0);
        KASSERT(ifc->ui_endpoints == NULL);
-       KASSERT(LIST_EMPTY(&ifc->ui_pipes));
+       KASSERTMSG(ifc->ui_busy == 0, "%"PRId64, ifc->ui_busy);
+}
+
+/*
+ * usbd_iface_lock/locked/unlock, usbd_iface_piperef/pipeunref
+ *
+ *     We lock the interface while we are setting it, and we acquire a
+ *     reference to the interface for each pipe opened on it.
+ *
+ *     Setting the interface while pipes are open is forbidden, and
+ *     opening pipes while the interface is being set is forbidden.
+ */
+
+bool
+usbd_iface_locked(struct usbd_interface *iface)
+{
+       bool locked;
+
+       mutex_enter(iface->ui_dev->ud_bus->ub_lock);
+       locked = (iface->ui_busy == -1);
+       mutex_exit(iface->ui_dev->ud_bus->ub_lock);
+
+       return locked;
+}
+
+static void
+usbd_iface_exlock(struct usbd_interface *iface)
+{
+
+       mutex_enter(iface->ui_dev->ud_bus->ub_lock);
+       KASSERTMSG(iface->ui_busy == 0, "interface is not idle,"
+           " busy=%"PRId64, iface->ui_busy);
+       iface->ui_busy = -1;
+       mutex_exit(iface->ui_dev->ud_bus->ub_lock);
+}
+
+usbd_status
+usbd_iface_lock(struct usbd_interface *iface)
+{
+       usbd_status err;
 
-       mutex_destroy(&ifc->ui_pipelock);
+       mutex_enter(iface->ui_dev->ud_bus->ub_lock);
+       KASSERTMSG(iface->ui_busy != -1, "interface is locked");
+       KASSERTMSG(iface->ui_busy >= 0, "%"PRId64, iface->ui_busy);
+       if (iface->ui_busy) {
+               err = USBD_IN_USE;
+       } else {
+               iface->ui_busy = -1;
+               err = 0;
+       }
+       mutex_exit(iface->ui_dev->ud_bus->ub_lock);
+
+       return err;
+}
+
+void
+usbd_iface_unlock(struct usbd_interface *iface)
+{
+
+       mutex_enter(iface->ui_dev->ud_bus->ub_lock);
+       KASSERTMSG(iface->ui_busy == -1, "interface is not locked,"
+           " busy=%"PRId64, iface->ui_busy);
+       iface->ui_busy = 0;
+       mutex_exit(iface->ui_dev->ud_bus->ub_lock);
+}
+
+usbd_status
+usbd_iface_piperef(struct usbd_interface *iface)
+{
+       usbd_status err;
+
+       mutex_enter(iface->ui_dev->ud_bus->ub_lock);
+       KASSERTMSG(iface->ui_busy >= -1, "%"PRId64, iface->ui_busy);
+       if (iface->ui_busy == -1) {
+               err = USBD_IN_USE;
+       } else {
+               iface->ui_busy++;
+               err = 0;
+       }
+       mutex_exit(iface->ui_dev->ud_bus->ub_lock);
+
+       return err;
+}
+
+void
+usbd_iface_pipeunref(struct usbd_interface *iface)
+{
+
+       mutex_enter(iface->ui_dev->ud_bus->ub_lock);
+       KASSERTMSG(iface->ui_busy != -1, "interface is locked");
+       KASSERTMSG(iface->ui_busy != 0, "interface not in use");
+       KASSERTMSG(iface->ui_busy >= 1, "%"PRId64, iface->ui_busy);
+       iface->ui_busy--;
+       mutex_exit(iface->ui_dev->ud_bus->ub_lock);
 }
 
 usbd_status
@@ -447,7 +537,7 @@ usbd_fill_iface_data(struct usbd_device 
        int endpt, nendpt;
 
        KASSERT(ifc->ui_dev == dev);
-       KASSERT(LIST_EMPTY(&ifc->ui_pipes));
+       KASSERT(usbd_iface_locked(ifc));
 
        idesc = usbd_find_idesc(dev->ud_cdesc, ifaceidx, altidx);
        if (idesc == NULL)
@@ -547,7 +637,7 @@ usbd_free_iface_data(struct usbd_device 
 
        KASSERT(ifc->ui_dev == dev);
        KASSERT(ifc->ui_idesc != NULL);
-       KASSERT(LIST_EMPTY(&ifc->ui_pipes));
+       KASSERT(usbd_iface_locked(ifc));
 
        if (ifc->ui_endpoints) {
                int nendpt = ifc->ui_idesc->bNumEndpoints;
@@ -608,7 +698,9 @@ usbd_set_config_index(struct usbd_device
                /* Free all configuration data structures. */
                nifc = dev->ud_cdesc->bNumInterface;
                for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+                       usbd_iface_exlock(&dev->ud_ifaces[ifcidx]);
                        usbd_free_iface_data(dev, ifcidx);
+                       usbd_iface_unlock(&dev->ud_ifaces[ifcidx]);
                        usbd_iface_fini(dev, ifcidx);
                }
                kmem_free(dev->ud_ifaces, nifc * sizeof(struct usbd_interface));
@@ -783,10 +875,14 @@ usbd_set_config_index(struct usbd_device
        dev->ud_config = cdp->bConfigurationValue;
        for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
                usbd_iface_init(dev, ifcidx);
+               usbd_iface_exlock(&dev->ud_ifaces[ifcidx]);
                err = usbd_fill_iface_data(dev, ifcidx, 0);
+               usbd_iface_unlock(&dev->ud_ifaces[ifcidx]);
                if (err) {
                        while (--ifcidx >= 0) {
+                               usbd_iface_exlock(&dev->ud_ifaces[ifcidx]);
                                usbd_free_iface_data(dev, ifcidx);
+                               usbd_iface_unlock(&dev->ud_ifaces[ifcidx]);
                                usbd_iface_fini(dev, ifcidx);
                        }
                        kmem_free(dev->ud_ifaces,
@@ -827,12 +923,15 @@ usbd_setup_pipe_flags(struct usbd_device
        USBHIST_FUNC();
        USBHIST_CALLARGS(usbdebug, "dev=%#jx addr=%jd iface=%#jx ep=%#jx",
            (uintptr_t)dev, dev->ud_addr, (uintptr_t)iface, (uintptr_t)ep);
-       struct usbd_pipe *p;
+       struct usbd_pipe *p = NULL;
+       bool ep_acquired = false;
        usbd_status err;
 
+       /* Block exclusive use of the endpoint by later pipes.  */
        err = usbd_endpoint_acquire(dev, ep, flags & USBD_EXCLUSIVE_USE);
        if (err)
-               return err;
+               goto out;
+       ep_acquired = true;
 
        p = kmem_alloc(dev->ud_bus->ub_pipesize, KM_SLEEP);
        DPRINTFN(1, "pipe=%#jx", (uintptr_t)p, 0, 0, 0);
@@ -848,24 +947,11 @@ usbd_setup_pipe_flags(struct usbd_device
        p->up_flags = flags;
        SIMPLEQ_INIT(&p->up_queue);
 
-       if (iface) {
-               mutex_enter(&iface->ui_pipelock);
-               LIST_INSERT_HEAD(&iface->ui_pipes, p, up_next);
-               mutex_exit(&iface->ui_pipelock);
-       }
-
        err = dev->ud_bus->ub_methods->ubm_open(p);
        if (err) {
                DPRINTF("endpoint=%#jx failed, error=%jd",
                    (uintptr_t)ep->ue_edesc->bEndpointAddress, err, 0, 0);
-               if (iface) {
-                       mutex_enter(&iface->ui_pipelock);
-                       LIST_REMOVE(p, up_next);
-                       mutex_exit(&iface->ui_pipelock);
-               }
-               kmem_free(p, dev->ud_bus->ub_pipesize);
-               usbd_endpoint_release(dev, ep);
-               return err;
+               goto out;
        }
 
        KASSERT(p->up_methods->upm_start || p->up_serialise == false);
@@ -874,7 +960,15 @@ usbd_setup_pipe_flags(struct usbd_device
            USB_TASKQ_MPSAFE);
        DPRINTFN(1, "pipe=%#jx", (uintptr_t)p, 0, 0, 0);
        *pipe = p;
-       return USBD_NORMAL_COMPLETION;
+       p = NULL;               /* handed off to caller */
+       ep_acquired = false;    /* handed off to pipe */
+       err = USBD_NORMAL_COMPLETION;
+
+out:   if (p)
+               kmem_free(p, dev->ud_bus->ub_pipesize);
+       if (ep_acquired)
+               usbd_endpoint_release(dev, ep);
+       return err;
 }
 
 usbd_status
@@ -1712,7 +1806,9 @@ usb_free_device(struct usbd_device *dev)
        if (dev->ud_ifaces != NULL) {
                nifc = dev->ud_cdesc->bNumInterface;
                for (ifcidx = 0; ifcidx < nifc; ifcidx++) {
+                       usbd_iface_exlock(&dev->ud_ifaces[ifcidx]);
                        usbd_free_iface_data(dev, ifcidx);
+                       usbd_iface_unlock(&dev->ud_ifaces[ifcidx]);
                        usbd_iface_fini(dev, ifcidx);
                }
                kmem_free(dev->ud_ifaces,
diff -r fe6e3b043b7b -r cf679e8b884a sys/dev/usb/usbdi.c
--- a/sys/dev/usb/usbdi.c       Sun Jun 13 00:11:57 2021 +0000
+++ b/sys/dev/usb/usbdi.c       Sun Jun 13 00:13:24 2021 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: usbdi.c,v 1.215 2021/06/12 15:49:45 riastradh Exp $    */
+/*     $NetBSD: usbdi.c,v 1.216 2021/06/13 00:13:24 riastradh Exp $    */
 
 /*
  * Copyright (c) 1998, 2012, 2015 The NetBSD Foundation, Inc.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: usbdi.c,v 1.215 2021/06/12 15:49:45 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: usbdi.c,v 1.216 2021/06/13 00:13:24 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_usb.h"
@@ -225,6 +225,7 @@ usbd_open_pipe_ival(struct usbd_interfac
 {
        struct usbd_pipe *p;
        struct usbd_endpoint *ep;
+       bool piperef = false;
        usbd_status err;
        int i;
 
@@ -232,22 +233,49 @@ usbd_open_pipe_ival(struct usbd_interfac
        USBHIST_CALLARGS(usbdebug, "iface = %#jx address = %#jx flags = %#jx",
            (uintptr_t)iface, address, flags, 0);
 
+       /*
+        * Block usbd_set_interface so we have a snapshot of the
+        * interface endpoints.  They will remain stable until we drop
+        * the reference in usbd_close_pipe (or on failure here).
+        */
+       err = usbd_iface_piperef(iface);
+       if (err)
+               goto out;
+       piperef = true;
+
+       /* Find the endpoint at this address.  */
        for (i = 0; i < iface->ui_idesc->bNumEndpoints; i++) {
                ep = &iface->ui_endpoints[i];
-               if (ep->ue_edesc == NULL)
-                       return USBD_IOERROR;
+               if (ep->ue_edesc == NULL) {
+                       err = USBD_IOERROR;
+                       goto out;
+               }
                if (ep->ue_edesc->bEndpointAddress == address)
-                       goto found;
+                       break;
        }
-       return USBD_BAD_ADDRESS;
- found:



Home | Main Index | Thread Index | Old Index