aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/acpi_memhotplug.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/acpi_memhotplug.c')
-rw-r--r--drivers/acpi/acpi_memhotplug.c146
1 files changed, 111 insertions, 35 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index e0a95ba72371..1012284ff4f7 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
57 57
58static int acpi_memory_device_add(struct acpi_device *device); 58static int acpi_memory_device_add(struct acpi_device *device);
59static int acpi_memory_device_remove(struct acpi_device *device, int type); 59static int acpi_memory_device_remove(struct acpi_device *device, int type);
60static int acpi_memory_device_start(struct acpi_device *device);
60 61
61static struct acpi_driver acpi_memory_device_driver = { 62static struct acpi_driver acpi_memory_device_driver = {
62 .name = ACPI_MEMORY_DEVICE_DRIVER_NAME, 63 .name = ACPI_MEMORY_DEVICE_DRIVER_NAME,
@@ -65,48 +66,79 @@ 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
73struct 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
71struct acpi_memory_device { 82struct 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
88static acpi_status
89acpi_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
80static int 125static int
81acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) 126acpi_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 ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources"); 131 ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources");
89 132
90 /* Get the range from the _CRS */ 133 status = acpi_walk_resources(mem_device->handle, METHOD_NAME__CRS,
91 status = acpi_get_current_resources(mem_device->handle, &buffer); 134 acpi_memory_get_resource, mem_device);
92 if (ACPI_FAILURE(status)) 135 if (ACPI_FAILURE(status)) {
93 return_VALUE(-EINVAL); 136 list_for_each_entry_safe(info, n, &mem_device->res_list, list)
94 137 kfree(info);
95 resource = (struct acpi_resource *)buffer.pointer; 138 return -EINVAL;
96 status = acpi_resource_to_address64(resource, &address64);
97 if (ACPI_SUCCESS(status)) {
98 if (address64.resource_type == ACPI_MEMORY_RANGE) {
99 /* Populate the structure */
100 mem_device->caching = address64.info.mem.caching;
101 mem_device->write_protect =
102 address64.info.mem.write_protect;
103 mem_device->start_addr = address64.minimum;
104 mem_device->length = address64.address_length;
105 }
106 } 139 }
107 140
108 acpi_os_free(buffer.pointer); 141 return 0;
109 return_VALUE(0);
110} 142}
111 143
112static int 144static int
@@ -181,7 +213,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
181 213
182static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) 214static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
183{ 215{
184 int result; 216 int result, num_enabled = 0;
217 struct acpi_memory_info *info;
218 int node;
185 219
186 ACPI_FUNCTION_TRACE("acpi_memory_enable_device"); 220 ACPI_FUNCTION_TRACE("acpi_memory_enable_device");
187 221
@@ -194,15 +228,35 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
194 return result; 228 return result;
195 } 229 }
196 230
231 node = acpi_get_node(mem_device->handle);
197 /* 232 /*
198 * Tell the VM there is more memory here... 233 * Tell the VM there is more memory here...
199 * Note: Assume that this function returns zero on success 234 * Note: Assume that this function returns zero on success
235 * We don't have memory-hot-add rollback function,now.
236 * (i.e. memory-hot-remove function)
200 */ 237 */
201 result = add_memory(mem_device->start_addr, mem_device->length); 238 list_for_each_entry(info, &mem_device->res_list, list) {
202 if (result) { 239 u64 start_pfn, end_pfn;
240
241 start_pfn = info->start_addr >> PAGE_SHIFT;
242 end_pfn = (info->start_addr + info->length - 1) >> PAGE_SHIFT;
243
244 if (pfn_valid(start_pfn) || pfn_valid(end_pfn)) {
245 /* already enabled. try next area */
246 num_enabled++;
247 continue;
248 }
249
250 result = add_memory(node, info->start_addr, info->length);
251 if (result)
252 continue;
253 info->enabled = 1;
254 num_enabled++;
255 }
256 if (!num_enabled) {
203 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "\nadd_memory failed\n")); 257 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "\nadd_memory failed\n"));
204 mem_device->state = MEMORY_INVALID_STATE; 258 mem_device->state = MEMORY_INVALID_STATE;
205 return result; 259 return -EINVAL;
206 } 260 }
207 261
208 return result; 262 return result;
@@ -246,8 +300,7 @@ static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
246static int acpi_memory_disable_device(struct acpi_memory_device *mem_device) 300static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
247{ 301{
248 int result; 302 int result;
249 u64 start = mem_device->start_addr; 303 struct acpi_memory_info *info, *n;
250 u64 len = mem_device->length;
251 304
252 ACPI_FUNCTION_TRACE("acpi_memory_disable_device"); 305 ACPI_FUNCTION_TRACE("acpi_memory_disable_device");
253 306
@@ -255,10 +308,13 @@ static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
255 * Ask the VM to offline this memory range. 308 * Ask the VM to offline this memory range.
256 * Note: Assume that this function returns zero on success 309 * Note: Assume that this function returns zero on success
257 */ 310 */
258 result = remove_memory(start, len); 311 list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
259 if (result) { 312 if (info->enabled) {
260 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n")); 313 result = remove_memory(info->start_addr, info->length);
261 return_VALUE(result); 314 if (result)
315 return result;
316 }
317 kfree(info);
262 } 318 }
263 319
264 /* Power-off and eject the device */ 320 /* Power-off and eject the device */
@@ -356,6 +412,7 @@ static int acpi_memory_device_add(struct acpi_device *device)
356 return_VALUE(-ENOMEM); 412 return_VALUE(-ENOMEM);
357 memset(mem_device, 0, sizeof(struct acpi_memory_device)); 413 memset(mem_device, 0, sizeof(struct acpi_memory_device));
358 414
415 INIT_LIST_HEAD(&mem_device->res_list);
359 mem_device->handle = device->handle; 416 mem_device->handle = device->handle;
360 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); 417 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
361 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); 418 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
@@ -391,6 +448,25 @@ static int acpi_memory_device_remove(struct acpi_device *device, int type)
391 return_VALUE(0); 448 return_VALUE(0);
392} 449}
393 450
451static int acpi_memory_device_start (struct acpi_device *device)
452{
453 struct acpi_memory_device *mem_device;
454 int result = 0;
455
456 ACPI_FUNCTION_TRACE("acpi_memory_device_start");
457
458 mem_device = acpi_driver_data(device);
459
460 if (!acpi_memory_check_device(mem_device)) {
461 /* call add_memory func */
462 result = acpi_memory_enable_device(mem_device);
463 if (result)
464 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
465 "Error in acpi_memory_enable_device\n"));
466 }
467 return_VALUE(result);
468}
469
394/* 470/*
395 * Helper function to check for memory device 471 * Helper function to check for memory device
396 */ 472 */