Skip to content

Commit 02b771b

Browse files
Lv Zhengrafaeljw
authored andcommitted
ACPI / EC: Fix an issue caused by the serialized _Qxx evaluations
It is proven that Windows evaluates _Qxx handlers in a parallel way. This patch follows this fact, splits _Qxx evaluations from the NOTIFY queue to form a separate queue, so that _Qxx evaluations can be queued up on different CPUs rather than being queued up on a CPU0 bound queue. Event handling related callbacks are also renamed and sorted in this patch. Link: https://bugzilla.kernel.org/show_bug.cgi?id=94411 Reported-and-tested-by: Gabriele Mazzotta <gabriele.mzt@gmail.com> Signed-off-by: Lv Zheng <lv.zheng@intel.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent c13dcf9 commit 02b771b

File tree

1 file changed

+60
-22
lines changed

1 file changed

+60
-22
lines changed

drivers/acpi/ec.c

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,16 @@ struct transaction {
165165
u8 flags;
166166
};
167167

168+
struct acpi_ec_query {
169+
struct transaction transaction;
170+
struct work_struct work;
171+
struct acpi_ec_query_handler *handler;
172+
};
173+
168174
static int acpi_ec_query(struct acpi_ec *ec, u8 *data);
169175
static void advance_transaction(struct acpi_ec *ec);
176+
static void acpi_ec_event_handler(struct work_struct *work);
177+
static void acpi_ec_event_processor(struct work_struct *work);
170178

171179
struct acpi_ec *boot_ec, *first_ec;
172180
EXPORT_SYMBOL(first_ec);
@@ -978,60 +986,90 @@ void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit)
978986
}
979987
EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
980988

981-
static void acpi_ec_run(void *cxt)
989+
static struct acpi_ec_query *acpi_ec_create_query(u8 *pval)
982990
{
983-
struct acpi_ec_query_handler *handler = cxt;
991+
struct acpi_ec_query *q;
992+
struct transaction *t;
993+
994+
q = kzalloc(sizeof (struct acpi_ec_query), GFP_KERNEL);
995+
if (!q)
996+
return NULL;
997+
INIT_WORK(&q->work, acpi_ec_event_processor);
998+
t = &q->transaction;
999+
t->command = ACPI_EC_COMMAND_QUERY;
1000+
t->rdata = pval;
1001+
t->rlen = 1;
1002+
return q;
1003+
}
1004+
1005+
static void acpi_ec_delete_query(struct acpi_ec_query *q)
1006+
{
1007+
if (q) {
1008+
if (q->handler)
1009+
acpi_ec_put_query_handler(q->handler);
1010+
kfree(q);
1011+
}
1012+
}
1013+
1014+
static void acpi_ec_event_processor(struct work_struct *work)
1015+
{
1016+
struct acpi_ec_query *q = container_of(work, struct acpi_ec_query, work);
1017+
struct acpi_ec_query_handler *handler = q->handler;
9841018

985-
if (!handler)
986-
return;
9871019
ec_dbg_evt("Query(0x%02x) started", handler->query_bit);
9881020
if (handler->func)
9891021
handler->func(handler->data);
9901022
else if (handler->handle)
9911023
acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
9921024
ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit);
993-
acpi_ec_put_query_handler(handler);
1025+
acpi_ec_delete_query(q);
9941026
}
9951027

9961028
static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
9971029
{
9981030
u8 value = 0;
9991031
int result;
1000-
acpi_status status;
10011032
struct acpi_ec_query_handler *handler;
1002-
struct transaction t = {.command = ACPI_EC_COMMAND_QUERY,
1003-
.wdata = NULL, .rdata = &value,
1004-
.wlen = 0, .rlen = 1};
1033+
struct acpi_ec_query *q;
1034+
1035+
q = acpi_ec_create_query(&value);
1036+
if (!q)
1037+
return -ENOMEM;
10051038

10061039
/*
10071040
* Query the EC to find out which _Qxx method we need to evaluate.
10081041
* Note that successful completion of the query causes the ACPI_EC_SCI
10091042
* bit to be cleared (and thus clearing the interrupt source).
10101043
*/
1011-
result = acpi_ec_transaction(ec, &t);
1012-
if (result)
1013-
return result;
1014-
if (data)
1015-
*data = value;
1044+
result = acpi_ec_transaction(ec, &q->transaction);
10161045
if (!value)
1017-
return -ENODATA;
1046+
result = -ENODATA;
1047+
if (result)
1048+
goto err_exit;
10181049

10191050
mutex_lock(&ec->mutex);
10201051
list_for_each_entry(handler, &ec->list, node) {
10211052
if (value == handler->query_bit) {
1022-
/* have custom handler for this bit */
1023-
handler = acpi_ec_get_query_handler(handler);
1053+
q->handler = acpi_ec_get_query_handler(handler);
10241054
ec_dbg_evt("Query(0x%02x) scheduled",
1025-
handler->query_bit);
1026-
status = acpi_os_execute((handler->func) ?
1027-
OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER,
1028-
acpi_ec_run, handler);
1029-
if (ACPI_FAILURE(status))
1055+
q->handler->query_bit);
1056+
/*
1057+
* It is reported that _Qxx are evaluated in a
1058+
* parallel way on Windows:
1059+
* https://bugzilla.kernel.org/show_bug.cgi?id=94411
1060+
*/
1061+
if (!schedule_work(&q->work))
10301062
result = -EBUSY;
10311063
break;
10321064
}
10331065
}
10341066
mutex_unlock(&ec->mutex);
1067+
1068+
err_exit:
1069+
if (result && q)
1070+
acpi_ec_delete_query(q);
1071+
if (data)
1072+
*data = value;
10351073
return result;
10361074
}
10371075

0 commit comments

Comments
 (0)