USB: Remove races in devio.c
[pandora-kernel.git] / drivers / usb / core / devio.c
index 8df4b76..e0f1079 100644 (file)
@@ -333,17 +333,14 @@ static struct async *async_getcompleted(struct dev_state *ps)
 static struct async *async_getpending(struct dev_state *ps,
                                             void __user *userurb)
 {
-       unsigned long flags;
        struct async *as;
 
-       spin_lock_irqsave(&ps->lock, flags);
        list_for_each_entry(as, &ps->async_pending, asynclist)
                if (as->userurb == userurb) {
                        list_del_init(&as->asynclist);
-                       spin_unlock_irqrestore(&ps->lock, flags);
                        return as;
                }
-       spin_unlock_irqrestore(&ps->lock, flags);
+
        return NULL;
 }
 
@@ -398,6 +395,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
 __releases(ps->lock)
 __acquires(ps->lock)
 {
+       struct urb *urb;
        struct async *as;
 
        /* Mark all the pending URBs that match bulk_addr, up to but not
@@ -420,8 +418,11 @@ __acquires(ps->lock)
        list_for_each_entry(as, &ps->async_pending, asynclist) {
                if (as->bulk_status == AS_UNLINK) {
                        as->bulk_status = 0;            /* Only once */
+                       urb = as->urb;
+                       usb_get_urb(urb);
                        spin_unlock(&ps->lock);         /* Allow completions */
-                       usb_unlink_urb(as->urb);
+                       usb_unlink_urb(urb);
+                       usb_put_urb(urb);
                        spin_lock(&ps->lock);
                        goto rescan;
                }
@@ -472,6 +473,7 @@ static void async_completed(struct urb *urb)
 
 static void destroy_async(struct dev_state *ps, struct list_head *list)
 {
+       struct urb *urb;
        struct async *as;
        unsigned long flags;
 
@@ -479,10 +481,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
        while (!list_empty(list)) {
                as = list_entry(list->next, struct async, asynclist);
                list_del_init(&as->asynclist);
+               urb = as->urb;
+               usb_get_urb(urb);
 
                /* drop the spinlock so the completion handler can run */
                spin_unlock_irqrestore(&ps->lock, flags);
-               usb_kill_urb(as->urb);
+               usb_kill_urb(urb);
+               usb_put_urb(urb);
                spin_lock_irqsave(&ps->lock, flags);
        }
        spin_unlock_irqrestore(&ps->lock, flags);
@@ -727,17 +732,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
        if (imajor(inode) == USB_DEVICE_MAJOR)
                dev = usbdev_lookup_by_devt(inode->i_rdev);
 
-#ifdef CONFIG_USB_DEVICEFS
-       /* procfs file */
-       if (!dev) {
-               dev = inode->i_private;
-               if (dev && dev->usbfs_dentry &&
-                                       dev->usbfs_dentry->d_inode == inode)
-                       usb_get_dev(dev);
-               else
-                       dev = NULL;
-       }
-#endif
        mutex_unlock(&usbfs_mutex);
 
        if (!dev)
@@ -1410,12 +1404,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
 
 static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
 {
+       struct urb *urb;
        struct async *as;
+       unsigned long flags;
 
+       spin_lock_irqsave(&ps->lock, flags);
        as = async_getpending(ps, arg);
-       if (!as)
+       if (!as) {
+               spin_unlock_irqrestore(&ps->lock, flags);
                return -EINVAL;
-       usb_kill_urb(as->urb);
+       }
+
+       urb = as->urb;
+       usb_get_urb(urb);
+       spin_unlock_irqrestore(&ps->lock, flags);
+
+       usb_kill_urb(urb);
+       usb_put_urb(urb);
+
        return 0;
 }
 
@@ -2062,44 +2068,13 @@ static void usbdev_remove(struct usb_device *udev)
        }
 }
 
-#ifdef CONFIG_USB_DEVICE_CLASS
-static struct class *usb_classdev_class;
-
-static int usb_classdev_add(struct usb_device *dev)
-{
-       struct device *cldev;
-
-       cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt,
-                             NULL, "usbdev%d.%d", dev->bus->busnum,
-                             dev->devnum);
-       if (IS_ERR(cldev))
-               return PTR_ERR(cldev);
-       dev->usb_classdev = cldev;
-       return 0;
-}
-
-static void usb_classdev_remove(struct usb_device *dev)
-{
-       if (dev->usb_classdev)
-               device_unregister(dev->usb_classdev);
-}
-
-#else
-#define usb_classdev_add(dev)          0
-#define usb_classdev_remove(dev)       do {} while (0)
-
-#endif
-
 static int usbdev_notify(struct notifier_block *self,
                               unsigned long action, void *dev)
 {
        switch (action) {
        case USB_DEVICE_ADD:
-               if (usb_classdev_add(dev))
-                       return NOTIFY_BAD;
                break;
        case USB_DEVICE_REMOVE:
-               usb_classdev_remove(dev);
                usbdev_remove(dev);
                break;
        }
@@ -2129,21 +2104,6 @@ int __init usb_devio_init(void)
                       USB_DEVICE_MAJOR);
                goto error_cdev;
        }
-#ifdef CONFIG_USB_DEVICE_CLASS
-       usb_classdev_class = class_create(THIS_MODULE, "usb_device");
-       if (IS_ERR(usb_classdev_class)) {
-               printk(KERN_ERR "Unable to register usb_device class\n");
-               retval = PTR_ERR(usb_classdev_class);
-               cdev_del(&usb_device_cdev);
-               usb_classdev_class = NULL;
-               goto out;
-       }
-       /* devices of this class shadow the major:minor of their parent
-        * device, so clear ->dev_kobj to prevent adding duplicate entries
-        * to /sys/dev
-        */
-       usb_classdev_class->dev_kobj = NULL;
-#endif
        usb_register_notify(&usbdev_nb);
 out:
        return retval;
@@ -2156,9 +2116,6 @@ error_cdev:
 void usb_devio_cleanup(void)
 {
        usb_unregister_notify(&usbdev_nb);
-#ifdef CONFIG_USB_DEVICE_CLASS
-       class_destroy(usb_classdev_class);
-#endif
        cdev_del(&usb_device_cdev);
        unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
 }