From c93d13b661a6ce34b9cd8241f5e410658078d7b1 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 10 Feb 2025 09:12:53 +0200 Subject: [PATCH 01/10] intel_idle: clean up BYT/CHT auto demotion disable Bay Trail (BYT) and Cherry Trail (CHT) platforms have a very specific way of disabling auto-demotion via specific MSR bits. Clean up the code so that BYT/CHT-specifics do not show up in the common 'struct idle_cpu' data structure. Remove the 'byt_auto_demotion_disable_flag' flag from 'struct idle_cpu', because a better coding pattern is to avoid very case-specific fields like 'bool byt_auto_demotion_disable_flag' in a common data structure, which is used for all platforms, not only BYT/CHT. The code is just more readable when common data structures contain only commonly used fields. Instead, match BYT/CHT in the 'intel_idle_init_cstates_icpu()' function, and introduce a small helper to take care of BYT/CHT auto-demotion. This is consistent with how platform-specific things are done for other platforms. No intended functional changes, compile-tested only. Signed-off-by: Artem Bityutskiy Link: https://patch.msgid.link/20250210071253.2991030-1-dedekind1@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 118fe1d37c22..324814dc34fa 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -89,7 +89,6 @@ struct idle_cpu { * Indicate which enable bits to clear here. */ unsigned long auto_demotion_disable_flags; - bool byt_auto_demotion_disable_flag; bool disable_promotion_to_c1e; bool use_acpi; }; @@ -1463,13 +1462,11 @@ static const struct idle_cpu idle_cpu_snx __initconst = { static const struct idle_cpu idle_cpu_byt __initconst = { .state_table = byt_cstates, .disable_promotion_to_c1e = true, - .byt_auto_demotion_disable_flag = true, }; static const struct idle_cpu idle_cpu_cht __initconst = { .state_table = cht_cstates, .disable_promotion_to_c1e = true, - .byt_auto_demotion_disable_flag = true, }; static const struct idle_cpu idle_cpu_ivb __initconst = { @@ -2055,6 +2052,15 @@ static void __init spr_idle_state_table_update(void) } } +/** + * byt_cht_auto_demotion_disable - Disable Bay/Cherry Trail auto-demotion. + */ +static void __init byt_cht_auto_demotion_disable(void) +{ + wrmsrl(MSR_CC6_DEMOTION_POLICY_CONFIG, 0); + wrmsrl(MSR_MC6_DEMOTION_POLICY_CONFIG, 0); +} + static bool __init intel_idle_verify_cstate(unsigned int mwait_hint) { unsigned int mwait_cstate = (MWAIT_HINT2CSTATE(mwait_hint) + 1) & @@ -2136,6 +2142,10 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv) case INTEL_ATOM_GRACEMONT: adl_idle_state_table_update(); break; + case INTEL_ATOM_SILVERMONT: + case INTEL_ATOM_AIRMONT: + byt_cht_auto_demotion_disable(); + break; } for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) { @@ -2178,11 +2188,6 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv) drv->state_count++; } - - if (icpu->byt_auto_demotion_disable_flag) { - wrmsrl(MSR_CC6_DEMOTION_POLICY_CONFIG, 0); - wrmsrl(MSR_MC6_DEMOTION_POLICY_CONFIG, 0); - } } /** From d2cd195b57cf5ffbe432be01e96f35637e7bd403 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2025 15:22:59 +0100 Subject: [PATCH 02/10] cpuidle: menu: Drop a redundant local variable Local variable min in get_typical_interval() is updated, but never accessed later, so drop it. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Christian Loehle Tested-by: Artem Bityutskiy Tested-by: Christian Loehle Tested-by: Aboorva Devarajan Link: https://patch.msgid.link/13699686.uLZWGnKmhe@rjwysocki.net --- drivers/cpuidle/governors/menu.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 28363bfa3e4c..b19406502b56 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -117,7 +117,7 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); static unsigned int get_typical_interval(struct menu_device *data) { int i, divisor; - unsigned int min, max, thresh, avg; + unsigned int max, thresh, avg; uint64_t sum, variance; thresh = INT_MAX; /* Discard outliers above this value */ @@ -125,7 +125,6 @@ static unsigned int get_typical_interval(struct menu_device *data) again: /* First calculate the average of past intervals */ - min = UINT_MAX; max = 0; sum = 0; divisor = 0; @@ -136,9 +135,6 @@ again: divisor++; if (value > max) max = value; - - if (value < min) - min = value; } } From 13982929fb08ed4691256072856f50bf7b206b9b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2025 15:24:18 +0100 Subject: [PATCH 03/10] cpuidle: menu: Use one loop for average and variance computations Use the observation that one loop is sufficient to compute the average of an array of values and their variance to eliminate one of the loops from get_typical_interval(). While at it, make get_typical_interval() consistently use u64 as the 64-bit unsigned integer data type and rearrange some white space and the declarations of local variables in it (to make them follow the reverse X-mas tree pattern). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Artem Bityutskiy Reviewed-by: Christian Loehle Tested-by: Christian Loehle Tested-by: Aboorva Devarajan Link: https://patch.msgid.link/3339073.aeNJFYEL58@rjwysocki.net --- drivers/cpuidle/governors/menu.c | 61 +++++++++++++++----------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index b19406502b56..ec472af38de6 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -116,49 +116,45 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); */ static unsigned int get_typical_interval(struct menu_device *data) { - int i, divisor; - unsigned int max, thresh, avg; - uint64_t sum, variance; - - thresh = INT_MAX; /* Discard outliers above this value */ + unsigned int max, divisor, thresh = INT_MAX; + u64 avg, variance, avg_sq; + int i; again: - - /* First calculate the average of past intervals */ + /* Compute the average and variance of past intervals. */ max = 0; - sum = 0; + avg = 0; + variance = 0; divisor = 0; for (i = 0; i < INTERVALS; i++) { unsigned int value = data->intervals[i]; - if (value <= thresh) { - sum += value; - divisor++; - if (value > max) - max = value; - } + + /* Discard data points above the threshold. */ + if (value > thresh) + continue; + + divisor++; + + avg += value; + variance += (u64)value * value; + + if (value > max) + max = value; } if (!max) return UINT_MAX; - if (divisor == INTERVALS) - avg = sum >> INTERVAL_SHIFT; - else - avg = div_u64(sum, divisor); - - /* Then try to determine variance */ - variance = 0; - for (i = 0; i < INTERVALS; i++) { - unsigned int value = data->intervals[i]; - if (value <= thresh) { - int64_t diff = (int64_t)value - avg; - variance += diff * diff; - } - } - if (divisor == INTERVALS) + if (divisor == INTERVALS) { + avg >>= INTERVAL_SHIFT; variance >>= INTERVAL_SHIFT; - else + } else { + do_div(avg, divisor); do_div(variance, divisor); + } + + avg_sq = avg * avg; + variance -= avg_sq; /* * The typical interval is obtained when standard deviation is @@ -173,10 +169,9 @@ again: * Use this result only if there is no timer to wake us up sooner. */ if (likely(variance <= U64_MAX/36)) { - if ((((u64)avg*avg > variance*36) && (divisor * 4 >= INTERVALS * 3)) - || variance <= 400) { + if ((avg_sq > variance * 36 && divisor * 4 >= INTERVALS * 3) || + variance <= 400) return avg; - } } /* From 60256e458e1c29652b2f9e4f2ba71fc7b09bd30c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2025 15:25:18 +0100 Subject: [PATCH 04/10] cpuidle: menu: Tweak threshold use in get_typical_interval() To prepare get_typical_interval() for subsequent changes, rearrange the use of the data point threshold in it a bit and initialize that threshold to UINT_MAX which is more consistent with its data type. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Tested-by: Artem Bityutskiy Reviewed-by: Christian Loehle Tested-by: Christian Loehle Tested-by: Aboorva Devarajan Link: https://patch.msgid.link/8490144.T7Z3S40VBb@rjwysocki.net --- drivers/cpuidle/governors/menu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index ec472af38de6..680ff20e5528 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -116,7 +116,7 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); */ static unsigned int get_typical_interval(struct menu_device *data) { - unsigned int max, divisor, thresh = INT_MAX; + unsigned int max, divisor, thresh = UINT_MAX; u64 avg, variance, avg_sq; int i; @@ -129,8 +129,8 @@ again: for (i = 0; i < INTERVALS; i++) { unsigned int value = data->intervals[i]; - /* Discard data points above the threshold. */ - if (value > thresh) + /* Discard data points above or at the threshold. */ + if (value >= thresh) continue; divisor++; @@ -186,7 +186,7 @@ again: if ((divisor * 4) <= INTERVALS * 3) return UINT_MAX; - thresh = max - 1; + thresh = max; goto again; } From 8de7606f0fe2bf5a918fe97d425e16e190a24fe6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2025 15:26:41 +0100 Subject: [PATCH 05/10] cpuidle: menu: Eliminate outliers on both ends of the sample set Currently, get_typical_interval() attempts to eliminate outliers at the high end of the sample set only (probably in order to bias the prediction toward lower values), but this it problematic because if the outliers are present at the low end of the sample set, discarding the highest values will not help to reduce the variance. Since the presence of outliers at the low end of the sample set is generally as likely as their presence at the high end of the sample set, modify get_typical_interval() to treat samples at the largest distances from the average (on both ends of the sample set) as outliers. This should increase the likelihood of making a meaningful prediction in some cases. Signed-off-by: Rafael J. Wysocki Reported-by: Artem Bityutskiy Tested-by: Artem Bityutskiy Reviewed-by: Christian Loehle Tested-by: Christian Loehle Tested-by: Aboorva Devarajan Link: https://patch.msgid.link/2301940.iZASKD2KPV@rjwysocki.net --- drivers/cpuidle/governors/menu.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 680ff20e5528..48ebbde750e5 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -116,30 +116,37 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); */ static unsigned int get_typical_interval(struct menu_device *data) { - unsigned int max, divisor, thresh = UINT_MAX; + s64 value, min_thresh = -1, max_thresh = UINT_MAX; + unsigned int max, min, divisor; u64 avg, variance, avg_sq; int i; again: /* Compute the average and variance of past intervals. */ max = 0; + min = UINT_MAX; avg = 0; variance = 0; divisor = 0; for (i = 0; i < INTERVALS; i++) { - unsigned int value = data->intervals[i]; - - /* Discard data points above or at the threshold. */ - if (value >= thresh) + value = data->intervals[i]; + /* + * Discard the samples outside the interval between the min and + * max thresholds. + */ + if (value <= min_thresh || value >= max_thresh) continue; divisor++; avg += value; - variance += (u64)value * value; + variance += value * value; if (value > max) max = value; + + if (value < min) + min = value; } if (!max) @@ -175,10 +182,10 @@ again: } /* - * If we have outliers to the upside in our distribution, discard - * those by setting the threshold to exclude these outliers, then + * If there are outliers, discard them by setting thresholds to exclude + * data points at a large enough distance from the average, then * calculate the average and standard deviation again. Once we get - * down to the bottom 3/4 of our samples, stop excluding samples. + * down to the last 3/4 of our samples, stop excluding samples. * * This can deal with workloads that have long pauses interspersed * with sporadic activity with a bunch of short pauses. @@ -186,7 +193,12 @@ again: if ((divisor * 4) <= INTERVALS * 3) return UINT_MAX; - thresh = max; + /* Update the thresholds for the next round. */ + if (avg - min > max - avg) + min_thresh = min; + else + max_thresh = max; + goto again; } From 85975daeaa4d6ec560bfcd354fc9c08ad7f38888 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 6 Feb 2025 15:29:05 +0100 Subject: [PATCH 06/10] cpuidle: menu: Avoid discarding useful information When giving up on making a high-confidence prediction, get_typical_interval() always returns UINT_MAX which means that the next idle interval prediction will be based entirely on the time till the next timer. However, the information represented by the most recent intervals may not be completely useless in those cases. Namely, the largest recent idle interval is an upper bound on the recently observed idle duration, so it is reasonable to assume that the next idle duration is unlikely to exceed it. Moreover, this is still true after eliminating the suspected outliers if the sample set still under consideration is at least as large as 50% of the maximum sample set size. Accordingly, make get_typical_interval() return the current maximum recent interval value in that case instead of UINT_MAX. Signed-off-by: Rafael J. Wysocki Reported-by: Artem Bityutskiy Tested-by: Artem Bityutskiy Reviewed-by: Christian Loehle Tested-by: Christian Loehle Tested-by: Aboorva Devarajan Link: https://patch.msgid.link/7770672.EvYhyI6sBW@rjwysocki.net --- drivers/cpuidle/governors/menu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 48ebbde750e5..30ffb1f69056 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -190,8 +190,19 @@ again: * This can deal with workloads that have long pauses interspersed * with sporadic activity with a bunch of short pauses. */ - if ((divisor * 4) <= INTERVALS * 3) + if (divisor * 4 <= INTERVALS * 3) { + /* + * If there are sufficiently many data points still under + * consideration after the outliers have been eliminated, + * returning without a prediction would be a mistake because it + * is likely that the next interval will not exceed the current + * maximum, so return the latter in that case. + */ + if (divisor >= INTERVALS / 2) + return max; + return UINT_MAX; + } /* Update the thresholds for the next round. */ if (avg - min > max - avg) From 5c350410999653dff8d2975d794088e4c166e8b5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 20 Feb 2025 21:13:12 +0100 Subject: [PATCH 07/10] cpuidle: menu: Update documentation after get_typical_interval() changes The documentation of the menu cpuidle governor needs to be updated to match the code behavior after some changes made recently. No functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Christian Loehle Link: https://patch.msgid.link/4998484.31r3eYUQgx@rjwysocki.net [ rjw: More specific subject, two typos fixed in the changelog ] Signed-off-by: Rafael J. Wysocki --- Documentation/admin-guide/pm/cpuidle.rst | 27 +++++++++++++--------- drivers/cpuidle/governors/menu.c | 29 ++++++++---------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/Documentation/admin-guide/pm/cpuidle.rst b/Documentation/admin-guide/pm/cpuidle.rst index eb58d7a5affd..0c090b076224 100644 --- a/Documentation/admin-guide/pm/cpuidle.rst +++ b/Documentation/admin-guide/pm/cpuidle.rst @@ -275,20 +275,25 @@ values and, when predicting the idle duration next time, it computes the average and variance of them. If the variance is small (smaller than 400 square milliseconds) or it is small relative to the average (the average is greater that 6 times the standard deviation), the average is regarded as the "typical -interval" value. Otherwise, the longest of the saved observed idle duration +interval" value. Otherwise, either the longest or the shortest (depending on +which one is farther from the average) of the saved observed idle duration values is discarded and the computation is repeated for the remaining ones. + Again, if the variance of them is small (in the above sense), the average is taken as the "typical interval" value and so on, until either the "typical -interval" is determined or too many data points are disregarded, in which case -the "typical interval" is assumed to equal "infinity" (the maximum unsigned -integer value). +interval" is determined or too many data points are disregarded. In the latter +case, if the size of the set of data points still under consideration is +sufficiently large, the next idle duration is not likely to be above the largest +idle duration value still in that set, so that value is taken as the predicted +next idle duration. Finally, if the set of data points still under +consideration is too small, no prediction is made. -If the "typical interval" computed this way is long enough, the governor obtains -the time until the closest timer event with the assumption that the scheduler -tick will be stopped. That time, referred to as the *sleep length* in what follows, -is the upper bound on the time before the next CPU wakeup. It is used to determine -the sleep length range, which in turn is needed to get the sleep length correction -factor. +If the preliminary prediction of the next idle duration computed this way is +long enough, the governor obtains the time until the closest timer event with +the assumption that the scheduler tick will be stopped. That time, referred to +as the *sleep length* in what follows, is the upper bound on the time before the +next CPU wakeup. It is used to determine the sleep length range, which in turn +is needed to get the sleep length correction factor. The ``menu`` governor maintains an array containing several correction factor values that correspond to different sleep length ranges organized so that each @@ -302,7 +307,7 @@ to 1 the correction factor becomes (it must fall between 0 and 1 inclusive). The sleep length is multiplied by the correction factor for the range that it falls into to obtain an approximation of the predicted idle duration that is compared to the "typical interval" determined previously and the minimum of -the two is taken as the idle duration prediction. +the two is taken as the final idle duration prediction. If the "typical interval" value is small, which means that the CPU is likely to be woken up soon enough, the sleep length computation is skipped as it may diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 30ffb1f69056..39aa0aea61c6 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -41,7 +41,7 @@ * the C state is required to actually break even on this cost. CPUIDLE * provides us this duration in the "target_residency" field. So all that we * need is a good prediction of how long we'll be idle. Like the traditional - * menu governor, we start with the actual known "next timer event" time. + * menu governor, we take the actual known "next timer event" time. * * Since there are other source of wakeups (interrupts for example) than * the next timer event, this estimation is rather optimistic. To get a @@ -50,30 +50,21 @@ * duration always was 50% of the next timer tick, the correction factor will * be 0.5. * - * menu uses a running average for this correction factor, however it uses a - * set of factors, not just a single factor. This stems from the realization - * that the ratio is dependent on the order of magnitude of the expected - * duration; if we expect 500 milliseconds of idle time the likelihood of - * getting an interrupt very early is much higher than if we expect 50 micro - * seconds of idle time. A second independent factor that has big impact on - * the actual factor is if there is (disk) IO outstanding or not. - * (as a special twist, we consider every sleep longer than 50 milliseconds - * as perfect; there are no power gains for sleeping longer than this) - * - * For these two reasons we keep an array of 12 independent factors, that gets - * indexed based on the magnitude of the expected duration as well as the - * "is IO outstanding" property. + * menu uses a running average for this correction factor, but it uses a set of + * factors, not just a single factor. This stems from the realization that the + * ratio is dependent on the order of magnitude of the expected duration; if we + * expect 500 milliseconds of idle time the likelihood of getting an interrupt + * very early is much higher than if we expect 50 micro seconds of idle time. + * For this reason, menu keeps an array of 6 independent factors, that gets + * indexed based on the magnitude of the expected duration. * * Repeatable-interval-detector * ---------------------------- * There are some cases where "next timer" is a completely unusable predictor: * Those cases where the interval is fixed, for example due to hardware - * interrupt mitigation, but also due to fixed transfer rate devices such as - * mice. + * interrupt mitigation, but also due to fixed transfer rate devices like mice. * For this, we use a different predictor: We track the duration of the last 8 - * intervals and if the stand deviation of these 8 intervals is below a - * threshold value, we use the average of these intervals as prediction. - * + * intervals and use them to estimate the duration of the next one. */ struct menu_device { From 5e7e39ae15b0ea370e783a9326fdd1d91357fc3e Mon Sep 17 00:00:00 2001 From: David Arcari Date: Thu, 20 Feb 2025 10:11:20 -0500 Subject: [PATCH 08/10] intel_idle: introduce 'no_native' module parameter Since commit 18734958e9bf ("intel_idle: Use ACPI _CST for processor models without C-state tables") the intel_idle driver has had the ability to use the ACPI _CST to populate C-states when the processor model is not recognized. However, even when the processor model is recognized (native mode) there are cases where it is useful to make the driver ignore the per-CPU idle states in lieu of ACPI C-states (such as specific application performance). Add a new 'no_native' module parameter to provide this functionality. Signed-off-by: David Arcari Link: https://patch.msgid.link/20250220151120.1131122-1-darcari@redhat.com Reviewed-by: Artem Bityutskiy [ rjw: Spell CPU in capitals ] Signed-off-by: Rafael J. Wysocki --- Documentation/admin-guide/pm/intel_idle.rst | 18 +++++++++++++----- drivers/idle/intel_idle.c | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Documentation/admin-guide/pm/intel_idle.rst b/Documentation/admin-guide/pm/intel_idle.rst index 39bd6ecce7de..5940528146eb 100644 --- a/Documentation/admin-guide/pm/intel_idle.rst +++ b/Documentation/admin-guide/pm/intel_idle.rst @@ -192,11 +192,19 @@ even if they have been enumerated (see :ref:`cpu-pm-qos` in Documentation/admin-guide/pm/cpuidle.rst). Setting ``max_cstate`` to 0 causes the ``intel_idle`` initialization to fail. -The ``no_acpi`` and ``use_acpi`` module parameters (recognized by ``intel_idle`` -if the kernel has been configured with ACPI support) can be set to make the -driver ignore the system's ACPI tables entirely or use them for all of the -recognized processor models, respectively (they both are unset by default and -``use_acpi`` has no effect if ``no_acpi`` is set). +The ``no_acpi``, ``use_acpi`` and ``no_native`` module parameters are +recognized by ``intel_idle`` if the kernel has been configured with ACPI +support. In the case that ACPI is not configured these flags have no impact +on functionality. + +``no_acpi`` - Do not use ACPI at all. Only native mode is available, no +ACPI mode. + +``use_acpi`` - No-op in ACPI mode, the driver will consult ACPI tables for +C-states on/off status in native mode. + +``no_native`` - Work only in ACPI mode, no native mode available (ignore +all custom tables). The value of the ``states_off`` module parameter (0 by default) represents a list of idle states to be disabled by default in the form of a bitmask. diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 324814dc34fa..0f4247e3070c 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -1692,6 +1692,10 @@ static bool force_use_acpi __read_mostly; /* No effect if no_acpi is set. */ module_param_named(use_acpi, force_use_acpi, bool, 0444); MODULE_PARM_DESC(use_acpi, "Use ACPI _CST for building the idle states list"); +static bool no_native __read_mostly; /* No effect if no_acpi is set. */ +module_param_named(no_native, no_native, bool, 0444); +MODULE_PARM_DESC(no_native, "Ignore cpu specific (native) idle states in lieu of ACPI idle states"); + static struct acpi_processor_power acpi_state_table __initdata; /** @@ -1831,6 +1835,11 @@ static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) } return true; } + +static inline bool ignore_native(void) +{ + return no_native && !no_acpi; +} #else /* !CONFIG_ACPI_PROCESSOR_CSTATE */ #define force_use_acpi (false) @@ -1840,6 +1849,7 @@ static inline bool intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) { return false; } +static inline bool ignore_native(void) { return false; } #endif /* !CONFIG_ACPI_PROCESSOR_CSTATE */ /** @@ -2333,6 +2343,10 @@ static int __init intel_idle_init(void) pr_debug("MWAIT substates: 0x%x\n", mwait_substates); icpu = (const struct idle_cpu *)id->driver_data; + if (icpu && ignore_native()) { + pr_debug("ignoring native CPU idle states\n"); + icpu = NULL; + } if (icpu) { if (icpu->state_table) cpuidle_state_table = icpu->state_table; From 64c66da08d1004f8ff620d32aa0ed3f6168c60d2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 20 Feb 2025 21:11:29 +0100 Subject: [PATCH 09/10] cpuidle: intel_idle: Update MAINTAINERS Update the intel_idle record in MAINTAINERS to reflect the current state of affairs. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/12621866.O9o76ZdvQC@rjwysocki.net --- MAINTAINERS | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index efee40ea589f..a4869cb54442 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11669,12 +11669,14 @@ F: Documentation/driver-api/crypto/iaa/iaa-crypto.rst F: drivers/crypto/intel/iaa/* INTEL IDLE DRIVER -M: Jacob Pan -M: Len Brown +M: Rafael J. Wysocki +M: Artem Bityutskiy +M: Artem Bityutskiy +R: Len Brown L: linux-pm@vger.kernel.org S: Supported B: https://bugzilla.kernel.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git F: drivers/idle/intel_idle.c INTEL IDXD DRIVER From 68cb0139fec8e05b93978dc0ef1bc8df90a86419 Mon Sep 17 00:00:00 2001 From: Jacky Bai Date: Fri, 7 Mar 2025 22:55:47 +0800 Subject: [PATCH 10/10] cpuidle: Init cpuidle only for present CPUs for_each_possible_cpu() is currently used to initialize cpuidle in below cpuidle drivers: drivers/cpuidle/cpuidle-arm.c drivers/cpuidle/cpuidle-big_little.c drivers/cpuidle/cpuidle-psci.c drivers/cpuidle/cpuidle-qcom-spm.c drivers/cpuidle/cpuidle-riscv-sbi.c However, in cpu_dev_register_generic(), for_each_present_cpu() is used to register CPU devices which means the CPU devices are only registered for present CPUs and not all possible CPUs. With nosmp or maxcpus=0, only the boot CPU is present, lead to the failure: | Failed to register cpuidle device for cpu1 Then rollback to cancel all CPUs' cpuidle registration. Change for_each_possible_cpu() to for_each_present_cpu() in the above cpuidle drivers to ensure it only registers cpuidle devices for CPUs that are actually present. Fixes: b0c69e1214bc ("drivers: base: Use present CPUs in GENERIC_CPU_DEVICES") Reviewed-by: Dhruva Gole Reviewed-by: Sudeep Holla Tested-by: Yuanjie Yang Signed-off-by: Jacky Bai Reviewed-by: Ulf Hansson Link: https://patch.msgid.link/20250307145547.2784821-1-ping.bai@nxp.com Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle-arm.c | 8 ++++---- drivers/cpuidle/cpuidle-big_little.c | 2 +- drivers/cpuidle/cpuidle-psci.c | 4 ++-- drivers/cpuidle/cpuidle-qcom-spm.c | 2 +- drivers/cpuidle/cpuidle-riscv-sbi.c | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c index caba6f4bb1b7..e044fefdb816 100644 --- a/drivers/cpuidle/cpuidle-arm.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -137,9 +137,9 @@ out_kfree_drv: /* * arm_idle_init - Initializes arm cpuidle driver * - * Initializes arm cpuidle driver for all CPUs, if any CPU fails - * to register cpuidle driver then rollback to cancel all CPUs - * registration. + * Initializes arm cpuidle driver for all present CPUs, if any + * CPU fails to register cpuidle driver then rollback to cancel + * all CPUs registration. */ static int __init arm_idle_init(void) { @@ -147,7 +147,7 @@ static int __init arm_idle_init(void) struct cpuidle_driver *drv; struct cpuidle_device *dev; - for_each_possible_cpu(cpu) { + for_each_present_cpu(cpu) { ret = arm_idle_init_cpu(cpu); if (ret) goto out_fail; diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c index 74972deda0ea..4abba42fcc31 100644 --- a/drivers/cpuidle/cpuidle-big_little.c +++ b/drivers/cpuidle/cpuidle-big_little.c @@ -148,7 +148,7 @@ static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id) if (!cpumask) return -ENOMEM; - for_each_possible_cpu(cpu) + for_each_present_cpu(cpu) if (smp_cpuid_part(cpu) == part_id) cpumask_set_cpu(cpu, cpumask); diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c index 2562dc001fc1..a4594c3d6562 100644 --- a/drivers/cpuidle/cpuidle-psci.c +++ b/drivers/cpuidle/cpuidle-psci.c @@ -400,7 +400,7 @@ deinit: /* * psci_idle_probe - Initializes PSCI cpuidle driver * - * Initializes PSCI cpuidle driver for all CPUs, if any CPU fails + * Initializes PSCI cpuidle driver for all present CPUs, if any CPU fails * to register cpuidle driver then rollback to cancel all CPUs * registration. */ @@ -410,7 +410,7 @@ static int psci_cpuidle_probe(struct platform_device *pdev) struct cpuidle_driver *drv; struct cpuidle_device *dev; - for_each_possible_cpu(cpu) { + for_each_present_cpu(cpu) { ret = psci_idle_init_cpu(&pdev->dev, cpu); if (ret) goto out_fail; diff --git a/drivers/cpuidle/cpuidle-qcom-spm.c b/drivers/cpuidle/cpuidle-qcom-spm.c index 3ab240e0e122..5f386761b156 100644 --- a/drivers/cpuidle/cpuidle-qcom-spm.c +++ b/drivers/cpuidle/cpuidle-qcom-spm.c @@ -135,7 +135,7 @@ static int spm_cpuidle_drv_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "set warm boot addr failed"); - for_each_possible_cpu(cpu) { + for_each_present_cpu(cpu) { ret = spm_cpuidle_register(&pdev->dev, cpu); if (ret && ret != -ENODEV) { dev_err(&pdev->dev, diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c index 0c92a628bbd4..0fe1ece9fbdc 100644 --- a/drivers/cpuidle/cpuidle-riscv-sbi.c +++ b/drivers/cpuidle/cpuidle-riscv-sbi.c @@ -529,8 +529,8 @@ static int sbi_cpuidle_probe(struct platform_device *pdev) return ret; } - /* Initialize CPU idle driver for each CPU */ - for_each_possible_cpu(cpu) { + /* Initialize CPU idle driver for each present CPU */ + for_each_present_cpu(cpu) { ret = sbi_cpuidle_init_cpu(&pdev->dev, cpu); if (ret) { pr_debug("HART%ld: idle driver init failed\n",