diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 06:58:05 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-28 06:58:05 -0400 |
commit | a204dbc61b7f4cb1a7e2cb3ad057b135164782da (patch) | |
tree | f82151c04a30f49c3dee8926d184575ad2e7b1e2 /drivers/acpi/scan.c | |
parent | 45e00374db944b1c12987b501bcaa279b3e36d93 (diff) | |
parent | 08f502c1c343031f0d126bd00e87dede38269d12 (diff) |
Merge branch 'acpi-hotplug'
* acpi-hotplug:
ACPI: Do not use CONFIG_ACPI_HOTPLUG_MEMORY_MODULE
ACPI / cpufreq: Add ACPI processor device IDs to acpi-cpufreq
Memory hotplug: Move alternative function definitions to header
ACPI / processor: Fix potential NULL pointer dereference in acpi_processor_add()
Memory hotplug / ACPI: Simplify memory removal
ACPI / scan: Add second pass of companion offlining to hot-remove code
Driver core / MM: Drop offline_memory_block()
ACPI / processor: Pass processor object handle to acpi_bind_one()
ACPI: Drop removal_type field from struct acpi_device
Driver core / memory: Simplify __memory_block_change_state()
ACPI / processor: Initialize per_cpu(processors, pr->id) properly
CPU: Fix sysfs cpu/online of offlined CPUs
Driver core: Introduce offline/online callbacks for memory blocks
ACPI / memhotplug: Bind removable memory blocks to ACPI device nodes
ACPI / processor: Use common hotplug infrastructure
ACPI / hotplug: Use device offline/online for graceful hot-removal
Driver core: Use generic offline/online for CPU offline/online
Driver core: Add offline/online device operations
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 120 |
1 files changed, 118 insertions, 2 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 27da63061e11..db118b1ad3e8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -27,6 +27,12 @@ extern struct acpi_device *acpi_root; | |||
27 | 27 | ||
28 | #define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) | 28 | #define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) |
29 | 29 | ||
30 | /* | ||
31 | * If set, devices will be hot-removed even if they cannot be put offline | ||
32 | * gracefully (from the kernel's standpoint). | ||
33 | */ | ||
34 | bool acpi_force_hot_remove; | ||
35 | |||
30 | static const char *dummy_hid = "device"; | 36 | static const char *dummy_hid = "device"; |
31 | 37 | ||
32 | static LIST_HEAD(acpi_device_list); | 38 | static LIST_HEAD(acpi_device_list); |
@@ -120,12 +126,78 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha | |||
120 | } | 126 | } |
121 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 127 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
122 | 128 | ||
129 | static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, | ||
130 | void *data, void **ret_p) | ||
131 | { | ||
132 | struct acpi_device *device = NULL; | ||
133 | struct acpi_device_physical_node *pn; | ||
134 | bool second_pass = (bool)data; | ||
135 | acpi_status status = AE_OK; | ||
136 | |||
137 | if (acpi_bus_get_device(handle, &device)) | ||
138 | return AE_OK; | ||
139 | |||
140 | mutex_lock(&device->physical_node_lock); | ||
141 | |||
142 | list_for_each_entry(pn, &device->physical_node_list, node) { | ||
143 | int ret; | ||
144 | |||
145 | if (second_pass) { | ||
146 | /* Skip devices offlined by the first pass. */ | ||
147 | if (pn->put_online) | ||
148 | continue; | ||
149 | } else { | ||
150 | pn->put_online = false; | ||
151 | } | ||
152 | ret = device_offline(pn->dev); | ||
153 | if (acpi_force_hot_remove) | ||
154 | continue; | ||
155 | |||
156 | if (ret >= 0) { | ||
157 | pn->put_online = !ret; | ||
158 | } else { | ||
159 | *ret_p = pn->dev; | ||
160 | if (second_pass) { | ||
161 | status = AE_ERROR; | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | mutex_unlock(&device->physical_node_lock); | ||
168 | |||
169 | return status; | ||
170 | } | ||
171 | |||
172 | static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, | ||
173 | void *data, void **ret_p) | ||
174 | { | ||
175 | struct acpi_device *device = NULL; | ||
176 | struct acpi_device_physical_node *pn; | ||
177 | |||
178 | if (acpi_bus_get_device(handle, &device)) | ||
179 | return AE_OK; | ||
180 | |||
181 | mutex_lock(&device->physical_node_lock); | ||
182 | |||
183 | list_for_each_entry(pn, &device->physical_node_list, node) | ||
184 | if (pn->put_online) { | ||
185 | device_online(pn->dev); | ||
186 | pn->put_online = false; | ||
187 | } | ||
188 | |||
189 | mutex_unlock(&device->physical_node_lock); | ||
190 | |||
191 | return AE_OK; | ||
192 | } | ||
193 | |||
123 | static int acpi_scan_hot_remove(struct acpi_device *device) | 194 | static int acpi_scan_hot_remove(struct acpi_device *device) |
124 | { | 195 | { |
125 | acpi_handle handle = device->handle; | 196 | acpi_handle handle = device->handle; |
126 | acpi_handle not_used; | 197 | acpi_handle not_used; |
127 | struct acpi_object_list arg_list; | 198 | struct acpi_object_list arg_list; |
128 | union acpi_object arg; | 199 | union acpi_object arg; |
200 | struct device *errdev; | ||
129 | acpi_status status; | 201 | acpi_status status; |
130 | unsigned long long sta; | 202 | unsigned long long sta; |
131 | 203 | ||
@@ -136,10 +208,53 @@ static int acpi_scan_hot_remove(struct acpi_device *device) | |||
136 | return -EINVAL; | 208 | return -EINVAL; |
137 | } | 209 | } |
138 | 210 | ||
211 | lock_device_hotplug(); | ||
212 | |||
213 | /* | ||
214 | * Carry out two passes here and ignore errors in the first pass, | ||
215 | * because if the devices in question are memory blocks and | ||
216 | * CONFIG_MEMCG is set, one of the blocks may hold data structures | ||
217 | * that the other blocks depend on, but it is not known in advance which | ||
218 | * block holds them. | ||
219 | * | ||
220 | * If the first pass is successful, the second one isn't needed, though. | ||
221 | */ | ||
222 | errdev = NULL; | ||
223 | acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, | ||
224 | NULL, acpi_bus_offline_companions, | ||
225 | (void *)false, (void **)&errdev); | ||
226 | acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev); | ||
227 | if (errdev) { | ||
228 | errdev = NULL; | ||
229 | acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, | ||
230 | NULL, acpi_bus_offline_companions, | ||
231 | (void *)true , (void **)&errdev); | ||
232 | if (!errdev || acpi_force_hot_remove) | ||
233 | acpi_bus_offline_companions(handle, 0, (void *)true, | ||
234 | (void **)&errdev); | ||
235 | |||
236 | if (errdev && !acpi_force_hot_remove) { | ||
237 | dev_warn(errdev, "Offline failed.\n"); | ||
238 | acpi_bus_online_companions(handle, 0, NULL, NULL); | ||
239 | acpi_walk_namespace(ACPI_TYPE_ANY, handle, | ||
240 | ACPI_UINT32_MAX, | ||
241 | acpi_bus_online_companions, NULL, | ||
242 | NULL, NULL); | ||
243 | |||
244 | unlock_device_hotplug(); | ||
245 | |||
246 | put_device(&device->dev); | ||
247 | return -EBUSY; | ||
248 | } | ||
249 | } | ||
250 | |||
139 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 251 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
140 | "Hot-removing device %s...\n", dev_name(&device->dev))); | 252 | "Hot-removing device %s...\n", dev_name(&device->dev))); |
141 | 253 | ||
142 | acpi_bus_trim(device); | 254 | acpi_bus_trim(device); |
255 | |||
256 | unlock_device_hotplug(); | ||
257 | |||
143 | /* Device node has been unregistered. */ | 258 | /* Device node has been unregistered. */ |
144 | put_device(&device->dev); | 259 | put_device(&device->dev); |
145 | device = NULL; | 260 | device = NULL; |
@@ -236,6 +351,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) | |||
236 | int error; | 351 | int error; |
237 | 352 | ||
238 | mutex_lock(&acpi_scan_lock); | 353 | mutex_lock(&acpi_scan_lock); |
354 | lock_device_hotplug(); | ||
239 | 355 | ||
240 | acpi_bus_get_device(handle, &device); | 356 | acpi_bus_get_device(handle, &device); |
241 | if (device) { | 357 | if (device) { |
@@ -259,6 +375,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) | |||
259 | kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); | 375 | kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); |
260 | 376 | ||
261 | out: | 377 | out: |
378 | unlock_device_hotplug(); | ||
262 | acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); | 379 | acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); |
263 | mutex_unlock(&acpi_scan_lock); | 380 | mutex_unlock(&acpi_scan_lock); |
264 | } | 381 | } |
@@ -952,7 +1069,6 @@ int acpi_device_add(struct acpi_device *device, | |||
952 | printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", | 1069 | printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", |
953 | dev_name(&device->dev)); | 1070 | dev_name(&device->dev)); |
954 | 1071 | ||
955 | device->removal_type = ACPI_BUS_REMOVAL_NORMAL; | ||
956 | return 0; | 1072 | return 0; |
957 | 1073 | ||
958 | err: | 1074 | err: |
@@ -1939,7 +2055,6 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used, | |||
1939 | if (!acpi_bus_get_device(handle, &device)) { | 2055 | if (!acpi_bus_get_device(handle, &device)) { |
1940 | struct acpi_scan_handler *dev_handler = device->handler; | 2056 | struct acpi_scan_handler *dev_handler = device->handler; |
1941 | 2057 | ||
1942 | device->removal_type = ACPI_BUS_REMOVAL_EJECT; | ||
1943 | if (dev_handler) { | 2058 | if (dev_handler) { |
1944 | if (dev_handler->detach) | 2059 | if (dev_handler->detach) |
1945 | dev_handler->detach(device); | 2060 | dev_handler->detach(device); |
@@ -2038,6 +2153,7 @@ int __init acpi_scan_init(void) | |||
2038 | 2153 | ||
2039 | acpi_pci_root_init(); | 2154 | acpi_pci_root_init(); |
2040 | acpi_pci_link_init(); | 2155 | acpi_pci_link_init(); |
2156 | acpi_processor_init(); | ||
2041 | acpi_platform_init(); | 2157 | acpi_platform_init(); |
2042 | acpi_lpss_init(); | 2158 | acpi_lpss_init(); |
2043 | acpi_container_init(); | 2159 | acpi_container_init(); |