usbfs: send disconnect signals when device is unregistered
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>
This commit is contained in:
parent
78d9a487ee
commit
cd9f03759d
3 changed files with 41 additions and 31 deletions
|
@ -59,6 +59,22 @@
|
|||
/* 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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
Reference in a new issue