aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/scan.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-28 06:58:05 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-28 06:58:05 -0400
commita204dbc61b7f4cb1a7e2cb3ad057b135164782da (patch)
treef82151c04a30f49c3dee8926d184575ad2e7b1e2 /drivers/acpi/scan.c
parent45e00374db944b1c12987b501bcaa279b3e36d93 (diff)
parent08f502c1c343031f0d126bd00e87dede38269d12 (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.c120
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 */
34bool acpi_force_hot_remove;
35
30static const char *dummy_hid = "device"; 36static const char *dummy_hid = "device";
31 37
32static LIST_HEAD(acpi_device_list); 38static LIST_HEAD(acpi_device_list);
@@ -120,12 +126,78 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
120} 126}
121static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); 127static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
122 128
129static 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
172static 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
123static int acpi_scan_hot_remove(struct acpi_device *device) 194static 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();