Skip to content

Commit

Permalink
ACPI: sleep: Avoid breaking S3 wakeup due to might_sleep()
Browse files Browse the repository at this point in the history
The addition of might_sleep() to down_timeout() caused the latter to
enable interrupts unconditionally in some cases, which in turn broke
the ACPI S3 wakeup path in acpi_suspend_enter(), where down_timeout()
is called by acpi_disable_all_gpes() via acpi_ut_acquire_mutex().

Namely, if CONFIG_DEBUG_ATOMIC_SLEEP is set, might_sleep() causes
might_resched() to be used and if CONFIG_PREEMPT_VOLUNTARY is set,
this triggers __cond_resched() which may call preempt_schedule_common(),
so __schedule() gets invoked and it ends up with enabled interrupts (in
the prev == next case).

Now, enabling interrupts early in the S3 wakeup path causes the kernel
to crash.

Address this by modifying acpi_suspend_enter() to disable GPEs without
attempting to acquire the sleeping lock which is not needed in that code
path anyway.

Fixes: 99409b9 ("locking/semaphore: Add might_sleep() to down_*() family")
Reported-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: 5.15+ <stable@vger.kernel.org> # 5.15+
  • Loading branch information
Rafael J. Wysocki committed Jun 15, 2023
1 parent 858fd16 commit 22db063
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 6 deletions.
2 changes: 0 additions & 2 deletions drivers/acpi/acpica/achware.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ acpi_status
acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
acpi_event_status *event_status);

acpi_status acpi_hw_disable_all_gpes(void);

acpi_status acpi_hw_enable_all_runtime_gpes(void);

acpi_status acpi_hw_enable_all_wakeup_gpes(void);
Expand Down
16 changes: 12 additions & 4 deletions drivers/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -636,11 +636,19 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
}

/*
* Disable and clear GPE status before interrupt is enabled. Some GPEs
* (like wakeup GPE) haven't handler, this can avoid such GPE misfire.
* acpi_leave_sleep_state will reenable specific GPEs later
* Disable all GPE and clear their status bits before interrupts are
* enabled. Some GPEs (like wakeup GPEs) have no handlers and this can
* prevent them from producing spurious interrups.
*
* acpi_leave_sleep_state() will reenable specific GPEs later.
*
* Because this code runs on one CPU with disabled interrupts (all of
* the other CPUs are offline at this time), it need not acquire any
* sleeping locks which may trigger an implicit preemption point even
* if there is no contention, so avoid doing that by using a low-level
* library routine here.
*/
acpi_disable_all_gpes();
acpi_hw_disable_all_gpes();
/* Allow EC transactions to happen. */
acpi_ec_unblock_transactions();

Expand Down
1 change: 1 addition & 0 deletions include/acpi/acpixf.h
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
acpi_event_status
*event_status))
ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_hw_disable_all_gpes(void))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))
Expand Down

0 comments on commit 22db063

Please sign in to comment.