aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/ec.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-03-03 19:52:58 -0500
committerLen Brown <len.brown@intel.com>2010-03-08 14:15:51 -0500
commitf6bb13aa1ea3bb26a4c783822347873f085b9000 (patch)
treee0350530685e0719d4318f052982aa4340a00650 /drivers/acpi/ec.c
parent60b341b778cc2929df16c0a504c91621b3c6a4ad (diff)
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. 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> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/ec.c')
-rw-r--r--drivers/acpi/ec.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index d6471bb6852..19f93e11422 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -76,8 +76,9 @@ enum ec_command {
76enum { 76enum {
77 EC_FLAGS_QUERY_PENDING, /* Query is pending */ 77 EC_FLAGS_QUERY_PENDING, /* Query is pending */
78 EC_FLAGS_GPE_STORM, /* GPE storm detected */ 78 EC_FLAGS_GPE_STORM, /* GPE storm detected */
79 EC_FLAGS_HANDLERS_INSTALLED /* Handlers for GPE and 79 EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and
80 * OpReg are installed */ 80 * OpReg are installed */
81 EC_FLAGS_FROZEN, /* Transactions are suspended */
81}; 82};
82 83
83/* If we find an EC via the ECDT, we need to keep a ptr to its context */ 84/* 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 acpi_ec *ec, struct transaction *t)
291 if (t->rdata) 292 if (t->rdata)
292 memset(t->rdata, 0, t->rlen); 293 memset(t->rdata, 0, t->rlen);
293 mutex_lock(&ec->lock); 294 mutex_lock(&ec->lock);
295 if (test_bit(EC_FLAGS_FROZEN, &ec->flags)) {
296 status = -EINVAL;
297 goto unlock;
298 }
294 if (ec->global_lock) { 299 if (ec->global_lock) {
295 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); 300 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
296 if (ACPI_FAILURE(status)) { 301 if (ACPI_FAILURE(status)) {
@@ -445,6 +450,32 @@ int ec_transaction(u8 command,
445 450
446EXPORT_SYMBOL(ec_transaction); 451EXPORT_SYMBOL(ec_transaction);
447 452
453void acpi_ec_suspend_transactions(void)
454{
455 struct acpi_ec *ec = first_ec;
456
457 if (!ec)
458 return;
459
460 mutex_lock(&ec->lock);
461 /* Prevent transactions from being carried out */
462 set_bit(EC_FLAGS_FROZEN, &ec->flags);
463 mutex_unlock(&ec->lock);
464}
465
466void acpi_ec_resume_transactions(void)
467{
468 struct acpi_ec *ec = first_ec;
469
470 if (!ec)
471 return;
472
473 mutex_lock(&ec->lock);
474 /* Allow transactions to be carried out again */
475 clear_bit(EC_FLAGS_FROZEN, &ec->flags);
476 mutex_unlock(&ec->lock);
477}
478
448static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data) 479static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 * data)
449{ 480{
450 int result; 481 int result;