lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 12 Aug 2015 11:12:02 +0800
From:	Lv Zheng <lv.zheng@...el.com>
To:	"Rafael J. Wysocki" <rafael.j.wysocki@...el.com>,
	Len Brown <len.brown@...el.com>
Cc:	Lv Zheng <lv.zheng@...el.com>, Lv Zheng <zetalog@...il.com>,
	<linux-kernel@...r.kernel.org>, linux-acpi@...r.kernel.org
Subject: [PATCH v2] 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@...il.com>
Signed-off-by: Lv Zheng <lv.zheng@...el.com>
---
 drivers/acpi/ec.c |   76 ++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 55 insertions(+), 21 deletions(-)

Index: linux-acpica/drivers/acpi/ec.c
===================================================================
--- linux-acpica.orig/drivers/acpi/ec.c
+++ linux-acpica/drivers/acpi/ec.c
@@ -161,8 +161,16 @@ struct transaction {
 	u8 flags;
 };
 
+struct acpi_ec_query {
+	struct transaction transaction;
+	struct work_struct work;
+	struct acpi_ec_query_handler *handler;
+};
+
 static int acpi_ec_query(struct acpi_ec *ec, u8 *data);
 static void advance_transaction(struct acpi_ec *ec);
+static void acpi_ec_event_handler(struct work_struct *work);
+static void acpi_ec_event_processor(struct work_struct *work);
 
 struct acpi_ec *boot_ec, *first_ec;
 EXPORT_SYMBOL(first_ec);
@@ -974,60 +982,90 @@ void acpi_ec_remove_query_handler(struct
 }
 EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler);
 
-static void acpi_ec_run(void *cxt)
+static struct acpi_ec_query *acpi_ec_create_query(u8 *pval)
 {
-	struct acpi_ec_query_handler *handler = cxt;
+	struct acpi_ec_query *q;
+	struct transaction *t;
+
+	q = kzalloc(sizeof (struct acpi_ec_query), GFP_KERNEL);
+	if (!q)
+		return NULL;
+	INIT_WORK(&q->work, acpi_ec_event_processor);
+	t = &q->transaction;
+	t->command = ACPI_EC_COMMAND_QUERY;
+	t->rdata = pval;
+	t->rlen = 1;
+	return q;
+}
+
+static void acpi_ec_delete_query(struct acpi_ec_query *q)
+{
+	if (q) {
+		if (q->handler)
+			acpi_ec_put_query_handler(q->handler);
+		kfree(q);
+	}
+}
+
+static void acpi_ec_event_processor(struct work_struct *work)
+{
+	struct acpi_ec_query *q = container_of(work, struct acpi_ec_query, work);
+	struct acpi_ec_query_handler *handler = q->handler;
 
-	if (!handler)
-		return;
 	ec_dbg_evt("Query(0x%02x) started", handler->query_bit);
 	if (handler->func)
 		handler->func(handler->data);
 	else if (handler->handle)
 		acpi_evaluate_object(handler->handle, NULL, NULL, NULL);
 	ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit);
-	acpi_ec_put_query_handler(handler);
+	acpi_ec_delete_query(q);
 }
 
 static int acpi_ec_query(struct acpi_ec *ec, u8 *data)
 {
 	u8 value = 0;
 	int result;
-	acpi_status status;
 	struct acpi_ec_query_handler *handler;
-	struct transaction t = {.command = ACPI_EC_COMMAND_QUERY,
-				.wdata = NULL, .rdata = &value,
-				.wlen = 0, .rlen = 1};
+	struct acpi_ec_query *q;
+
+	q = acpi_ec_create_query(&value);
+	if (!q)
+		return -ENOMEM;
 
 	/*
 	 * Query the EC to find out which _Qxx method we need to evaluate.
 	 * Note that successful completion of the query causes the ACPI_EC_SCI
 	 * bit to be cleared (and thus clearing the interrupt source).
 	 */
-	result = acpi_ec_transaction(ec, &t);
-	if (result)
-		return result;
-	if (data)
-		*data = value;
+	result = acpi_ec_transaction(ec, &q->transaction);
 	if (!value)
-		return -ENODATA;
+		result = -ENODATA;
+	if (result)
+		goto err_exit;
 
 	mutex_lock(&ec->mutex);
 	list_for_each_entry(handler, &ec->list, node) {
 		if (value == handler->query_bit) {
-			/* have custom handler for this bit */
-			handler = acpi_ec_get_query_handler(handler);
+			q->handler = acpi_ec_get_query_handler(handler);
 			ec_dbg_evt("Query(0x%02x) scheduled",
-				   handler->query_bit);
-			status = acpi_os_execute((handler->func) ?
-				OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER,
-				acpi_ec_run, handler);
-			if (ACPI_FAILURE(status))
+				   q->handler->query_bit);
+			/*
+			 * It is reported that _Qxx are evaluated in a
+			 * parallel way on Windows:
+			 * https://bugzilla.kernel.org/show_bug.cgi?id=94411
+			 */
+			if (!schedule_work(&q->work))
 				result = -EBUSY;
 			break;
 		}
 	}
 	mutex_unlock(&ec->mutex);
+
+err_exit:
+	if (result && q)
+		acpi_ec_delete_query(q);
+	if (data)
+		*data = value;
 	return result;
 }
 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