diff options
Diffstat (limited to 'drivers/acpi/acpi_memhotplug.c')
-rw-r--r-- | drivers/acpi/acpi_memhotplug.c | 42 |
1 files changed, 33 insertions, 9 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index e52ad5d3792d..f7e300769966 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c | |||
@@ -78,6 +78,7 @@ struct acpi_memory_info { | |||
78 | unsigned short caching; /* memory cache attribute */ | 78 | unsigned short caching; /* memory cache attribute */ |
79 | unsigned short write_protect; /* memory read/write attribute */ | 79 | unsigned short write_protect; /* memory read/write attribute */ |
80 | unsigned int enabled:1; | 80 | unsigned int enabled:1; |
81 | unsigned int failed:1; | ||
81 | }; | 82 | }; |
82 | 83 | ||
83 | struct acpi_memory_device { | 84 | struct acpi_memory_device { |
@@ -257,9 +258,23 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
257 | node = memory_add_physaddr_to_nid(info->start_addr); | 258 | node = memory_add_physaddr_to_nid(info->start_addr); |
258 | 259 | ||
259 | result = add_memory(node, info->start_addr, info->length); | 260 | result = add_memory(node, info->start_addr, info->length); |
260 | if (result) | 261 | |
262 | /* | ||
263 | * If the memory block has been used by the kernel, add_memory() | ||
264 | * returns -EEXIST. If add_memory() returns the other error, it | ||
265 | * means that this memory block is not used by the kernel. | ||
266 | */ | ||
267 | if (result && result != -EEXIST) { | ||
268 | info->failed = 1; | ||
261 | continue; | 269 | continue; |
262 | info->enabled = 1; | 270 | } |
271 | |||
272 | if (!result) | ||
273 | info->enabled = 1; | ||
274 | /* | ||
275 | * Add num_enable even if add_memory() returns -EEXIST, so the | ||
276 | * device is bound to this driver. | ||
277 | */ | ||
263 | num_enabled++; | 278 | num_enabled++; |
264 | } | 279 | } |
265 | if (!num_enabled) { | 280 | if (!num_enabled) { |
@@ -280,21 +295,30 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
280 | 295 | ||
281 | static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) | 296 | static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) |
282 | { | 297 | { |
283 | int result; | 298 | int result = 0; |
284 | struct acpi_memory_info *info, *n; | 299 | struct acpi_memory_info *info, *n; |
285 | 300 | ||
286 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { | 301 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { |
287 | if (info->enabled) { | 302 | if (info->failed) |
288 | result = remove_memory(info->start_addr, info->length); | 303 | /* The kernel does not use this memory block */ |
289 | if (result) | 304 | continue; |
290 | return result; | 305 | |
291 | } | 306 | if (!info->enabled) |
307 | /* | ||
308 | * The kernel uses this memory block, but it may be not | ||
309 | * managed by us. | ||
310 | */ | ||
311 | return -EBUSY; | ||
312 | |||
313 | result = remove_memory(info->start_addr, info->length); | ||
314 | if (result) | ||
315 | return result; | ||
292 | 316 | ||
293 | list_del(&info->list); | 317 | list_del(&info->list); |
294 | kfree(info); | 318 | kfree(info); |
295 | } | 319 | } |
296 | 320 | ||
297 | return 0; | 321 | return result; |
298 | } | 322 | } |
299 | 323 | ||
300 | static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) | 324 | static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) |