Commit 6b1f21a1 authored by Lv Zheng's avatar Lv Zheng Committed by Greg Kroah-Hartman

ACPI / EC: Work around method reentrancy limit in ACPICA for _Qxx

commit e1191bd4 upstream.

A regression is caused by the following commit:

  Commit: 02b771b6
  Subject: ACPI / EC: Fix an issue caused by the serialized _Qxx evaluations

In this commit, using system workqueue causes that the maximum parallel
executions of _Qxx can exceed 255. This violates the method reentrancy
limit in ACPICA and generates the following error log:

  ACPI Error: Method reached maximum reentrancy limit (255) (20150818/dsmethod-341)

This patch creates a seperate workqueue and limits the number of parallel
_Qxx evaluations down to a configurable value (can be tuned against number
of online CPUs).

Since EC events are handled after driver probe, we can create the workqueue
in acpi_ec_init().

Fixes: 02b771b6 (ACPI / EC: Fix an issue caused by the serialized _Qxx evaluations)
Link: https://bugzilla.kernel.org/show_bug.cgi?id=135691Reported-and-tested-by: default avatarHelen Buus <ubuntu@hbuus.com>
Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 32b04db4
...@@ -101,6 +101,7 @@ enum ec_command { ...@@ -101,6 +101,7 @@ enum ec_command {
#define ACPI_EC_UDELAY_POLL 550 /* Wait 1ms for EC transaction polling */ #define ACPI_EC_UDELAY_POLL 550 /* Wait 1ms for EC transaction polling */
#define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query
* when trying to clear the EC */ * when trying to clear the EC */
#define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */
enum { enum {
EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_QUERY_PENDING, /* Query is pending */
...@@ -121,6 +122,10 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; ...@@ -121,6 +122,10 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY;
module_param(ec_delay, uint, 0644); module_param(ec_delay, uint, 0644);
MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes");
static unsigned int ec_max_queries __read_mostly = ACPI_EC_MAX_QUERIES;
module_param(ec_max_queries, uint, 0644);
MODULE_PARM_DESC(ec_max_queries, "Maximum parallel _Qxx evaluations");
static bool ec_busy_polling __read_mostly; static bool ec_busy_polling __read_mostly;
module_param(ec_busy_polling, bool, 0644); module_param(ec_busy_polling, bool, 0644);
MODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction"); MODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction");
...@@ -174,6 +179,7 @@ static void acpi_ec_event_processor(struct work_struct *work); ...@@ -174,6 +179,7 @@ static void acpi_ec_event_processor(struct work_struct *work);
struct acpi_ec *boot_ec, *first_ec; struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec); EXPORT_SYMBOL(first_ec);
static struct workqueue_struct *ec_query_wq;
static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */
static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */
...@@ -1097,7 +1103,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) ...@@ -1097,7 +1103,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
* work queue execution. * work queue execution.
*/ */
ec_dbg_evt("Query(0x%02x) scheduled", value); ec_dbg_evt("Query(0x%02x) scheduled", value);
if (!schedule_work(&q->work)) { if (!queue_work(ec_query_wq, &q->work)) {
ec_dbg_evt("Query(0x%02x) overlapped", value); ec_dbg_evt("Query(0x%02x) overlapped", value);
result = -EBUSY; result = -EBUSY;
} }
...@@ -1657,15 +1663,41 @@ static struct acpi_driver acpi_ec_driver = { ...@@ -1657,15 +1663,41 @@ static struct acpi_driver acpi_ec_driver = {
}, },
}; };
static inline int acpi_ec_query_init(void)
{
if (!ec_query_wq) {
ec_query_wq = alloc_workqueue("kec_query", 0,
ec_max_queries);
if (!ec_query_wq)
return -ENODEV;
}
return 0;
}
static inline void acpi_ec_query_exit(void)
{
if (ec_query_wq) {
destroy_workqueue(ec_query_wq);
ec_query_wq = NULL;
}
}
int __init acpi_ec_init(void) int __init acpi_ec_init(void)
{ {
int result = 0; int result;
/* register workqueue for _Qxx evaluations */
result = acpi_ec_query_init();
if (result)
goto err_exit;
/* Now register the driver for the EC */ /* Now register the driver for the EC */
result = acpi_bus_register_driver(&acpi_ec_driver); result = acpi_bus_register_driver(&acpi_ec_driver);
if (result < 0) if (result)
return -ENODEV; goto err_exit;
err_exit:
if (result)
acpi_ec_query_exit();
return result; return result;
} }
...@@ -1675,5 +1707,6 @@ static void __exit acpi_ec_exit(void) ...@@ -1675,5 +1707,6 @@ static void __exit acpi_ec_exit(void)
{ {
acpi_bus_unregister_driver(&acpi_ec_driver); acpi_bus_unregister_driver(&acpi_ec_driver);
acpi_ec_query_exit();
} }
#endif /* 0 */ #endif /* 0 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment