diff options
Diffstat (limited to 'drivers/acpi/scan.c')
| -rw-r--r-- | drivers/acpi/scan.c | 62 | 
1 files changed, 37 insertions, 25 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6d85289f1c1..5b049cd7955 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -6,6 +6,8 @@  #include <linux/init.h>  #include <linux/kernel.h>  #include <linux/acpi.h> +#include <linux/signal.h> +#include <linux/kthread.h>  #include <acpi/acpi_drivers.h>  #include <acpi/acinterp.h>	/* for acpi_ex_eisa_id_to_string() */ @@ -92,17 +94,37 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha  }  static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static int acpi_eject_operation(acpi_handle handle, int lockable) +static int acpi_bus_hot_remove_device(void *context)  { +	struct acpi_device *device; +	acpi_handle handle = context;  	struct acpi_object_list arg_list;  	union acpi_object arg;  	acpi_status status = AE_OK; -	/* -	 * TBD: evaluate _PS3? -	 */ +	if (acpi_bus_get_device(handle, &device)) +		return 0; -	if (lockable) { +	if (!device) +		return 0; + +	ACPI_DEBUG_PRINT((ACPI_DB_INFO, +		"Hot-removing device %s...\n", device->dev.bus_id)); + + +	if (acpi_bus_trim(device, 1)) { +		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, +				"Removing device failed\n")); +		return -1; +	} + +	/* power off device */ +	status = acpi_evaluate_object(handle, "_PS3", NULL, NULL); +	if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) +		ACPI_DEBUG_PRINT((ACPI_DB_WARN, +				"Power-off device failed\n")); + +	if (device->flags.lockable) {  		arg_list.count = 1;  		arg_list.pointer = &arg;  		arg.type = ACPI_TYPE_INTEGER; @@ -118,26 +140,22 @@ static int acpi_eject_operation(acpi_handle handle, int lockable)  	/*  	 * TBD: _EJD support.  	 */ -  	status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); -	if (ACPI_FAILURE(status)) { -		return (-ENODEV); -	} +	if (ACPI_FAILURE(status)) +		return -ENODEV; -	return (0); +	return 0;  }  static ssize_t  acpi_eject_store(struct device *d, struct device_attribute *attr,  		const char *buf, size_t count)  { -	int result;  	int ret = count; -	int islockable;  	acpi_status status; -	acpi_handle handle;  	acpi_object_type type = 0;  	struct acpi_device *acpi_device = to_acpi_device(d); +	struct task_struct *task;  	if ((!count) || (buf[0] != '1')) {  		return -EINVAL; @@ -154,18 +172,12 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,  		goto err;  	} -	islockable = acpi_device->flags.lockable; -	handle = acpi_device->handle; - -	result = acpi_bus_trim(acpi_device, 1); - -	if (!result) -		result = acpi_eject_operation(handle, islockable); - -	if (result) { -		ret = -EBUSY; -	} -      err: +	/* remove the device in another thread to fix the deadlock issue */ +	task = kthread_run(acpi_bus_hot_remove_device, +				acpi_device->handle, "acpi_hot_remove_device"); +	if (IS_ERR(task)) +		ret = PTR_ERR(task); +err:  	return ret;  }  |