On Tue, Jun 09, 2020 at 10:53:24AM +0200, Martin Pieuchot wrote:
> 
> The audio(4) drivers has an unaccounted reference to uaudio(4)'s softc.
> So when the USB thread responsible for detaching device kicks in to
> clean up the software state of an uaudio(4), it first spins on the
> KERNEL_LOCK().  If any of the threads playing/recording audio sleeps
> while holding an unaccounted reference to uaudio(4)'s softc, the above
> issue can happen.
> 
> A way to fix this is to use usbd_ref_incr(9) and its counterpart
> usbd_ref_wait(9) in uaudio_detach().
> 
> I'm not sure if it's possible for audio(4) to increment the reference
> only once.  Is there a place where such increment/decrement can be put?
> 
> Otherwise every operation should do the dance.

Thanks for the explanation.

- uaudio_{open,close}() could increment/decrement the counter for the
  audio code-paths

- uaudio_{query_devinfo,set_port,get_port} for the controls
  manipulation code-paths. These functions are called directly by the
  kernel, and are not wrapped in uaudio_{open,close} calls (yet).

Resulting diff below

Index: uaudio.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uaudio.c,v
retrieving revision 1.159
diff -u -p -u -p -r1.159 uaudio.c
--- uaudio.c    4 May 2020 19:19:26 -0000       1.159
+++ uaudio.c    9 Jun 2020 12:07:20 -0000
@@ -3854,6 +3854,8 @@ uaudio_detach(struct device *self, int f
 
        rv = config_detach_children(self, flags);
 
+       usbd_ref_wait(sc->udev);
+
        while ((alt = sc->alts) != NULL) {
                sc->alts = alt->next;
                free(alt, M_DEVBUF, sizeof(struct uaudio_alt));
@@ -3892,6 +3894,8 @@ uaudio_open(void *self, int flags)
        if (usbd_is_dying(sc->udev))
                return EIO;
 
+       usbd_ref_incr(sc->udev);
+
        flags &= (FREAD | FWRITE);
 
        for (p = sc->params_list; p != NULL; p = p->next) {
@@ -3914,6 +3918,7 @@ uaudio_open(void *self, int flags)
                }
        }
 
+       usbd_ref_decr(sc->udev);
        return ENXIO;
 }
 
@@ -3923,6 +3928,7 @@ uaudio_close(void *self)
        struct uaudio_softc *sc = self;
 
        sc->mode = 0;
+       usbd_ref_decr(sc->udev);
 }
 
 int
@@ -4183,9 +4189,8 @@ uaudio_get_props(void *self)
 }
 
 int
-uaudio_get_port(void *arg, struct mixer_ctrl *ctl)
+uaudio_get_port_do(struct uaudio_softc *sc, struct mixer_ctrl *ctl)
 {
-       struct uaudio_softc *sc = arg;
        struct uaudio_unit *u;
        struct uaudio_mixent *m;
        unsigned char req_buf[4];
@@ -4253,9 +4258,8 @@ uaudio_get_port(void *arg, struct mixer_
 }
 
 int
-uaudio_set_port(void *arg, struct mixer_ctrl *ctl)
+uaudio_set_port_do(struct uaudio_softc *sc, struct mixer_ctrl *ctl)
 {
-       struct uaudio_softc *sc = arg;
        struct uaudio_unit *u;
        struct uaudio_mixent *m;
        unsigned char req_buf[4];
@@ -4312,9 +4316,8 @@ uaudio_set_port(void *arg, struct mixer_
 }
 
 int
-uaudio_query_devinfo(void *arg, struct mixer_devinfo *devinfo)
+uaudio_query_devinfo_do(struct uaudio_softc *sc, struct mixer_devinfo *devinfo)
 {
-       struct uaudio_softc *sc = arg;
        struct uaudio_unit *u;
        struct uaudio_mixent *m;
 
@@ -4379,4 +4382,40 @@ uaudio_query_devinfo(void *arg, struct m
                break;
        }
        return 0;
+}
+
+int
+uaudio_get_port(void *arg, struct mixer_ctrl *ctl)
+{
+       struct uaudio_softc *sc = arg;
+       int rc;
+
+       usbd_ref_incr(sc->udev);
+       rc = uaudio_get_port_do(sc, ctl);
+       usbd_ref_decr(sc->udev);
+       return rc;
+}
+
+int
+uaudio_set_port(void *arg, struct mixer_ctrl *ctl)
+{
+       struct uaudio_softc *sc = arg;
+       int rc;
+
+       usbd_ref_incr(sc->udev);
+       rc = uaudio_set_port_do(sc, ctl);
+       usbd_ref_decr(sc->udev);
+       return rc;
+}
+
+int
+uaudio_query_devinfo(void *arg, struct mixer_devinfo *devinfo)
+{
+       struct uaudio_softc *sc = arg;
+       int rc;
+
+       usbd_ref_incr(sc->udev);
+       rc = uaudio_query_devinfo_do(sc, devinfo);
+       usbd_ref_decr(sc->udev);
+       return rc;
 }

Reply via email to