From: "Rafael J. Wysocki" <rjw@sisk.pl>
To: Len Brown <lenb@kernel.org>
Cc: ACPI Devel Maling List <linux-acpi@vger.kernel.org>,
Alexey Starikovskiy <astarikovskiy@suse.de>,
pm list <linux-pm@lists.linux-foundation.org>,
Thomas Renninger <trenn@suse.de>,
Maxim Levitsky <maximlevitsky@gmail.com>,
Matthew Garrett <mjg59@srcf.ucam.org>,
LKML <linux-kernel@vger.kernel.org>,
Henrique de Moraes Holschuh <hmh@hmh.eng.br>,
Alan Jenkins <sourcejedi.lkml@googlemail.com>
Subject: [PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
Date: Thu, 4 Feb 2010 01:33:22 +0100 [thread overview]
Message-ID: <201002040133.22604.rjw@sisk.pl> (raw)
In-Reply-To: <201002030132.21406.rjw@sisk.pl>
On Wednesday 03 February 2010, Rafael J. Wysocki wrote:
> On Sunday 31 January 2010, Rafael J. Wysocki wrote:
> > Hi,
> >
> > While Maxim is testing if the patch below helps with
> > http://bugzilla.kernel.org/show_bug.cgi?id=14668
> > I think it's necessary anyway.
> >
> > The problem is that currently there's nothing to prevent us from suspending in
> > the middle of an EC transaction in progress, at least as far as I can see.
> > As a result, we can suspend with the ACPI global lock held or something like
> > this, which leads to problems especially for hibernation (if the resume kernel
> > passes control to the image kernel in the middle of an EC transaction, things
> > aren't nice). For this reason I think we should wait until there are no EC
> > transactions in progress before we suspend and we should prevent any new
> > EC transactions from starting after that point. The patch below does that.
>
> After discussing the patch with Alex I have a new version which is appended
> below. This one causes EC transactions to be discarded after we've executed
> _PTS (which is reasonable IMO) and allows them to happen again very early
> during wake-up, so effectively the resume behavior should be unchanged.
Unfortunately I overlooked the fact that this patch would break the error paths
and test modes where we also have to allow EC transactions to happen again
after they've been "suspended". Sorry about that.
Updated patch follows.
Rafael
---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
There is a race between the suspend process and the EC driver that
may result in suspending in the middle of an EC transaction in
progress, which in turn may lead to unpredictable behavior of the
platform.
To remove that race condition, add a helpers for suspending and
resuming EC transactions in a safe way to be executed by the ACPI
platform suspend/hibernate callbacks. Modify these callbacks so
that the EC transactions are suspended right after executing the
_PTS global control method and are allowed to happen right after
the low-level wake-up.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reported-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
drivers/acpi/ec.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/acpi/internal.h | 3 +++
drivers/acpi/sleep.c | 47 +++++++++++++++++++++++++++++------------------
3 files changed, 76 insertions(+), 19 deletions(-)
Index: linux-2.6/drivers/acpi/ec.c
===================================================================
--- linux-2.6.orig/drivers/acpi/ec.c
+++ linux-2.6/drivers/acpi/ec.c
@@ -76,8 +76,9 @@ enum ec_command {
enum {
EC_FLAGS_QUERY_PENDING, /* Query is pending */
EC_FLAGS_GPE_STORM, /* GPE storm detected */
- EC_FLAGS_HANDLERS_INSTALLED /* Handlers for GPE and
+ EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and
* OpReg are installed */
+ EC_FLAGS_SUSPENDED, /* Driver is suspended */
};
/* If we find an EC via the ECDT, we need to keep a ptr to its context */
@@ -291,6 +292,12 @@ static int acpi_ec_transaction(struct ac
if (t->rdata)
memset(t->rdata, 0, t->rlen);
mutex_lock(&ec->lock);
+ if (test_bit(EC_FLAGS_SUSPENDED, &ec->flags)) {
+ pr_err(PREFIX
+ "transaction discarded due to power transition\n");
+ status = -EINVAL;
+ goto unlock;
+ }
if (ec->global_lock) {
status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
if (ACPI_FAILURE(status)) {
@@ -445,6 +452,42 @@ int ec_transaction(u8 command,
EXPORT_SYMBOL(ec_transaction);
+void acpi_ec_suspend_transactions(void)
+{
+ struct acpi_ec *ec = first_ec;
+
+ if (!ec)
+ return;
+
+ mutex_lock(&ec->lock);
+ /* Prevent transactions from happening while suspended */
+ set_bit(EC_FLAGS_SUSPENDED, &ec->flags);
+ mutex_unlock(&ec->lock);
+}
+
+void acpi_ec_resume_transactions(void)
+{
+ struct acpi_ec *ec = first_ec;
+
+ if (!ec)
+ return;
+
+ mutex_lock(&ec->lock);
+ /* Allow transactions to happen again */
+ clear_bit(EC_FLAGS_SUSPENDED, &ec->flags);
+ mutex_unlock(&ec->lock);
+}
+
+void acpi_ec_resume_transactions_noirq(void)
+{
+ /*
+ * Allow transactions to happen again (this function is called from
+ * atomic context during wake-up, so we don't need to acquire the mutex)
+ */
+ if (first_ec)
+ clear_bit(EC_FLAGS_SUSPENDED, &first_ec->flags);
+}
+
static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data)
{
int result;
Index: linux-2.6/drivers/acpi/internal.h
===================================================================
--- linux-2.6.orig/drivers/acpi/internal.h
+++ linux-2.6/drivers/acpi/internal.h
@@ -49,6 +49,9 @@ void acpi_early_processor_set_pdc(void);
int acpi_ec_init(void);
int acpi_ec_ecdt_probe(void);
int acpi_boot_ec_enable(void);
+void acpi_ec_suspend_transactions(void);
+void acpi_ec_resume_transactions(void);
+void acpi_ec_resume_transactions_noirq(void);
/*--------------------------------------------------------------------------
Suspend/Resume
Index: linux-2.6/drivers/acpi/sleep.c
===================================================================
--- linux-2.6.orig/drivers/acpi/sleep.c
+++ linux-2.6/drivers/acpi/sleep.c
@@ -110,11 +110,12 @@ void __init acpi_old_suspend_ordering(vo
}
/**
- * acpi_pm_disable_gpes - Disable the GPEs.
+ * acpi_pm_freeze - Disable the GPEs and suspend EC transactions.
*/
-static int acpi_pm_disable_gpes(void)
+static int acpi_pm_freeze(void)
{
acpi_disable_all_gpes();
+ acpi_ec_suspend_transactions();
return 0;
}
@@ -142,7 +143,8 @@ static int acpi_pm_prepare(void)
int error = __acpi_pm_prepare();
if (!error)
- acpi_disable_all_gpes();
+ acpi_pm_freeze();
+
return error;
}
@@ -276,6 +278,8 @@ static int acpi_suspend_enter(suspend_st
*/
acpi_disable_all_gpes();
+ acpi_ec_resume_transactions_noirq();
+
local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n");
@@ -286,6 +290,12 @@ static int acpi_suspend_enter(suspend_st
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
+static void acpi_suspend_finish(void)
+{
+ acpi_ec_resume_transactions();
+ acpi_pm_finish();
+}
+
static int acpi_suspend_state_valid(suspend_state_t pm_state)
{
u32 acpi_state;
@@ -307,7 +317,7 @@ static struct platform_suspend_ops acpi_
.begin = acpi_suspend_begin,
.prepare_late = acpi_pm_prepare,
.enter = acpi_suspend_enter,
- .wake = acpi_pm_finish,
+ .wake = acpi_suspend_finish,
.end = acpi_pm_end,
};
@@ -333,9 +343,9 @@ static int acpi_suspend_begin_old(suspen
static struct platform_suspend_ops acpi_suspend_ops_old = {
.valid = acpi_suspend_state_valid,
.begin = acpi_suspend_begin_old,
- .prepare_late = acpi_pm_disable_gpes,
+ .prepare_late = acpi_pm_freeze,
.enter = acpi_suspend_enter,
- .wake = acpi_pm_finish,
+ .wake = acpi_suspend_finish,
.end = acpi_pm_end,
.recover = acpi_pm_finish,
};
@@ -522,6 +532,7 @@ static int acpi_hibernation_enter(void)
status = acpi_enter_sleep_state(ACPI_STATE_S4);
/* Reprogram control registers and execute _BFS */
acpi_leave_sleep_state_prep(ACPI_STATE_S4);
+ acpi_ec_resume_transactions_noirq();
local_irq_restore(flags);
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
@@ -530,6 +541,7 @@ static int acpi_hibernation_enter(void)
static void acpi_hibernation_finish(void)
{
hibernate_nvs_free();
+ acpi_ec_resume_transactions();
acpi_pm_finish();
}
@@ -550,10 +562,12 @@ static void acpi_hibernation_leave(void)
}
/* Restore the NVS memory area */
hibernate_nvs_restore();
+ acpi_ec_resume_transactions_noirq();
}
-static void acpi_pm_enable_gpes(void)
+static void acpi_pm_thaw(void)
{
+ acpi_ec_resume_transactions();
acpi_enable_all_runtime_gpes();
}
@@ -565,8 +579,8 @@ static struct platform_hibernation_ops a
.prepare = acpi_pm_prepare,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
- .pre_restore = acpi_pm_disable_gpes,
- .restore_cleanup = acpi_pm_enable_gpes,
+ .pre_restore = acpi_pm_freeze,
+ .restore_cleanup = acpi_pm_thaw,
};
/**
@@ -598,12 +612,9 @@ static int acpi_hibernation_begin_old(vo
static int acpi_hibernation_pre_snapshot_old(void)
{
- int error = acpi_pm_disable_gpes();
-
- if (!error)
- hibernate_nvs_save();
-
- return error;
+ acpi_pm_freeze();
+ hibernate_nvs_save();
+ return 0;
}
/*
@@ -615,11 +626,11 @@ static struct platform_hibernation_ops a
.end = acpi_pm_end,
.pre_snapshot = acpi_hibernation_pre_snapshot_old,
.finish = acpi_hibernation_finish,
- .prepare = acpi_pm_disable_gpes,
+ .prepare = acpi_pm_freeze,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
- .pre_restore = acpi_pm_disable_gpes,
- .restore_cleanup = acpi_pm_enable_gpes,
+ .pre_restore = acpi_pm_freeze,
+ .restore_cleanup = acpi_pm_thaw,
.recover = acpi_pm_finish,
};
#endif /* CONFIG_HIBERNATION */
prev parent reply other threads:[~2010-02-04 0:32 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-01-30 23:29 [RFC][RFT][PATCH] ACPI: Protection from suspending in the middle of EC transaction Rafael J. Wysocki
2010-01-31 14:11 ` Alan Jenkins
2010-01-31 17:09 ` Maxim Levitsky
2010-02-01 9:46 ` Henrique de Moraes Holschuh
2010-02-01 10:22 ` Alexey Starikovskiy
2010-02-01 22:42 ` Rafael J. Wysocki
2010-02-02 20:23 ` [PATCH] ACPI / EC: Remover race between EC driver and suspend process (was: Re: [RFC][RFT][PATCH] ACPI: Protection from suspending in the middle of EC transaction) Rafael J. Wysocki
2010-01-31 20:41 ` [RFC][RFT][PATCH] ACPI: Protection from suspending in the middle of EC transaction Maxim Levitsky
2010-01-31 21:09 ` Rafael J. Wysocki
2010-01-31 22:31 ` Rafael J. Wysocki
2010-02-03 0:32 ` [PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 2) (was: Re: [RFC][RFT][PATCH] ACPI: Protection from suspending in the middle of EC transaction) Rafael J. Wysocki
2010-02-04 0:33 ` Rafael J. Wysocki [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201002040133.22604.rjw@sisk.pl \
--to=rjw@sisk.pl \
--cc=astarikovskiy@suse.de \
--cc=hmh@hmh.eng.br \
--cc=lenb@kernel.org \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@lists.linux-foundation.org \
--cc=maximlevitsky@gmail.com \
--cc=mjg59@srcf.ucam.org \
--cc=sourcejedi.lkml@googlemail.com \
--cc=trenn@suse.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox