Commit 61197547 authored by Lv Zheng's avatar Lv Zheng Committed by Rafael J. Wysocki

ACPI / EC: Fix a race issue in acpi_ec_guard_event()

In acpi_ec_guard_event(), EC transaction state machine variables should be
checked with the EC spinlock locked.
The bug doesn't trigger any real issue now because this bug can only occur
when the ec_event_clearing=event mode is applied while there is no user
currently using this mode.
Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 0700c047
...@@ -441,17 +441,31 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) ...@@ -441,17 +441,31 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
static bool acpi_ec_guard_event(struct acpi_ec *ec) static bool acpi_ec_guard_event(struct acpi_ec *ec)
{ {
bool guarded = true;
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
/*
* If firmware SCI_EVT clearing timing is "event", we actually
* don't know when the SCI_EVT will be cleared by firmware after
* evaluating _Qxx, so we need to re-check SCI_EVT after waiting an
* acceptable period.
*
* The guarding period begins when EC_FLAGS_QUERY_PENDING is
* flagged, which means SCI_EVT check has just been performed.
* But if the current transaction is ACPI_EC_COMMAND_QUERY, the
* guarding should have already been performed (via
* EC_FLAGS_QUERY_GUARDING) and should not be applied so that the
* ACPI_EC_COMMAND_QUERY transaction can be transitioned into
* ACPI_EC_COMMAND_POLL state immediately.
*/
if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS ||
ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY || ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY ||
!test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) || !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) ||
(ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY)) (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY))
return false; guarded = false;
spin_unlock_irqrestore(&ec->lock, flags);
/* return guarded;
* Postpone the query submission to allow the firmware to proceed,
* we shouldn't check SCI_EVT before the firmware reflagging it.
*/
return true;
} }
static int ec_transaction_polled(struct acpi_ec *ec) static int ec_transaction_polled(struct acpi_ec *ec)
...@@ -597,6 +611,7 @@ static int ec_guard(struct acpi_ec *ec) ...@@ -597,6 +611,7 @@ static int ec_guard(struct acpi_ec *ec)
unsigned long guard = usecs_to_jiffies(ec_polling_guard); unsigned long guard = usecs_to_jiffies(ec_polling_guard);
unsigned long timeout = ec->timestamp + guard; unsigned long timeout = ec->timestamp + guard;
/* Ensure guarding period before polling EC status */
do { do {
if (ec_busy_polling) { if (ec_busy_polling) {
/* Perform busy polling */ /* Perform busy polling */
...@@ -606,11 +621,13 @@ static int ec_guard(struct acpi_ec *ec) ...@@ -606,11 +621,13 @@ static int ec_guard(struct acpi_ec *ec)
} else { } else {
/* /*
* Perform wait polling * Perform wait polling
* * 1. Wait the transaction to be completed by the
* For SCI_EVT clearing timing of "event", * GPE handler after the transaction enters
* performing guarding before re-checking the * ACPI_EC_COMMAND_POLL state.
* SCI_EVT. Otherwise, such guarding is not needed * 2. A special guarding logic is also required
* due to the old practices. * for event clearing mode "event" before the
* transaction enters ACPI_EC_COMMAND_POLL
* state.
*/ */
if (!ec_transaction_polled(ec) && if (!ec_transaction_polled(ec) &&
!acpi_ec_guard_event(ec)) !acpi_ec_guard_event(ec))
...@@ -620,7 +637,6 @@ static int ec_guard(struct acpi_ec *ec) ...@@ -620,7 +637,6 @@ static int ec_guard(struct acpi_ec *ec)
guard)) guard))
return 0; return 0;
} }
/* Guard the register accesses for the polling modes */
} while (time_before(jiffies, timeout)); } while (time_before(jiffies, timeout));
return -ETIME; return -ETIME;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment