Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ACPI / hotplug: Use device offline/online for graceful hot-removal

Modify the generic ACPI hotplug code to be able to check if devices
scheduled for hot-removal may be gracefully removed from the system
using the device offline/online mechanism introduced previously.

Namely, make acpi_scan_hot_remove() handling device hot-removal call
device_offline() for all physical companions of the ACPI device nodes
involved in the operation and check the results. If any of the
device_offline() calls fails, the function will not progress to the
removal phase (which cannot be aborted), unless its (new) force
argument is set (in case of a failing offline it will put the devices
offlined by it back online).

In support of 'forced' device hot-removal, add a new sysfs attribute
'force_remove' that will reside under /sys/firmware/acpi/hotplug/.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Toshi Kani <toshi.kani@hp.com>

+128
+10
Documentation/ABI/testing/sysfs-firmware-acpi
··· 44 44 or 0 (unset). Attempts to write any other values to it will 45 45 cause -EINVAL to be returned. 46 46 47 + What: /sys/firmware/acpi/hotplug/force_remove 48 + Date: May 2013 49 + Contact: Rafael J. Wysocki <rafael.j.wysocki@intel.com> 50 + Description: 51 + The number in this file (0 or 1) determines whether (1) or not 52 + (0) the ACPI subsystem will allow devices to be hot-removed even 53 + if they cannot be put offline gracefully (from the kernel's 54 + viewpoint). That number can be changed by writing a boolean 55 + value to this file. 56 + 47 57 What: /sys/firmware/acpi/interrupts/ 48 58 Date: February 2008 49 59 Contact: Len Brown <lenb@kernel.org>
+2
drivers/acpi/internal.h
··· 47 47 static inline void acpi_memory_hotplug_init(void) {} 48 48 #endif 49 49 50 + extern bool acpi_force_hot_remove; 51 + 50 52 void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, 51 53 const char *name); 52 54 int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
+84
drivers/acpi/scan.c
··· 27 27 28 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 36 static const char *dummy_hid = "device"; 31 37 32 38 static LIST_HEAD(acpi_device_list); ··· 126 120 } 127 121 static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); 128 122 123 + static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, 124 + void *data, void **ret_p) 125 + { 126 + struct acpi_device *device = NULL; 127 + struct acpi_device_physical_node *pn; 128 + acpi_status status = AE_OK; 129 + 130 + if (acpi_bus_get_device(handle, &device)) 131 + return AE_OK; 132 + 133 + mutex_lock(&device->physical_node_lock); 134 + 135 + list_for_each_entry(pn, &device->physical_node_list, node) { 136 + int ret; 137 + 138 + ret = device_offline(pn->dev); 139 + if (acpi_force_hot_remove) 140 + continue; 141 + 142 + if (ret < 0) { 143 + status = AE_ERROR; 144 + break; 145 + } 146 + pn->put_online = !ret; 147 + } 148 + 149 + mutex_unlock(&device->physical_node_lock); 150 + 151 + return status; 152 + } 153 + 154 + static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, 155 + void *data, void **ret_p) 156 + { 157 + struct acpi_device *device = NULL; 158 + struct acpi_device_physical_node *pn; 159 + 160 + if (acpi_bus_get_device(handle, &device)) 161 + return AE_OK; 162 + 163 + mutex_lock(&device->physical_node_lock); 164 + 165 + list_for_each_entry(pn, &device->physical_node_list, node) 166 + if (pn->put_online) { 167 + device_online(pn->dev); 168 + pn->put_online = false; 169 + } 170 + 171 + mutex_unlock(&device->physical_node_lock); 172 + 173 + return AE_OK; 174 + } 175 + 129 176 static int acpi_scan_hot_remove(struct acpi_device *device) 130 177 { 131 178 acpi_handle handle = device->handle; ··· 195 136 return -EINVAL; 196 137 } 197 138 139 + lock_device_hotplug(); 140 + 141 + status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, 142 + NULL, acpi_bus_offline_companions, NULL, 143 + NULL); 144 + if (ACPI_SUCCESS(status) || acpi_force_hot_remove) 145 + status = acpi_bus_offline_companions(handle, 0, NULL, NULL); 146 + 147 + if (ACPI_FAILURE(status) && !acpi_force_hot_remove) { 148 + acpi_bus_online_companions(handle, 0, NULL, NULL); 149 + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, 150 + acpi_bus_online_companions, NULL, NULL, 151 + NULL); 152 + 153 + unlock_device_hotplug(); 154 + 155 + put_device(&device->dev); 156 + return -EBUSY; 157 + } 158 + 198 159 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 199 160 "Hot-removing device %s...\n", dev_name(&device->dev))); 200 161 201 162 acpi_bus_trim(device); 163 + 164 + unlock_device_hotplug(); 165 + 202 166 /* Device node has been unregistered. */ 203 167 put_device(&device->dev); 204 168 device = NULL; ··· 318 236 int error; 319 237 320 238 mutex_lock(&acpi_scan_lock); 239 + lock_device_hotplug(); 321 240 322 241 acpi_bus_get_device(handle, &device); 323 242 if (device) { ··· 342 259 kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); 343 260 344 261 out: 262 + unlock_device_hotplug(); 345 263 acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); 346 264 mutex_unlock(&acpi_scan_lock); 347 265 }
+31
drivers/acpi/sysfs.c
··· 780 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 810 int __init acpi_sysfs_init(void) 784 811 { 785 812 int result; ··· 816 789 return result; 817 790 818 791 hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); 792 + result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr); 793 + if (result) 794 + return result; 795 + 819 796 result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); 820 797 return result; 821 798 }
+1
include/acpi/acpi_bus.h
··· 286 286 u8 node_id; 287 287 struct list_head node; 288 288 struct device *dev; 289 + bool put_online:1; 289 290 }; 290 291 291 292 /* set maximum of physical nodes to 32 for expansibility */