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
This commit is contained in:
@@ -27,6 +27,12 @@ extern struct acpi_device *acpi_root;
|
||||
|
||||
#define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent)
|
||||
|
||||
/*
|
||||
* If set, devices will be hot-removed even if they cannot be put offline
|
||||
* gracefully (from the kernel's standpoint).
|
||||
*/
|
||||
bool acpi_force_hot_remove;
|
||||
|
||||
static const char *dummy_hid = "device";
|
||||
|
||||
static LIST_HEAD(acpi_device_list);
|
||||
@@ -120,12 +126,78 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
|
||||
}
|
||||
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
|
||||
|
||||
static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl,
|
||||
void *data, void **ret_p)
|
||||
{
|
||||
struct acpi_device *device = NULL;
|
||||
struct acpi_device_physical_node *pn;
|
||||
bool second_pass = (bool)data;
|
||||
acpi_status status = AE_OK;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return AE_OK;
|
||||
|
||||
mutex_lock(&device->physical_node_lock);
|
||||
|
||||
list_for_each_entry(pn, &device->physical_node_list, node) {
|
||||
int ret;
|
||||
|
||||
if (second_pass) {
|
||||
/* Skip devices offlined by the first pass. */
|
||||
if (pn->put_online)
|
||||
continue;
|
||||
} else {
|
||||
pn->put_online = false;
|
||||
}
|
||||
ret = device_offline(pn->dev);
|
||||
if (acpi_force_hot_remove)
|
||||
continue;
|
||||
|
||||
if (ret >= 0) {
|
||||
pn->put_online = !ret;
|
||||
} else {
|
||||
*ret_p = pn->dev;
|
||||
if (second_pass) {
|
||||
status = AE_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&device->physical_node_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl,
|
||||
void *data, void **ret_p)
|
||||
{
|
||||
struct acpi_device *device = NULL;
|
||||
struct acpi_device_physical_node *pn;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return AE_OK;
|
||||
|
||||
mutex_lock(&device->physical_node_lock);
|
||||
|
||||
list_for_each_entry(pn, &device->physical_node_list, node)
|
||||
if (pn->put_online) {
|
||||
device_online(pn->dev);
|
||||
pn->put_online = false;
|
||||
}
|
||||
|
||||
mutex_unlock(&device->physical_node_lock);
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
{
|
||||
acpi_handle handle = device->handle;
|
||||
acpi_handle not_used;
|
||||
struct acpi_object_list arg_list;
|
||||
union acpi_object arg;
|
||||
struct device *errdev;
|
||||
acpi_status status;
|
||||
unsigned long long sta;
|
||||
|
||||
@@ -136,10 +208,53 @@ static int acpi_scan_hot_remove(struct acpi_device *device)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lock_device_hotplug();
|
||||
|
||||
/*
|
||||
* Carry out two passes here and ignore errors in the first pass,
|
||||
* because if the devices in question are memory blocks and
|
||||
* CONFIG_MEMCG is set, one of the blocks may hold data structures
|
||||
* that the other blocks depend on, but it is not known in advance which
|
||||
* block holds them.
|
||||
*
|
||||
* If the first pass is successful, the second one isn't needed, though.
|
||||
*/
|
||||
errdev = NULL;
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
|
||||
NULL, acpi_bus_offline_companions,
|
||||
(void *)false, (void **)&errdev);
|
||||
acpi_bus_offline_companions(handle, 0, (void *)false, (void **)&errdev);
|
||||
if (errdev) {
|
||||
errdev = NULL;
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
|
||||
NULL, acpi_bus_offline_companions,
|
||||
(void *)true , (void **)&errdev);
|
||||
if (!errdev || acpi_force_hot_remove)
|
||||
acpi_bus_offline_companions(handle, 0, (void *)true,
|
||||
(void **)&errdev);
|
||||
|
||||
if (errdev && !acpi_force_hot_remove) {
|
||||
dev_warn(errdev, "Offline failed.\n");
|
||||
acpi_bus_online_companions(handle, 0, NULL, NULL);
|
||||
acpi_walk_namespace(ACPI_TYPE_ANY, handle,
|
||||
ACPI_UINT32_MAX,
|
||||
acpi_bus_online_companions, NULL,
|
||||
NULL, NULL);
|
||||
|
||||
unlock_device_hotplug();
|
||||
|
||||
put_device(&device->dev);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"Hot-removing device %s...\n", dev_name(&device->dev)));
|
||||
|
||||
acpi_bus_trim(device);
|
||||
|
||||
unlock_device_hotplug();
|
||||
|
||||
/* Device node has been unregistered. */
|
||||
put_device(&device->dev);
|
||||
device = NULL;
|
||||
@@ -236,6 +351,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
|
||||
int error;
|
||||
|
||||
mutex_lock(&acpi_scan_lock);
|
||||
lock_device_hotplug();
|
||||
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (device) {
|
||||
@@ -259,6 +375,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source)
|
||||
kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
|
||||
|
||||
out:
|
||||
unlock_device_hotplug();
|
||||
acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL);
|
||||
mutex_unlock(&acpi_scan_lock);
|
||||
}
|
||||
@@ -952,7 +1069,6 @@ int acpi_device_add(struct acpi_device *device,
|
||||
printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
|
||||
dev_name(&device->dev));
|
||||
|
||||
device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
@@ -1939,7 +2055,6 @@ static acpi_status acpi_bus_device_detach(acpi_handle handle, u32 lvl_not_used,
|
||||
if (!acpi_bus_get_device(handle, &device)) {
|
||||
struct acpi_scan_handler *dev_handler = device->handler;
|
||||
|
||||
device->removal_type = ACPI_BUS_REMOVAL_EJECT;
|
||||
if (dev_handler) {
|
||||
if (dev_handler->detach)
|
||||
dev_handler->detach(device);
|
||||
@@ -2038,6 +2153,7 @@ int __init acpi_scan_init(void)
|
||||
|
||||
acpi_pci_root_init();
|
||||
acpi_pci_link_init();
|
||||
acpi_processor_init();
|
||||
acpi_platform_init();
|
||||
acpi_lpss_init();
|
||||
acpi_container_init();
|
||||
|
Reference in New Issue
Block a user