Source-Changes-HG archive

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

[src/netbsd-9]: src/sys/dev/usb Pull up following revision(s) (requested by r...



details:   https://anonhg.NetBSD.org/src/rev/129a3e6b6d01
branches:  netbsd-9
changeset: 937824:129a3e6b6d01
user:      martin <martin%NetBSD.org@localhost>
date:      Thu Aug 27 14:13:18 2020 +0000

description:
Pull up following revision(s) (requested by riastradh in ticket #1065):

        sys/dev/usb/usbdevices.config: revision 1.41 (patch)
        sys/dev/usb/ugen.c: revision 1.152
        sys/dev/usb/ugen.c: revision 1.153
        sys/dev/usb/ugen.c: revision 1.154
        sys/dev/usb/ugen.c: revision 1.155 (patch)
        sys/dev/usb/ugen.c: revision 1.156
        sys/dev/usb/ugen.c: revision 1.157

Remove UGEN_ASLP microoptimization.
cv_signal already has this microoptimization.
While here, make the lock cover the relevant things we're issuing
cv_signal about -- progress toward real MP-safety.

Hold the lock over access to the data structures it covers.
Still not MPSAFE, but progress.

Convert DIAGNOSTIC prints to KASSERTs.

Share unit numbering for ugen and ugenif.
This way putting ugenif in kernel config actually works to wire it to
the /dev/ugenN.MM device nodes in userland.

Not a fully fleshed out solution to the ugen problem -- there's no
way for a userland driver to kick out a kernel driver and take over,
but this will let us, e.g., use uhidev(4) for Yubikey OTP/U2F/FIDO2
but ugen(4), with pcscd(8), for Yubikey CCID.

Fix various MP-safety issues while here (still not MPSAFE, but more
progress).

Expose Yubikey CCID interface to userland via ugenif.

Fix sloppy mistakes in previous.
1. Give the offset of the rbnode, not some other random members to
   overwrite with garbage.
2. Don't try to unlock a mutex at NULL.
3. Make sure all paths out after ugenif_acquire go via
   ugenif_release.

Fix ugen detach after partial attach.

While here, register null pmf handler even for partially attached
devices so they don't needlessly interfere with suspend.

diffstat:

 sys/dev/usb/ugen.c            |  514 ++++++++++++++++++++++++-----------------
 sys/dev/usb/usbdevices.config |   11 +-
 2 files changed, 314 insertions(+), 211 deletions(-)

diffs (truncated from 1014 to 300 lines):

diff -r 88aba95a8784 -r 129a3e6b6d01 sys/dev/usb/ugen.c
--- a/sys/dev/usb/ugen.c        Thu Aug 27 09:16:52 2020 +0000
+++ b/sys/dev/usb/ugen.c        Thu Aug 27 14:13:18 2020 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ugen.c,v 1.146.2.1 2019/12/11 14:56:36 martin Exp $    */
+/*     $NetBSD: ugen.c,v 1.146.2.2 2020/08/27 14:13:18 martin 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.146.2.1 2019/12/11 14:56:36 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ugen.c,v 1.146.2.2 2020/08/27 14:13:18 martin Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -58,6 +58,8 @@
 #include <sys/vnode.h>
 #include <sys/poll.h>
 #include <sys/compat_stub.h>
+#include <sys/module.h>
+#include <sys/rbtree.h>
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
@@ -97,7 +99,6 @@
        usb_endpoint_descriptor_t *edesc;
        struct usbd_interface *iface;
        int state;
-#define        UGEN_ASLP       0x02    /* waiting for data */
 #define UGEN_SHORT_OK  0x04    /* short xfers are OK */
 #define UGEN_BULK_RA   0x08    /* in bulk read-ahead mode */
 #define UGEN_BULK_WB   0x10    /* in bulk write-behind mode */
@@ -124,6 +125,8 @@
 struct ugen_softc {
        device_t sc_dev;                /* base device */
        struct usbd_device *sc_udev;
+       struct rb_node sc_node;
+       unsigned sc_unit;
 
        kmutex_t                sc_lock;
        kcondvar_t              sc_detach_cv;
@@ -136,8 +139,109 @@
        int sc_refcnt;
        char sc_buffer[UGEN_BBSIZE];
        u_char sc_dying;
+       u_char sc_attached;
 };
 
+static struct {
+       kmutex_t        lock;
+       rb_tree_t       tree;
+} ugenif __cacheline_aligned;
+
+static int
+compare_ugen(void *cookie, const void *vsca, const void *vscb)
+{
+       const struct ugen_softc *sca = vsca;
+       const struct ugen_softc *scb = vscb;
+
+       if (sca->sc_unit < scb->sc_unit)
+               return -1;
+       if (sca->sc_unit > scb->sc_unit)
+               return +1;
+       return 0;
+}
+
+static int
+compare_ugen_key(void *cookie, const void *vsc, const void *vk)
+{
+       const struct ugen_softc *sc = vsc;
+       const unsigned *k = vk;
+
+       if (sc->sc_unit < *k)
+               return -1;
+       if (sc->sc_unit > *k)
+               return +1;
+       return 0;
+}
+
+static const rb_tree_ops_t ugenif_tree_ops = {
+       .rbto_compare_nodes = compare_ugen,
+       .rbto_compare_key = compare_ugen_key,
+       .rbto_node_offset = offsetof(struct ugen_softc, sc_node),
+};
+
+static void
+ugenif_get_unit(struct ugen_softc *sc)
+{
+       struct ugen_softc *sc0;
+       unsigned i;
+
+       mutex_enter(&ugenif.lock);
+       for (i = 0, sc0 = RB_TREE_MIN(&ugenif.tree);
+            sc0 != NULL && i == sc0->sc_unit;
+            i++, sc0 = RB_TREE_NEXT(&ugenif.tree, sc0))
+               KASSERT(i < UINT_MAX);
+       KASSERT(rb_tree_find_node(&ugenif.tree, &i) == NULL);
+       sc->sc_unit = i;
+       sc0 = rb_tree_insert_node(&ugenif.tree, sc);
+       KASSERT(sc0 == sc);
+       KASSERT(rb_tree_find_node(&ugenif.tree, &i) == sc);
+       mutex_exit(&ugenif.lock);
+}
+
+static void
+ugenif_put_unit(struct ugen_softc *sc)
+{
+
+       mutex_enter(&ugenif.lock);
+       KASSERT(rb_tree_find_node(&ugenif.tree, &sc->sc_unit) == sc);
+       rb_tree_remove_node(&ugenif.tree, sc);
+       sc->sc_unit = -1;
+       mutex_exit(&ugenif.lock);
+}
+
+static struct ugen_softc *
+ugenif_acquire(unsigned unit)
+{
+       struct ugen_softc *sc;
+
+       mutex_enter(&ugenif.lock);
+       sc = rb_tree_find_node(&ugenif.tree, &unit);
+       if (sc == NULL)
+               goto out;
+       mutex_enter(&sc->sc_lock);
+       if (sc->sc_dying) {
+               mutex_exit(&sc->sc_lock);
+               sc = NULL;
+               goto out;
+       }
+       KASSERT(sc->sc_refcnt < INT_MAX);
+       sc->sc_refcnt++;
+       mutex_exit(&sc->sc_lock);
+out:   mutex_exit(&ugenif.lock);
+
+       return sc;
+}
+
+static void
+ugenif_release(struct ugen_softc *sc)
+{
+
+       mutex_enter(&sc->sc_lock);
+       if (--sc->sc_refcnt < 0)
+               cv_broadcast(&sc->sc_detach_cv);
+       mutex_exit(&sc->sc_lock);
+}
+
 dev_type_open(ugenopen);
 dev_type_close(ugenclose);
 dev_type_read(ugenread);
@@ -276,6 +380,9 @@
                }
        }
 
