Эх сурвалжийг харах

Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6:
  USB: prevent autosuspend during hub initialization
  USB: Unusual dev for the "Kyocera / Contax SL300R T*" digital camera.
  USB: usbtmc: Use explicit unsigned type for input buffer instead of char*
  USB: fix crash when URBs are unlinked after the device is gone
Linus Torvalds 16 жил өмнө
parent
commit
52e8e19b01

+ 1 - 1
drivers/usb/class/usbtmc.c

@@ -133,7 +133,7 @@ static int usbtmc_release(struct inode *inode, struct file *file)
 
 
 static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
 static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data)
 {
 {
-	char *buffer;
+	u8 *buffer;
 	struct device *dev;
 	struct device *dev;
 	int rv;
 	int rv;
 	int n;
 	int n;

+ 32 - 3
drivers/usb/core/hcd.c

@@ -106,6 +106,9 @@ static DEFINE_SPINLOCK(hcd_root_hub_lock);
 /* used when updating an endpoint's URB list */
 /* used when updating an endpoint's URB list */
 static DEFINE_SPINLOCK(hcd_urb_list_lock);
 static DEFINE_SPINLOCK(hcd_urb_list_lock);
 
 
+/* used to protect against unlinking URBs after the device is gone */
+static DEFINE_SPINLOCK(hcd_urb_unlink_lock);
+
 /* wait queue for synchronous unlinks */
 /* wait queue for synchronous unlinks */
 DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
 DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
 
 
@@ -1376,10 +1379,25 @@ static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
 int usb_hcd_unlink_urb (struct urb *urb, int status)
 int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
 {
 	struct usb_hcd		*hcd;
 	struct usb_hcd		*hcd;
-	int			retval;
+	int			retval = -EIDRM;
+	unsigned long		flags;
 
 
-	hcd = bus_to_hcd(urb->dev->bus);
-	retval = unlink1(hcd, urb, status);
+	/* Prevent the device and bus from going away while
+	 * the unlink is carried out.  If they are already gone
+	 * then urb->use_count must be 0, since disconnected
+	 * devices can't have any active URBs.
+	 */
+	spin_lock_irqsave(&hcd_urb_unlink_lock, flags);
+	if (atomic_read(&urb->use_count) > 0) {
+		retval = 0;
+		usb_get_dev(urb->dev);
+	}
+	spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags);
+	if (retval == 0) {
+		hcd = bus_to_hcd(urb->dev->bus);
+		retval = unlink1(hcd, urb, status);
+		usb_put_dev(urb->dev);
+	}
 
 
 	if (retval == 0)
 	if (retval == 0)
 		retval = -EINPROGRESS;
 		retval = -EINPROGRESS;
@@ -1528,6 +1546,17 @@ void usb_hcd_disable_endpoint(struct usb_device *udev,
 		hcd->driver->endpoint_disable(hcd, ep);
 		hcd->driver->endpoint_disable(hcd, ep);
 }
 }
 
 
+/* Protect against drivers that try to unlink URBs after the device
+ * is gone, by waiting until all unlinks for @udev are finished.
+ * Since we don't currently track URBs by device, simply wait until
+ * nothing is running in the locked region of usb_hcd_unlink_urb().
+ */
+void usb_hcd_synchronize_unlinks(struct usb_device *udev)
+{
+	spin_lock_irq(&hcd_urb_unlink_lock);
+	spin_unlock_irq(&hcd_urb_unlink_lock);
+}
+
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 /* called in any context */
 /* called in any context */

+ 1 - 0
drivers/usb/core/hcd.h

@@ -232,6 +232,7 @@ extern void usb_hcd_flush_endpoint(struct usb_device *udev,
 		struct usb_host_endpoint *ep);
 		struct usb_host_endpoint *ep);
 extern void usb_hcd_disable_endpoint(struct usb_device *udev,
 extern void usb_hcd_disable_endpoint(struct usb_device *udev,
 		struct usb_host_endpoint *ep);
 		struct usb_host_endpoint *ep);
+extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
 extern int usb_hcd_get_frame_number(struct usb_device *udev);
 extern int usb_hcd_get_frame_number(struct usb_device *udev);
 
 
 extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
 extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,

+ 4 - 0
drivers/usb/core/hub.c

@@ -659,6 +659,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 			PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
 			PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2);
 			schedule_delayed_work(&hub->init_work,
 			schedule_delayed_work(&hub->init_work,
 					msecs_to_jiffies(delay));
 					msecs_to_jiffies(delay));
