usbfs: send disconnect signals when device is unregistered
Alan Stern [Tue, 24 Jun 2008 18:47:04 +0000 (14:47 -0400)]
USB device files are accessible in two ways: as files in usbfs and as
character device nodes.  The two paths are supposed to behave
identically, but they don't.  When the underlying USB device is
unplugged, disconnect signals are sent to processes with open usbfs
files (if they requested these signals) but not to processes with open
device node files.

This patch (as1104) fixes the bug by moving the disconnect-signalling
code into a common subroutine which is called from both paths.
Putting this subroutine in devio.c removes the only out-of-file
reference to struct dev_state, and so the structure's declaration can
be moved from usb.h into devio.c.

Finally, the new subroutine performs one extra action: It kills all
the outstanding async URBs.  (I'd kill the outstanding synchronous
URBs too, if there was any way to do it.)  In the past this hasn't
mattered much, because devices were unregistered from usbfs only
when they were disconnected.  But now the unregistration can also
occur whenever devices are unbound from the usb_generic driver.  At
any rate, killing URBs when a device is unregistered from usbfs seems
like a good thing to do.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

drivers/usb/core/devio.c
drivers/usb/core/inode.c
drivers/usb/core/usb.h

index e09935a..bbd029f 100644 (file)
 /* Mutual exclusion for removal, open, and release */
 DEFINE_MUTEX(usbfs_mutex);
 
+struct dev_state {
+       struct list_head list;      /* state list */
+       struct usb_device *dev;
+       struct file *file;
+       spinlock_t lock;            /* protects the async urb lists */
+       struct list_head async_pending;
+       struct list_head async_completed;
+       wait_queue_head_t wait;     /* wake up if a request completed */
+       unsigned int discsignr;
+       struct pid *disc_pid;
+       uid_t disc_uid, disc_euid;
+       void __user *disccontext;
+       unsigned long ifclaimed;
+       u32 secid;
+};
+
 struct async {
        struct list_head asynclist;
        struct dev_state *ps;
@@ -1680,6 +1696,28 @@ const struct file_operations usbdev_file_operations = {
        .release =      usbdev_release,
 };
 
+void usb_fs_classdev_common_remove(struct usb_device *udev)
+{
+       struct dev_state *ps;
+       struct siginfo sinfo;
+
+       while (!list_empty(&udev->filelist)) {
+               ps = list_entry(udev->filelist.next, struct dev_state, list);
+               destroy_all_async(ps);
+               wake_up_all(&ps->wait);
+               list_del_init(&ps->list);
+               if (ps->discsignr) {
+                       sinfo.si_signo = ps->discsignr;
+                       sinfo.si_errno = EPIPE;
+                       sinfo.si_code = SI_ASYNCIO;
+                       sinfo.si_addr = ps->disccontext;
+                       kill_pid_info_as_uid(ps->discsignr, &sinfo,
+                                       ps->disc_pid, ps->disc_uid,
+                                       ps->disc_euid, ps->secid);
+               }
+       }
+}
+
 #ifdef CONFIG_USB_DEVICE_CLASS
 static struct class *usb_classdev_class;
 
@@ -1699,6 +1737,7 @@ static int usb_classdev_add(struct usb_device *dev)
 static void usb_classdev_remove(struct usb_device *dev)
 {
        device_unregister(dev->usb_classdev);
+       usb_fs_classdev_common_remove(dev);
 }
 
 static int usb_classdev_notify(struct notifier_block *self,
index 1d253dd..db410e9 100644 (file)
@@ -712,25 +712,11 @@ static void usbfs_add_device(struct usb_device *dev)
 
 static void usbfs_remove_device(struct usb_device *dev)
 {
-       struct dev_state *ds;
-       struct siginfo sinfo;
-
        if (dev->usbfs_dentry) {
                fs_remove_file (dev->usbfs_dentry);
                dev->usbfs_dentry = NULL;
        }
-       while (!list_empty(&dev->filelist)) {
-               ds = list_entry(dev->filelist.next, struct dev_state, list);
-               wake_up_all(&ds->wait);
-               list_del_init(&ds->list);
-               if (ds->discsignr) {
-                       sinfo.si_signo = ds->discsignr;
-                       sinfo.si_errno = EPIPE;
-                       sinfo.si_code = SI_ASYNCIO;
-                       sinfo.si_addr = ds->disccontext;
-                       kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid);
-               }
-       }
+       usb_fs_classdev_common_remove(dev);
 }
 
 static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
index d3eb0a2..d9a6e16 100644 (file)
@@ -142,26 +142,11 @@ extern struct usb_driver usbfs_driver;
 extern const struct file_operations usbfs_devices_fops;
 extern const struct file_operations usbdev_file_operations;
 extern void usbfs_conn_disc_event(void);
+extern void usb_fs_classdev_common_remove(struct usb_device *udev);
 
 extern int usb_devio_init(void);
 extern void usb_devio_cleanup(void);
 
-struct dev_state {
-       struct list_head list;      /* state list */
-       struct usb_device *dev;
-       struct file *file;
-       spinlock_t lock;            /* protects the async urb lists */
-       struct list_head async_pending;
-       struct list_head async_completed;
-       wait_queue_head_t wait;     /* wake up if a request completed */
-       unsigned int discsignr;
-       struct pid *disc_pid;
-       uid_t disc_uid, disc_euid;
-       void __user *disccontext;
-       unsigned long ifclaimed;
-       u32 secid;
-};
-
 /* internal notify stuff */
 extern void usb_notify_add_device(struct usb_device *udev);
 extern void usb_notify_remove_device(struct usb_device *udev);