diff options
author | KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> | 2006-06-27 05:53:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-27 20:32:35 -0400 |
commit | 9ac023989e6dd1b97140b47fb942a7940d0b2af2 (patch) | |
tree | ba7a242235d2c7d4632d8e0a413ccb28684a7180 /drivers | |
parent | 5c31f2738ab124ebc1f8948a5fc17dd7a08ed1ec (diff) |
[PATCH] acpi memory hotplug cannot manage _CRS with plural resoureces
Current acpi memory hotplug just looks into the first entry of resources in
_CRS. But, _CRS can contain plural resources. So, if _CRS contains plural
resoureces, acpi memory hot add cannot add all memory.
With this patch, acpi memory hotplug can deal with Memory Device, whose
_CRS contains plural resources.
Tested on ia64 memory hotplug test envrionment (not emulation, uses alpha
version firmware which supports dynamic reconfiguration of NUMA.)
Note: Microsoft's Windows Server 2003 requires big (>4G)resoureces to be
divided into small (<4G) resources. looks crazy, but not invalid.
(See http://www.microsoft.com/whdc/system/pnppwr/hotadd/hotaddmem.mspx)
For this reason, a firmware vendor who supports Windows writes plural
resources in a _CRS even if they are contiguous.
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: "Brown, Len" <len.brown@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/acpi_memhotplug.c | 112 |
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 | ||
71 | struct 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 | |||
71 | struct acpi_memory_device { | 80 | struct 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 | ||
86 | static acpi_status | ||
87 | acpi_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 | |||
80 | static int | 123 | static int |
81 | acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) | 124 | acpi_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 | ||
112 | static int | 142 | static int |
@@ -181,7 +211,8 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device) | |||
181 | 211 | ||
182 | static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) | 212 | static 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) | |||
246 | static int acpi_memory_disable_device(struct acpi_memory_device *mem_device) | 285 | static 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); |