aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/acpi_memhotplug.c112
1 files changed, 77 insertions, 35 deletions
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index e0a95ba72371..1486e03bb41a 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -68,45 +68,75 @@ static struct acpi_driver acpi_memory_device_driver = {
68 }, 68 },
69}; 69};
70 70
71struct acpi_memory_info {
72 struct list_head list;
73 u64 start_addr; /* Memory Range start physical addr */
74 u64 length; /* Memory Range length */
75 unsigned short caching; /* memory cache attribute */
76 unsigned short write_protect; /* memory read/write attribute */
77 unsigned int enabled:1;
78};
79
71struct acpi_memory_device { 80struct acpi_memory_device {
72 acpi_handle handle; 81 acpi_handle handle;
73 unsigned int state; /* State of the memory device */ 82 unsigned int state; /* State of the memory device */
74 unsigned short caching; /* memory cache attribute */ 83 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}; 84};
79 85
86static acpi_status
87acpi_memory_get_resource(struct acpi_resource *resource, void *context)
88{
89 struct acpi_memory_device *mem_device = context;
90 struct acpi_resource_address64 address64;
91 struct acpi_memory_info *info, *new;
92 acpi_status status;
93
94 status = acpi_resource_to_address64(resource, &address64);
95 if (ACPI_FAILURE(status) ||
96 (address64.resource_type != ACPI_MEMORY_RANGE))
97 return AE_OK;
98
99 list_for_each_entry(info, &mem_device->res_list, list) {
100 /* Can we combine the resource range information? */
101 if ((info->caching == address64.info.mem.caching) &&
102 (info->write_protect == address64.info.mem.write_protect) &&
103 (info->start_addr + info->length == address64.minimum)) {
104 info->length += address64.address_length;
105 return AE_OK;
106 }
107 }
108
109 new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
110 if (!new)
111 return AE_ERROR;
112
113 INIT_LIST_HEAD(&new->list);
114 new->caching = address64.info.mem.caching;
115 new->write_protect = address64.info.mem.write_protect;
116 new->start_addr = address64.minimum;
117 new->length = address64.address_length;
118 list_add_tail(&new->list, &mem_device->res_list);
119
120 return AE_OK;
121}
122
80static int 123static int
81acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) 124acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
82{ 125{
83 acpi_status status; 126 acpi_status status;
84 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 127 struct acpi_memory_info *info, *n;
85 struct acpi_resource *resource = NULL;
86 struct acpi_resource_address64 address64;
87 128
88 ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources"); 129 ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources");
89 130
90 /* Get the range from the _CRS */ 131 status = acpi_walk_resources(mem_device->handle, METHOD_NAME__CRS,
91 status = acpi_get_current_resources(mem_device->handle, &buffer); 132 acpi_memory_get_resource, mem_device);
92 if (ACPI_FAILURE(status)) 133 if (ACPI_FAILURE(status)) {
93 return_VALUE(-EINVAL); 134 list_for_each_entry_safe(info, n, &mem_device->res_list, list)
94 135 kfree(info);
95 resource = (struct acpi_resource *)buffer.pointer; 136 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 } 137 }
107 138
108 acpi_os_free(buffer.pointer); 139 return 0;
109 return_VALUE(0);
110} 140}
111 141
112static int 142static int
@@ -181,7 +211,8 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
181 211
182static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) 212static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
183{ 213{
184 int result; 214 int result, num_enabled = 0;
215 struct acpi_memory_info *info;
185 216
186 ACPI_FUNCTION_TRACE("acpi_memory_enable_device"); 217 ACPI_FUNCTION_TRACE("acpi_memory_enable_device");
187 218
@@ -197,12 +228,20 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
197 /* 228 /*
198 * Tell the VM there is more memory here... 229 * Tell the VM there is more memory here...
199 * Note: Assume that this function returns zero on success 230 * Note: Assume that this function returns zero on success
231 * We don't have memory-hot-add rollback function,now.
232 * (i.e. memory-hot-remove function)
200 */ 233 */
201 result = add_memory(mem_device->start_addr, mem_device->length); 234 list_for_each_entry(info, &mem_device->res_list, list) {
202 if (result) { 235 result = add_memory(info->start_addr, info->length);
236 if (result)
237 continue;
238 info->enabled = 1;
239 num_enabled++;
240 }
241 if (!num_enabled) {
203 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "\nadd_memory failed\n")); 242 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "\nadd_memory failed\n"));
204 mem_device->state = MEMORY_INVALID_STATE; 243 mem_device->state = MEMORY_INVALID_STATE;
205 return result; 244 return -EINVAL;
206 } 245 }
207 246
208 return result; 247 return result;
@@ -246,8 +285,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) 285static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
247{ 286{
248 int result; 287 int result;
249 u64 start = mem_device->start_addr; 288 struct acpi_memory_info *info, *n;
250 u64 len = mem_device->length;
251 289
252 ACPI_FUNCTION_TRACE("acpi_memory_disable_device"); 290 ACPI_FUNCTION_TRACE("acpi_memory_disable_device");
253 291
@@ -255,10 +293,13 @@ static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
255 * Ask the VM to offline this memory range. 293 * Ask the VM to offline this memory range.
256 * Note: Assume that this function returns zero on success 294 * Note: Assume that this function returns zero on success
257 */ 295 */
258 result = remove_memory(start, len); 296 list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
259 if (result) { 297 if (info->enabled) {
260 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n")); 298 result = remove_memory(info->start_addr, info->length);
261 return_VALUE(result); 299 if (result)
300 return result;
301 }
302 kfree(info);
262 } 303 }
263 304
264 /* Power-off and eject the device */ 305 /* Power-off and eject the device */
@@ -356,6 +397,7 @@ static int acpi_memory_device_add(struct acpi_device *device)
356 return_VALUE(-ENOMEM); 397 return_VALUE(-ENOMEM);
357 memset(mem_device, 0, sizeof(struct acpi_memory_device)); 398 memset(mem_device, 0, sizeof(struct acpi_memory_device));
358 399
400 INIT_LIST_HEAD(&mem_device->res_list);
359 mem_device->handle = device->handle; 401 mem_device->handle = device->handle;
360 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); 402 sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
361 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); 403 sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);