diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 69 |
1 files changed, 48 insertions, 21 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c7676ee8eca4..d16a94ef0baf 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -42,6 +42,18 @@ struct acpi_device_bus_id{ struct list_head node; }; +void acpi_scan_lock_acquire(void) +{ + mutex_lock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_acquire); + +void acpi_scan_lock_release(void) +{ + mutex_unlock(&acpi_scan_lock); +} +EXPORT_SYMBOL_GPL(acpi_scan_lock_release); + int acpi_scan_add_handler(struct acpi_scan_handler *handler) { if (!handler || !handler->attach) @@ -95,8 +107,6 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static void __acpi_bus_trim(struct acpi_device *start); - /** * acpi_bus_hot_remove_device: hot-remove a device and its children * @context: struct acpi_eject_event pointer (freed in this func) @@ -107,7 +117,7 @@ static void __acpi_bus_trim(struct acpi_device *start); */ void acpi_bus_hot_remove_device(void *context) { - struct acpi_eject_event *ej_event = (struct acpi_eject_event *) context; + struct acpi_eject_event *ej_event = context; struct acpi_device *device = ej_event->device; acpi_handle handle = device->handle; acpi_handle temp; @@ -118,11 +128,19 @@ void acpi_bus_hot_remove_device(void *context) mutex_lock(&acpi_scan_lock); + /* If there is no handle, the device node has been unregistered. */ + if (!device->handle) { + dev_dbg(&device->dev, "ACPI handle missing\n"); + put_device(&device->dev); + goto out; + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); - __acpi_bus_trim(device); - /* Device node has been released. */ + acpi_bus_trim(device); + /* Device node has been unregistered. */ + put_device(&device->dev); device = NULL; if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", &temp))) { @@ -151,6 +169,7 @@ void acpi_bus_hot_remove_device(void *context) ost_code, NULL); } + out: mutex_unlock(&acpi_scan_lock); kfree(context); return; @@ -212,6 +231,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } + get_device(&acpi_device->dev); ej_event->device = acpi_device; if (acpi_device->flags.eject_pending) { /* event originated from ACPI eject notification */ @@ -224,7 +244,11 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, ej_event->event, ACPI_OST_SC_EJECT_IN_PROGRESS, NULL); } - acpi_os_hotplug_execute(acpi_bus_hot_remove_device, (void *)ej_event); + status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, ej_event); + if (ACPI_FAILURE(status)) { + put_device(&acpi_device->dev); + kfree(ej_event); + } err: return ret; } @@ -779,6 +803,7 @@ static void acpi_device_unregister(struct acpi_device *device) * no more references. */ acpi_device_set_power(device, ACPI_STATE_D3_COLD); + device->handle = NULL; put_device(&device->dev); } @@ -1623,14 +1648,14 @@ static acpi_status acpi_bus_device_attach(acpi_handle handle, u32 lvl_not_used, * there has been a real error. There just have been no suitable ACPI objects * in the table trunk from which the kernel could create a device and add an * appropriate driver. + * + * Must be called under acpi_scan_lock. */ int acpi_bus_scan(acpi_handle handle) { void *device = NULL; int error = 0; - mutex_lock(&acpi_scan_lock); - if (ACPI_SUCCESS(acpi_bus_check_add(handle, 0, NULL, &device))) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_check_add, NULL, NULL, &device); @@ -1641,7 +1666,6 @@ int acpi_bus_scan(acpi_handle handle) acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, acpi_bus_device_attach, NULL, NULL, NULL); - mutex_unlock(&acpi_scan_lock); return error; } EXPORT_SYMBOL(acpi_bus_scan); @@ -1678,7 +1702,13 @@ static acpi_status acpi_bus_remove(acpi_handle handle, u32 lvl_not_used, return AE_OK; } -static void __acpi_bus_trim(struct acpi_device *start) +/** + * acpi_bus_trim - Remove ACPI device node and all of its descendants + * @start: Root of the ACPI device nodes subtree to remove. + * + * Must be called under acpi_scan_lock. + */ +void acpi_bus_trim(struct acpi_device *start) { /* * Execute acpi_bus_device_detach() as a post-order callback to detach @@ -1695,13 +1725,6 @@ static void __acpi_bus_trim(struct acpi_device *start) acpi_bus_remove, NULL, NULL); acpi_bus_remove(start->handle, 0, NULL, NULL); } - -void acpi_bus_trim(struct acpi_device *start) -{ - mutex_lock(&acpi_scan_lock); - __acpi_bus_trim(start); - mutex_unlock(&acpi_scan_lock); -} EXPORT_SYMBOL_GPL(acpi_bus_trim); static int acpi_bus_scan_fixed(void) @@ -1758,23 +1781,27 @@ int __init acpi_scan_init(void) acpi_csrt_init(); acpi_container_init(); + mutex_lock(&acpi_scan_lock); /* * Enumerate devices in the ACPI namespace. */ result = acpi_bus_scan(ACPI_ROOT_OBJECT); if (result) - return result; + goto out; result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root); if (result) - return result; + goto out; result = acpi_bus_scan_fixed(); if (result) { acpi_device_unregister(acpi_root); - return result; + goto out; } acpi_update_all_gpes(); - return 0; + + out: + mutex_unlock(&acpi_scan_lock); + return result; } |