aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-05-02 16:15:29 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-05-12 08:14:09 -0400
commit4f3549d72d1b5c90ecc7e673402f38f4486d22c2 (patch)
treebec88e98010e129f4105cab3ff0e490166e1cbe3
parentdd3652ed1a5328a91352409fe81af3e7d629e44b (diff)
Driver core: Add offline/online device operations
In some cases, graceful hot-removal of devices is not possible, although in principle the devices in question support hotplug. For example, that may happen for the last CPU in the system or for memory modules holding kernel memory. In those cases it is nice to be able to check if the given device can be gracefully hot-removed before triggering a removal procedure that cannot be aborted or reversed. Unfortunately, however, the kernel currently doesn't provide any support for that. To address that deficiency, introduce support for offline and online operations that can be performed on devices, respectively, before a hot-removal and in case when it is necessary (or convenient) to put a device back online after a successful offline (that has not been followed by removal). The idea is that the offline will fail whenever the given device cannot be gracefully removed from the system and it will not be allowed to use the device after a successful offline (until a subsequent online) in analogy with the existing CPU offline/online mechanism. For now, the offline and online operations are introduced at the bus type level, as that should be sufficient for the most urgent use cases (CPUs and memory modules). In the future, however, the approach may be extended to cover some more complicated device offline/online scenarios involving device drivers etc. The lock_device_hotplug() and unlock_device_hotplug() functions are introduced because subsequent patches need to put larger pieces of code under device_hotplug_lock to prevent race conditions between device offline and removal from happening. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Toshi Kani <toshi.kani@hp.com>
-rw-r--r--Documentation/ABI/testing/sysfs-devices-online20
-rw-r--r--drivers/base/core.c130
-rw-r--r--include/linux/device.h21
3 files changed, 171 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-online b/Documentation/ABI/testing/sysfs-devices-online
new file mode 100644
index 000000000000..f990026c0740
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-online
@@ -0,0 +1,20 @@
1What: /sys/devices/.../online
2Date: April 2013
3Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
4Description:
5 The /sys/devices/.../online attribute is only present for
6 devices whose bus types provide .online() and .offline()
7 callbacks. The number read from it (0 or 1) reflects the value
8 of the device's 'offline' field. If that number is 1 and '0'
9 (or 'n', or 'N') is written to this file, the device bus type's
10 .offline() callback is executed for the device and (if
11 successful) its 'offline' field is updated accordingly. In
12 turn, if that number is 0 and '1' (or 'y', or 'Y') is written to
13 this file, the device bus type's .online() callback is executed
14 for the device and (if successful) its 'offline' field is
15 updated as appropriate.
16
17 After a successful execution of the bus type's .offline()
18 callback the device cannot be used for any purpose until either
19 it is removed (i.e. device_del() is called for it), or its bus
20 type's .online() is exeucted successfully.
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 016312437577..60c975686089 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -403,6 +403,36 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
403static struct device_attribute uevent_attr = 403static struct device_attribute uevent_attr =
404 __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); 404 __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);
405 405
406static ssize_t show_online(struct device *dev, struct device_attribute *attr,
407 char *buf)
408{
409 bool val;
410
411 lock_device_hotplug();
412 val = !dev->offline;
413 unlock_device_hotplug();
414 return sprintf(buf, "%u\n", val);
415}
416
417static ssize_t store_online(struct device *dev, struct device_attribute *attr,
418 const char *buf, size_t count)
419{
420 bool val;
421 int ret;
422
423 ret = strtobool(buf, &val);
424 if (ret < 0)
425 return ret;
426
427 lock_device_hotplug();
428 ret = val ? device_online(dev) : device_offline(dev);
429 unlock_device_hotplug();
430 return ret < 0 ? ret : count;
431}
432
433static struct device_attribute online_attr =
434 __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online);
435
406static int device_add_attributes(struct device *dev, 436static int device_add_attributes(struct device *dev,
407 struct device_attribute *attrs) 437 struct device_attribute *attrs)
408{ 438{
@@ -516,6 +546,12 @@ static int device_add_attrs(struct device *dev)
516 if (error) 546 if (error)
517 goto err_remove_type_groups; 547 goto err_remove_type_groups;
518 548
549 if (device_supports_offline(dev) && !dev->offline_disabled) {
550 error = device_create_file(dev, &online_attr);
551 if (error)
552 goto err_remove_type_groups;
553 }
554
519 return 0; 555 return 0;
520 556
521 err_remove_type_groups: 557 err_remove_type_groups:
@@ -536,6 +572,7 @@ static void device_remove_attrs(struct device *dev)
536 struct class *class = dev->class; 572 struct class *class = dev->class;
537 const struct device_type *type = dev->type; 573 const struct device_type *type = dev->type;
538 574
575 device_remove_file(dev, &online_attr);
539 device_remove_groups(dev, dev->groups); 576 device_remove_groups(dev, dev->groups);
540 577
541 if (type) 578 if (type)
@@ -1431,6 +1468,99 @@ EXPORT_SYMBOL_GPL(put_device);
1431EXPORT_SYMBOL_GPL(device_create_file); 1468EXPORT_SYMBOL_GPL(device_create_file);
1432EXPORT_SYMBOL_GPL(device_remove_file); 1469EXPORT_SYMBOL_GPL(device_remove_file);
1433 1470
1471static DEFINE_MUTEX(device_hotplug_lock);
1472
1473void lock_device_hotplug(void)
1474{
1475 mutex_lock(&device_hotplug_lock);
1476}
1477
1478void unlock_device_hotplug(void)
1479{
1480 mutex_unlock(&device_hotplug_lock);
1481}
1482
1483static int device_check_offline(struct device *dev, void *not_used)
1484{
1485 int ret;
1486
1487 ret = device_for_each_child(dev, NULL, device_check_offline);
1488 if (ret)
1489 return ret;
1490
1491 return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0;
1492}
1493
1494/**
1495 * device_offline - Prepare the device for hot-removal.
1496 * @dev: Device to be put offline.
1497 *
1498 * Execute the device bus type's .offline() callback, if present, to prepare
1499 * the device for a subsequent hot-removal. If that succeeds, the device must
1500 * not be used until either it is removed or its bus type's .online() callback
1501 * is executed.
1502 *
1503 * Call under device_hotplug_lock.
1504 */
1505int device_offline(struct device *dev)
1506{
1507 int ret;
1508
1509 if (dev->offline_disabled)
1510 return -EPERM;
1511
1512 ret = device_for_each_child(dev, NULL, device_check_offline);
1513 if (ret)
1514 return ret;
1515
1516 device_lock(dev);
1517 if (device_supports_offline(dev)) {
1518 if (dev->offline) {
1519 ret = 1;
1520 } else {
1521 ret = dev->bus->offline(dev);
1522 if (!ret) {
1523 kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
1524 dev->offline = true;
1525 }
1526 }
1527 }
1528 device_unlock(dev);
1529
1530 return ret;
1531}
1532
1533/**
1534 * device_online - Put the device back online after successful device_offline().
1535 * @dev: Device to be put back online.
1536 *
1537 * If device_offline() has been successfully executed for @dev, but the device
1538 * has not been removed subsequently, execute its bus type's .online() callback
1539 * to indicate that the device can be used again.
1540 *
1541 * Call under device_hotplug_lock.
1542 */
1543int device_online(struct device *dev)
1544{
1545 int ret = 0;
1546
1547 device_lock(dev);
1548 if (device_supports_offline(dev)) {
1549 if (dev->offline) {
1550 ret = dev->bus->online(dev);
1551 if (!ret) {
1552 kobject_uevent(&dev->kobj, KOBJ_ONLINE);
1553 dev->offline = false;
1554 }
1555 } else {
1556 ret = 1;
1557 }
1558 }
1559 device_unlock(dev);
1560
1561 return ret;
1562}
1563
1434struct root_device { 1564struct root_device {
1435 struct device dev; 1565 struct device dev;
1436 struct module *owner; 1566 struct module *owner;
diff --git a/include/linux/device.h b/include/linux/device.h
index c0a126125325..eeb33315514c 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -71,6 +71,10 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
71 * the specific driver's probe to initial the matched device. 71 * the specific driver's probe to initial the matched device.
72 * @remove: Called when a device removed from this bus. 72 * @remove: Called when a device removed from this bus.
73 * @shutdown: Called at shut-down time to quiesce the device. 73 * @shutdown: Called at shut-down time to quiesce the device.
74 *
75 * @online: Called to put the device back online (after offlining it).
76 * @offline: Called to put the device offline for hot-removal. May fail.
77 *
74 * @suspend: Called when a device on this bus wants to go to sleep mode. 78 * @suspend: Called when a device on this bus wants to go to sleep mode.
75 * @resume: Called to bring a device on this bus out of sleep mode. 79 * @resume: Called to bring a device on this bus out of sleep mode.
76 * @pm: Power management operations of this bus, callback the specific 80 * @pm: Power management operations of this bus, callback the specific
@@ -104,6 +108,9 @@ struct bus_type {
104 int (*remove)(struct device *dev); 108 int (*remove)(struct device *dev);
105 void (*shutdown)(struct device *dev); 109 void (*shutdown)(struct device *dev);
106 110
111 int (*online)(struct device *dev);
112 int (*offline)(struct device *dev);
113
107 int (*suspend)(struct device *dev, pm_message_t state); 114 int (*suspend)(struct device *dev, pm_message_t state);
108 int (*resume)(struct device *dev); 115 int (*resume)(struct device *dev);
109 116
@@ -648,6 +655,8 @@ struct acpi_dev_node {
648 * @release: Callback to free the device after all references have 655 * @release: Callback to free the device after all references have
649 * gone away. This should be set by the allocator of the 656 * gone away. This should be set by the allocator of the
650 * device (i.e. the bus driver that discovered the device). 657 * device (i.e. the bus driver that discovered the device).
658 * @offline_disabled: If set, the device is permanently online.
659 * @offline: Set after successful invocation of bus type's .offline().
651 * 660 *
652 * At the lowest level, every device in a Linux system is represented by an 661 * At the lowest level, every device in a Linux system is represented by an
653 * instance of struct device. The device structure contains the information 662 * instance of struct device. The device structure contains the information
@@ -720,6 +729,9 @@ struct device {
720 729
721 void (*release)(struct device *dev); 730 void (*release)(struct device *dev);
722 struct iommu_group *iommu_group; 731 struct iommu_group *iommu_group;
732
733 bool offline_disabled:1;
734 bool offline:1;
723}; 735};
724 736
725static inline struct device *kobj_to_dev(struct kobject *kobj) 737static inline struct device *kobj_to_dev(struct kobject *kobj)
@@ -856,6 +868,15 @@ extern const char *device_get_devnode(struct device *dev,
856extern void *dev_get_drvdata(const struct device *dev); 868extern void *dev_get_drvdata(const struct device *dev);
857extern int dev_set_drvdata(struct device *dev, void *data); 869extern int dev_set_drvdata(struct device *dev, void *data);
858 870
871static inline bool device_supports_offline(struct device *dev)
872{
873 return dev->bus && dev->bus->offline && dev->bus->online;
874}
875
876extern void lock_device_hotplug(void);
877extern void unlock_device_hotplug(void);
878extern int device_offline(struct device *dev);
879extern int device_online(struct device *dev);
859/* 880/*
860 * Root device objects for grouping under /sys/devices 881 * Root device objects for grouping under /sys/devices
861 */ 882 */