+       if (!pmf_device_register(self, NULL, NULL))
+               aprint_error_dev(self, "couldn't establish power handler\n");
+
        if (uiaa->uiaa_ifaceno < 0) {
                /*
                 * If we attach the whole device,
@@ -285,7 +392,6 @@
                if (err) {
                        aprint_error_dev(self,
                            "setting configuration index 0 failed\n");
-                       sc->sc_dying = 1;
                        return;
                }
        }
@@ -298,15 +404,12 @@
        if (err) {
                aprint_error_dev(self, "setting configuration %d failed\n",
                    conf);
-               sc->sc_dying = 1;
                return;
        }
 
+       ugenif_get_unit(sc);
        usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
-
-       if (!pmf_device_register(self, NULL, NULL))
-               aprint_error_dev(self, "couldn't establish power handler\n");
-
+       sc->sc_attached = 1;
 }
 
 Static void
@@ -404,9 +507,9 @@
        usbd_status err;
        struct usbd_xfer *xfer;
        int i, j;
+       int error;
 
-       sc = device_lookup_private(&ugen_cd, unit);
-       if (sc == NULL || sc->sc_dying)
+       if ((sc = ugenif_acquire(unit)) == NULL)
                return ENXIO;
 
        DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n",
@@ -415,18 +518,23 @@
        /* The control endpoint allows multiple opens. */
        if (endpt == USB_CONTROL_ENDPOINT) {
                sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1;
-               return 0;
+               error = 0;
+               goto out;
        }
 
-       if (sc->sc_is_open[endpt])
-               return EBUSY;
+       if (sc->sc_is_open[endpt]) {
+               error = EBUSY;
+               goto out;
+       }
 
        /* Make sure there are pipes for all directions. */
        for (dir = OUT; dir <= IN; dir++) {
                if (flag & (dir == OUT ? FWRITE : FREAD)) {
                        sce = &sc->sc_endpoints[endpt][dir];
-                       if (sce->edesc == NULL)
-                               return ENXIO;
+                       if (sce->edesc == NULL) {
+                               error = ENXIO;
+                               goto out;
+                       }
                }
        }
 
@@ -446,20 +554,25 @@
                        if (dir == OUT) {
                                err = usbd_open_pipe(sce->iface,
                                    edesc->bEndpointAddress, 0, &sce->pipeh);
-                               if (err)
-                                       return EIO;
+                               if (err) {
+                                       error = EIO;
+                                       goto out;
+                               }
                                break;
                        }
                        isize = UGETW(edesc->wMaxPacketSize);
-                       if (isize == 0) /* shouldn't happen */
-                               return EINVAL;
+                       if (isize == 0) {       /* shouldn't happen */
+                               error = EINVAL;
+                               goto out;
+                       }
                        sce->ibuf = kmem_alloc(isize, KM_SLEEP);
                        DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n",
                                     endpt, isize));
                        if (clalloc(&sce->q, UGEN_IBSIZE, 0) == -1) {
                                kmem_free(sce->ibuf, isize);
                                sce->ibuf = NULL;
-                               return ENOMEM;
+                               error = ENOMEM;
+                               goto out;
                        }
                        err = usbd_open_pipe_intr(sce->iface,
                                  edesc->bEndpointAddress,
@@ -470,15 +583,18 @@
                                clfree(&sce->q);
                                kmem_free(sce->ibuf, isize);
                                sce->ibuf = NULL;
-                               return EIO;
+                               error = EIO;
+                               goto out;
                        }
                        DPRINTFN(5, ("ugenopen: interrupt open done\n"));
                        break;
                case UE_BULK:
                        err = usbd_open_pipe(sce->iface,
                                  edesc->bEndpointAddress, 0, &sce->pipeh);
-                       if (err)
-                               return EIO;
+                       if (err) {
+                               error = EIO;
+                               goto out;
+                       }
                        sce->ra_wb_bufsize = UGEN_BULK_RA_WB_BUFSIZE;
                        /*
                         * Use request size for non-RA/WB transfers
@@ -487,11 +603,15 @@
                        sce->ra_wb_reqsize = UGEN_BBSIZE;
                        break;
                case UE_ISOCHRONOUS:
-                       if (dir == OUT)
-                               return EINVAL;
+                       if (dir == OUT) {
+                               error = EINVAL;
+                               goto out;
+                       }
                        isize = UGETW(edesc->wMaxPacketSize);
-                       if (isize == 0) /* shouldn't happen */
-                               return EINVAL;
+                       if (isize == 0) {       /* shouldn't happen */
+                               error = EINVAL;
+                               goto out;



Home | Main Index | Thread Index | Old Index