diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/container.c | 48 | ||||
-rw-r--r-- | drivers/acpi/internal.h | 1 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 8 |
3 files changed, 50 insertions, 7 deletions
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 83d232c10f13..0b6ae6eb5c4a 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -27,8 +27,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #include <linux/acpi.h> - -#include "internal.h" +#include <linux/container.h> #include "internal.h" @@ -44,16 +43,56 @@ static const struct acpi_device_id container_device_ids[] = { {"", 0}, }; +static int acpi_container_offline(struct container_dev *cdev) +{ + struct acpi_device *adev = ACPI_COMPANION(&cdev->dev); + struct acpi_device *child; + + /* Check all of the dependent devices' physical companions. */ + list_for_each_entry(child, &adev->children, node) + if (!acpi_scan_is_offline(child, false)) + return -EBUSY; + + return 0; +} + +static void acpi_container_release(struct device *dev) +{ + kfree(to_container_dev(dev)); +} + static int container_device_attach(struct acpi_device *adev, const struct acpi_device_id *not_used) { - kobject_uevent(&adev->dev.kobj, KOBJ_ONLINE); + struct container_dev *cdev; + struct device *dev; + int ret; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->offline = acpi_container_offline; + dev = &cdev->dev; + dev->bus = &container_subsys; + dev_set_name(dev, "%s", dev_name(&adev->dev)); + ACPI_COMPANION_SET(dev, adev); + dev->release = acpi_container_release; + ret = device_register(dev); + if (ret) + return ret; + + adev->driver_data = dev; return 1; } static void container_device_detach(struct acpi_device *adev) { - kobject_uevent(&adev->dev.kobj, KOBJ_OFFLINE); + struct device *dev = acpi_driver_data(adev); + + adev->driver_data = NULL; + if (dev) + device_unregister(dev); } static struct acpi_scan_handler container_handler = { @@ -62,6 +101,7 @@ static struct acpi_scan_handler container_handler = { .detach = container_device_detach, .hotplug = { .enabled = true, + .demand_offline = true, }, }; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index b125fdb0b30c..3375129bb5b7 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -73,6 +73,7 @@ static inline void acpi_lpss_init(void) {} #endif bool acpi_queue_hotplug_work(struct work_struct *work); +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent); /* -------------------------------------------------------------------------- Device Node Initialization / Removal diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 65243b9dd868..32b340171d41 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -126,7 +126,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static bool acpi_scan_is_offline(struct acpi_device *adev) +bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent) { struct acpi_device_physical_node *pn; bool offline = true; @@ -135,7 +135,9 @@ static bool acpi_scan_is_offline(struct acpi_device *adev) list_for_each_entry(pn, &adev->physical_node_list, node) if (device_supports_offline(pn->dev) && !pn->dev->offline) { - kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); + if (uevent) + kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); + offline = false; break; } @@ -267,7 +269,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_status status; if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) { - if (!acpi_scan_is_offline(device)) + if (!acpi_scan_is_offline(device, true)) return -EBUSY; } else { int error = acpi_scan_try_to_offline(device); |