aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/core.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-08-28 15:41:01 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-08-29 16:00:53 -0400
commit5e33bc4165f3edd558d9633002465a95230effc1 (patch)
treeefcc4857f4f097667a1f4e1315ffd2140078ac1d /drivers/base/core.c
parentd8dfad3876e4386666b759da3c833d62fb8b2267 (diff)
driver core / ACPI: Avoid device hot remove locking issues
device_hotplug_lock is held around the acpi_bus_trim() call in acpi_scan_hot_remove() which generally removes devices (it removes ACPI device objects at least, but it may also remove "physical" device objects through .detach() callbacks of ACPI scan handlers). Thus, potentially, device sysfs attributes are removed under that lock and to remove those attributes it is necessary to hold the s_active references of their directory entries for writing. On the other hand, the execution of a .show() or .store() callback from a sysfs attribute is carried out with that attribute's s_active reference held for reading. Consequently, if any device sysfs attribute that may be removed from within acpi_scan_hot_remove() through acpi_bus_trim() has a .store() or .show() callback which acquires device_hotplug_lock, the execution of that callback may deadlock with the removal of the attribute. [Unfortunately, the "online" device attribute of CPUs and memory blocks is one of them.] To avoid such deadlocks, make all of the sysfs attribute callbacks that need to lock device hotplug, for example store_online(), use a special function, lock_device_hotplug_sysfs(), to lock device hotplug and return the result of that function immediately if it is not zero. This will cause the s_active reference of the directory entry in question to be released and the syscall to be restarted if device_hotplug_lock cannot be acquired. [show_online() actually doesn't need to lock device hotplug, but it is useful to serialize it with respect to device_offline() and device_online() for the same device (in case user space attempts to run them concurrently) which can be done with the help of device_lock().] Reported-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Reported-and-tested-by: Gu Zheng <guz.fnst@cn.fujitsu.com> Suggested-by: Tejun Heo <tj@kernel.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Toshi Kani <toshi.kani@hp.com>
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c43
1 files changed, 28 insertions, 15 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 8856d74545d9..ac419a15fcd4 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -49,6 +49,28 @@ static struct kobject *dev_kobj;
49struct kobject *sysfs_dev_char_kobj; 49struct kobject *sysfs_dev_char_kobj;
50struct kobject *sysfs_dev_block_kobj; 50struct kobject *sysfs_dev_block_kobj;
51 51
52static DEFINE_MUTEX(device_hotplug_lock);
53
54void lock_device_hotplug(void)
55{
56 mutex_lock(&device_hotplug_lock);
57}
58
59void unlock_device_hotplug(void)
60{
61 mutex_unlock(&device_hotplug_lock);
62}
63
64int lock_device_hotplug_sysfs(void)
65{
66 if (mutex_trylock(&device_hotplug_lock))
67 return 0;
68
69 /* Avoid busy looping (5 ms of sleep should do). */
70 msleep(5);
71 return restart_syscall();
72}
73
52#ifdef CONFIG_BLOCK 74#ifdef CONFIG_BLOCK
53static inline int device_is_not_partition(struct device *dev) 75static inline int device_is_not_partition(struct device *dev)
54{ 76{
@@ -408,9 +430,9 @@ static ssize_t show_online(struct device *dev, struct device_attribute *attr,
408{ 430{
409 bool val; 431 bool val;
410 432
411 lock_device_hotplug(); 433 device_lock(dev);
412 val = !dev->offline; 434 val = !dev->offline;
413 unlock_device_hotplug(); 435 device_unlock(dev);
414 return sprintf(buf, "%u\n", val); 436 return sprintf(buf, "%u\n", val);
415} 437}
416 438
@@ -424,7 +446,10 @@ static ssize_t store_online(struct device *dev, struct device_attribute *attr,
424 if (ret < 0) 446 if (ret < 0)
425 return ret; 447 return ret;
426 448
427 lock_device_hotplug(); 449 ret = lock_device_hotplug_sysfs();
450 if (ret)
451 return ret;
452
428 ret = val ? device_online(dev) : device_offline(dev); 453 ret = val ? device_online(dev) : device_offline(dev);
429 unlock_device_hotplug(); 454 unlock_device_hotplug();
430 return ret < 0 ? ret : count; 455 return ret < 0 ? ret : count;
@@ -1479,18 +1504,6 @@ EXPORT_SYMBOL_GPL(put_device);
1479EXPORT_SYMBOL_GPL(device_create_file); 1504EXPORT_SYMBOL_GPL(device_create_file);
1480EXPORT_SYMBOL_GPL(device_remove_file); 1505EXPORT_SYMBOL_GPL(device_remove_file);
1481 1506
1482static DEFINE_MUTEX(device_hotplug_lock);
1483
1484void lock_device_hotplug(void)
1485{
1486 mutex_lock(&device_hotplug_lock);
1487}
1488
1489void unlock_device_hotplug(void)
1490{
1491 mutex_unlock(&device_hotplug_lock);
1492}
1493
1494static int device_check_offline(struct device *dev, void *not_used) 1507static int device_check_offline(struct device *dev, void *not_used)
1495{ 1508{
1496 int ret; 1509 int ret;