svn commit: r190729 - head/sys/dev/usb
Andrew Thompson
thompsa at FreeBSD.org
Sun Apr 5 11:19:50 PDT 2009
Author: thompsa
Date: Sun Apr 5 18:19:42 2009
New Revision: 190729
URL: http://svn.freebsd.org/changeset/base/190729
Log:
MFp4 //depot/projects/usb at 159864
Fix possible deadlock with UGEN at detach.
Submitted by: Hans Petter Selasky
Modified:
head/sys/dev/usb/usb_dev.c
Modified: head/sys/dev/usb/usb_dev.c
==============================================================================
--- head/sys/dev/usb/usb_dev.c Sun Apr 5 18:19:30 2009 (r190728)
+++ head/sys/dev/usb/usb_dev.c Sun Apr 5 18:19:42 2009 (r190729)
@@ -89,7 +89,7 @@ static void usb2_loc_fill(struct usb2_fs
struct usb2_cdev_privdata *);
static void usb2_close(void *);
static usb2_error_t usb2_ref_device(struct usb2_cdev_privdata *, int);
-static usb2_error_t usb2_usb_ref_location(struct usb2_cdev_privdata *);
+static usb2_error_t usb2_usb_ref_device(struct usb2_cdev_privdata *);
static void usb2_unref_device(struct usb2_cdev_privdata *);
static d_open_t usb2_open;
@@ -180,6 +180,22 @@ usb2_ref_device(struct usb2_cdev_privdat
DPRINTFN(2, "no dev ref\n");
goto error;
}
+ if (need_uref) {
+ DPRINTFN(2, "ref udev - needed\n");
+ cpd->udev->refcount++;
+ cpd->is_uref = 1;
+
+ mtx_unlock(&usb2_ref_lock);
+
+ /*
+ * We need to grab the sx-lock before grabbing the
+ * FIFO refs to avoid deadlock at detach!
+ */
+ sx_xlock(cpd->udev->default_sx + 1);
+
+ mtx_lock(&usb2_ref_lock);
+ }
+
/* check if we are doing an open */
if (cpd->fflags == 0) {
/* set defaults */
@@ -240,31 +256,28 @@ usb2_ref_device(struct usb2_cdev_privdat
DPRINTFN(2, "ref read\n");
cpd->rxfifo->refcount++;
}
- if (need_uref) {
- DPRINTFN(2, "ref udev - needed\n");
- cpd->udev->refcount++;
- cpd->is_uref = 1;
- }
mtx_unlock(&usb2_ref_lock);
if (cpd->is_uref) {
- /*
- * We are about to alter the bus-state. Apply the
- * required locks.
- */
- sx_xlock(cpd->udev->default_sx + 1);
mtx_lock(&Giant); /* XXX */
}
return (0);
error:
+ if (cpd->is_uref) {
+ sx_unlock(cpd->udev->default_sx + 1);
+ if (--(cpd->udev->refcount) == 0) {
+ usb2_cv_signal(cpd->udev->default_cv + 1);
+ }
+ cpd->is_uref = 0;
+ }
mtx_unlock(&usb2_ref_lock);
DPRINTFN(2, "fail\n");
return (USB_ERR_INVAL);
}
/*------------------------------------------------------------------------*
- * usb2_usb_ref_location
+ * usb2_usb_ref_device
*
* This function is used to upgrade an USB reference to include the
* USB device reference on a USB location.
@@ -274,46 +287,21 @@ error:
* Else: Failure.
*------------------------------------------------------------------------*/
static usb2_error_t
-usb2_usb_ref_location(struct usb2_cdev_privdata *cpd)
+usb2_usb_ref_device(struct usb2_cdev_privdata *cpd)
{
/*
* Check if we already got an USB reference on this location:
*/
- if (cpd->is_uref) {
+ if (cpd->is_uref)
return (0); /* success */
- }
- mtx_lock(&usb2_ref_lock);
- if (cpd->bus != devclass_get_softc(usb2_devclass_ptr, cpd->bus_index)) {
- DPRINTFN(2, "bus changed at %u\n", cpd->bus_index);
- goto error;
- }
- if (cpd->udev != cpd->bus->devices[cpd->dev_index]) {
- DPRINTFN(2, "device changed at %u\n", cpd->dev_index);
- goto error;
- }
- if (cpd->udev->refcount == USB_DEV_REF_MAX) {
- DPRINTFN(2, "no dev ref\n");
- goto error;
- }
- DPRINTFN(2, "ref udev\n");
- cpd->udev->refcount++;
- mtx_unlock(&usb2_ref_lock);
-
- /* set "uref" */
- cpd->is_uref = 1;
/*
- * We are about to alter the bus-state. Apply the
- * required locks.
+ * To avoid deadlock at detach we need to drop the FIFO ref
+ * and re-acquire a new ref!
*/
- sx_xlock(cpd->udev->default_sx + 1);
- mtx_lock(&Giant); /* XXX */
- return (0);
+ usb2_unref_device(cpd);
-error:
- mtx_unlock(&usb2_ref_lock);
- DPRINTFN(2, "fail\n");
- return (USB_ERR_INVAL);
+ return (usb2_ref_device(cpd, 1 /* need uref */));
}
/*------------------------------------------------------------------------*
@@ -1038,7 +1026,7 @@ usb2_ioctl(struct cdev *dev, u_long cmd,
err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
if (err == ENOIOCTL) {
- if (usb2_usb_ref_location(cpd)) {
+ if (usb2_usb_ref_device(cpd)) {
err = ENXIO;
goto done;
}
More information about the svn-src-head
mailing list