aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-05-23 04:43:13 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-01 15:37:10 -0400
commit303bfdb1a14d0460feb859cd008ff81da36b517c (patch)
treeb83243cb57f069ceb5b3c97efaf22612b777f996
parentea50be59345a2b714fd3ed43e1bba89906c177c3 (diff)
ACPI / scan: Add second pass of companion offlining to hot-remove code
As indicated by comments in mm/memory_hotplug.c:remove_memory(), if CONFIG_MEMCG is set, it may not be possible to offline all of the memory blocks held by one module (FRU) in one pass (because one of them may be used by the others to store page cgroup in that case and that block has to be offlined before the other ones). To handle that arguably corner case, add a second pass of companion device offlining to acpi_scan_hot_remove() and make it ignore errors returned in the first pass (and make it skip the second pass if the first one is successful). Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Toshi Kani <toshi.kani@hp.com>
-rw-r--r--drivers/acpi/scan.c71
1 files changed, 52 insertions, 19 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index ba8ee6cbf0f1..2959fe1ce43e 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -131,6 +131,7 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
131{ 131{
132 struct acpi_device *device = NULL; 132 struct acpi_device *device = NULL;
133 struct acpi_device_physical_node *pn; 133 struct acpi_device_physical_node *pn;
134 bool second_pass = (bool)data;
134 acpi_status status = AE_OK; 135 acpi_status status = AE_OK;
135 136
136 if (acpi_bus_get_device(handle, &device)) 137 if (acpi_bus_get_device(handle, &device))
@@ -141,15 +142,26 @@ static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
141 list_for_each_entry(pn, &device->physical_node_list, node) { 142 list_for_each_entry(pn, &device->physical_node_list, node) {
142 int ret; 143 int ret;
143 144
145 if (second_pass) {
146 /* Skip devices offlined by the first pass. */
147 if (pn->put_online)
148 continue;
149 } else {
150 pn->put_online = false;
151 }
144 ret = device_offline(pn->dev); 152 ret = device_offline(pn->dev);
145 if (acpi_force_hot_remove) 153 if (acpi_force_hot_remove)
146 continue; 154 continue;
147 155
148 if (ret < 0) { 156 if (ret >= 0) {
149 status = AE_ERROR; 157 pn->put_online = !ret;
150 break; 158 } else {
159 *ret_p = pn->dev;
160 if (second_pass) {
161 status = AE_ERROR;
162 break;
163 }
151 } 164 }
152 pn->put_online = !ret;
153 } 165 }
154 166
155 mutex_unlock(&device->physical_node_lock); 167 mutex_unlock(&device->physical_node_lock);
@@ -185,6 +197,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
185 acpi_handle not_used; 197 acpi_handle not_used;
186 struct acpi_object_list arg_list; 198 struct acpi_object_list arg_list;
187 union acpi_object arg; 199 union acpi_object arg;
200 struct device *errdev;
188 acpi_status status; 201 acpi_status status;
189 unsigned long long sta; 202 unsigned long long sta;
190 203
@@ -197,22 +210,42 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
197 210
198 lock_device_hotplug(); 211 lock_device_hotplug();
199 212
200 status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, 213 /*
201 NULL, acpi_bus_offline_companions, NULL, 214 * Carry out two passes here and ignore errors in the first pass,
202 NULL); 215 * because if the devices in question are memory blocks and
203 if (ACPI_SUCCESS(status) || acpi_force_hot_remove) 216 * CONFIG_MEMCG is set, one of the blocks may hold data structures
204 status = acpi_bus_offline_companions(handle, 0, NULL, NULL); 217 * that the other blocks depend on, but it is not known in advance which
205 218 * block holds them.
206 if (ACPI_FAILURE(status) && !acpi_force_hot_remove) { 219 *
207 acpi_bus_online_companions(handle, 0, NULL, NULL); 220 * If the first pass is successful, the second one isn't needed, though.
221 */
222 errdev = NULL;
223 acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
224 NULL, acpi_bus_offline_companions,
225 (void *)false, (void **)&errdev);
226 acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev);
227 if (errdev) {
228 errdev = NULL;
208 acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, 229 acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
209 acpi_bus_online_companions, NULL, NULL, 230 NULL, acpi_bus_offline_companions,
210 NULL); 231 (void *)true , (void **)&errdev);
211 232 if (!errdev || acpi_force_hot_remove)
212 unlock_device_hotplug(); 233 acpi_bus_offline_companions(handle, 0, (void *)true,
213 234 (void **)&errdev);
214 put_device(&device->dev); 235
215 return -EBUSY; 236 if (errdev && !acpi_force_hot_remove) {
237 dev_warn(errdev, "Offline failed.\n");
238 acpi_bus_online_companions(handle, 0, NULL, NULL);
239 acpi_walk_namespace(ACPI_TYPE_ANY, handle,
240 ACPI_UINT32_MAX,
241 acpi_bus_online_companions, NULL,
242 NULL, NULL);
243
244 unlock_device_hotplug();
245
246 put_device(&device->dev);
247 return -EBUSY;
248 }
216 } 249 }
217 250
218 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 251 ACPI_DEBUG_PRINT((ACPI_DB_INFO,