]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Shaohua Li <shaohua.li@intel.com> |
2 | Subject: fix hotplug race | |
3 | Patch-mainline: submitted 2008-08-28 | |
4 | References: fate#304731,bnc#401740 | |
5 | ||
6 | hotplug notification handler and drivers' notification handler are all | |
7 | running in one workqueue. Before hotplug removes an acpi device, the | |
8 | device driver's notification handler is already be recorded to run just | |
9 | after global notification handler. After hotplug notification handler | |
10 | runs, acpica will notice a NULL notification handler and crash. This | |
11 | patch runs hotplug in other workqueue and wait for all acpi notication | |
12 | handlers finish. This is found in battery hotplug, but actually all | |
13 | hotplug can be affected. | |
14 | ||
15 | Signed-off-by: Zhang Rui <rui.zhang@intel.com> | |
16 | Signed-off-by: Shaohua Li <shaohua.li@intel.com> | |
17 | Signed-off-by: Holger Macht <hmacht@suse.de> | |
18 | --- | |
19 | ||
20 | --- | |
21 | drivers/acpi/dock.c | 25 ++++++++++++++++++++++++- | |
22 | drivers/acpi/osl.c | 46 +++++++++++++++++++++++++++++++++++++++++----- | |
23 | include/acpi/acpiosxf.h | 3 +++ | |
24 | 3 files changed, 68 insertions(+), 6 deletions(-) | |
25 | ||
26 | --- a/drivers/acpi/dock.c | |
27 | +++ b/drivers/acpi/dock.c | |
28 | @@ -748,6 +748,20 @@ static void dock_notify(acpi_handle hand | |
29 | } | |
30 | } | |
31 | ||
32 | +struct dock_data { | |
33 | + acpi_handle handle; | |
34 | + unsigned long event; | |
35 | + struct dock_station *ds; | |
36 | +}; | |
37 | + | |
38 | +static void acpi_dock_deferred_cb(void *context) | |
39 | +{ | |
40 | + struct dock_data *data = (struct dock_data *)context; | |
41 | + | |
42 | + dock_notify(data->handle, data->event, data->ds); | |
43 | + kfree(data); | |
44 | +} | |
45 | + | |
46 | static int acpi_dock_notifier_call(struct notifier_block *this, | |
47 | unsigned long event, void *data) | |
48 | { | |
49 | @@ -759,7 +773,16 @@ static int acpi_dock_notifier_call(struc | |
50 | return 0; | |
51 | list_for_each_entry(dock_station, &dock_stations, sibiling) { | |
52 | if (dock_station->handle == handle) { | |
53 | - dock_notify(handle, event, dock_station); | |
54 | + struct dock_data *dock_data; | |
55 | + | |
56 | + dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL); | |
57 | + if (!dock_data) | |
58 | + return 0; | |
59 | + dock_data->handle = handle; | |
60 | + dock_data->event = event; | |
61 | + dock_data->ds = dock_station; | |
62 | + acpi_os_hotplug_execute(acpi_dock_deferred_cb, | |
63 | + dock_data); | |
64 | return 0 ; | |
65 | } | |
66 | } | |
67 | --- a/drivers/acpi/osl.c | |
68 | +++ b/drivers/acpi/osl.c | |
69 | @@ -705,6 +705,22 @@ static void acpi_os_execute_deferred(str | |
70 | return; | |
71 | } | |
72 | ||
73 | +static void acpi_os_execute_hp_deferred(struct work_struct *work) | |
74 | +{ | |
75 | + struct acpi_os_dpc *dpc = container_of(work, struct acpi_os_dpc, work); | |
76 | + if (!dpc) { | |
77 | + printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); | |
78 | + return; | |
79 | + } | |
80 | + | |
81 | + acpi_os_wait_events_complete(NULL); | |
82 | + | |
83 | + dpc->function(dpc->context); | |
84 | + kfree(dpc); | |
85 | + | |
86 | + return; | |
87 | +} | |
88 | + | |
89 | /******************************************************************************* | |
90 | * | |
91 | * FUNCTION: acpi_os_execute | |
92 | @@ -720,12 +736,13 @@ static void acpi_os_execute_deferred(str | |
93 | * | |
94 | ******************************************************************************/ | |
95 | ||
96 | -acpi_status acpi_os_execute(acpi_execute_type type, | |
97 | - acpi_osd_exec_callback function, void *context) | |
98 | +static acpi_status __acpi_os_execute(acpi_execute_type type, | |
99 | + acpi_osd_exec_callback function, void *context, int hp) | |
100 | { | |
101 | acpi_status status = AE_OK; | |
102 | struct acpi_os_dpc *dpc; | |
103 | struct workqueue_struct *queue; | |
104 | + int ret; | |
105 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, | |
106 | "Scheduling function [%p(%p)] for deferred execution.\n", | |
107 | function, context)); | |
108 | @@ -749,9 +766,17 @@ acpi_status acpi_os_execute(acpi_execute | |
109 | dpc->function = function; | |
110 | dpc->context = context; | |
111 | ||
112 | - INIT_WORK(&dpc->work, acpi_os_execute_deferred); | |
113 | - queue = (type == OSL_NOTIFY_HANDLER) ? kacpi_notify_wq : kacpid_wq; | |
114 | - if (!queue_work(queue, &dpc->work)) { | |
115 | + if (!hp) { | |
116 | + INIT_WORK(&dpc->work, acpi_os_execute_deferred); | |
117 | + queue = (type == OSL_NOTIFY_HANDLER) ? | |
118 | + kacpi_notify_wq : kacpid_wq; | |
119 | + ret = queue_work(queue, &dpc->work); | |
120 | + } else { | |
121 | + INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred); | |
122 | + ret = schedule_work(&dpc->work); | |
123 | + } | |
124 | + | |
125 | + if (!ret) { | |
126 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
127 | "Call to queue_work() failed.\n")); | |
128 | status = AE_ERROR; | |
129 | @@ -760,8 +785,19 @@ acpi_status acpi_os_execute(acpi_execute | |
130 | return_ACPI_STATUS(status); | |
131 | } | |
132 | ||
133 | +acpi_status acpi_os_execute(acpi_execute_type type, | |
134 | + acpi_osd_exec_callback function, void *context) | |
135 | +{ | |
136 | + return __acpi_os_execute(type, function, context, 0); | |
137 | +} | |
138 | EXPORT_SYMBOL(acpi_os_execute); | |
139 | ||
140 | +acpi_status acpi_os_hotplug_execute(acpi_osd_exec_callback function, | |
141 | + void *context) | |
142 | +{ | |
143 | + return __acpi_os_execute(0, function, context, 1); | |
144 | +} | |
145 | + | |
146 | void acpi_os_wait_events_complete(void *context) | |
147 | { | |
148 | flush_workqueue(kacpid_wq); | |
149 | --- a/include/acpi/acpiosxf.h | |
150 | +++ b/include/acpi/acpiosxf.h | |
151 | @@ -193,6 +193,9 @@ acpi_status | |
152 | acpi_os_execute(acpi_execute_type type, | |
153 | acpi_osd_exec_callback function, void *context); | |
154 | ||
155 | +acpi_status | |
156 | +acpi_os_hotplug_execute(acpi_osd_exec_callback function, void *context); | |
157 | + | |
158 | void acpi_os_wait_events_complete(void *context); | |
159 | ||
160 | void acpi_os_sleep(acpi_integer milliseconds); |