Blame view
drivers/acpi/acpi_memhotplug.c
10.3 KB
1da177e4c Linux-2.6.12-rc2 |
1 |
/* |
0a3476441 ACPI / scan: Make... |
2 3 4 |
* Copyright (C) 2004, 2013 Intel Corporation * Author: Naveen B S <naveen.b.s@intel.com> * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
1da177e4c Linux-2.6.12-rc2 |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
* * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or * NON INFRINGEMENT. See the GNU General Public License for more * details. * |
1da177e4c Linux-2.6.12-rc2 |
19 |
* ACPI based HotPlug driver that supports Memory Hotplug |
c7060d9e9 trivial: Fix miss... |
20 |
* This driver fields notifications from firmware for memory add |
1da177e4c Linux-2.6.12-rc2 |
21 22 23 |
* and remove operations and alerts the VM of the affected memory * ranges. */ |
ab6c57099 ACPI: Update Memo... |
24 |
#include <linux/acpi.h> |
e2ff39400 ACPI / memhotplug... |
25 |
#include <linux/memory.h> |
0a3476441 ACPI / scan: Make... |
26 27 28 |
#include <linux/memory_hotplug.h> #include "internal.h" |
1da177e4c Linux-2.6.12-rc2 |
29 |
|
1da177e4c Linux-2.6.12-rc2 |
30 31 |
#define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_HID "PNP0C80" |
1da177e4c Linux-2.6.12-rc2 |
32 33 34 |
#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" #define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT |
aa7b2b2e9 ACPI: Don't treat... |
35 36 |
#undef PREFIX #define PREFIX "ACPI:memory_hp:" |
f52fd66d2 ACPI: clean up AC... |
37 |
ACPI_MODULE_NAME("acpi_memhotplug"); |
1da177e4c Linux-2.6.12-rc2 |
38 |
|
cccd42085 ACPI / scan: alwa... |
39 40 41 42 43 44 |
static const struct acpi_device_id memory_device_ids[] = { {ACPI_MEMORY_DEVICE_HID, 0}, {"", 0}, }; #ifdef CONFIG_ACPI_HOTPLUG_MEMORY |
1da177e4c Linux-2.6.12-rc2 |
45 46 47 48 |
/* Memory Device States */ #define MEMORY_INVALID_STATE 0 #define MEMORY_POWER_ON_STATE 1 #define MEMORY_POWER_OFF_STATE 2 |
0a3476441 ACPI / scan: Make... |
49 50 51 |
static int acpi_memory_device_add(struct acpi_device *device, const struct acpi_device_id *not_used); static void acpi_memory_device_remove(struct acpi_device *device); |
1da177e4c Linux-2.6.12-rc2 |
52 |
|
0a3476441 ACPI / scan: Make... |
53 |
static struct acpi_scan_handler memory_device_handler = { |
1ba90e3a8 ACPI: autoload mo... |
54 |
.ids = memory_device_ids, |
0a3476441 ACPI / scan: Make... |
55 56 57 58 59 |
.attach = acpi_memory_device_add, .detach = acpi_memory_device_remove, .hotplug = { .enabled = true, }, |
1da177e4c Linux-2.6.12-rc2 |
60 |
}; |
9ac023989 [PATCH] acpi memo... |
61 62 63 64 65 66 67 68 |
struct acpi_memory_info { struct list_head list; u64 start_addr; /* Memory Range start physical addr */ u64 length; /* Memory Range length */ unsigned short caching; /* memory cache attribute */ unsigned short write_protect; /* memory read/write attribute */ unsigned int enabled:1; }; |
1da177e4c Linux-2.6.12-rc2 |
69 |
struct acpi_memory_device { |
3b74863df ACPI: acpi_memhot... |
70 |
struct acpi_device * device; |
4be44fcd3 [ACPI] Lindent al... |
71 |
unsigned int state; /* State of the memory device */ |
9ac023989 [PATCH] acpi memo... |
72 |
struct list_head res_list; |
1da177e4c Linux-2.6.12-rc2 |
73 |
}; |
9ac023989 [PATCH] acpi memo... |
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
static acpi_status acpi_memory_get_resource(struct acpi_resource *resource, void *context) { struct acpi_memory_device *mem_device = context; struct acpi_resource_address64 address64; struct acpi_memory_info *info, *new; acpi_status status; status = acpi_resource_to_address64(resource, &address64); if (ACPI_FAILURE(status) || (address64.resource_type != ACPI_MEMORY_RANGE)) return AE_OK; list_for_each_entry(info, &mem_device->res_list, list) { /* Can we combine the resource range information? */ if ((info->caching == address64.info.mem.caching) && (info->write_protect == address64.info.mem.write_protect) && |
a45de93eb ACPICA: Resources... |
91 92 |
(info->start_addr + info->length == address64.address.minimum)) { info->length += address64.address.address_length; |
9ac023989 [PATCH] acpi memo... |
93 94 95 96 97 98 99 100 101 102 103 |
return AE_OK; } } new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL); if (!new) return AE_ERROR; INIT_LIST_HEAD(&new->list); new->caching = address64.info.mem.caching; new->write_protect = address64.info.mem.write_protect; |
a45de93eb ACPICA: Resources... |
104 105 |
new->start_addr = address64.address.minimum; new->length = address64.address.address_length; |
9ac023989 [PATCH] acpi memo... |
106 107 108 109 |
list_add_tail(&new->list, &mem_device->res_list); return AE_OK; } |
386e52b95 ACPI / memhotplug... |
110 111 112 113 114 115 116 117 118 |
static void acpi_memory_free_device_resources(struct acpi_memory_device *mem_device) { struct acpi_memory_info *info, *n; list_for_each_entry_safe(info, n, &mem_device->res_list, list) kfree(info); INIT_LIST_HEAD(&mem_device->res_list); } |
1da177e4c Linux-2.6.12-rc2 |
119 120 121 122 |
static int acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) { acpi_status status; |
1da177e4c Linux-2.6.12-rc2 |
123 |
|
5d2870faa [PATCH] memory ho... |
124 125 |
if (!list_empty(&mem_device->res_list)) return 0; |
b86327852 ACPI: acpi_memhot... |
126 |
status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS, |
9ac023989 [PATCH] acpi memo... |
127 128 |
acpi_memory_get_resource, mem_device); if (ACPI_FAILURE(status)) { |
386e52b95 ACPI / memhotplug... |
129 |
acpi_memory_free_device_resources(mem_device); |
d550d98d3 ACPI: delete trac... |
130 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
131 |
} |
d550d98d3 ACPI: delete trac... |
132 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
133 |
} |
4be44fcd3 [ACPI] Lindent al... |
134 |
static int acpi_memory_check_device(struct acpi_memory_device *mem_device) |
1da177e4c Linux-2.6.12-rc2 |
135 |
{ |
27663c585 ACPI: Change acpi... |
136 |
unsigned long long current_status; |
1da177e4c Linux-2.6.12-rc2 |
137 138 |
/* Get device present/absent information from the _STA */ |
16ff816d3 ACPI / memhotplug... |
139 140 141 |
if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, METHOD_NAME__STA, NULL, ¤t_status))) |
d550d98d3 ACPI: delete trac... |
142 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
143 144 145 146 |
/* * Check for device status. Device should be * present/enabled/functioning. */ |
a0bd4ac49 ACPI: Remove dupl... |
147 148 149 |
if (!((current_status & ACPI_STA_DEVICE_PRESENT) && (current_status & ACPI_STA_DEVICE_ENABLED) && (current_status & ACPI_STA_DEVICE_FUNCTIONING))) |
d550d98d3 ACPI: delete trac... |
150 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
151 |
|
d550d98d3 ACPI: delete trac... |
152 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
153 |
} |
e2ff39400 ACPI / memhotplug... |
154 155 156 157 158 159 160 161 162 163 164 165 |
static unsigned long acpi_meminfo_start_pfn(struct acpi_memory_info *info) { return PFN_DOWN(info->start_addr); } static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info) { return PFN_UP(info->start_addr + info->length-1); } static int acpi_bind_memblk(struct memory_block *mem, void *arg) { |
24dee1fc9 ACPI / bind: Pass... |
166 |
return acpi_bind_one(&mem->dev, arg); |
e2ff39400 ACPI / memhotplug... |
167 168 169 |
} static int acpi_bind_memory_blocks(struct acpi_memory_info *info, |
24dee1fc9 ACPI / bind: Pass... |
170 |
struct acpi_device *adev) |
e2ff39400 ACPI / memhotplug... |
171 172 |
{ return walk_memory_range(acpi_meminfo_start_pfn(info), |
24dee1fc9 ACPI / bind: Pass... |
173 |
acpi_meminfo_end_pfn(info), adev, |
e2ff39400 ACPI / memhotplug... |
174 175 176 177 178 179 180 181 |
acpi_bind_memblk); } static int acpi_unbind_memblk(struct memory_block *mem, void *arg) { acpi_unbind_one(&mem->dev); return 0; } |
24dee1fc9 ACPI / bind: Pass... |
182 |
static void acpi_unbind_memory_blocks(struct acpi_memory_info *info) |
e2ff39400 ACPI / memhotplug... |
183 184 185 186 |
{ walk_memory_range(acpi_meminfo_start_pfn(info), acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk); } |
4be44fcd3 [ACPI] Lindent al... |
187 |
static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) |
1da177e4c Linux-2.6.12-rc2 |
188 |
{ |
e2ff39400 ACPI / memhotplug... |
189 |
acpi_handle handle = mem_device->device->handle; |
9ac023989 [PATCH] acpi memo... |
190 191 |
int result, num_enabled = 0; struct acpi_memory_info *info; |
1e3590e2e [PATCH] pgdat all... |
192 |
int node; |
1da177e4c Linux-2.6.12-rc2 |
193 |
|
e2ff39400 ACPI / memhotplug... |
194 |
node = acpi_get_node(handle); |
1da177e4c Linux-2.6.12-rc2 |
195 196 197 |
/* * Tell the VM there is more memory here... * Note: Assume that this function returns zero on success |
9ac023989 [PATCH] acpi memo... |
198 199 |
* We don't have memory-hot-add rollback function,now. * (i.e. memory-hot-remove function) |
1da177e4c Linux-2.6.12-rc2 |
200 |
*/ |
9ac023989 [PATCH] acpi memo... |
201 |
list_for_each_entry(info, &mem_device->res_list, list) { |
fa25d8d6d [PATCH] memory ho... |
202 |
if (info->enabled) { /* just sanity check...*/ |
dd56a8e36 [PATCH] Catch not... |
203 204 205 |
num_enabled++; continue; } |
5d2619fca ACPI: Ingore the ... |
206 207 208 209 210 211 |
/* * If the memory block size is zero, please ignore it. * Don't try to do the following memory hotplug flowchart. */ if (!info->length) continue; |
8c2676a58 [PATCH] hot-add-m... |
212 213 |
if (node < 0) node = memory_add_physaddr_to_nid(info->start_addr); |
bc02af93d [PATCH] pgdat all... |
214 |
result = add_memory(node, info->start_addr, info->length); |
654794725 ACPI / memhotplug... |
215 216 217 218 219 220 |
/* * If the memory block has been used by the kernel, add_memory() * returns -EEXIST. If add_memory() returns the other error, it * means that this memory block is not used by the kernel. */ |
fd4655c25 ACPI / memhotplug... |
221 |
if (result && result != -EEXIST) |
9ac023989 [PATCH] acpi memo... |
222 |
continue; |
654794725 ACPI / memhotplug... |
223 |
|
24dee1fc9 ACPI / bind: Pass... |
224 |
result = acpi_bind_memory_blocks(info, mem_device->device); |
e2ff39400 ACPI / memhotplug... |
225 |
if (result) { |
24dee1fc9 ACPI / bind: Pass... |
226 |
acpi_unbind_memory_blocks(info); |
e2ff39400 ACPI / memhotplug... |
227 228 |
return -ENODEV; } |
bb49d82dd ACPI / memhotplug... |
229 |
info->enabled = 1; |
654794725 ACPI / memhotplug... |
230 231 232 233 |
/* * Add num_enable even if add_memory() returns -EEXIST, so the * device is bound to this driver. */ |
9ac023989 [PATCH] acpi memo... |
234 235 236 |
num_enabled++; } if (!num_enabled) { |
ab6c57099 ACPI: Update Memo... |
237 238 |
dev_err(&mem_device->device->dev, "add_memory failed "); |
1da177e4c Linux-2.6.12-rc2 |
239 |
mem_device->state = MEMORY_INVALID_STATE; |
9ac023989 [PATCH] acpi memo... |
240 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
241 |
} |
5d2619fca ACPI: Ingore the ... |
242 243 244 245 246 247 248 249 250 |
/* * Sometimes the memory device will contain several memory blocks. * When one memory block is hot-added to the system memory, it will * be regarded as a success. * Otherwise if the last memory block can't be hot-added to the system * memory, it will be failure and the memory device can't be bound with * driver. */ return 0; |
1da177e4c Linux-2.6.12-rc2 |
251 |
} |
242831eb1 Memory hotplug / ... |
252 |
static void acpi_memory_remove_memory(struct acpi_memory_device *mem_device) |
1da177e4c Linux-2.6.12-rc2 |
253 |
{ |
e2ff39400 ACPI / memhotplug... |
254 |
acpi_handle handle = mem_device->device->handle; |
9ac023989 [PATCH] acpi memo... |
255 |
struct acpi_memory_info *info, *n; |
242831eb1 Memory hotplug / ... |
256 |
int nid = acpi_get_node(handle); |
60a5a19e7 memory-hotplug: r... |
257 |
|
9ac023989 [PATCH] acpi memo... |
258 |
list_for_each_entry_safe(info, n, &mem_device->res_list, list) { |
654794725 ACPI / memhotplug... |
259 |
if (!info->enabled) |
fd4655c25 ACPI / memhotplug... |
260 |
continue; |
654794725 ACPI / memhotplug... |
261 |
|
1bb25df0f ACPI / mm: use NU... |
262 |
if (nid == NUMA_NO_NODE) |
60a5a19e7 memory-hotplug: r... |
263 |
nid = memory_add_physaddr_to_nid(info->start_addr); |
e2ff39400 ACPI / memhotplug... |
264 |
|
24dee1fc9 ACPI / bind: Pass... |
265 |
acpi_unbind_memory_blocks(info); |
242831eb1 Memory hotplug / ... |
266 |
remove_memory(nid, info->start_addr, info->length); |
19387b27e ACPI / memory-hot... |
267 |
list_del(&info->list); |
9ac023989 [PATCH] acpi memo... |
268 |
kfree(info); |
1da177e4c Linux-2.6.12-rc2 |
269 |
} |
19387b27e ACPI / memory-hot... |
270 |
} |
386e52b95 ACPI / memhotplug... |
271 272 273 274 275 276 |
static void acpi_memory_device_free(struct acpi_memory_device *mem_device) { if (!mem_device) return; acpi_memory_free_device_resources(mem_device); |
0a3476441 ACPI / scan: Make... |
277 |
mem_device->device->driver_data = NULL; |
386e52b95 ACPI / memhotplug... |
278 279 |
kfree(mem_device); } |
0a3476441 ACPI / scan: Make... |
280 281 |
static int acpi_memory_device_add(struct acpi_device *device, const struct acpi_device_id *not_used) |
1da177e4c Linux-2.6.12-rc2 |
282 |
{ |
0a3476441 ACPI / scan: Make... |
283 |
struct acpi_memory_device *mem_device; |
1da177e4c Linux-2.6.12-rc2 |
284 |
int result; |
1da177e4c Linux-2.6.12-rc2 |
285 286 |
if (!device) |
d550d98d3 ACPI: delete trac... |
287 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
288 |
|
36bcbec7c ACPI: replace kma... |
289 |
mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
290 |
if (!mem_device) |
d550d98d3 ACPI: delete trac... |
291 |
return -ENOMEM; |
1da177e4c Linux-2.6.12-rc2 |
292 |
|
9ac023989 [PATCH] acpi memo... |
293 |
INIT_LIST_HEAD(&mem_device->res_list); |
3b74863df ACPI: acpi_memhot... |
294 |
mem_device->device = device; |
1da177e4c Linux-2.6.12-rc2 |
295 296 |
sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); |
db89b4f0d ACPI: catch calls... |
297 |
device->driver_data = mem_device; |
1da177e4c Linux-2.6.12-rc2 |
298 299 300 301 |
/* Get the range from the _CRS */ result = acpi_memory_get_device_resources(mem_device); if (result) { |
d19f503e2 ACPI / memhotplug... |
302 |
device->driver_data = NULL; |
1da177e4c Linux-2.6.12-rc2 |
303 |
kfree(mem_device); |
d550d98d3 ACPI: delete trac... |
304 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
305 306 307 308 |
} /* Set the device state */ mem_device->state = MEMORY_POWER_ON_STATE; |
0a3476441 ACPI / scan: Make... |
309 310 311 312 313 |
result = acpi_memory_check_device(mem_device); if (result) { acpi_memory_device_free(mem_device); return 0; } |
1da177e4c Linux-2.6.12-rc2 |
314 |
|
0a3476441 ACPI / scan: Make... |
315 316 317 318 319 |
result = acpi_memory_enable_device(mem_device); if (result) { dev_err(&device->dev, "acpi_memory_enable_device() error "); acpi_memory_device_free(mem_device); |
e2ff39400 ACPI / memhotplug... |
320 |
return result; |
1f425994f [PATCH] Catch not... |
321 |
} |
0a3476441 ACPI / scan: Make... |
322 323 324 325 |
dev_dbg(&device->dev, "Memory device configured by ACPI "); return 1; |
1f425994f [PATCH] Catch not... |
326 |
} |
0a3476441 ACPI / scan: Make... |
327 |
static void acpi_memory_device_remove(struct acpi_device *device) |
80f20fef6 ACPI: memory hotp... |
328 |
{ |
0a3476441 ACPI / scan: Make... |
329 |
struct acpi_memory_device *mem_device; |
80f20fef6 ACPI: memory hotp... |
330 331 |
if (!device || !acpi_driver_data(device)) |
0a3476441 ACPI / scan: Make... |
332 |
return; |
80f20fef6 ACPI: memory hotp... |
333 334 |
mem_device = acpi_driver_data(device); |
0a3476441 ACPI / scan: Make... |
335 |
acpi_memory_remove_memory(mem_device); |
386e52b95 ACPI / memhotplug... |
336 |
acpi_memory_device_free(mem_device); |
1da177e4c Linux-2.6.12-rc2 |
337 |
} |
00159a201 ACPI / memhotplug... |
338 |
static bool __initdata acpi_no_memhotplug; |
0a3476441 ACPI / scan: Make... |
339 |
void __init acpi_memory_hotplug_init(void) |
1da177e4c Linux-2.6.12-rc2 |
340 |
{ |
cccd42085 ACPI / scan: alwa... |
341 342 343 |
if (acpi_no_memhotplug) { memory_device_handler.attach = NULL; acpi_scan_add_handler(&memory_device_handler); |
00159a201 ACPI / memhotplug... |
344 |
return; |
cccd42085 ACPI / scan: alwa... |
345 |
} |
0a3476441 ACPI / scan: Make... |
346 |
acpi_scan_add_handler_with_hotplug(&memory_device_handler, "memory"); |
1da177e4c Linux-2.6.12-rc2 |
347 |
} |
00159a201 ACPI / memhotplug... |
348 349 350 351 352 353 354 |
static int __init disable_acpi_memory_hotplug(char *str) { acpi_no_memhotplug = true; return 1; } __setup("acpi_no_memhotplug", disable_acpi_memory_hotplug); |
cccd42085 ACPI / scan: alwa... |
355 356 357 358 359 360 361 362 363 364 365 366 367 |
#else static struct acpi_scan_handler memory_device_handler = { .ids = memory_device_ids, }; void __init acpi_memory_hotplug_init(void) { acpi_scan_add_handler(&memory_device_handler); } #endif /* CONFIG_ACPI_HOTPLUG_MEMORY */ |