diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2010-01-08 12:56:19 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-02 17:54:08 -0500 |
commit | 62e299e61a6ffe8131fa85a984c3058b68586f5d (patch) | |
tree | d10709c5b5e6d280e1329c7ed4f30f990246893e | |
parent | 0f3dda9f7ff2db8dbf4d6fbab4d4438251446002 (diff) |
USB: change locking for device-level autosuspend
This patch (as1323) changes the locking requirements for
usb_autosuspend_device(), usb_autoresume_device(), and
usb_try_autosuspend_device(). This isn't a very important change;
mainly it's meant to make the locking more uniform.
The most tricky part of the patch involves changes to usbdev_open().
To avoid an ABBA locking problem, it was necessary to reduce the
region protected by usbfs_mutex. Since that mutex now protects only
against simultaneous open and remove, this posed no difficulty -- its
scope was larger than necessary.
And it turns out that usbfs_mutex is no longer needed in
usbdev_release() at all. The list of usbfs "ps" structures is now
protected by the device lock instead of by usbfs_mutex.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/devio.c | 40 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 2 |
3 files changed, 30 insertions, 20 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 431d17287a86..825e0abfed0a 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
@@ -654,19 +654,21 @@ static int usbdev_open(struct inode *inode, struct file *file) | |||
654 | int ret; | 654 | int ret; |
655 | 655 | ||
656 | lock_kernel(); | 656 | lock_kernel(); |
657 | /* Protect against simultaneous removal or release */ | ||
658 | mutex_lock(&usbfs_mutex); | ||
659 | 657 | ||
660 | ret = -ENOMEM; | 658 | ret = -ENOMEM; |
661 | ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL); | 659 | ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL); |
662 | if (!ps) | 660 | if (!ps) |
663 | goto out; | 661 | goto out_free_ps; |
664 | 662 | ||
665 | ret = -ENODEV; | 663 | ret = -ENODEV; |
666 | 664 | ||
665 | /* Protect against simultaneous removal or release */ | ||
666 | mutex_lock(&usbfs_mutex); | ||
667 | |||
667 | /* usbdev device-node */ | 668 | /* usbdev device-node */ |
668 | if (imajor(inode) == USB_DEVICE_MAJOR) | 669 | if (imajor(inode) == USB_DEVICE_MAJOR) |
669 | dev = usbdev_lookup_by_devt(inode->i_rdev); | 670 | dev = usbdev_lookup_by_devt(inode->i_rdev); |
671 | |||
670 | #ifdef CONFIG_USB_DEVICEFS | 672 | #ifdef CONFIG_USB_DEVICEFS |
671 | /* procfs file */ | 673 | /* procfs file */ |
672 | if (!dev) { | 674 | if (!dev) { |
@@ -678,13 +680,19 @@ static int usbdev_open(struct inode *inode, struct file *file) | |||
678 | dev = NULL; | 680 | dev = NULL; |
679 | } | 681 | } |
680 | #endif | 682 | #endif |
681 | if (!dev || dev->state == USB_STATE_NOTATTACHED) | 683 | mutex_unlock(&usbfs_mutex); |
682 | goto out; | 684 | |
685 | if (!dev) | ||
686 | goto out_free_ps; | ||
687 | |||
688 | usb_lock_device(dev); | ||
689 | if (dev->state == USB_STATE_NOTATTACHED) | ||
690 | goto out_unlock_device; | ||
691 | |||
683 | ret = usb_autoresume_device(dev); | 692 | ret = usb_autoresume_device(dev); |
684 | if (ret) | 693 | if (ret) |
685 | goto out; | 694 | goto out_unlock_device; |
686 | 695 | ||
687 | ret = 0; | ||
688 | ps->dev = dev; | 696 | ps->dev = dev; |
689 | ps->file = file; | 697 | ps->file = file; |
690 | spin_lock_init(&ps->lock); | 698 | spin_lock_init(&ps->lock); |
@@ -702,14 +710,17 @@ static int usbdev_open(struct inode *inode, struct file *file) | |||
702 | smp_wmb(); | 710 | smp_wmb(); |
703 | list_add_tail(&ps->list, &dev->filelist); | 711 | list_add_tail(&ps->list, &dev->filelist); |
704 | file->private_data = ps; | 712 | file->private_data = ps; |
713 | usb_unlock_device(dev); | ||
705 | snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current), | 714 | snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current), |
706 | current->comm); | 715 | current->comm); |
707 | out: | 716 | unlock_kernel(); |
708 | if (ret) { | 717 | return ret; |
709 | kfree(ps); | 718 | |
710 | usb_put_dev(dev); | 719 | out_unlock_device: |
711 | } | 720 | usb_unlock_device(dev); |
712 | mutex_unlock(&usbfs_mutex); | 721 | usb_put_dev(dev); |
722 | out_free_ps: | ||
723 | kfree(ps); | ||
713 | unlock_kernel(); | 724 | unlock_kernel(); |
714 | return ret; | 725 | return ret; |
715 | } | 726 | } |
@@ -724,10 +735,7 @@ static int usbdev_release(struct inode *inode, struct file *file) | |||
724 | usb_lock_device(dev); | 735 | usb_lock_device(dev); |
725 | usb_hub_release_all_ports(dev, ps); | 736 | usb_hub_release_all_ports(dev, ps); |
726 | 737 | ||
727 | /* Protect against simultaneous open */ | ||
728 | mutex_lock(&usbfs_mutex); | ||
729 | list_del_init(&ps->list); | 738 | list_del_init(&ps->list); |
730 | mutex_unlock(&usbfs_mutex); | ||
731 | 739 | ||
732 | for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); | 740 | for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); |
733 | ifnum++) { | 741 | ifnum++) { |
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index fcafb2dce3ac..2b39583040d0 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -1478,8 +1478,7 @@ void usb_autoresume_work(struct work_struct *work) | |||
1478 | * driver requires remote-wakeup capability during autosuspend but remote | 1478 | * driver requires remote-wakeup capability during autosuspend but remote |
1479 | * wakeup is disabled, the autosuspend will fail. | 1479 | * wakeup is disabled, the autosuspend will fail. |
1480 | * | 1480 | * |
1481 | * Often the caller will hold @udev's device lock, but this is not | 1481 | * The caller must hold @udev's device lock. |
1482 | * necessary. | ||
1483 | * | 1482 | * |
1484 | * This routine can run only in process context. | 1483 | * This routine can run only in process context. |
1485 | */ | 1484 | */ |
@@ -1503,6 +1502,8 @@ void usb_autosuspend_device(struct usb_device *udev) | |||
1503 | * for an active interface is greater than 0, or autosuspend is not allowed | 1502 | * for an active interface is greater than 0, or autosuspend is not allowed |
1504 | * for any other reason, no autosuspend request will be queued. | 1503 | * for any other reason, no autosuspend request will be queued. |
1505 | * | 1504 | * |
1505 | * The caller must hold @udev's device lock. | ||
1506 | * | ||
1506 | * This routine can run only in process context. | 1507 | * This routine can run only in process context. |
1507 | */ | 1508 | */ |
1508 | void usb_try_autosuspend_device(struct usb_device *udev) | 1509 | void usb_try_autosuspend_device(struct usb_device *udev) |
@@ -1526,8 +1527,7 @@ void usb_try_autosuspend_device(struct usb_device *udev) | |||
1526 | * @udev's usage counter is incremented to prevent subsequent autosuspends. | 1527 | * @udev's usage counter is incremented to prevent subsequent autosuspends. |
1527 | * However if the autoresume fails then the usage counter is re-decremented. | 1528 | * However if the autoresume fails then the usage counter is re-decremented. |
1528 | * | 1529 | * |
1529 | * Often the caller will hold @udev's device lock, but this is not | 1530 | * The caller must hold @udev's device lock. |
1530 | * necessary (and attempting it might cause deadlock). | ||
1531 | * | 1531 | * |
1532 | * This routine can run only in process context. | 1532 | * This routine can run only in process context. |
1533 | */ | 1533 | */ |
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 1b3c00b3ca3f..d8f3bfe1559f 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -352,6 +352,7 @@ set_autosuspend(struct device *dev, struct device_attribute *attr, | |||
352 | return -EINVAL; | 352 | return -EINVAL; |
353 | value *= HZ; | 353 | value *= HZ; |
354 | 354 | ||
355 | usb_lock_device(udev); | ||
355 | udev->autosuspend_delay = value; | 356 | udev->autosuspend_delay = value; |
356 | if (value >= 0) | 357 | if (value >= 0) |
357 | usb_try_autosuspend_device(udev); | 358 | usb_try_autosuspend_device(udev); |
@@ -359,6 +360,7 @@ set_autosuspend(struct device *dev, struct device_attribute *attr, | |||
359 | if (usb_autoresume_device(udev) == 0) | 360 | if (usb_autoresume_device(udev) == 0) |
360 | usb_autosuspend_device(udev); | 361 | usb_autosuspend_device(udev); |
361 | } | 362 | } |
363 | usb_unlock_device(udev); | ||
362 | return count; | 364 | return count; |
363 | } | 365 | } |
364 | 366 | ||