ACPI: processor_idle: Fix invalid comparison with insertion sort for latency
authorKuan-Wei Chiu <visitorckw@gmail.com>
Mon, 1 Jul 2024 20:56:39 +0000 (04:56 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 2 Jul 2024 18:35:29 +0000 (20:35 +0200)
The acpi_cst_latency_cmp() comparison function currently used for
sorting C-state latencies does not satisfy transitivity, causing
incorrect sorting results.

Specifically, if there are two valid acpi_processor_cx elements A and B
and one invalid element C, it may occur that A < B, A = C, and B = C.
Sorting algorithms assume that if A < B and A = C, then C < B, leading
to incorrect ordering.

Given the small size of the array (<=8), we replace the library sort
function with a simple insertion sort that properly ignores invalid
elements and sorts valid ones based on latency. This change ensures
correct ordering of the C-state latencies.

Fixes: 65ea8f2c6e23 ("ACPI: processor idle: Fix up C-state latency if not ordered")
Reported-by: Julian Sikorski <belegdol@gmail.com>
Closes: https://lore.kernel.org/lkml/70674dc7-5586-4183-8953-8095567e73df@gmail.com
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Tested-by: Julian Sikorski <belegdol@gmail.com>
Cc: All applicable <stable@vger.kernel.org>
Link: https://patch.msgid.link/20240701205639.117194-1-visitorckw@gmail.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/processor_idle.c

index bd6a7857ce058260bfa2564b632900911c534a54..831fa4a121598545749a64b518128ab538834a55 100644 (file)
@@ -16,7 +16,6 @@
 #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>
@@ -386,25 +385,24 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
        acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, 1);
 }
 
-static int acpi_cst_latency_cmp(const void *a, const void *b)
+static void acpi_cst_latency_sort(struct acpi_processor_cx *states, size_t length)
 {
-       const struct acpi_processor_cx *x = a, *y = b;
+       int i, j, k;
 
-       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;
+       for (i = 1; i < length; i++) {
+               if (!states[i].valid)
+                       continue;
 
-       if (!(x->valid && y->valid))
-               return;
-       swap(x->latency, y->latency);
+               for (j = i - 1, k = i; j >= 0; j--) {
+                       if (!states[j].valid)
+                               continue;
+
+                       if (states[j].latency > states[k].latency)
+                               swap(states[j].latency, states[k].latency);
+
+                       k = j;
+               }
+       }
 }
 
 static int acpi_processor_power_verify(struct acpi_processor *pr)
@@ -449,10 +447,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
 
        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);
+               acpi_cst_latency_sort(&pr->power.states[1], max_cstate);
        }
 
        lapic_timer_propagate_broadcast(pr);