Commit 7f28ddec authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

ACPI / hotplug: Refuse to hot-remove all objects with disabled hotplug

In theory, an ACPI device object may be the parent of another
device object whose hotplug is disabled by user space through its
scan handler.  In that case, the eject operation targeting the
parent should fail as though the parent's own hotplug was disabled,
but currently this is not the case, because acpi_scan_hot_remove()
doesn't check the disable/enable hotplug status of the children
of the top-most object passed to it.

To fix this, modify acpi_bus_offline_companions() to return an
error code if hotplug is disabled for the given device object.
[Also change the name of the function to acpi_bus_offline(),
because it is not only about companions any more, and change
the name of acpi_bus_online_companions() accordingly.]  Make
acpi_scan_hot_remove() propagate that error to its callers.
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: default avatarToshi Kani <toshi.kani@hp.com>
parent 6931007c
...@@ -125,8 +125,8 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha ...@@ -125,8 +125,8 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
} }
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data,
void *data, void **ret_p) void **ret_p)
{ {
struct acpi_device *device = NULL; struct acpi_device *device = NULL;
struct acpi_device_physical_node *pn; struct acpi_device_physical_node *pn;
...@@ -136,6 +136,11 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, ...@@ -136,6 +136,11 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
if (acpi_bus_get_device(handle, &device)) if (acpi_bus_get_device(handle, &device))
return AE_OK; return AE_OK;
if (device->handler && !device->handler->hotplug.enabled) {
*ret_p = &device->dev;
return AE_SUPPORT;
}
mutex_lock(&device->physical_node_lock); mutex_lock(&device->physical_node_lock);
list_for_each_entry(pn, &device->physical_node_list, node) { list_for_each_entry(pn, &device->physical_node_list, node) {
...@@ -168,8 +173,8 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, ...@@ -168,8 +173,8 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
return status; return status;
} }
static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data,
void *data, void **ret_p) void **ret_p)
{ {
struct acpi_device *device = NULL; struct acpi_device *device = NULL;
struct acpi_device_physical_node *pn; struct acpi_device_physical_node *pn;
...@@ -214,26 +219,32 @@ static int acpi_scan_hot_remove(struct acpi_device *device) ...@@ -214,26 +219,32 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
* If the first pass is successful, the second one isn't needed, though. * If the first pass is successful, the second one isn't needed, though.
*/ */
errdev = NULL; errdev = NULL;
status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
NULL, acpi_bus_offline, (void *)false,
(void **)&errdev);
if (status == AE_SUPPORT) {
dev_warn(errdev, "Offline disabled.\n");
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
NULL, acpi_bus_offline_companions, acpi_bus_online, NULL, NULL, NULL);
(void *)false, (void **)&errdev); put_device(&device->dev);
acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev); return -EPERM;
}
acpi_bus_offline(handle, 0, (void *)false, (void **)&errdev);
if (errdev) { if (errdev) {
errdev = NULL; errdev = NULL;
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
NULL, acpi_bus_offline_companions, NULL, acpi_bus_offline, (void *)true,
(void *)true , (void **)&errdev); (void **)&errdev);
if (!errdev || acpi_force_hot_remove) if (!errdev || acpi_force_hot_remove)
acpi_bus_offline_companions(handle, 0, (void *)true, acpi_bus_offline(handle, 0, (void *)true,
(void **)&errdev); (void **)&errdev);
if (errdev && !acpi_force_hot_remove) { if (errdev && !acpi_force_hot_remove) {
dev_warn(errdev, "Offline failed.\n"); dev_warn(errdev, "Offline failed.\n");
acpi_bus_online_companions(handle, 0, NULL, NULL); acpi_bus_online(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_ANY, handle, acpi_walk_namespace(ACPI_TYPE_ANY, handle,
ACPI_UINT32_MAX, ACPI_UINT32_MAX, acpi_bus_online,
acpi_bus_online_companions, NULL, NULL, NULL, NULL);
NULL, NULL);
put_device(&device->dev); put_device(&device->dev);
return -EBUSY; return -EBUSY;
} }
...@@ -290,10 +301,9 @@ static void acpi_bus_device_eject(void *context) ...@@ -290,10 +301,9 @@ static void acpi_bus_device_eject(void *context)
goto err_out; goto err_out;
handler = device->handler; handler = device->handler;
if (!handler || !handler->hotplug.enabled) { if (!handler || !handler->hotplug.enabled)
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED; goto err_support;
goto err_out;
}
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST,
ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); ACPI_OST_SC_EJECT_IN_PROGRESS, NULL);
if (handler->hotplug.mode == AHM_CONTAINER) if (handler->hotplug.mode == AHM_CONTAINER)
...@@ -301,14 +311,19 @@ static void acpi_bus_device_eject(void *context) ...@@ -301,14 +311,19 @@ static void acpi_bus_device_eject(void *context)
get_device(&device->dev); get_device(&device->dev);
error = acpi_scan_hot_remove(device); error = acpi_scan_hot_remove(device);
if (error) if (error == -EPERM) {
goto err_support;
} else if (error) {
goto err_out; goto err_out;
}
out: out:
mutex_unlock(&acpi_scan_lock); mutex_unlock(&acpi_scan_lock);
unlock_device_hotplug(); unlock_device_hotplug();
return; return;
err_support:
ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
err_out: err_out:
acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code, acpi_evaluate_hotplug_ost(handle, ACPI_NOTIFY_EJECT_REQUEST, ost_code,
NULL); NULL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment