diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 2 | ||||
-rw-r--r-- | drivers/acpi/acpi_memhotplug.c | 142 | ||||
-rw-r--r-- | drivers/acpi/numa.c | 15 |
3 files changed, 124 insertions, 35 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ce15aed1df3f..bc2652d72fdc 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -335,7 +335,7 @@ config ACPI_CONTAINER | |||
335 | config ACPI_HOTPLUG_MEMORY | 335 | config ACPI_HOTPLUG_MEMORY |
336 | tristate "Memory Hotplug" | 336 | tristate "Memory Hotplug" |
337 | depends on ACPI | 337 | depends on ACPI |
338 | depends on MEMORY_HOTPLUG || X86_64 | 338 | depends on MEMORY_HOTPLUG |
339 | default n | 339 | default n |
340 | help | 340 | help |
341 | This driver adds supports for ACPI Memory Hotplug. This driver | 341 | This driver adds supports for ACPI Memory Hotplug. This driver |
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index b05469513842..6f5e395c78af 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c | |||
@@ -57,6 +57,7 @@ MODULE_LICENSE("GPL"); | |||
57 | 57 | ||
58 | static int acpi_memory_device_add(struct acpi_device *device); | 58 | static int acpi_memory_device_add(struct acpi_device *device); |
59 | static int acpi_memory_device_remove(struct acpi_device *device, int type); | 59 | static int acpi_memory_device_remove(struct acpi_device *device, int type); |
60 | static int acpi_memory_device_start(struct acpi_device *device); | ||
60 | 61 | ||
61 | static struct acpi_driver acpi_memory_device_driver = { | 62 | static struct acpi_driver acpi_memory_device_driver = { |
62 | .name = ACPI_MEMORY_DEVICE_DRIVER_NAME, | 63 | .name = ACPI_MEMORY_DEVICE_DRIVER_NAME, |
@@ -65,46 +66,77 @@ static struct acpi_driver acpi_memory_device_driver = { | |||
65 | .ops = { | 66 | .ops = { |
66 | .add = acpi_memory_device_add, | 67 | .add = acpi_memory_device_add, |
67 | .remove = acpi_memory_device_remove, | 68 | .remove = acpi_memory_device_remove, |
69 | .start = acpi_memory_device_start, | ||
68 | }, | 70 | }, |
69 | }; | 71 | }; |
70 | 72 | ||
73 | struct acpi_memory_info { | ||
74 | struct list_head list; | ||
75 | u64 start_addr; /* Memory Range start physical addr */ | ||
76 | u64 length; /* Memory Range length */ | ||
77 | unsigned short caching; /* memory cache attribute */ | ||
78 | unsigned short write_protect; /* memory read/write attribute */ | ||
79 | unsigned int enabled:1; | ||
80 | }; | ||
81 | |||
71 | struct acpi_memory_device { | 82 | struct acpi_memory_device { |
72 | acpi_handle handle; | 83 | acpi_handle handle; |
73 | unsigned int state; /* State of the memory device */ | 84 | unsigned int state; /* State of the memory device */ |
74 | unsigned short caching; /* memory cache attribute */ | 85 | struct list_head res_list; |
75 | unsigned short write_protect; /* memory read/write attribute */ | ||
76 | u64 start_addr; /* Memory Range start physical addr */ | ||
77 | u64 length; /* Memory Range length */ | ||
78 | }; | 86 | }; |
79 | 87 | ||
88 | static acpi_status | ||
89 | acpi_memory_get_resource(struct acpi_resource *resource, void *context) | ||
90 | { | ||
91 | struct acpi_memory_device *mem_device = context; | ||
92 | struct acpi_resource_address64 address64; | ||
93 | struct acpi_memory_info *info, *new; | ||
94 | acpi_status status; | ||
95 | |||
96 | status = acpi_resource_to_address64(resource, &address64); | ||
97 | if (ACPI_FAILURE(status) || | ||
98 | (address64.resource_type != ACPI_MEMORY_RANGE)) | ||
99 | return AE_OK; | ||
100 | |||
101 | list_for_each_entry(info, &mem_device->res_list, list) { | ||
102 | /* Can we combine the resource range information? */ | ||
103 | if ((info->caching == address64.info.mem.caching) && | ||
104 | (info->write_protect == address64.info.mem.write_protect) && | ||
105 | (info->start_addr + info->length == address64.minimum)) { | ||
106 | info->length += address64.address_length; | ||
107 | return AE_OK; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL); | ||
112 | if (!new) | ||
113 | return AE_ERROR; | ||
114 | |||
115 | INIT_LIST_HEAD(&new->list); | ||
116 | new->caching = address64.info.mem.caching; | ||
117 | new->write_protect = address64.info.mem.write_protect; | ||
118 | new->start_addr = address64.minimum; | ||
119 | new->length = address64.address_length; | ||
120 | list_add_tail(&new->list, &mem_device->res_list); | ||
121 | |||
122 | return AE_OK; | ||
123 | } | ||
124 | |||
80 | static int | 125 | static int |
81 | acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) | 126 | acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) |
82 | { | 127 | { |
83 | acpi_status status; | 128 | acpi_status status; |
84 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 129 | struct acpi_memory_info *info, *n; |
85 | struct acpi_resource *resource = NULL; | ||
86 | struct acpi_resource_address64 address64; | ||
87 | 130 | ||
88 | 131 | ||
89 | /* Get the range from the _CRS */ | 132 | status = acpi_walk_resources(mem_device->handle, METHOD_NAME__CRS, |
90 | status = acpi_get_current_resources(mem_device->handle, &buffer); | 133 | acpi_memory_get_resource, mem_device); |
91 | if (ACPI_FAILURE(status)) | 134 | if (ACPI_FAILURE(status)) { |
135 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) | ||
136 | kfree(info); | ||
92 | return -EINVAL; | 137 | return -EINVAL; |
93 | |||
94 | resource = (struct acpi_resource *)buffer.pointer; | ||
95 | status = acpi_resource_to_address64(resource, &address64); | ||
96 | if (ACPI_SUCCESS(status)) { | ||
97 | if (address64.resource_type == ACPI_MEMORY_RANGE) { | ||
98 | /* Populate the structure */ | ||
99 | mem_device->caching = address64.info.mem.caching; | ||
100 | mem_device->write_protect = | ||
101 | address64.info.mem.write_protect; | ||
102 | mem_device->start_addr = address64.minimum; | ||
103 | mem_device->length = address64.address_length; | ||
104 | } | ||
105 | } | 138 | } |
106 | 139 | ||
107 | acpi_os_free(buffer.pointer); | ||
108 | return 0; | 140 | return 0; |
109 | } | 141 | } |
110 | 142 | ||
@@ -177,7 +209,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device) | |||
177 | 209 | ||
178 | static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | 210 | static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) |
179 | { | 211 | { |
180 | int result; | 212 | int result, num_enabled = 0; |
213 | struct acpi_memory_info *info; | ||
214 | int node; | ||
181 | 215 | ||
182 | 216 | ||
183 | /* Get the range from the _CRS */ | 217 | /* Get the range from the _CRS */ |
@@ -188,15 +222,35 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | |||
188 | return result; | 222 | return result; |
189 | } | 223 | } |
190 | 224 | ||
225 | node = acpi_get_node(mem_device->handle); | ||
191 | /* | 226 | /* |
192 | * Tell the VM there is more memory here... | 227 | * Tell the VM there is more memory here... |
193 | * Note: Assume that this function returns zero on success | 228 | * Note: Assume that this function returns zero on success |
229 | * We don't have memory-hot-add rollback function,now. | ||
230 | * (i.e. memory-hot-remove function) | ||
194 | */ | 231 | */ |
195 | result = add_memory(mem_device->start_addr, mem_device->length); | 232 | list_for_each_entry(info, &mem_device->res_list, list) { |
196 | if (result) { | 233 | u64 start_pfn, end_pfn; |
197 | printk(KERN_ERR PREFIX "add_memory failed\n"); | 234 | |
235 | start_pfn = info->start_addr >> PAGE_SHIFT; | ||
236 | end_pfn = (info->start_addr + info->length - 1) >> PAGE_SHIFT; | ||
237 | |||
238 | if (pfn_valid(start_pfn) || pfn_valid(end_pfn)) { | ||
239 | /* already enabled. try next area */ | ||
240 | num_enabled++; | ||
241 | continue; | ||
242 | } | ||
243 | |||
244 | result = add_memory(node, info->start_addr, info->length); | ||
245 | if (result) | ||
246 | continue; | ||
247 | info->enabled = 1; | ||
248 | num_enabled++; | ||
249 | } | ||
250 | if (!num_enabled) { | ||
251 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "\nadd_memory failed\n")); | ||
198 | mem_device->state = MEMORY_INVALID_STATE; | 252 | mem_device->state = MEMORY_INVALID_STATE; |
199 | return result; | 253 | return -EINVAL; |
200 | } | 254 | } |
201 | 255 | ||
202 | return result; | 256 | return result; |
@@ -239,17 +293,21 @@ static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device) | |||
239 | static int acpi_memory_disable_device(struct acpi_memory_device *mem_device) | 293 | static int acpi_memory_disable_device(struct acpi_memory_device *mem_device) |
240 | { | 294 | { |
241 | int result; | 295 | int result; |
242 | u64 start = mem_device->start_addr; | 296 | struct acpi_memory_info *info, *n; |
243 | u64 len = mem_device->length; | ||
244 | 297 | ||
245 | 298 | ||
246 | /* | 299 | /* |
247 | * Ask the VM to offline this memory range. | 300 | * Ask the VM to offline this memory range. |
248 | * Note: Assume that this function returns zero on success | 301 | * Note: Assume that this function returns zero on success |
249 | */ | 302 | */ |
250 | result = remove_memory(start, len); | 303 | list_for_each_entry_safe(info, n, &mem_device->res_list, list) { |
251 | if (result) | 304 | if (info->enabled) { |
252 | return result; | 305 | result = remove_memory(info->start_addr, info->length); |
306 | if (result) | ||
307 | return result; | ||
308 | } | ||
309 | kfree(info); | ||
310 | } | ||
253 | 311 | ||
254 | /* Power-off and eject the device */ | 312 | /* Power-off and eject the device */ |
255 | result = acpi_memory_powerdown_device(mem_device); | 313 | result = acpi_memory_powerdown_device(mem_device); |
@@ -339,6 +397,7 @@ static int acpi_memory_device_add(struct acpi_device *device) | |||
339 | return -ENOMEM; | 397 | return -ENOMEM; |
340 | memset(mem_device, 0, sizeof(struct acpi_memory_device)); | 398 | memset(mem_device, 0, sizeof(struct acpi_memory_device)); |
341 | 399 | ||
400 | INIT_LIST_HEAD(&mem_device->res_list); | ||
342 | mem_device->handle = device->handle; | 401 | mem_device->handle = device->handle; |
343 | sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); | 402 | sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); |
344 | sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); | 403 | sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); |
@@ -373,6 +432,23 @@ static int acpi_memory_device_remove(struct acpi_device *device, int type) | |||
373 | return 0; | 432 | return 0; |
374 | } | 433 | } |
375 | 434 | ||
435 | static int acpi_memory_device_start (struct acpi_device *device) | ||
436 | { | ||
437 | struct acpi_memory_device *mem_device; | ||
438 | int result = 0; | ||
439 | |||
440 | mem_device = acpi_driver_data(device); | ||
441 | |||
442 | if (!acpi_memory_check_device(mem_device)) { | ||
443 | /* call add_memory func */ | ||
444 | result = acpi_memory_enable_device(mem_device); | ||
445 | if (result) | ||
446 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
447 | "Error in acpi_memory_enable_device\n")); | ||
448 | } | ||
449 | return result; | ||
450 | } | ||
451 | |||
376 | /* | 452 | /* |
377 | * Helper function to check for memory device | 453 | * Helper function to check for memory device |
378 | */ | 454 | */ |
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index e2c1a16078c9..13d6d5bdea26 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c | |||
@@ -254,5 +254,18 @@ int acpi_get_pxm(acpi_handle h) | |||
254 | } while (ACPI_SUCCESS(status)); | 254 | } while (ACPI_SUCCESS(status)); |
255 | return -1; | 255 | return -1; |
256 | } | 256 | } |
257 | |||
258 | EXPORT_SYMBOL(acpi_get_pxm); | 257 | EXPORT_SYMBOL(acpi_get_pxm); |
258 | |||
259 | int acpi_get_node(acpi_handle *handle) | ||
260 | { | ||
261 | int pxm, node = -1; | ||
262 | |||
263 | ACPI_FUNCTION_TRACE("acpi_get_node"); | ||
264 | |||
265 | pxm = acpi_get_pxm(handle); | ||
266 | if (pxm >= 0) | ||
267 | node = acpi_map_pxm_to_node(pxm); | ||
268 | |||
269 | return_VALUE(node); | ||
270 | } | ||
271 | EXPORT_SYMBOL(acpi_get_node); | ||