| From a840d71b60fcb8d12ac047327183395db4e5cb21 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 29 Sep 2021 18:31:25 +0200 |
| Subject: ACPICA: Avoid evaluating methods too early during system resume |
| |
| From: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| |
| [ Upstream commit d3c4b6f64ad356c0d9ddbcf73fa471e6a841cc5c ] |
| |
| ACPICA commit 0762982923f95eb652cf7ded27356b247c9774de |
| |
| During wakeup from system-wide sleep states, acpi_get_sleep_type_data() |
| is called and it tries to get memory from the slab allocator in order |
| to evaluate a control method, but if KFENCE is enabled in the kernel, |
| the memory allocation attempt causes an IRQ work to be queued and a |
| self-IPI to be sent to the CPU running the code which requires the |
| memory controller to be ready, so if that happens too early in the |
| wakeup path, it doesn't work. |
| |
| Prevent that from taking place by calling acpi_get_sleep_type_data() |
| for S0 upfront, when preparing to enter a given sleep state, and |
| saving the data obtained by it for later use during system wakeup. |
| |
| BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=214271 |
| Reported-by: Reik Keutterling <spielkind@gmail.com> |
| Tested-by: Reik Keutterling <spielkind@gmail.com> |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/acpi/acpica/acglobal.h | 2 ++ |
| drivers/acpi/acpica/hwesleep.c | 8 ++------ |
| drivers/acpi/acpica/hwsleep.c | 11 ++++------- |
| drivers/acpi/acpica/hwxfsleep.c | 7 +++++++ |
| 4 files changed, 15 insertions(+), 13 deletions(-) |
| |
| diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h |
| index fd3beea934213..42c4bfd796f42 100644 |
| --- a/drivers/acpi/acpica/acglobal.h |
| +++ b/drivers/acpi/acpica/acglobal.h |
| @@ -220,6 +220,8 @@ extern struct acpi_bit_register_info |
| acpi_gbl_bit_register_info[ACPI_NUM_BITREG]; |
| ACPI_GLOBAL(u8, acpi_gbl_sleep_type_a); |
| ACPI_GLOBAL(u8, acpi_gbl_sleep_type_b); |
| +ACPI_GLOBAL(u8, acpi_gbl_sleep_type_a_s0); |
| +ACPI_GLOBAL(u8, acpi_gbl_sleep_type_b_s0); |
| |
| /***************************************************************************** |
| * |
| diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c |
| index dee3affaca491..aa502ae3b6b31 100644 |
| --- a/drivers/acpi/acpica/hwesleep.c |
| +++ b/drivers/acpi/acpica/hwesleep.c |
| @@ -147,17 +147,13 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state) |
| |
| acpi_status acpi_hw_extended_wake_prep(u8 sleep_state) |
| { |
| - acpi_status status; |
| u8 sleep_type_value; |
| |
| ACPI_FUNCTION_TRACE(hw_extended_wake_prep); |
| |
| - status = acpi_get_sleep_type_data(ACPI_STATE_S0, |
| - &acpi_gbl_sleep_type_a, |
| - &acpi_gbl_sleep_type_b); |
| - if (ACPI_SUCCESS(status)) { |
| + if (acpi_gbl_sleep_type_a_s0 != ACPI_SLEEP_TYPE_INVALID) { |
| sleep_type_value = |
| - ((acpi_gbl_sleep_type_a << ACPI_X_SLEEP_TYPE_POSITION) & |
| + ((acpi_gbl_sleep_type_a_s0 << ACPI_X_SLEEP_TYPE_POSITION) & |
| ACPI_X_SLEEP_TYPE_MASK); |
| |
| (void)acpi_write((u64)(sleep_type_value | ACPI_X_SLEEP_ENABLE), |
| diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c |
| index b62db8ec446fa..5f7d63badbe9d 100644 |
| --- a/drivers/acpi/acpica/hwsleep.c |
| +++ b/drivers/acpi/acpica/hwsleep.c |
| @@ -179,7 +179,7 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state) |
| |
| acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) |
| { |
| - acpi_status status; |
| + acpi_status status = AE_OK; |
| struct acpi_bit_register_info *sleep_type_reg_info; |
| struct acpi_bit_register_info *sleep_enable_reg_info; |
| u32 pm1a_control; |
| @@ -192,10 +192,7 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) |
| * This is unclear from the ACPI Spec, but it is required |
| * by some machines. |
| */ |
| - status = acpi_get_sleep_type_data(ACPI_STATE_S0, |
| - &acpi_gbl_sleep_type_a, |
| - &acpi_gbl_sleep_type_b); |
| - if (ACPI_SUCCESS(status)) { |
| + if (acpi_gbl_sleep_type_a_s0 != ACPI_SLEEP_TYPE_INVALID) { |
| sleep_type_reg_info = |
| acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE); |
| sleep_enable_reg_info = |
| @@ -216,9 +213,9 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) |
| |
| /* Insert the SLP_TYP bits */ |
| |
| - pm1a_control |= (acpi_gbl_sleep_type_a << |
| + pm1a_control |= (acpi_gbl_sleep_type_a_s0 << |
| sleep_type_reg_info->bit_position); |
| - pm1b_control |= (acpi_gbl_sleep_type_b << |
| + pm1b_control |= (acpi_gbl_sleep_type_b_s0 << |
| sleep_type_reg_info->bit_position); |
| |
| /* Write the control registers and ignore any errors */ |
| diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c |
| index abbf9702aa7f2..79731efbe8fe2 100644 |
| --- a/drivers/acpi/acpica/hwxfsleep.c |
| +++ b/drivers/acpi/acpica/hwxfsleep.c |
| @@ -214,6 +214,13 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) |
| return_ACPI_STATUS(status); |
| } |
| |
| + status = acpi_get_sleep_type_data(ACPI_STATE_S0, |
| + &acpi_gbl_sleep_type_a_s0, |
| + &acpi_gbl_sleep_type_b_s0); |
| + if (ACPI_FAILURE(status)) { |
| + acpi_gbl_sleep_type_a_s0 = ACPI_SLEEP_TYPE_INVALID; |
| + } |
| + |
| /* Execute the _PTS method (Prepare To Sleep) */ |
| |
| arg_list.count = 1; |
| -- |
| 2.33.0 |
| |