diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 06:58:05 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 06:58:05 -0400 |
commit | a204dbc61b7f4cb1a7e2cb3ad057b135164782da (patch) | |
tree | f82151c04a30f49c3dee8926d184575ad2e7b1e2 /drivers/base/core.c | |
parent | 45e00374db944b1c12987b501bcaa279b3e36d93 (diff) | |
parent | 08f502c1c343031f0d126bd00e87dede38269d12 (diff) |
Merge branch 'acpi-hotplug'
* acpi-hotplug:
ACPI: Do not use CONFIG_ACPI_HOTPLUG_MEMORY_MODULE
ACPI / cpufreq: Add ACPI processor device IDs to acpi-cpufreq
Memory hotplug: Move alternative function definitions to header
ACPI / processor: Fix potential NULL pointer dereference in acpi_processor_add()
Memory hotplug / ACPI: Simplify memory removal
ACPI / scan: Add second pass of companion offlining to hot-remove code
Driver core / MM: Drop offline_memory_block()
ACPI / processor: Pass processor object handle to acpi_bind_one()
ACPI: Drop removal_type field from struct acpi_device
Driver core / memory: Simplify __memory_block_change_state()
ACPI / processor: Initialize per_cpu(processors, pr->id) properly
CPU: Fix sysfs cpu/online of offlined CPUs
Driver core: Introduce offline/online callbacks for memory blocks
ACPI / memhotplug: Bind removable memory blocks to ACPI device nodes
ACPI / processor: Use common hotplug infrastructure
ACPI / hotplug: Use device offline/online for graceful hot-removal
Driver core: Use generic offline/online for CPU offline/online
Driver core: Add offline/online device operations
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r-- | drivers/base/core.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 2499cefdcdf2..2166f34b7d84 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, | |||
403 | static struct device_attribute uevent_attr = | 403 | static 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 | ||
406 | static 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 | |||
417 | static 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 | |||
433 | static struct device_attribute online_attr = | ||
434 | __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online); | ||
435 | |||
406 | static int device_add_attributes(struct device *dev, | 436 | static 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) |
@@ -1433,6 +1470,99 @@ EXPORT_SYMBOL_GPL(put_device); | |||
1433 | EXPORT_SYMBOL_GPL(device_create_file); | 1470 | EXPORT_SYMBOL_GPL(device_create_file); |
1434 | EXPORT_SYMBOL_GPL(device_remove_file); | 1471 | EXPORT_SYMBOL_GPL(device_remove_file); |
1435 | 1472 | ||
1473 | static DEFINE_MUTEX(device_hotplug_lock); | ||
1474 | |||
1475 | void lock_device_hotplug(void) | ||
1476 | { | ||
1477 | mutex_lock(&device_hotplug_lock); | ||
1478 | } | ||
1479 | |||
1480 | void unlock_device_hotplug(void) | ||
1481 | { | ||
1482 | mutex_unlock(&device_hotplug_lock); | ||
1483 | } | ||
1484 | |||
1485 | static int device_check_offline(struct device *dev, void *not_used) | ||
1486 | { | ||
1487 | int ret; | ||
1488 | |||
1489 | ret = device_for_each_child(dev, NULL, device_check_offline); | ||
1490 | if (ret) | ||
1491 | return ret; | ||
1492 | |||
1493 | return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0; | ||
1494 | } | ||
1495 | |||
1496 | /** | ||
1497 | * device_offline - Prepare the device for hot-removal. | ||
1498 | * @dev: Device to be put offline. | ||
1499 | * | ||
1500 | * Execute the device bus type's .offline() callback, if present, to prepare | ||
1501 | * the device for a subsequent hot-removal. If that succeeds, the device must | ||
1502 | * not be used until either it is removed or its bus type's .online() callback | ||
1503 | * is executed. | ||
1504 | * | ||
1505 | * Call under device_hotplug_lock. | ||
1506 | */ | ||
1507 | int device_offline(struct device *dev) | ||
1508 | { | ||
1509 | int ret; | ||
1510 | |||
1511 | if (dev->offline_disabled) | ||
1512 | return -EPERM; | ||
1513 | |||
1514 | ret = device_for_each_child(dev, NULL, device_check_offline); | ||
1515 | if (ret) | ||
1516 | return ret; | ||
1517 | |||
1518 | device_lock(dev); | ||
1519 | if (device_supports_offline(dev)) { | ||
1520 | if (dev->offline) { | ||
1521 | ret = 1; | ||
1522 | } else { | ||
1523 | ret = dev->bus->offline(dev); | ||
1524 | if (!ret) { | ||
1525 | kobject_uevent(&dev->kobj, KOBJ_OFFLINE); | ||
1526 | dev->offline = true; | ||
1527 | } | ||
1528 | } | ||
1529 | } | ||
1530 | device_unlock(dev); | ||
1531 | |||
1532 | return ret; | ||
1533 | } | ||
1534 | |||
1535 | /** | ||
1536 | * device_online - Put the device back online after successful device_offline(). | ||
1537 | * @dev: Device to be put back online. | ||
1538 | * | ||
1539 | * If device_offline() has been successfully executed for @dev, but the device | ||
1540 | * has not been removed subsequently, execute its bus type's .online() callback | ||
1541 | * to indicate that the device can be used again. | ||
1542 | * | ||
1543 | * Call under device_hotplug_lock. | ||
1544 | */ | ||
1545 | int device_online(struct device *dev) | ||
1546 | { | ||
1547 | int ret = 0; | ||
1548 | |||
1549 | device_lock(dev); | ||
1550 | if (device_supports_offline(dev)) { | ||
1551 | if (dev->offline) { | ||
1552 | ret = dev->bus->online(dev); | ||
1553 | if (!ret) { | ||
1554 | kobject_uevent(&dev->kobj, KOBJ_ONLINE); | ||
1555 | dev->offline = false; | ||
1556 | } | ||
1557 | } else { | ||
1558 | ret = 1; | ||
1559 | } | ||
1560 | } | ||
1561 | device_unlock(dev); | ||
1562 | |||
1563 | return ret; | ||
1564 | } | ||
1565 | |||
1436 | struct root_device { | 1566 | struct root_device { |
1437 | struct device dev; | 1567 | struct device dev; |
1438 | struct module *owner; | 1568 | struct module *owner; |