From patchwork Sun Jun 15 00:42:07 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lv Zheng X-Patchwork-Id: 4353901 Return-Path: X-Original-To: patchwork-linux-acpi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 6A416BEEAA for ; Sun, 15 Jun 2014 00:43:27 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 87BCC2027D for ; Sun, 15 Jun 2014 00:43:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8702A2025A for ; Sun, 15 Jun 2014 00:43:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751958AbaFOAmV (ORCPT ); Sat, 14 Jun 2014 20:42:21 -0400 Received: from mail-pd0-f178.google.com ([209.85.192.178]:54122 "EHLO mail-pd0-f178.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751844AbaFOAmS (ORCPT ); Sat, 14 Jun 2014 20:42:18 -0400 Received: by mail-pd0-f178.google.com with SMTP id r10so3272097pdi.9 for ; Sat, 14 Jun 2014 17:42:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=MIIL5yD4gTapeA5KDaO1zAixZOCxsoG51pbeB1dHOZs=; b=Kq77vAytPoRoUh+OGB+vIJso+Mh+cwsAsYNnkjKjwY4ZcjZkPzuExG3I1nmioj7/ri 6oGAPd26EXDBHxxa5rg7t6Hkua4fVrwlJljwLuKEcAWJQvoMG4ZTvHQ/MyNhbXMrqPKR SkdKN2XVjqjhsfMlCTosN/IRZ6CbigXK/RFzkT1uj0jRaME18X2LspBaRvoKSPGEBMah jC5aZSa69xkjQJSem1uh7W172Do3fiioWafpbjC3Qp2VlM989TeytZcvw8zoq4l8SFc0 mqRl8GvMrQuva5BB0adzwj+BKhRSaZhgUSYM9jF1TN6jECEQoWw6x762Eb3gPtV2wzIA WZ5Q== X-Received: by 10.66.146.72 with SMTP id ta8mr146279pab.150.1402792937793; Sat, 14 Jun 2014 17:42:17 -0700 (PDT) Received: from localhost.localdomain ([180.169.136.70]) by mx.google.com with ESMTPSA id ee5sm10872970pbc.47.2014.06.14.17.42.14 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 14 Jun 2014 17:42:17 -0700 (PDT) From: Lv Zheng To: "Rafael J. Wysocki" , Len Brown Cc: Lv Zheng , Lv Zheng , , linux-acpi@vger.kernel.org Subject: [PATCH v2 4/7] ACPI/EC: Fix a race condition in ec_transaction_completed(). Date: Sun, 15 Jun 2014 08:42:07 +0800 Message-Id: X-Mailer: git-send-email 1.7.10 In-Reply-To: References: Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Lv Zheng There is a race condition in ec_transaction_completed(). When ec_transaction_completed() is called in the GPE handler, it could return true because of (ec->curr == NULL). Then the wake_up() invocation could complete the next command unexpectedly since there is no lock between the 2 invocations. With the previous cleanup, we now needn't handle IBF=0 waiter race. It's now safe for us to return a flag from advance_condition() to indicate the requirement of wakeup, the flag is returned from a locked context. The ec_transaction_completed() now is only invoked by the ec_poll() where the ec->curr is ensured to be !NULL. After cleaning up, the EVT_SCI=1 check should be moved out of the wakeup condition so that an EVT_SCI raised with (ec->curr == NULL) can trigger a QR_SC command. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=70891 Signed-off-by: Lv Zheng Reported-and-tested-by: Gareth Williams Tested-by: Steffen Weber [zetalog: first affected by:] Cc: # 2.6.11: 7c6db4e0: ACPI: EC: do transaction from interrupt context [zetalog: cleanly applying to:] Cc: # 3.14.x: 42b946bb: ACPI / EC: disable GPE before removing GPE handler --- drivers/acpi/ec.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index d016ea3..49d8990 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -158,16 +158,17 @@ static int ec_transaction_completed(struct acpi_ec *ec) unsigned long flags; int ret = 0; spin_lock_irqsave(&ec->lock, flags); - if (!ec->curr || (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) + if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) ret = 1; spin_unlock_irqrestore(&ec->lock, flags); return ret; } -static void advance_transaction(struct acpi_ec *ec) +static bool advance_transaction(struct acpi_ec *ec) { struct transaction *t; u8 status; + bool wakeup = false; pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK"); status = acpi_ec_read_status(ec); @@ -183,21 +184,25 @@ static void advance_transaction(struct acpi_ec *ec) } else if (t->rlen > t->ri) { if ((status & ACPI_EC_FLAG_OBF) == 1) { t->rdata[t->ri++] = acpi_ec_read_data(ec); - if (t->rlen == t->ri) + if (t->rlen == t->ri) { t->flags |= ACPI_EC_COMMAND_COMPLETE; + wakeup = true; + } } else goto err; } else if (t->wlen == t->wi && - (status & ACPI_EC_FLAG_IBF) == 0) + (status & ACPI_EC_FLAG_IBF) == 0) { t->flags |= ACPI_EC_COMMAND_COMPLETE; - return; + wakeup = true; + } + return wakeup; } else { if ((status & ACPI_EC_FLAG_IBF) == 0) { acpi_ec_write_cmd(ec, t->command); t->flags |= ACPI_EC_COMMAND_POLL; } else goto err; - return; + return wakeup; } err: /* @@ -208,13 +213,14 @@ err: if (in_interrupt() && t) ++t->irq_count; } + return wakeup; } static void start_transaction(struct acpi_ec *ec) { ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; ec->curr->flags = 0; - advance_transaction(ec); + (void)advance_transaction(ec); } static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data); @@ -248,7 +254,7 @@ static int ec_poll(struct acpi_ec *ec) return 0; } spin_lock_irqsave(&ec->lock, flags); - advance_transaction(ec); + (void)advance_transaction(ec); spin_unlock_irqrestore(&ec->lock, flags); } while (time_before(jiffies, delay)); pr_debug("controller reset, restart transaction\n"); @@ -627,12 +633,10 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, struct acpi_ec *ec = data; spin_lock_irqsave(&ec->lock, flags); - advance_transaction(ec); - spin_unlock_irqrestore(&ec->lock, flags); - if (ec_transaction_completed(ec)) { + if (advance_transaction(ec)) wake_up(&ec->wait); - ec_check_sci(ec, acpi_ec_read_status(ec)); - } + spin_unlock_irqrestore(&ec->lock, flags); + ec_check_sci(ec, acpi_ec_read_status(ec)); return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; }