+
+			/* Suppress autosuspend until init is done */
+			to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
 			return;		/* Continues at init2: below */
 			return;		/* Continues at init2: below */
 		} else {
 		} else {
 			hub_power_on(hub, true);
 			hub_power_on(hub, true);
@@ -1429,6 +1432,7 @@ void usb_disconnect(struct usb_device **pdev)
 	 */
 	 */
 	dev_dbg (&udev->dev, "unregistering device\n");
 	dev_dbg (&udev->dev, "unregistering device\n");
 	usb_disable_device(udev, 0);
 	usb_disable_device(udev, 0);
+	usb_hcd_synchronize_unlinks(udev);
 
 
 	usb_unlock_device(udev);
 	usb_unlock_device(udev);
 
 

+ 22 - 0
drivers/usb/core/urb.c

@@ -474,6 +474,12 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);
  * indicating that the request has been canceled (rather than any other
  * indicating that the request has been canceled (rather than any other
  * code).
  * code).
  *
  *
+ * Drivers should not call this routine or related routines, such as
+ * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect
+ * method has returned.  The disconnect function should synchronize with
+ * a driver's I/O routines to insure that all URB-related activity has
+ * completed before it returns.
+ *
  * This request is always asynchronous.  Success is indicated by
  * This request is always asynchronous.  Success is indicated by
  * returning -EINPROGRESS, at which time the URB will probably not yet
  * returning -EINPROGRESS, at which time the URB will probably not yet
  * have been given back to the device driver.  When it is eventually
  * have been given back to the device driver.  When it is eventually
@@ -550,6 +556,9 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb);
  * This routine may not be used in an interrupt context (such as a bottom
  * This routine may not be used in an interrupt context (such as a bottom
  * half or a completion handler), or when holding a spinlock, or in other
  * half or a completion handler), or when holding a spinlock, or in other
  * situations where the caller can't schedule().
  * situations where the caller can't schedule().
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
  */
  */
 void usb_kill_urb(struct urb *urb)
 void usb_kill_urb(struct urb *urb)
 {
 {
@@ -588,6 +597,9 @@ EXPORT_SYMBOL_GPL(usb_kill_urb);
  * This routine may not be used in an interrupt context (such as a bottom
  * This routine may not be used in an interrupt context (such as a bottom
  * half or a completion handler), or when holding a spinlock, or in other
  * half or a completion handler), or when holding a spinlock, or in other
  * situations where the caller can't schedule().
  * situations where the caller can't schedule().
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
  */
  */
 void usb_poison_urb(struct urb *urb)
 void usb_poison_urb(struct urb *urb)
 {
 {
@@ -622,6 +634,9 @@ EXPORT_SYMBOL_GPL(usb_unpoison_urb);
  *
  *
  * this allows all outstanding URBs to be killed starting
  * this allows all outstanding URBs to be killed starting
  * from the back of the queue
  * from the back of the queue
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
  */
  */
 void usb_kill_anchored_urbs(struct usb_anchor *anchor)
 void usb_kill_anchored_urbs(struct usb_anchor *anchor)
 {
 {
@@ -651,6 +666,9 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
  * this allows all outstanding URBs to be poisoned starting
  * this allows all outstanding URBs to be poisoned starting
  * from the back of the queue. Newly added URBs will also be
  * from the back of the queue. Newly added URBs will also be
  * poisoned
  * poisoned
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
  */
  */
 void usb_poison_anchored_urbs(struct usb_anchor *anchor)
 void usb_poison_anchored_urbs(struct usb_anchor *anchor)
 {
 {
@@ -672,6 +690,7 @@ void usb_poison_anchored_urbs(struct usb_anchor *anchor)
 	spin_unlock_irq(&anchor->lock);
 	spin_unlock_irq(&anchor->lock);
 }
 }
 EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
 EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
+
 /**
 /**
  * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
  * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
  * @anchor: anchor the requests are bound to
  * @anchor: anchor the requests are bound to
@@ -680,6 +699,9 @@ EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
  * from the back of the queue. This function is asynchronous.
  * from the back of the queue. This function is asynchronous.
  * The unlinking is just tiggered. It may happen after this
  * The unlinking is just tiggered. It may happen after this
  * function has returned.
  * function has returned.
+ *
+ * This routine should not be called by a driver after its disconnect
+ * method has returned.
  */
  */
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 {
 {

+ 7 - 0
drivers/usb/storage/unusual_devs.h

@@ -333,6 +333,13 @@ UNUSUAL_DEV(  0x0482, 0x0103, 0x0100, 0x0100,
 		"Finecam S5",
 		"Finecam S5",
 		US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
 		US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY),
 
 
+/* Patch submitted by Jens Taprogge <jens.taprogge@taprogge.org> */
+UNUSUAL_DEV(  0x0482, 0x0107, 0x0100, 0x0100,
+		"Kyocera",
+		"CONTAX SL300R T*",
+		US_SC_DEVICE, US_PR_DEVICE, NULL,
+		US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE),
+
 /* Reported by Paul Stewart <stewart@wetlogic.net>
 /* Reported by Paul Stewart <stewart@wetlogic.net>
  * This entry is needed because the device reports Sub=ff */
  * This entry is needed because the device reports Sub=ff */
 UNUSUAL_DEV(  0x04a4, 0x0004, 0x0001, 0x0001,
 UNUSUAL_DEV(  0x04a4, 0x0004, 0x0001, 0x0001,