diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-12-29 09:25:35 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-12-29 09:25:35 -0500 |
commit | d22ddcbc4fb7a483d0721eddfda3f0558821d372 (patch) | |
tree | 9f3623f9faac8a7d4e1aac59e51123187fb94925 | |
parent | bfecc2b3e34c6751343bacd317c4dfd1d695142c (diff) |
ACPI / hotplug: Add demand_offline hotplug profile flag
Add a new ACPI hotplug profile flag, demand_offline, such that if
set for the given ACPI device object's scan handler, it will cause
acpi_scan_hot_remove() to check if that device object's physical
companions are offline upfront and fail the hot removal if that
is not the case.
That flag will be useful to overcome a problem with containers on
some system where they can only be hot-removed after some cleanup
operations carried out by user space, which needs to be notified
of the container hot-removal before the kernel attempts to offline
devices in the container. In those cases the current implementation
of acpi_scan_hot_remove() is not sufficient, because it first tries
to offline the devices in the container and only if that is
suffcessful it tries to offline the container itself. As a result,
the container hot-removal notification is not delivered to user space
at the right time.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/acpi/scan.c | 41 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 3 |
2 files changed, 39 insertions, 5 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5383c81a8a1b..65243b9dd868 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -126,6 +126,24 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha | |||
126 | } | 126 | } |
127 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 127 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
128 | 128 | ||
129 | static bool acpi_scan_is_offline(struct acpi_device *adev) | ||
130 | { | ||
131 | struct acpi_device_physical_node *pn; | ||
132 | bool offline = true; | ||
133 | |||
134 | mutex_lock(&adev->physical_node_lock); | ||
135 | |||
136 | list_for_each_entry(pn, &adev->physical_node_list, node) | ||
137 | if (device_supports_offline(pn->dev) && !pn->dev->offline) { | ||
138 | kobject_uevent(&pn->dev->kobj, KOBJ_CHANGE); | ||
139 | offline = false; | ||
140 | break; | ||
141 | } | ||
142 | |||
143 | mutex_unlock(&adev->physical_node_lock); | ||
144 | return offline; | ||
145 | } | ||
146 | |||
129 | static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data, | 147 | static acpi_status acpi_bus_offline(acpi_handle handle, u32 lvl, void *data, |
130 | void **ret_p) | 148 | void **ret_p) |
131 | { | 149 | { |
@@ -196,12 +214,11 @@ static acpi_status acpi_bus_online(acpi_handle handle, u32 lvl, void *data, | |||
196 | return AE_OK; | 214 | return AE_OK; |
197 | } | 215 | } |
198 | 216 | ||
199 | static int acpi_scan_hot_remove(struct acpi_device *device) | 217 | static int acpi_scan_try_to_offline(struct acpi_device *device) |
200 | { | 218 | { |
201 | acpi_handle handle = device->handle; | 219 | acpi_handle handle = device->handle; |
202 | struct device *errdev; | 220 | struct device *errdev = NULL; |
203 | acpi_status status; | 221 | acpi_status status; |
204 | unsigned long long sta; | ||
205 | 222 | ||
206 | /* | 223 | /* |
207 | * Carry out two passes here and ignore errors in the first pass, | 224 | * Carry out two passes here and ignore errors in the first pass, |
@@ -212,7 +229,6 @@ static int acpi_scan_hot_remove(struct acpi_device *device) | |||
212 | * | 229 | * |
213 | * If the first pass is successful, the second one isn't needed, though. | 230 | * If the first pass is successful, the second one isn't needed, though. |
214 | */ | 231 | */ |
215 | errdev = NULL; | ||
216 | status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, | 232 | status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, |
217 | NULL, acpi_bus_offline, (void *)false, | 233 | NULL, acpi_bus_offline, (void *)false, |
218 | (void **)&errdev); | 234 | (void **)&errdev); |
@@ -241,6 +257,23 @@ static int acpi_scan_hot_remove(struct acpi_device *device) | |||
241 | return -EBUSY; | 257 | return -EBUSY; |
242 | } | 258 | } |
243 | } | 259 | } |
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static int acpi_scan_hot_remove(struct acpi_device *device) | ||
264 | { | ||
265 | acpi_handle handle = device->handle; | ||
266 | unsigned long long sta; | ||
267 | acpi_status status; | ||
268 | |||
269 | if (device->handler->hotplug.demand_offline && !acpi_force_hot_remove) { | ||
270 | if (!acpi_scan_is_offline(device)) | ||
271 | return -EBUSY; | ||
272 | } else { | ||
273 | int error = acpi_scan_try_to_offline(device); | ||
274 | if (error) | ||
275 | return error; | ||
276 | } | ||
244 | 277 | ||
245 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 278 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
246 | "Hot-removing device %s...\n", dev_name(&device->dev))); | 279 | "Hot-removing device %s...\n", dev_name(&device->dev))); |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 7135fe3d6daa..48d302501539 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
@@ -91,8 +91,9 @@ struct acpi_device; | |||
91 | 91 | ||
92 | struct acpi_hotplug_profile { | 92 | struct acpi_hotplug_profile { |
93 | struct kobject kobj; | 93 | struct kobject kobj; |
94 | bool enabled:1; | ||
95 | int (*scan_dependent)(struct acpi_device *adev); | 94 | int (*scan_dependent)(struct acpi_device *adev); |
95 | bool enabled:1; | ||
96 | bool demand_offline:1; | ||
96 | }; | 97 | }; |
97 | 98 | ||
98 | static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( | 99 | static inline struct acpi_hotplug_profile *to_acpi_hotplug_profile( |