From: Mario Limonciello mario.limonciello@amd.com
stable inclusion from stable-5.10.50 commit 0f2f5293023f1e71a439b2e9c9fb629ee423eff4 bugzilla: 174522 https://gitee.com/openeuler/kernel/issues/I4DNFY
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit 65ea8f2c6e230bdf71fed0137cf9e9d1b307db32 ]
Generally, the C-state latency is provided by the _CST method or FADT, but some OEM platforms using AMD Picasso, Renoir, Van Gogh, and Cezanne set the C2 latency greater than C3's which causes the C2 state to be skipped.
That will block the core entering PC6, which prevents S0ix working properly on Linux systems.
In other operating systems, the latency values are not validated and this does not cause problems by skipping states.
To avoid this issue on Linux, detect when latencies are not an arithmetic progression and sort them.
Link: https://gitlab.freedesktop.org/agd5f/linux/-/commit/026d186e4592c1ee9c1cb442... Link: https://gitlab.freedesktop.org/drm/amd/-/issues/1230#note_712174 Suggested-by: Prike Liang Prike.Liang@amd.com Suggested-by: Alex Deucher alexander.deucher@amd.com Signed-off-by: Mario Limonciello mario.limonciello@amd.com [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Signed-off-by: Sasha Levin sashal@kernel.org
Signed-off-by: Chen Jun chenjun102@huawei.com Acked-by: Weilong Chen chenweilong@huawei.com Signed-off-by: Chen Jun chenjun102@huawei.com --- drivers/acpi/processor_idle.c | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index fb161a21d0ae..8377c3ed10ff 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -16,6 +16,7 @@ #include <linux/acpi.h> #include <linux/dmi.h> #include <linux/sched.h> /* need_resched() */ +#include <linux/sort.h> #include <linux/tick.h> #include <linux/cpuidle.h> #include <linux/cpu.h> @@ -389,10 +390,37 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, return; }
+static int acpi_cst_latency_cmp(const void *a, const void *b) +{ + const struct acpi_processor_cx *x = a, *y = b; + + if (!(x->valid && y->valid)) + return 0; + if (x->latency > y->latency) + return 1; + if (x->latency < y->latency) + return -1; + return 0; +} +static void acpi_cst_latency_swap(void *a, void *b, int n) +{ + struct acpi_processor_cx *x = a, *y = b; + u32 tmp; + + if (!(x->valid && y->valid)) + return; + tmp = x->latency; + x->latency = y->latency; + y->latency = tmp; +} + static int acpi_processor_power_verify(struct acpi_processor *pr) { unsigned int i; unsigned int working = 0; + unsigned int last_latency = 0; + unsigned int last_type = 0; + bool buggy_latency = false;
pr->power.timer_broadcast_on_state = INT_MAX;
@@ -416,12 +444,24 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) } if (!cx->valid) continue; + if (cx->type >= last_type && cx->latency < last_latency) + buggy_latency = true; + last_latency = cx->latency; + last_type = cx->type;
lapic_timer_check_state(i, pr, cx); tsc_check_state(cx->type); working++; }
+ if (buggy_latency) { + pr_notice("FW issue: working around C-state latencies out of order\n"); + sort(&pr->power.states[1], max_cstate, + sizeof(struct acpi_processor_cx), + acpi_cst_latency_cmp, + acpi_cst_latency_swap); + } + lapic_timer_propagate_broadcast(pr);
return (working);