diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/internal.h | 2 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 84 | ||||
-rw-r--r-- | drivers/acpi/sysfs.c | 31 |
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); | |||
47 | static inline void acpi_memory_hotplug_init(void) {} | 47 | static inline void acpi_memory_hotplug_init(void) {} |
48 | #endif | 48 | #endif |
49 | 49 | ||
50 | extern bool acpi_force_hot_remove; | ||
51 | |||
50 | void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, | 52 | void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, |
51 | const char *name); | 53 | const char *name); |
52 | int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, | 54 | int 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 | */ | ||
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,6 +126,59 @@ 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 | 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 | |||
160 | static 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 | |||
123 | static int acpi_scan_hot_remove(struct acpi_device *device) | 182 | static 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 | ||
783 | static 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 | |||
789 | static 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 | |||
806 | static const struct kobj_attribute force_remove_attr = | ||
807 | __ATTR(force_remove, S_IRUGO | S_IWUSR, force_remove_show, | ||
808 | force_remove_store); | ||
809 | |||
783 | int __init acpi_sysfs_init(void) | 810 | int __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 | } |