aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/internal.h2
-rw-r--r--drivers/acpi/scan.c84
-rw-r--r--drivers/acpi/sysfs.c31
3 files changed, 117 insertions, 0 deletions
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 6f1afd9118c8..4548f0a114ce 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -47,6 +47,8 @@ void acpi_memory_hotplug_init(void);
47static inline void acpi_memory_hotplug_init(void) {} 47static inline void acpi_memory_hotplug_init(void) {}
48#endif 48#endif
49 49
50extern bool acpi_force_hot_remove;
51
50void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, 52void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
51 const char *name); 53 const char *name);
52int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, 54int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index fe158fd4f1df..4fd392005ef1 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,6 +126,59 @@ 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 acpi_status status = AE_OK;
135
136 if (acpi_bus_get_device(handle, &device))
137 return AE_OK;
138
139 mutex_lock(&device->physical_node_lock);
140
141 list_for_each_entry(pn, &device->physical_node_list, node) {
142 int ret;
143
144 ret = device_offline(pn->dev);
145 if (acpi_force_hot_remove)
146 continue;
147
148 if (ret < 0) {
149 status = AE_ERROR;
150 break;
151 }
152 pn->put_online = !ret;
153 }
154
155 mutex_unlock(&device->physical_node_lock);
156
157 return status;
158}
159
160static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl,
161 void *data, void **ret_p)
162{
163 struct acpi_device *device = NULL;
164 struct acpi_device_physical_node *pn;
165
166 if (acpi_bus_get_device(handle, &device))
167 return AE_OK;
168
169 mutex_lock(&device->physical_node_lock);
170
171 list_for_each_entry(pn, &device->physical_node_list, node)
172 if (pn->put_online) {
173 device_online(pn->dev);
174 pn->put_online = false;
175 }
176
177 mutex_unlock(&device->physical_node_lock);
178
179 return AE_OK;
180}
181
123static int acpi_scan_hot_remove(struct acpi_device *device) 182static int acpi_scan_hot_remove(struct acpi_device *device)
124{ 183{
125 acpi_handle handle = device->handle; 184 acpi_handle handle = device->handle;
@@ -136,10 +195,33 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
136 return -EINVAL; 195 return -EINVAL;
137 } 196 }
138 197
198 lock_device_hotplug();
199
200 status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
201 NULL, acpi_bus_offline_companions, NULL,
202 NULL);
203 if (ACPI_SUCCESS(status) || acpi_force_hot_remove)
204 status = acpi_bus_offline_companions(handle, 0, NULL, NULL);
205
206 if (ACPI_FAILURE(status) && !acpi_force_hot_remove) {
207 acpi_bus_online_companions(handle, 0, NULL, NULL);
208 acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
209 acpi_bus_online_companions, NULL, NULL,
210 NULL);
211
212 unlock_device_hotplug();
213
214 put_device(&device->dev);
215 return -EBUSY;
216 }
217
139 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 218 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
140 "Hot-removing device %s...\n", dev_name(&device->dev))); 219 "Hot-removing device %s...\n", dev_name(&device->dev)));
141 220
142 acpi_bus_trim(device); 221 acpi_bus_trim(device);
222
223 unlock_device_hotplug();
224
143 /* Device node has been unregistered. */ 225 /* Device node has been unregistered. */
144 put_device(&device->dev); 226 put_device(&device->dev);
145 device = NULL; 227 device = NULL;
@@ -236,6 +318,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
236 int error; 318 int error;
237 319
238 mutex_lock(&acpi_scan_lock); 320 mutex_lock(&acpi_scan_lock);
321 lock_device_hotplug();
239 322
240 acpi_bus_get_device(handle, &device); 323 acpi_bus_get_device(handle, &device);
241 if (device) { 324 if (device) {
@@ -259,6 +342,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
259 kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); 342 kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
260 343
261 out: 344 out:
345 unlock_device_hotplug();
262 acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); 346 acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
263 mutex_unlock(&acpi_scan_lock); 347 mutex_unlock(&acpi_scan_lock);
264} 348}
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index fcae5fa2e1b3..5c5d1624fa2c 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -780,6 +780,33 @@ void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug,
780 pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); 780 pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name);
781} 781}
782 782
783static ssize_t force_remove_show(struct kobject *kobj,
784 struct kobj_attribute *attr, char *buf)
785{
786 return sprintf(buf, "%d\n", !!acpi_force_hot_remove);
787}
788
789static ssize_t force_remove_store(struct kobject *kobj,
790 struct kobj_attribute *attr,
791 const char *buf, size_t size)
792{
793 bool val;
794 int ret;
795
796 ret = strtobool(buf, &val);
797 if (ret < 0)
798 return ret;
799
800 lock_device_hotplug();
801 acpi_force_hot_remove = val;
802 unlock_device_hotplug();
803 return size;
804}
805
806static const struct kobj_attribute force_remove_attr =
807 __ATTR(force_remove, S_IRUGO | S_IWUSR, force_remove_show,
808 force_remove_store);
809
783int __init acpi_sysfs_init(void) 810int __init acpi_sysfs_init(void)
784{ 811{
785 int result; 812 int result;
@@ -789,6 +816,10 @@ int __init acpi_sysfs_init(void)
789 return result; 816 return result;
790 817
791 hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); 818 hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj);
819 result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr);
820 if (result)
821 return result;
822
792 result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); 823 result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr);
793 return result; 824 return result;
794} 825}