summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-06-02 17:30:18 +0000
committerthompsa <thompsa@FreeBSD.org>2009-06-02 17:30:18 +0000
commitead8330a16ac3e9ec19555dee82640e70eff25f3 (patch)
treeed1fcaaa80a1fb986410023b46f1facd104438ff /sys/dev
parent31080d34af91f693e27da8d38d04e10b2b65ac54 (diff)
downloadFreeBSD-src-ead8330a16ac3e9ec19555dee82640e70eff25f3.zip
FreeBSD-src-ead8330a16ac3e9ec19555dee82640e70eff25f3.tar.gz
Fix multithread issue where the is_uref variable was not set and cleared
properly in the CDEV private data. Submitted by: Hans Petter Selasky
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/usb_dev.c31
1 files changed, 23 insertions, 8 deletions
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c
index 138fc5c..5768bb2 100644
--- a/sys/dev/usb/usb_dev.c
+++ b/sys/dev/usb/usb_dev.c
@@ -169,21 +169,23 @@ usb2_ref_device(struct usb_cdev_privdata* cpd, int need_uref)
cpd->bus = devclass_get_softc(usb2_devclass_ptr, cpd->bus_index);
if (cpd->bus == NULL) {
DPRINTFN(2, "no bus at %u\n", cpd->bus_index);
+ need_uref = 0;
goto error;
}
cpd->udev = cpd->bus->devices[cpd->dev_index];
if (cpd->udev == NULL) {
DPRINTFN(2, "no device at %u\n", cpd->dev_index);
+ need_uref = 0;
goto error;
}
if (cpd->udev->refcount == USB_DEV_REF_MAX) {
DPRINTFN(2, "no dev ref\n");
+ need_uref = 0;
goto error;
}
if (need_uref) {
DPRINTFN(2, "ref udev - needed\n");
cpd->udev->refcount++;
- cpd->is_uref = 1;
mtx_unlock(&usb2_ref_lock);
@@ -194,6 +196,11 @@ usb2_ref_device(struct usb_cdev_privdata* cpd, int need_uref)
sx_xlock(cpd->udev->default_sx + 1);
mtx_lock(&usb2_ref_lock);
+
+ /*
+ * Set "is_uref" after grabbing the default SX lock
+ */
+ cpd->is_uref = 1;
}
/* check if we are doing an open */
@@ -258,18 +265,18 @@ usb2_ref_device(struct usb_cdev_privdata* cpd, int need_uref)
}
mtx_unlock(&usb2_ref_lock);
- if (cpd->is_uref) {
+ if (need_uref) {
mtx_lock(&Giant); /* XXX */
}
return (0);
error:
- if (cpd->is_uref) {
+ if (need_uref) {
+ cpd->is_uref = 0;
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");
@@ -289,10 +296,14 @@ error:
static usb_error_t
usb2_usb_ref_device(struct usb_cdev_privdata *cpd)
{
+ uint8_t is_uref;
+
+ is_uref = cpd->is_uref && sx_xlocked(cpd->udev->default_sx + 1);
+
/*
* Check if we already got an USB reference on this location:
*/
- if (cpd->is_uref)
+ if (is_uref)
return (0); /* success */
/*
@@ -313,7 +324,12 @@ usb2_usb_ref_device(struct usb_cdev_privdata *cpd)
void
usb2_unref_device(struct usb_cdev_privdata *cpd)
{
- if (cpd->is_uref) {
+ uint8_t is_uref;
+
+ is_uref = cpd->is_uref && sx_xlocked(cpd->udev->default_sx + 1);
+
+ if (is_uref) {
+ cpd->is_uref = 0;
mtx_unlock(&Giant); /* XXX */
sx_unlock(cpd->udev->default_sx + 1);
}
@@ -330,11 +346,10 @@ usb2_unref_device(struct usb_cdev_privdata *cpd)
}
cpd->is_write = 0;
}
- if (cpd->is_uref) {
+ if (is_uref) {
if (--(cpd->udev->refcount) == 0) {
usb2_cv_signal(cpd->udev->default_cv + 1);
}
- cpd->is_uref = 0;
}
mtx_unlock(&usb2_ref_lock);
}
OpenPOWER on IntegriCloud