diff options
author | Andrew Thompson <thompsa@FreeBSD.org> | 2009-04-05 18:19:42 +0000 |
---|---|---|
committer | Andrew Thompson <thompsa@FreeBSD.org> | 2009-04-05 18:19:42 +0000 |
commit | 0ed53d451527feed4ba41b35c1ea4c8ac0f05485 (patch) | |
tree | 7dede267bcca686dcad870e1234889b26b831693 /sys | |
parent | dab47558c8c53f8cf1b3bef37148e7d05b52edf1 (diff) | |
download | src-0ed53d451527feed4ba41b35c1ea4c8ac0f05485.tar.gz src-0ed53d451527feed4ba41b35c1ea4c8ac0f05485.zip |
MFp4 //depot/projects/usb@159864
Fix possible deadlock with UGEN at detach.
Submitted by: Hans Petter Selasky
Notes
Notes:
svn path=/head/; revision=190729
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/usb/usb_dev.c | 76 |
1 files changed, 32 insertions, 44 deletions
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c index 5de51e241b3b..d9d81e0800d0 100644 --- a/sys/dev/usb/usb_dev.c +++ b/sys/dev/usb/usb_dev.c @@ -89,7 +89,7 @@ static void usb2_loc_fill(struct usb2_fs_privdata *, 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_privdata* cpd, int need_uref) 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_privdata* cpd, int need_uref) 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, caddr_t addr, int fflag, struct thread* 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; } |