aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/core.c
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 /drivers/base/core.c
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>
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c130
1 files changed, 130 insertions, 0 deletions
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;