diff options
author | Zhang Rui <rui.zhang@intel.com> | 2008-04-29 02:35:48 -0400 |
---|---|---|
committer | Andi Kleen <andi@basil.nowhere.org> | 2008-07-16 17:27:00 -0400 |
commit | 26d46867b7d27f68a446b073dac7817721ae4c8f (patch) | |
tree | 608134a54bbad0e276a8633301f2da1a3a3ee47b | |
parent | a3cf859321486f69506326146ab3e2fd15c05c3f (diff) |
fix a deadlock issue when poking "eject" file
"/sys/devices/LNXSYSTM:00/.../eject" is used to evaluate _EJx method
and eject a device in user space.
But system hangs when poking the "eject" file because that
the device hot-removal code invoke the driver .remove method which will
try to remove the "eject" file as a result.
Queues the hot-removal function for deferred execution in this patch.
http://bugzilla.kernel.org/show_bug.cgi?id=9772
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
-rw-r--r-- | drivers/acpi/scan.c | 56 |
1 files changed, 32 insertions, 24 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6d85289f1c12..9a84ed250d9f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <linux/init.h> | 6 | #include <linux/init.h> |
7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
8 | #include <linux/acpi.h> | 8 | #include <linux/acpi.h> |
9 | #include <asm/signal.h> | ||
9 | 10 | ||
10 | #include <acpi/acpi_drivers.h> | 11 | #include <acpi/acpi_drivers.h> |
11 | #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ | 12 | #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ |
@@ -92,17 +93,37 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha | |||
92 | } | 93 | } |
93 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); | 94 | static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); |
94 | 95 | ||
95 | static int acpi_eject_operation(acpi_handle handle, int lockable) | 96 | static int acpi_bus_hot_remove_device(void *context) |
96 | { | 97 | { |
98 | struct acpi_device *device; | ||
99 | acpi_handle handle = context; | ||
97 | struct acpi_object_list arg_list; | 100 | struct acpi_object_list arg_list; |
98 | union acpi_object arg; | 101 | union acpi_object arg; |
99 | acpi_status status = AE_OK; | 102 | acpi_status status = AE_OK; |
100 | 103 | ||
101 | /* | 104 | if (acpi_bus_get_device(handle, &device)) |
102 | * TBD: evaluate _PS3? | 105 | return 0; |
103 | */ | ||
104 | 106 | ||
105 | if (lockable) { | 107 | if (!device) |
108 | return 0; | ||
109 | |||
110 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
111 | "Hot-removing device %s...\n", device->dev.bus_id)); | ||
112 | |||
113 | |||
114 | if (acpi_bus_trim(device, 1)) { | ||
115 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
116 | "Removing device failed\n")); | ||
117 | return -1; | ||
118 | } | ||
119 | |||
120 | /* power off device */ | ||
121 | status = acpi_evaluate_object(handle, "_PS3", NULL, NULL); | ||
122 | if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) | ||
123 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, | ||
124 | "Power-off device failed\n")); | ||
125 | |||
126 | if (device->flags.lockable) { | ||
106 | arg_list.count = 1; | 127 | arg_list.count = 1; |
107 | arg_list.pointer = &arg; | 128 | arg_list.pointer = &arg; |
108 | arg.type = ACPI_TYPE_INTEGER; | 129 | arg.type = ACPI_TYPE_INTEGER; |
@@ -118,24 +139,19 @@ static int acpi_eject_operation(acpi_handle handle, int lockable) | |||
118 | /* | 139 | /* |
119 | * TBD: _EJD support. | 140 | * TBD: _EJD support. |
120 | */ | 141 | */ |
121 | |||
122 | status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); | 142 | status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); |
123 | if (ACPI_FAILURE(status)) { | 143 | if (ACPI_FAILURE(status)) |
124 | return (-ENODEV); | 144 | return -ENODEV; |
125 | } | ||
126 | 145 | ||
127 | return (0); | 146 | return 0; |
128 | } | 147 | } |
129 | 148 | ||
130 | static ssize_t | 149 | static ssize_t |
131 | acpi_eject_store(struct device *d, struct device_attribute *attr, | 150 | acpi_eject_store(struct device *d, struct device_attribute *attr, |
132 | const char *buf, size_t count) | 151 | const char *buf, size_t count) |
133 | { | 152 | { |
134 | int result; | ||
135 | int ret = count; | 153 | int ret = count; |
136 | int islockable; | ||
137 | acpi_status status; | 154 | acpi_status status; |
138 | acpi_handle handle; | ||
139 | acpi_object_type type = 0; | 155 | acpi_object_type type = 0; |
140 | struct acpi_device *acpi_device = to_acpi_device(d); | 156 | struct acpi_device *acpi_device = to_acpi_device(d); |
141 | 157 | ||
@@ -154,17 +170,9 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, | |||
154 | goto err; | 170 | goto err; |
155 | } | 171 | } |
156 | 172 | ||
157 | islockable = acpi_device->flags.lockable; | 173 | /* remove the device in another thread to fix the deadlock issue */ |
158 | handle = acpi_device->handle; | 174 | ret = kernel_thread(acpi_bus_hot_remove_device, |
159 | 175 | acpi_device->handle, SIGCHLD); | |
160 | result = acpi_bus_trim(acpi_device, 1); | ||
161 | |||
162 | if (!result) | ||
163 | result = acpi_eject_operation(handle, islockable); | ||
164 | |||
165 | if (result) { | ||
166 | ret = -EBUSY; | ||
167 | } | ||
168 | err: | 176 | err: |
169 | return ret; | 177 | return ret; |
170 | } | 178 | } |