public inbox for linux-acpi@vger.kernel.org
 help / color / mirror / Atom feed
* [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
@ 2010-02-27 20:40 Rafael J. Wysocki
  2010-03-03 21:49 ` Len Brown
  0 siblings, 1 reply; 7+ messages in thread
From: Rafael J. Wysocki @ 2010-02-27 20:40 UTC (permalink / raw)
  To: Len Brown; +Cc: Alexey Starikovskiy, ACPI Devel Maling List, pm list, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

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 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.

Fixes http://bugzilla.kernel.org/show_bug.cgi?id=14668

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reported-and-tested-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/acpi/ec.c       |   45 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/acpi/internal.h |    3 +++
 drivers/acpi/sleep.c    |   48 ++++++++++++++++++++++++++++++------------------
 3 files changed, 77 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)) {
@@ -453,6 +460,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,13 @@ 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_os_wait_events_complete(NULL);
+	acpi_ec_suspend_transactions();
 	return 0;
 }
 
@@ -142,7 +144,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 +279,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 +291,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 +318,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 +344,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 +533,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 +542,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 +563,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 +580,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 +613,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 +627,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 */

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
  2010-02-27 20:40 [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3) Rafael J. Wysocki
@ 2010-03-03 21:49 ` Len Brown
  2010-03-04  0:52   ` Rafael J. Wysocki
  0 siblings, 1 reply; 7+ messages in thread
From: Len Brown @ 2010-03-03 21:49 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Alexey Starikovskiy, ACPI Devel Maling List, pm list, LKML

For the record, Rafael, Alexey and I discusseed this a bit further...

We reasoned that this failure is impossible during the Suspend-to-RAM 
path, and the failure is specific to the hibernate reboot kernel handoff
to the resume image.

On a related note, we had trouble justifying the existence
of the current EC .suspend and .resume routines.

thanks,
Len Brown, Intel Open Source Technology Center


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
  2010-03-03 21:49 ` Len Brown
@ 2010-03-04  0:52   ` Rafael J. Wysocki
  2010-03-04  5:55     ` Len Brown
  2010-03-08 21:10     ` Rafael J. Wysocki
  0 siblings, 2 replies; 7+ messages in thread
From: Rafael J. Wysocki @ 2010-03-04  0:52 UTC (permalink / raw)
  To: Len Brown
  Cc: Alexey Starikovskiy, ACPI Devel Maling List, pm list, LKML,
	Maxim Levitsky

On Wednesday 03 March 2010, Len Brown wrote:
> For the record, Rafael, Alexey and I discusseed this a bit further...
> 
> We reasoned that this failure is impossible during the Suspend-to-RAM 
> path, and the failure is specific to the hibernate reboot kernel handoff
> to the resume image.

Below is a simplified version of the patch that also should fix the problem.

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / EC / PM: Close race between EC and resume from hibernation

There is a race between resume from hibernation and the EC driver
that may result in restoring the hibernation image 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 hibernate pre-restore and restore cleanup callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reported-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/acpi/ec.c       |   33 ++++++++++++++++++++++++++++++++-
 drivers/acpi/internal.h |    2 ++
 drivers/acpi/sleep.c    |   19 ++++++++++++++-----
 3 files changed, 48 insertions(+), 6 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_FROZEN,		/* Transactions are suspended */
 };
 
 /* If we find an EC via the ECDT, we need to keep a ptr to its context */
@@ -291,6 +292,10 @@ 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_FROZEN, &ec->flags)) {
+		status = -EINVAL;
+		goto unlock;
+	}
 	if (ec->global_lock) {
 		status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
 		if (ACPI_FAILURE(status)) {
@@ -453,6 +458,32 @@ 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 being carried out */
+	set_bit(EC_FLAGS_FROZEN, &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 be carried out again */
+	clear_bit(EC_FLAGS_FROZEN, &ec->flags);
+	mutex_unlock(&ec->lock);
+}
+
 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,8 @@ 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);
 
 /*--------------------------------------------------------------------------
                                   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
@@ -552,8 +552,17 @@ static void acpi_hibernation_leave(void)
 	hibernate_nvs_restore();
 }
 
-static void acpi_pm_enable_gpes(void)
+static int acpi_pm_pre_restore(void)
 {
+	acpi_disable_all_gpes();
+	acpi_os_wait_events_complete(NULL);
+	acpi_ec_suspend_transactions();
+	return 0;
+}
+
+static void acpi_pm_restore_cleanup(void)
+{
+	acpi_ec_resume_transactions();
 	acpi_enable_all_runtime_gpes();
 }
 
@@ -565,8 +574,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_pre_restore,
+	.restore_cleanup = acpi_pm_restore_cleanup,
 };
 
 /**
@@ -618,8 +627,8 @@ static struct platform_hibernation_ops a
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
-	.pre_restore = acpi_pm_disable_gpes,
-	.restore_cleanup = acpi_pm_enable_gpes,
+	.pre_restore = acpi_pm_pre_restore,
+	.restore_cleanup = acpi_pm_restore_cleanup,
 	.recover = acpi_pm_finish,
 };
 #endif /* CONFIG_HIBERNATION */

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
  2010-03-04  0:52   ` Rafael J. Wysocki
@ 2010-03-04  5:55     ` Len Brown
  2010-03-04  9:32       ` Alan Jenkins
  2010-03-08 21:10     ` Rafael J. Wysocki
  1 sibling, 1 reply; 7+ messages in thread
From: Len Brown @ 2010-03-04  5:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Alexey Starikovskiy, ACPI Devel Maling List, pm list, LKML,
	Maxim Levitsky

Isn't the problem at hand that the boot-kernel stops
in mid-transaction, and the resumed hibernate image
then blunders forward with its first transaction
only to find the EC in an intermediate state?

If yes, I then making the transaction atomic WRT
the boot kernel stopping should fix the problem.

The patch sets a FROZEN bit, and subsequent
requests for EC transactions simply fail.
How do we know that we set the bit at the right time?
What transactions or parts of transactions will fail,
and what are the consequences of those failures?

thanks,
Len Brown, Intel Open Source Technology Center


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
  2010-03-04  5:55     ` Len Brown
@ 2010-03-04  9:32       ` Alan Jenkins
  2010-03-04 19:20         ` Rafael J. Wysocki
  0 siblings, 1 reply; 7+ messages in thread
From: Alan Jenkins @ 2010-03-04  9:32 UTC (permalink / raw)
  To: Len Brown
  Cc: Rafael J. Wysocki, Alexey Starikovskiy, ACPI Devel Maling List,
	pm list, LKML, Maxim Levitsky

On 3/4/10, Len Brown <lenb@kernel.org> wrote:
> Isn't the problem at hand that the boot-kernel stops
> in mid-transaction, and the resumed hibernate image
> then blunders forward with its first transaction
> only to find the EC in an intermediate state?
>
> If yes, I then making the transaction atomic WRT
> the boot kernel stopping should fix the problem.
>
> The patch sets a FROZEN bit, and subsequent
> requests for EC transactions simply fail.
> How do we know that we set the bit at the right time?
> What transactions or parts of transactions will fail,
> and what are the consequences of those failures?

The problem transactions on my machine were probably started by
ec_check_sci().  I.e. QUERY transactions which read the value of an
event raised by the EC.  (I triggered the problem by pressing ACPI
hotkeys, although I haven't been able to reproduce it since).

That might explain why the transactions can be asynchronous with the
s2disk task - because these transactions are run in the ACPI
workqueue.  Perhaps the workqueue needs to be flushed after the EC GPE
is disabled?

Alan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
  2010-03-04  9:32       ` Alan Jenkins
@ 2010-03-04 19:20         ` Rafael J. Wysocki
  0 siblings, 0 replies; 7+ messages in thread
From: Rafael J. Wysocki @ 2010-03-04 19:20 UTC (permalink / raw)
  To: Alan Jenkins
  Cc: Len Brown, Alexey Starikovskiy, ACPI Devel Maling List, pm list,
	LKML, Maxim Levitsky

On Thursday 04 March 2010, Alan Jenkins wrote:
> On 3/4/10, Len Brown <lenb@kernel.org> wrote:
> > Isn't the problem at hand that the boot-kernel stops
> > in mid-transaction, and the resumed hibernate image
> > then blunders forward with its first transaction
> > only to find the EC in an intermediate state?
> >
> > If yes, I then making the transaction atomic WRT
> > the boot kernel stopping should fix the problem.
> >
> > The patch sets a FROZEN bit, and subsequent
> > requests for EC transactions simply fail.
> > How do we know that we set the bit at the right time?

Well, setting it later simply doesn't make sense and setting it earlier might
disturb things.

> > What transactions or parts of transactions will fail,
> > and what are the consequences of those failures?
> 
> The problem transactions on my machine were probably started by
> ec_check_sci().  I.e. QUERY transactions which read the value of an
> event raised by the EC.  (I triggered the problem by pressing ACPI
> hotkeys, although I haven't been able to reproduce it since).
> 
> That might explain why the transactions can be asynchronous with the
> s2disk task - because these transactions are run in the ACPI
> workqueue.  Perhaps the workqueue needs to be flushed after the EC GPE
> is disabled?

Yes, and that's what the patch does.  It calls acpi_os_wait_events_complete()
to flush the workqueue (actually, two of them) and _then_ sets the FROZEN
bit.

Dropping stuff on the floor after the FROZEN bit has been set doesn't change
things too much, because we're going to turn interrupts off shortly after that.
However, the FROZEN bit is set under the EC mutex, which automatically makes
us wait if any transaction is in progress while we're trying to set the FROZEN
bit.

Rafael

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3)
  2010-03-04  0:52   ` Rafael J. Wysocki
  2010-03-04  5:55     ` Len Brown
@ 2010-03-08 21:10     ` Rafael J. Wysocki
  1 sibling, 0 replies; 7+ messages in thread
From: Rafael J. Wysocki @ 2010-03-08 21:10 UTC (permalink / raw)
  To: Len Brown
  Cc: Alexey Starikovskiy, ACPI Devel Maling List, pm list, LKML,
	Maxim Levitsky

On Thursday 04 March 2010, Rafael J. Wysocki wrote:
> On Wednesday 03 March 2010, Len Brown wrote:
> > For the record, Rafael, Alexey and I discusseed this a bit further...
> > 
> > We reasoned that this failure is impossible during the Suspend-to-RAM 
> > path, and the failure is specific to the hibernate reboot kernel handoff
> > to the resume image.
> 
> Below is a simplified version of the patch that also should fix the problem.

Appended is updated version with the names changed as discussed on IRC.

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ACPI / EC / PM: Close race between EC and resume from hibernation (rev. 2)

There is a race between resume from hibernation and the EC driver
that may result in restoring the hibernation image 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 hibernate pre-restore and restore cleanup callbacks.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reported-by: Maxim Levitsky <maximlevitsky@gmail.com>
---
 drivers/acpi/ec.c       |   33 ++++++++++++++++++++++++++++++++-
 drivers/acpi/internal.h |    2 ++
 drivers/acpi/sleep.c    |   19 ++++++++++++++-----
 3 files changed, 48 insertions(+), 6 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_BLOCKED,		/* Transactions are blocked */
 };
 
 /* If we find an EC via the ECDT, we need to keep a ptr to its context */
@@ -291,6 +292,10 @@ 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_BLOCKED, &ec->flags)) {
+		status = -EINVAL;
+		goto unlock;
+	}
 	if (ec->global_lock) {
 		status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
 		if (ACPI_FAILURE(status)) {
@@ -453,6 +458,32 @@ int ec_transaction(u8 command,
 
 EXPORT_SYMBOL(ec_transaction);
 
+void acpi_ec_block_transactions(void)
+{
+	struct acpi_ec *ec = first_ec;
+
+	if (!ec)
+		return;
+
+	mutex_lock(&ec->lock);
+	/* Prevent transactions from being carried out */
+	set_bit(EC_FLAGS_BLOCKED, &ec->flags);
+	mutex_unlock(&ec->lock);
+}
+
+void acpi_ec_unblock_transactions(void)
+{
+	struct acpi_ec *ec = first_ec;
+
+	if (!ec)
+		return;
+
+	mutex_lock(&ec->lock);
+	/* Allow transactions to be carried out again */
+	clear_bit(EC_FLAGS_BLOCKED, &ec->flags);
+	mutex_unlock(&ec->lock);
+}
+
 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,8 @@ 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_block_transactions(void);
+void acpi_ec_unblock_transactions(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
@@ -552,8 +552,17 @@ static void acpi_hibernation_leave(void)
 	hibernate_nvs_restore();
 }
 
-static void acpi_pm_enable_gpes(void)
+static int acpi_pm_pre_restore(void)
 {
+	acpi_disable_all_gpes();
+	acpi_os_wait_events_complete(NULL);
+	acpi_ec_block_transactions();
+	return 0;
+}
+
+static void acpi_pm_restore_cleanup(void)
+{
+	acpi_ec_unblock_transactions();
 	acpi_enable_all_runtime_gpes();
 }
 
@@ -565,8 +574,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_pre_restore,
+	.restore_cleanup = acpi_pm_restore_cleanup,
 };
 
 /**
@@ -618,8 +627,8 @@ static struct platform_hibernation_ops a
 	.prepare = acpi_pm_disable_gpes,
 	.enter = acpi_hibernation_enter,
 	.leave = acpi_hibernation_leave,
-	.pre_restore = acpi_pm_disable_gpes,
-	.restore_cleanup = acpi_pm_enable_gpes,
+	.pre_restore = acpi_pm_pre_restore,
+	.restore_cleanup = acpi_pm_restore_cleanup,
 	.recover = acpi_pm_finish,
 };
 #endif /* CONFIG_HIBERNATION */

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-03-08 21:08 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-02-27 20:40 [Resend][PATCH] ACPI / EC: Remove race between EC driver and suspend process (rev. 3) Rafael J. Wysocki
2010-03-03 21:49 ` Len Brown
2010-03-04  0:52   ` Rafael J. Wysocki
2010-03-04  5:55     ` Len Brown
2010-03-04  9:32       ` Alan Jenkins
2010-03-04 19:20         ` Rafael J. Wysocki
2010-03-08 21:10     ` Rafael J. Wysocki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox