Linux Power Management development
 help / color / mirror / Atom feed
* [PATCH v4 06/12] amd-pstate: Add sysfs support for floor_freq and floor_count
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy, Mario Limonciello
In-Reply-To: <20260326114756.20374-1-gautham.shenoy@amd.com>

When Floor Performance feature is supported by the platform, expose
two sysfs files:

   * amd_pstate_floor_freq to allow userspace to request the floor
     frequency for each CPU.

   * amd_pstate_floor_count which advertises the number of distinct
     levels of floor frequencies supported on this platform.

Reset the floor_perf to bios_floor_perf in the suspend, offline, and
exit paths, and restore the value to the cached user-request
floor_freq on the resume and online paths mirroring how bios_min_perf
is handled for MSR_AMD_CPPC_REQ.

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
---
 drivers/cpufreq/amd-pstate.c | 93 +++++++++++++++++++++++++++++++++---
 drivers/cpufreq/amd-pstate.h |  2 +
 2 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 53b8173ff183..a068c4457a8f 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -383,8 +383,10 @@ static int amd_pstate_init_floor_perf(struct cpufreq_policy *policy)
 			return ret;
 	}
 
-	cpudata->bios_floor_perf = floor_perf;
 
+	cpudata->bios_floor_perf = floor_perf;
+	cpudata->floor_freq = perf_to_freq(cpudata->perf, cpudata->nominal_freq,
+					   floor_perf);
 	return 0;
 }
 
@@ -1288,6 +1290,46 @@ static ssize_t show_energy_performance_preference(
 	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
 }
 
+static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
+					   const char *buf, size_t count)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+	union perf_cached perf = READ_ONCE(cpudata->perf);
+	unsigned int freq;
+	u8 floor_perf;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &freq);
+	if (ret)
+		return ret;
+
+	if (freq < policy->cpuinfo.min_freq || freq > policy->max)
+		return -EINVAL;
+
+	floor_perf = freq_to_perf(perf, cpudata->nominal_freq, freq);
+	ret = amd_pstate_set_floor_perf(policy, floor_perf);
+
+	if (!ret)
+		cpudata->floor_freq = freq;
+
+	return ret ?: count;
+}
+
+static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+
+	return sysfs_emit(buf, "%u\n", cpudata->floor_freq);
+}
+
+static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+	u8 count = cpudata->floor_perf_cnt;
+
+	return sysfs_emit(buf, "%u\n", count);
+}
+
 cpufreq_freq_attr_ro(amd_pstate_max_freq);
 cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);
 
@@ -1296,6 +1338,8 @@ cpufreq_freq_attr_ro(amd_pstate_prefcore_ranking);
 cpufreq_freq_attr_ro(amd_pstate_hw_prefcore);
 cpufreq_freq_attr_rw(energy_performance_preference);
 cpufreq_freq_attr_ro(energy_performance_available_preferences);
+cpufreq_freq_attr_rw(amd_pstate_floor_freq);
+cpufreq_freq_attr_ro(amd_pstate_floor_count);
 
 struct freq_attr_visibility {
 	struct freq_attr *attr;
@@ -1320,6 +1364,12 @@ static bool epp_visibility(void)
 	return cppc_state == AMD_PSTATE_ACTIVE;
 }
 
+/* Determines whether amd_pstate_floor_freq related attributes should be visible */
+static bool floor_freq_visibility(void)
+{
+	return cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO);
+}
+
 static struct freq_attr_visibility amd_pstate_attr_visibility[] = {
 	{&amd_pstate_max_freq, always_visible},
 	{&amd_pstate_lowest_nonlinear_freq, always_visible},
@@ -1328,6 +1378,8 @@ static struct freq_attr_visibility amd_pstate_attr_visibility[] = {
 	{&amd_pstate_hw_prefcore, prefcore_visibility},
 	{&energy_performance_preference, epp_visibility},
 	{&energy_performance_available_preferences, epp_visibility},
+	{&amd_pstate_floor_freq, floor_freq_visibility},
+	{&amd_pstate_floor_count, floor_freq_visibility},
 };
 
 static struct freq_attr **get_freq_attrs(void)
@@ -1748,24 +1800,39 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)
 
 static int amd_pstate_cpu_online(struct cpufreq_policy *policy)
 {
-	return amd_pstate_cppc_enable(policy);
+	struct amd_cpudata *cpudata = policy->driver_data;
+	union perf_cached perf = READ_ONCE(cpudata->perf);
+	u8 cached_floor_perf;
+	int ret;
+
+	ret = amd_pstate_cppc_enable(policy);
+	if (ret)
+		return ret;
+
+	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
+	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
 }
 
 static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
 	union perf_cached perf = READ_ONCE(cpudata->perf);
+	int ret;
 
 	/*
 	 * Reset CPPC_REQ MSR to the BIOS value, this will allow us to retain the BIOS specified
 	 * min_perf value across kexec reboots. If this CPU is just onlined normally after this, the
 	 * limits, epp and desired perf will get reset to the cached values in cpudata struct
 	 */
-	return amd_pstate_update_perf(policy, perf.bios_min_perf,
+	ret = amd_pstate_update_perf(policy, perf.bios_min_perf,
 				     FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
 				     FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
 				     FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
 				     false);
+	if (ret)
+		return ret;
+
+	return amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
 }
 
 static int amd_pstate_suspend(struct cpufreq_policy *policy)
@@ -1787,6 +1854,10 @@ static int amd_pstate_suspend(struct cpufreq_policy *policy)
 	if (ret)
 		return ret;
 
+	ret = amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
+	if (ret)
+		return ret;
+
 	/* set this flag to avoid setting core offline*/
 	cpudata->suspended = true;
 
@@ -1798,15 +1869,24 @@ static int amd_pstate_resume(struct cpufreq_policy *policy)
 	struct amd_cpudata *cpudata = policy->driver_data;
 	union perf_cached perf = READ_ONCE(cpudata->perf);
 	int cur_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->cur);
+	u8 cached_floor_perf;
+	int ret;
 
 	/* Set CPPC_REQ to last sane value until the governor updates it */
-	return amd_pstate_update_perf(policy, perf.min_limit_perf, cur_perf, perf.max_limit_perf,
-				      0U, false);
+	ret = amd_pstate_update_perf(policy, perf.min_limit_perf, cur_perf, perf.max_limit_perf,
+				     0U, false);
+	if (ret)
+		return ret;
+
+	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
+	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
 }
 
 static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
+	union perf_cached perf = READ_ONCE(cpudata->perf);
+	u8 cached_floor_perf;
 
 	if (cpudata->suspended) {
 		int ret;
@@ -1819,7 +1899,8 @@ static int amd_pstate_epp_resume(struct cpufreq_policy *policy)
 		cpudata->suspended = false;
 	}
 
-	return 0;
+	cached_floor_perf = freq_to_perf(perf, cpudata->nominal_freq, cpudata->floor_freq);
+	return amd_pstate_set_floor_perf(policy, cached_floor_perf);
 }
 
 static struct cpufreq_driver amd_pstate_driver = {
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index 303da70b0afa..453adfb445f8 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -74,6 +74,7 @@ struct amd_aperf_mperf {
  * @max_limit_freq: Cached value of policy->max (in khz)
  * @nominal_freq: the frequency (in khz) that mapped to nominal_perf
  * @lowest_nonlinear_freq: the frequency (in khz) that mapped to lowest_nonlinear_perf
+ * @floor_freq: Cached value of the user requested floor_freq
  * @cur: Difference of Aperf/Mperf/tsc count between last and current sample
  * @prev: Last Aperf/Mperf/tsc count value read from register
  * @freq: current cpu frequency value (in khz)
@@ -103,6 +104,7 @@ struct amd_cpudata {
 	u32	max_limit_freq;
 	u32	nominal_freq;
 	u32	lowest_nonlinear_freq;
+	u32	floor_freq;
 
 	struct amd_aperf_mperf cur;
 	struct amd_aperf_mperf prev;
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 05/12] amd-pstate: Add support for CPPC_REQ2 and FLOOR_PERF
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy, Mario Limonciello
In-Reply-To: <20260326114756.20374-1-gautham.shenoy@amd.com>

Some future AMD processors have feature named "CPPC Performance
Priority" which lets userspace specify different floor performance
levels for different CPUs. The platform firmware takes these different
floor performance levels into consideration while throttling the CPUs
under power/thermal constraints. The presence of this feature is
indicated by bit 16 of the EDX register for CPUID leaf
0x80000007. More details can be found in AMD Publication titled "AMD64
Collaborative Processor Performance Control (CPPC) Performance
Priority" Revision 1.10.

The number of distinct floor performance levels supported on the
platform will be advertised through the bits 32:39 of the
MSR_AMD_CPPC_CAP1. Bits 0:7 of a new MSR MSR_AMD_CPPC_REQ2
(0xc00102b5) will be used to specify the desired floor performance
level for that CPU.

Add support for the aforementioned MSR_AMD_CPPC_REQ2, and macros for
parsing and updating the relevant bits from MSR_AMD_CPPC_CAP1 and
MSR_AMD_CPPC_REQ2.

On boot if the default value of the MSR_AMD_CPPC_REQ2[7:0] (Floor
Perf) is lower than CPPC.lowest_perf, and thus invalid, initialize it
to MSR_AMD_CPPC_CAP1.nominal_perf which is a sane default value.

Save the boot-time floor_perf during amd_pstate_init_floor_perf(). In
a subsequent patch it will be restored in the suspend, offline, and
exit paths, mirroring how bios_min_perf is handled for
MSR_AMD_CPPC_REQ.

Link: https://docs.amd.com/v/u/en-US/69206_1.10_AMD64_CPPC_PUB
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
---
 arch/x86/include/asm/msr-index.h |  5 ++
 drivers/cpufreq/amd-pstate.c     | 78 +++++++++++++++++++++++++++++++-
 drivers/cpufreq/amd-pstate.h     |  8 ++++
 3 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 6673601246b3..e126c7fb69cf 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -765,12 +765,14 @@
 #define MSR_AMD_CPPC_CAP2		0xc00102b2
 #define MSR_AMD_CPPC_REQ		0xc00102b3
 #define MSR_AMD_CPPC_STATUS		0xc00102b4
+#define MSR_AMD_CPPC_REQ2		0xc00102b5
 
 /* Masks for use with MSR_AMD_CPPC_CAP1 */
 #define AMD_CPPC_LOWEST_PERF_MASK	GENMASK(7, 0)
 #define AMD_CPPC_LOWNONLIN_PERF_MASK	GENMASK(15, 8)
 #define AMD_CPPC_NOMINAL_PERF_MASK	GENMASK(23, 16)
 #define AMD_CPPC_HIGHEST_PERF_MASK	GENMASK(31, 24)
+#define AMD_CPPC_FLOOR_PERF_CNT_MASK	GENMASK_ULL(39, 32)
 
 /* Masks for use with MSR_AMD_CPPC_REQ */
 #define AMD_CPPC_MAX_PERF_MASK		GENMASK(7, 0)
@@ -778,6 +780,9 @@
 #define AMD_CPPC_DES_PERF_MASK		GENMASK(23, 16)
 #define AMD_CPPC_EPP_PERF_MASK		GENMASK(31, 24)
 
+/* Masks for use with MSR_AMD_CPPC_REQ2 */
+#define AMD_CPPC_FLOOR_PERF_MASK	GENMASK(7, 0)
+
 /* AMD Performance Counter Global Status and Control MSRs */
 #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS	0xc0000300
 #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL		0xc0000301
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 4de2037a414c..53b8173ff183 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -329,6 +329,65 @@ static inline int amd_pstate_set_epp(struct cpufreq_policy *policy, u8 epp)
 	return static_call(amd_pstate_set_epp)(policy, epp);
 }
 
+static int amd_pstate_set_floor_perf(struct cpufreq_policy *policy, u8 perf)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+	u64 value, prev;
+	int ret;
+
+	if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO))
+		return 0;
+
+	value = prev = READ_ONCE(cpudata->cppc_req2_cached);
+	FIELD_MODIFY(AMD_CPPC_FLOOR_PERF_MASK, &value, perf);
+
+	if (value == prev)
+		return 0;
+
+	ret = wrmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ2, value);
+	if (ret) {
+		pr_err("failed to set CPPC REQ2 value. Error (%d)\n", ret);
+		return ret;
+	}
+
+	WRITE_ONCE(cpudata->cppc_req2_cached, value);
+
+	return ret;
+}
+
+static int amd_pstate_init_floor_perf(struct cpufreq_policy *policy)
+{
+	struct amd_cpudata *cpudata = policy->driver_data;
+	u8 floor_perf;
+	u64 value;
+	int ret;
+
+	if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO))
+		return 0;
+
+	ret = rdmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ2, &value);
+	if (ret) {
+		pr_err("failed to read CPPC REQ2 value. Error (%d)\n", ret);
+		return ret;
+	}
+
+	WRITE_ONCE(cpudata->cppc_req2_cached, value);
+	floor_perf = FIELD_GET(AMD_CPPC_FLOOR_PERF_MASK,
+			       cpudata->cppc_req2_cached);
+
+	/* Set a sane value for floor_perf if the default value is invalid */
+	if (floor_perf < cpudata->perf.lowest_perf) {
+		floor_perf = cpudata->perf.nominal_perf;
+		ret = amd_pstate_set_floor_perf(policy, floor_perf);
+		if (ret)
+			return ret;
+	}
+
+	cpudata->bios_floor_perf = floor_perf;
+
+	return 0;
+}
+
 static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp)
 {
 	struct amd_cpudata *cpudata = policy->driver_data;
@@ -426,6 +485,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
 	perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
 	WRITE_ONCE(cpudata->perf, perf);
 	WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1));
+	WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1));
 
 	return 0;
 }
@@ -1024,6 +1084,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
 							      cpudata->nominal_freq,
 							      perf.highest_perf);
 
+	policy->driver_data = cpudata;
 	ret = amd_pstate_cppc_enable(policy);
 	if (ret)
 		goto free_cpudata1;
@@ -1036,6 +1097,12 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
 	if (cpu_feature_enabled(X86_FEATURE_CPPC))
 		policy->fast_switch_possible = true;
 
+	ret = amd_pstate_init_floor_perf(policy);
+	if (ret) {
+		dev_err(dev, "Failed to initialize Floor Perf (%d)\n", ret);
+		goto free_cpudata1;
+	}
+
 	ret = freq_qos_add_request(&policy->constraints, &cpudata->req[0],
 				   FREQ_QOS_MIN, FREQ_QOS_MIN_DEFAULT_VALUE);
 	if (ret < 0) {
@@ -1050,7 +1117,6 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
 		goto free_cpudata2;
 	}
 
-	policy->driver_data = cpudata;
 
 	if (!current_pstate_driver->adjust_perf)
 		current_pstate_driver->adjust_perf = amd_pstate_adjust_perf;
@@ -1062,6 +1128,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
 free_cpudata1:
 	pr_warn("Failed to initialize CPU %d: %d\n", policy->cpu, ret);
 	kfree(cpudata);
+	policy->driver_data = NULL;
 	return ret;
 }
 
@@ -1072,6 +1139,7 @@ static void amd_pstate_cpu_exit(struct cpufreq_policy *policy)
 
 	/* Reset CPPC_REQ MSR to the BIOS value */
 	amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
+	amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
 
 	freq_qos_remove_request(&cpudata->req[1]);
 	freq_qos_remove_request(&cpudata->req[0]);
@@ -1598,6 +1666,12 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 	if (ret)
 		goto free_cpudata1;
 
+	ret = amd_pstate_init_floor_perf(policy);
+	if (ret) {
+		dev_err(dev, "Failed to initialize Floor Perf (%d)\n", ret);
+		goto free_cpudata1;
+	}
+
 	current_pstate_driver->adjust_perf = NULL;
 
 	return 0;
@@ -1605,6 +1679,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 free_cpudata1:
 	pr_warn("Failed to initialize CPU %d: %d\n", policy->cpu, ret);
 	kfree(cpudata);
+	policy->driver_data = NULL;
 	return ret;
 }
 
@@ -1617,6 +1692,7 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
 
 		/* Reset CPPC_REQ MSR to the BIOS value */
 		amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
+		amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
 
 		kfree(cpudata);
 		policy->driver_data = NULL;
diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h
index cb45fdca27a6..303da70b0afa 100644
--- a/drivers/cpufreq/amd-pstate.h
+++ b/drivers/cpufreq/amd-pstate.h
@@ -62,9 +62,14 @@ struct amd_aperf_mperf {
  * @cpu: CPU number
  * @req: constraint request to apply
  * @cppc_req_cached: cached performance request hints
+ * @cppc_req2_cached: cached value of MSR_AMD_CPPC_REQ2
  * @perf: cached performance-related data
  * @prefcore_ranking: the preferred core ranking, the higher value indicates a higher
  * 		  priority.
+ * @floor_perf_cnt: Cached value of the number of distinct floor
+ *                  performance levels supported
+ * @bios_floor_perf: Cached value of the boot-time floor performance level from
+ *                   MSR_AMD_CPPC_REQ2
  * @min_limit_freq: Cached value of policy->min (in khz)
  * @max_limit_freq: Cached value of policy->max (in khz)
  * @nominal_freq: the frequency (in khz) that mapped to nominal_perf
@@ -87,10 +92,13 @@ struct amd_cpudata {
 
 	struct	freq_qos_request req[2];
 	u64	cppc_req_cached;
+	u64	cppc_req2_cached;
 
 	union perf_cached perf;
 
 	u8	prefcore_ranking;
+	u8	floor_perf_cnt;
+	u8	bios_floor_perf;
 	u32	min_limit_freq;
 	u32	max_limit_freq;
 	u32	nominal_freq;
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 03/12] amd-pstate: Make certain freq_attrs conditionally visible
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy, Mario Limonciello
In-Reply-To: <20260326114756.20374-1-gautham.shenoy@amd.com>

Certain amd_pstate freq_attrs such as amd_pstate_hw_prefcore and
amd_pstate_prefcore_ranking are enabled even when preferred core is
not supported on the platform.

Similarly there are common freq_attrs between the amd-pstate and the
amd-pstate-epp drivers (eg: amd_pstate_max_freq,
amd_pstate_lowest_nonlinear_freq, etc.) but are duplicated in two
different freq_attr structs.

Unify all the attributes in a single place and associate each of them
with a visibility function that determines whether the attribute
should be visible based on the underlying platform support and the
current amd_pstate mode.

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
---
 drivers/cpufreq/amd-pstate.c | 124 ++++++++++++++++++++++++++---------
 1 file changed, 93 insertions(+), 31 deletions(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 24cdeffbcd40..4de2037a414c 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1220,12 +1220,87 @@ static ssize_t show_energy_performance_preference(
 	return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
 }
 
+cpufreq_freq_attr_ro(amd_pstate_max_freq);
+cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);
+
+cpufreq_freq_attr_ro(amd_pstate_highest_perf);
+cpufreq_freq_attr_ro(amd_pstate_prefcore_ranking);
+cpufreq_freq_attr_ro(amd_pstate_hw_prefcore);
+cpufreq_freq_attr_rw(energy_performance_preference);
+cpufreq_freq_attr_ro(energy_performance_available_preferences);
+
+struct freq_attr_visibility {
+	struct freq_attr *attr;
+	bool (*visibility_fn)(void);
+};
+
+/* For attributes which are always visible */
+static bool always_visible(void)
+{
+	return true;
+}
+
+/* Determines whether prefcore related attributes should be visible */
+static bool prefcore_visibility(void)
+{
+	return amd_pstate_prefcore;
+}
+
+/* Determines whether energy performance preference should be visible */
+static bool epp_visibility(void)
+{
+	return cppc_state == AMD_PSTATE_ACTIVE;
+}
+
+static struct freq_attr_visibility amd_pstate_attr_visibility[] = {
+	{&amd_pstate_max_freq, always_visible},
+	{&amd_pstate_lowest_nonlinear_freq, always_visible},
+	{&amd_pstate_highest_perf, always_visible},
+	{&amd_pstate_prefcore_ranking, prefcore_visibility},
+	{&amd_pstate_hw_prefcore, prefcore_visibility},
+	{&energy_performance_preference, epp_visibility},
+	{&energy_performance_available_preferences, epp_visibility},
+};
+
+static struct freq_attr **get_freq_attrs(void)
+{
+	bool attr_visible[ARRAY_SIZE(amd_pstate_attr_visibility)];
+	struct freq_attr **attrs;
+	int i, j, count;
+
+	for (i = 0, count = 0; i < ARRAY_SIZE(amd_pstate_attr_visibility); i++) {
+		struct freq_attr_visibility *v = &amd_pstate_attr_visibility[i];
+
+		attr_visible[i] = v->visibility_fn();
+		if (attr_visible[i])
+			count++;
+	}
+
+	/* amd_pstate_{max_freq, lowest_nonlinear_freq, highest_perf} should always be visible */
+	BUG_ON(!count);
+
+	attrs = kcalloc(count + 1, sizeof(struct freq_attr *), GFP_KERNEL);
+	if (!attrs)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0, j = 0; i < ARRAY_SIZE(amd_pstate_attr_visibility); i++) {
+		if (!attr_visible[i])
+			continue;
+
+		attrs[j++] = amd_pstate_attr_visibility[i].attr;
+	}
+
+	return attrs;
+}
+
 static void amd_pstate_driver_cleanup(void)
 {
 	if (amd_pstate_prefcore)
 		sched_clear_itmt_support();
 
 	cppc_state = AMD_PSTATE_DISABLE;
+	kfree(current_pstate_driver->attr);
+	current_pstate_driver->attr = NULL;
 	current_pstate_driver = NULL;
 }
 
@@ -1250,6 +1325,7 @@ static int amd_pstate_set_driver(int mode_idx)
 
 static int amd_pstate_register_driver(int mode)
 {
+	struct freq_attr **attr = NULL;
 	int ret;
 
 	ret = amd_pstate_set_driver(mode);
@@ -1258,6 +1334,22 @@ static int amd_pstate_register_driver(int mode)
 
 	cppc_state = mode;
 
+	/*
+	 * Note: It is important to compute the attrs _after_
+	 * re-initializing the cppc_state.  Some attributes become
+	 * visible only when cppc_state is AMD_PSTATE_ACTIVE.
+	 */
+	attr = get_freq_attrs();
+	if (IS_ERR(attr)) {
+		ret = (int) PTR_ERR(attr);
+		pr_err("Couldn't compute freq_attrs for current mode %s [%d]\n",
+			amd_pstate_get_mode_string(cppc_state), ret);
+		amd_pstate_driver_cleanup();
+		return ret;
+	}
+
+	current_pstate_driver->attr = attr;
+
 	/* at least one CPU supports CPB */
 	current_pstate_driver->boost_enabled = cpu_feature_enabled(X86_FEATURE_CPB);
 
@@ -1399,37 +1491,9 @@ static ssize_t prefcore_show(struct device *dev,
 	return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
 }
 
-cpufreq_freq_attr_ro(amd_pstate_max_freq);
-cpufreq_freq_attr_ro(amd_pstate_lowest_nonlinear_freq);
-
-cpufreq_freq_attr_ro(amd_pstate_highest_perf);
-cpufreq_freq_attr_ro(amd_pstate_prefcore_ranking);
-cpufreq_freq_attr_ro(amd_pstate_hw_prefcore);
-cpufreq_freq_attr_rw(energy_performance_preference);
-cpufreq_freq_attr_ro(energy_performance_available_preferences);
 static DEVICE_ATTR_RW(status);
 static DEVICE_ATTR_RO(prefcore);
 
-static struct freq_attr *amd_pstate_attr[] = {
-	&amd_pstate_max_freq,
-	&amd_pstate_lowest_nonlinear_freq,
-	&amd_pstate_highest_perf,
-	&amd_pstate_prefcore_ranking,
-	&amd_pstate_hw_prefcore,
-	NULL,
-};
-
-static struct freq_attr *amd_pstate_epp_attr[] = {
-	&amd_pstate_max_freq,
-	&amd_pstate_lowest_nonlinear_freq,
-	&amd_pstate_highest_perf,
-	&amd_pstate_prefcore_ranking,
-	&amd_pstate_hw_prefcore,
-	&energy_performance_preference,
-	&energy_performance_available_preferences,
-	NULL,
-};
-
 static struct attribute *pstate_global_attributes[] = {
 	&dev_attr_status.attr,
 	&dev_attr_prefcore.attr,
@@ -1696,7 +1760,6 @@ static struct cpufreq_driver amd_pstate_driver = {
 	.set_boost	= amd_pstate_set_boost,
 	.update_limits	= amd_pstate_update_limits,
 	.name		= "amd-pstate",
-	.attr		= amd_pstate_attr,
 };
 
 static struct cpufreq_driver amd_pstate_epp_driver = {
@@ -1712,7 +1775,6 @@ static struct cpufreq_driver amd_pstate_epp_driver = {
 	.update_limits	= amd_pstate_update_limits,
 	.set_boost	= amd_pstate_set_boost,
 	.name		= "amd-pstate-epp",
-	.attr		= amd_pstate_epp_attr,
 };
 
 /*
@@ -1858,7 +1920,7 @@ static int __init amd_pstate_init(void)
 	return ret;
 
 global_attr_free:
-	cpufreq_unregister_driver(current_pstate_driver);
+	amd_pstate_unregister_driver(0);
 	return ret;
 }
 device_initcall(amd_pstate_init);
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 04/12] x86/cpufeatures: Add AMD CPPC Performance Priority feature.
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy, H. Peter Anvin,
	Borislav Petkov, Dave Hansen, Thomas Gleixner, Ingo Molnar, x86
In-Reply-To: <20260326114756.20374-1-gautham.shenoy@amd.com>

Some future AMD processors have feature named "CPPC Performance
Priority" which lets userspace specify different floor performance
levels for different CPUs. The platform firmware takes these different
floor performance levels into consideration while throttling the CPUs
under power/thermal constraints. The presence of this feature is
indicated by bit 16 of the EDX register for CPUID leaf
0x80000007. More details can be found in AMD Publication titled "AMD64
Collaborative Processor Performance Control (CPPC) Performance
Priority" Revision 1.10.

Define a new feature bit named X86_FEATURE_CPPC_PERF_PRIO to map to
CPUID 0x80000007.EDX[16].

Reviewed-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
---
 arch/x86/include/asm/cpufeatures.h       | 2 +-
 arch/x86/kernel/cpu/scattered.c          | 1 +
 tools/arch/x86/include/asm/cpufeatures.h | 2 +-
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index dbe104df339b..86d17b195e79 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -415,7 +415,7 @@
  */
 #define X86_FEATURE_OVERFLOW_RECOV	(17*32+ 0) /* "overflow_recov" MCA overflow recovery support */
 #define X86_FEATURE_SUCCOR		(17*32+ 1) /* "succor" Uncorrectable error containment and recovery */
-
+#define X86_FEATURE_CPPC_PERF_PRIO	(17*32+ 2) /* CPPC Floor Perf support */
 #define X86_FEATURE_SMCA		(17*32+ 3) /* "smca" Scalable MCA */
 
 /* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 42c7eac0c387..837d6a4b0c28 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -52,6 +52,7 @@ static const struct cpuid_bit cpuid_bits[] = {
 	{ X86_FEATURE_CPB,			CPUID_EDX,  9, 0x80000007, 0 },
 	{ X86_FEATURE_PROC_FEEDBACK,		CPUID_EDX, 11, 0x80000007, 0 },
 	{ X86_FEATURE_AMD_FAST_CPPC,		CPUID_EDX, 15, 0x80000007, 0 },
+	{ X86_FEATURE_CPPC_PERF_PRIO,		CPUID_EDX, 16, 0x80000007, 0 },
 	{ X86_FEATURE_MBA,			CPUID_EBX,  6, 0x80000008, 0 },
 	{ X86_FEATURE_X2AVIC_EXT,		CPUID_ECX,  6, 0x8000000a, 0 },
 	{ X86_FEATURE_COHERENCY_SFW_NO,		CPUID_EBX, 31, 0x8000001f, 0 },
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index dbe104df339b..86d17b195e79 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -415,7 +415,7 @@
  */
 #define X86_FEATURE_OVERFLOW_RECOV	(17*32+ 0) /* "overflow_recov" MCA overflow recovery support */
 #define X86_FEATURE_SUCCOR		(17*32+ 1) /* "succor" Uncorrectable error containment and recovery */
-
+#define X86_FEATURE_CPPC_PERF_PRIO	(17*32+ 2) /* CPPC Floor Perf support */
 #define X86_FEATURE_SMCA		(17*32+ 3) /* "smca" Scalable MCA */
 
 /* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 01/12] amd-pstate: Fix memory leak in amd_pstate_epp_cpu_init()
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy, Mario Limonciello
In-Reply-To: <20260326114756.20374-1-gautham.shenoy@amd.com>

On failure to set the epp, the function amd_pstate_epp_cpu_init()
returns with an error code without freeing the cpudata object that was
allocated at the beginning of the function.

Ensure that the cpudata object is freed before returning from the
function.

This memory leak was discovered by Claude Opus 4.6 with the aid of
Chris Mason's AI review-prompts
(https://github.com/masoncl/review-prompts/tree/main/kernel).

Assisted-by: Claude:claude-opus-4.6 review-prompts/linux
Fixes: f9a378ff6443 ("cpufreq/amd-pstate: Set different default EPP policy for Epyc and Ryzen")
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
---
 drivers/cpufreq/amd-pstate.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 5aa9fcd80cf5..d57969c72c9d 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1533,7 +1533,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
 
 	ret = amd_pstate_set_epp(policy, cpudata->epp_default);
 	if (ret)
-		return ret;
+		goto free_cpudata1;
 
 	current_pstate_driver->adjust_perf = NULL;
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 02/12] amd-pstate: Update cppc_req_cached in fast_switch case
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy, Mario Limonciello
In-Reply-To: <20260326114756.20374-1-gautham.shenoy@amd.com>

The function msr_update_perf() does not cache the new value that is
written to MSR_AMD_CPPC_REQ into the variable cpudata->cppc_req_cached
when the update is happening from the fast path.

Fix that by caching the value everytime the MSR_AMD_CPPC_REQ gets
updated.

This issue was discovered by Claude Opus 4.6 with the aid of Chris
Mason's AI review-prompts
(https://github.com/masoncl/review-prompts/tree/main/kernel).

Assisted-by: Claude:claude-opus-4.6 review-prompts/linux
Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
Fixes: fff395796917 ("cpufreq/amd-pstate: Always write EPP value when updating perf")
Signed-off-by: Gautham R. Shenoy <gautham.shenoy@amd.com>
---
 drivers/cpufreq/amd-pstate.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index d57969c72c9d..24cdeffbcd40 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -261,7 +261,6 @@ static int msr_update_perf(struct cpufreq_policy *policy, u8 min_perf,
 
 	if (fast_switch) {
 		wrmsrq(MSR_AMD_CPPC_REQ, value);
-		return 0;
 	} else {
 		int ret = wrmsrq_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, value);
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 00/12] amd-pstate: Introduce AMD CPPC Performance Priority
From: Gautham R. Shenoy @ 2026-03-26 11:47 UTC (permalink / raw)
  To: Mario Limonciello, Rafael J . Wysocki, Viresh Kumar,
	K Prateek Nayak
  Cc: linux-kernel, linux-pm, Gautham R. Shenoy

Hello,

This is the v4 of the patchset to add support to the amd-pstate driver
for a new feature named "CPPC Performance Priority" that will be
available on some of the future AMD processors.

Details of the feature can be found in the AMD Publication titled
"AMD64 Collaborative Processor Performance Control (CPPC) Performance
Priority" (https://docs.amd.com/v/u/en-US/69206_1.10_AMD64_CPPC_PUB)

v3--> v4 changes:

 * Modified Patch 8 to run a subset of test-cases specified as
   comma-seperated list while loading the module [Mario, Prateek]

v2-->v3 changes:

 * Picked up the Reviewed-by: tags from Mario except for Patches 5 and
   6 which have major changes (see below)
 
 * Patch 3: Fixed the subtle bug in amd_pstate_driver_cleanup() by cleaning
   setting current_pstate_driver->attr to NULL [Claude Opus 4.6 +
   review-prompts]

 * Patch 5: Added bios_floor_perf field to struct amd_cpudata to cache
   boot-time floor_perf value [New]

 * Patch 5: Moved policy->driver_data = cpudata assignment earlier in
   amd_pstate_cpu_init() (before amd_pstate_cppc_enable()) so that
   policy->driver_data is valid when amd_pstate_init_floor_perf() is
   called inside amd_pstate_cppc_enable() [Bug discovered while
   running amd-pstate-ut tests]

 * Patch 5: Added policy->driver_data = NULL in error paths of both
   amd_pstate_cpu_init() and amd_pstate_epp_cpu_init() to clean up on
   failure. [Code-Hardening]

 * Patch 5: Restores bios_floor_perf via amd_pstate_set_floor_perf() in both
   amd_pstate_cpu_exit() and amd_pstate_epp_cpu_exit() [New]

 * Patch 6: Added input validation in store_amd_pstate_floor_freq():
   rejects frequencies outside [cpuinfo_min_freq, scaling_max_freq]
   with -EINVAL. [Code Hardening]

 * Patch 6: Removed stray blank line between
   show_amd_pstate_floor_freq() and
   show_amd_pstate_floor_count(). [Mario]

 * Patch 6: Reset the floor_perf to bios_floor_perf in the suspend,
   offline, and exit paths, and restore the value to the cached
   user-request floor_freq on the resume and online paths mirroring
   how bios_min_perf is handled for MSR_AMD_CPPC_REQ [New]

 * Patch 8: Add the capability to run a single test from amd_pstate_ut [New]

 * Patch 9: New unit test to validate the driver->attrs [Mario]

 * Patch 10 and 11: Split the Documentation fixes for
   amd_pstate_hw_prefcore and amd_pstate_prefcore_ranking into two
   different patches [Mario]

v1 --> v2 Changes:
 * Picked up the Reviewed-by: tags from Boris and Mario for a couple of patches.
 
 * Defined AMD_CPPC_FLOOR_PERF_CNT_MASK via GENMASK_ULL() instead of
   GENMASK() to fix the build errors reported by the kernel test robot
   (https://lore.kernel.org/lkml/202603070431.ykswVnpp-lkp@intel.com/)

 * Moved the code from amd_pstate_cache_cppc_req2() into
   amd_pstate_init_floor_perf() since there are no other callers of
   amd_pstate_cache_cppc_req2().

 * Cached the user requested amd_pstate_floor_freq into
   cpudata->floor_freq [Prateek] and return the same when the user
   reads the syfs file.

Description:

This feature allows userspace to specify different floor performance
levels for different CPUs. The platform firmware takes these different
floor performance levels into consideration while throttling the CPUs
under power/thermal constraints.
    
The presence of this feature is advertised through bit 16 of EDX
register for CPUID leaf 0x80000007. The number of distinct floor
performance levels supported on the platform will be advertised
through the bits 32:39 of the MSR_AMD_CPPC_CAP1. Bits 0:7 of a new MSR
MSR_AMD_CPPC_REQ2 (0xc00102b5) will be used to specify the desired
floor performance level for that CPU.
    
Key changes made by this patchset:

   * Fix a memory leak bug and a control-flow bug.
   
   * Plumb in proper visibility controls for the freq_attr attributes
     so that only relevant attributes can be made visible depending on
     the underlying platform and the current amd-pstate driver mode.

   * Add support for the new CPUID bits, the new MSR and parsing bits
     32:39 of MSR_AMD_CPPC_CAP1.

   * Set the default value for MSR_AMD_CPPC_REQ2[0:7] (Floor perf) to
     CPPC.nominal_perf when the value at boot-time is lower than
     CPPC.lowest_perf

   * Add sysfs support for floor_freq and floor_count

   * Add the capability to only run a subset of unit-tests in the
     amd-pstate-ut module.

   * Add a new unit-test in amd-pstate-ut to validate the driver
     freq_attrs.

   * Introduce a tracepoint trace_amd_pstate_cppc_req2 for tracking
     the updates to MSR_AMD_CPPC_REQ2.

   * Add documentation for amd_pstate_floor_{freq,count}

Gautham R. Shenoy (12):
  amd-pstate: Fix memory leak in amd_pstate_epp_cpu_init()
  amd-pstate: Update cppc_req_cached in fast_switch case
  amd-pstate: Make certain freq_attrs conditionally visible
  x86/cpufeatures: Add AMD CPPC Performance Priority feature.
  amd-pstate: Add support for CPPC_REQ2 and FLOOR_PERF
  amd-pstate: Add sysfs support for floor_freq and floor_count
  amd-pstate: Introduce a tracepoint trace_amd_pstate_cppc_req2()
  amd-pstate-ut: Add module parameter to select testcases
  amd-pstate-ut: Add a testcase to validate the visibility of driver attributes
  Documentation/amd-pstate: List amd_pstate_hw_prefcore sysfs file
  Documentation/amd-pstate: List amd_pstate_prefcore_ranking sysfs file
  Documentation/amd-pstate: Add documentation for amd_pstate_floor_{freq,count}

 Documentation/admin-guide/pm/amd-pstate.rst |  42 ++-
 arch/x86/include/asm/cpufeatures.h          |   2 +-
 arch/x86/include/asm/msr-index.h            |   5 +
 arch/x86/kernel/cpu/scattered.c             |   1 +
 drivers/cpufreq/amd-pstate-trace.h          |  35 +++
 drivers/cpufreq/amd-pstate-ut.c             | 170 ++++++++++-
 drivers/cpufreq/amd-pstate.c                | 312 +++++++++++++++++---
 drivers/cpufreq/amd-pstate.h                |  12 +
 tools/arch/x86/include/asm/cpufeatures.h    |   2 +-
 9 files changed, 531 insertions(+), 50 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH v1] thermal: core: Address thermal zone removal races with resume
From: Rafael J. Wysocki @ 2026-03-26 11:45 UTC (permalink / raw)
  To: Linux PM; +Cc: Mauricio Faria de Oliveira, LKML, Daniel Lezcano, Lukasz Luba

From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>

Since thermal_zone_pm_complete() and thermal_zone_device_resume()
reinitialize the poll_queue delayed work for the given thermal zone,
the cancel_delayed_work_sync() in thermal_zone_device_unregister()
may miss some already running work items and the thermal zone may
be freed prematurely [1].

There are two failing scenarios that both start with
running thermal_pm_notify_complete() right before invoking
thermal_zone_device_unregister() for one of the thermal zones.

In the first scenario, there is a work item already running for
the given thermal zone when thermal_pm_notify_complete() calls
thermal_zone_pm_complete() for that thermal zone and it continues to
run when thermal_zone_device_unregister() starts.  Since the poll_queue
delayed work has been reinitialized by thermal_pm_notify_complete(), the
running work item will be missed by the cancel_delayed_work_sync() in
thermal_zone_device_unregister() and if it continues to run past the
freeing of the thermal zone object, a use-after-free will occur.

In the second scenario, thermal_zone_device_resume() queued up by
thermal_pm_notify_complete() runs right after the thermal_zone_exit()
called by thermal_zone_device_unregister() has returned.  The poll_queue
delayed work is reinitialized by it before cancel_delayed_work_sync() is
called by thermal_zone_device_unregister(), so it may continue to run
after the freeing of the thermal zone object, which also leads to a
use-after-free.

Address the first failing scenario by ensuring that no thermal work
items will be running when thermal_pm_notify_complete() is called.
For this purpose, first move the cancel_delayed_work() call from
thermal_zone_pm_complete() to thermal_zone_pm_prepare() to prevent
new work from entering the workqueue going forward.  Next, switch
over to using a dedicated workqueue for thermal events and update
the code in thermal_pm_notify() to flush that workqueue after
thermal_pm_notify_prepare() has returned which will take care of
all leftover thermal work already on the workqueue (that leftover
work would do nothing useful anyway because all of the thermal zones
have been flagged as suspended).

The second failing scenario is addressed by adding a tz->state check
to thermal_zone_device_resume() to prevent it from reinitializing
the poll_queue delayed work if the thermal zone is going away.

Note that the above changes will also facilitate relocating the suspend
and resume of thermal zones closer to the suspend and resume of devices,
respectively.

Fixes: 5a5efdaffda5 ("thermal: core: Resume thermal zones asynchronously")
Reported-by: Mauricio Faria de Oliveira <mfo@igalia.com>
Closes: https://lore.kernel.org/linux-pm/20260324-thermal-core-uaf-init_delayed_work-v1-1-6611ae76a8a1@igalia.com/ [1]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/thermal/thermal_core.c | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index b7d706ed7ed9..248bd0a82a08 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -41,6 +41,8 @@ static struct thermal_governor *def_governor;
 
 static bool thermal_pm_suspended;
 
+static struct workqueue_struct *thermal_wq __ro_after_init;
+
 /*
  * Governor section: set of functions to handle thermal governors
  *
@@ -313,7 +315,7 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
 	if (delay > HZ)
 		delay = round_jiffies_relative(delay);
 
-	mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, delay);
+	mod_delayed_work(thermal_wq, &tz->poll_queue, delay);
 }
 
 static void thermal_zone_recheck(struct thermal_zone_device *tz, int error)
@@ -1785,6 +1787,10 @@ static void thermal_zone_device_resume(struct work_struct *work)
 
 	guard(thermal_zone)(tz);
 
+	/* If the thermal zone is going away, there's nothing to do. */
+	if (tz->state & TZ_STATE_FLAG_EXIT)
+		return;
+
 	tz->state &= ~(TZ_STATE_FLAG_SUSPENDED | TZ_STATE_FLAG_RESUMING);
 
 	thermal_debug_tz_resume(tz);
@@ -1811,6 +1817,9 @@ static void thermal_zone_pm_prepare(struct thermal_zone_device *tz)
 	}
 
 	tz->state |= TZ_STATE_FLAG_SUSPENDED;
+
+	/* Prevent new work from getting to the workqueue subsequently. */
+	cancel_delayed_work(&tz->poll_queue);
 }
 
 static void thermal_pm_notify_prepare(void)
@@ -1829,8 +1838,6 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
 {
 	guard(thermal_zone)(tz);
 
-	cancel_delayed_work(&tz->poll_queue);
-
 	reinit_completion(&tz->resume);
 	tz->state |= TZ_STATE_FLAG_RESUMING;
 
@@ -1840,7 +1847,7 @@ static void thermal_zone_pm_complete(struct thermal_zone_device *tz)
 	 */
 	INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_resume);
 	/* Queue up the work without a delay. */
-	mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, 0);
+	mod_delayed_work(thermal_wq, &tz->poll_queue, 0);
 }
 
 static void thermal_pm_notify_complete(void)
@@ -1863,6 +1870,11 @@ static int thermal_pm_notify(struct notifier_block *nb,
 	case PM_RESTORE_PREPARE:
 	case PM_SUSPEND_PREPARE:
 		thermal_pm_notify_prepare();
+		/*
+		 * Allow any leftover thermal work items already on the
+		 * worqueue to complete so they don't get in the way later.
+		 */
+		flush_workqueue(thermal_wq);
 		break;
 	case PM_POST_HIBERNATION:
 	case PM_POST_RESTORE:
@@ -1895,9 +1907,14 @@ static int __init thermal_init(void)
 	if (result)
 		goto error;
 
+	thermal_wq = alloc_workqueue("thermal_events",
+				      WQ_FREEZABLE | WQ_POWER_EFFICIENT | WQ_PERCPU, 0);
+	if (!thermal_wq)
+		goto unregister_netlink;
+
 	result = thermal_register_governors();
 	if (result)
-		goto unregister_netlink;
+		goto destroy_workqueue;
 
 	thermal_class = kzalloc_obj(*thermal_class);
 	if (!thermal_class) {
@@ -1924,6 +1941,8 @@ static int __init thermal_init(void)
 
 unregister_governors:
 	thermal_unregister_governors();
+destroy_workqueue:
+	destroy_workqueue(thermal_wq);
 unregister_netlink:
 	thermal_netlink_exit();
 error:
-- 
2.51.0





^ permalink raw reply related

* [PATCH v2] PM: hibernate: call preallocate_image after freeze prepare
From: Matthew Leach @ 2026-03-26 11:36 UTC (permalink / raw)
  To: Rafael J. Wysocki, Pavel Machek, Len Brown, Mario Limonciello
  Cc: linux-pm, linux-kernel, YoungJun Park, kernel, Matthew Leach

Certain drivers release resources (pinned pages, etc.) into system
memory during the prepare freeze PM op, making them swappable.
Currently, hibernate_preallocate_memory is called before prepare freeze,
so those drivers have no opportunity to release resources first. If a
driver is holding a large amount of unswappable system RAM, this can
cause hibernate_preallocate_memory to fail.

Move the call to hibernate_preallocate_memory after prepare freeze.
According to the documentation for the prepare callback, devices should
be left in a usable state, so storage drivers should still be able to
service I/O requests. This allows drivers to release unswappable
resources prior to preallocation, so they can be swapped out through
hibernate_preallocate_memory's reclaim path. Also remove
shrink_shmem_memory since hibernate_preallocate_memory will have
reclaimed enough memory for the hibernation image.

Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
---
Changes in v2:
- Removed shrink_shmem_memory.
- Fixed missing call to dpm_prepare in error path.
- Link to v1: https://lore.kernel.org/r/20260321-hibernation-fixes-v1-1-5fe9637b6ff9@collabora.com
---
 kernel/power/hibernate.c | 46 +++++++++-------------------------------------
 1 file changed, 9 insertions(+), 37 deletions(-)

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index af8d07bafe02..39b0a8ea4024 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -392,23 +392,6 @@ static int create_image(int platform_mode)
 	return error;
 }
 
-static void shrink_shmem_memory(void)
-{
-	struct sysinfo info;
-	unsigned long nr_shmem_pages, nr_freed_pages;
-
-	si_meminfo(&info);
-	nr_shmem_pages = info.sharedram; /* current page count used for shmem */
-	/*
-	 * The intent is to reclaim all shmem pages. Though shrink_all_memory() can
-	 * only reclaim about half of them, it's enough for creating the hibernation
-	 * image.
-	 */
-	nr_freed_pages = shrink_all_memory(nr_shmem_pages);
-	pr_debug("requested to reclaim %lu shmem pages, actually freed %lu pages\n",
-			nr_shmem_pages, nr_freed_pages);
-}
-
 /**
  * hibernation_snapshot - Quiesce devices and create a hibernation image.
  * @platform_mode: If set, use platform driver to prepare for the transition.
@@ -425,14 +408,9 @@ int hibernation_snapshot(int platform_mode)
 	if (error)
 		goto Close;
 
-	/* Preallocate image memory before shutting down devices. */
-	error = hibernate_preallocate_memory();
-	if (error)
-		goto Close;
-
 	error = freeze_kernel_threads();
 	if (error)
-		goto Cleanup;
+		goto Close;
 
 	if (hibernation_test(TEST_FREEZER)) {
 
@@ -441,23 +419,17 @@ int hibernation_snapshot(int platform_mode)
 		 * successful freezer test.
 		 */
 		freezer_test_done = true;
-		goto Thaw;
+		goto ThawKThreads;
 	}
 
 	error = dpm_prepare(PMSG_FREEZE);
-	if (error) {
-		dpm_complete(PMSG_RECOVER);
+	if (error)
 		goto Thaw;
-	}
 
-	/*
-	 * Device drivers may move lots of data to shmem in dpm_prepare(). The shmem
-	 * pages will use lots of system memory, causing hibernation image creation
-	 * fail due to insufficient free memory.
-	 * This call is to force flush the shmem pages to swap disk and reclaim
-	 * the system memory so that image creation can succeed.
-	 */
-	shrink_shmem_memory();
+	/* Preallocate image memory before shutting down devices. */
+	error = hibernate_preallocate_memory();
+	if (error)
+		goto Thaw;
 
 	console_suspend_all();
 	pm_restrict_gfp_mask();
@@ -493,9 +465,9 @@ int hibernation_snapshot(int platform_mode)
 	return error;
 
  Thaw:
+	dpm_complete(PMSG_RECOVER);
+ ThawKThreads:
 	thaw_kernel_threads();
- Cleanup:
-	swsusp_free();
 	goto Close;
 }
 

---
base-commit: f338e77383789c0cae23ca3d48adcc5e9e137e3c
change-id: 20260321-hibernation-fixes-69bca2ee5b65

Best regards,
--  
Matt


^ permalink raw reply related

* Re: [PATCH v3 05/12] amd-pstate: Add support for CPPC_REQ2 and FLOOR_PERF
From: Gautham R. Shenoy @ 2026-03-26 11:30 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: Rafael J . Wysocki, Viresh Kumar, K Prateek Nayak, linux-kernel,
	linux-pm
In-Reply-To: <48262f07-a852-44e0-92a0-1215bc48e9cf@amd.com>

Hello Mario,

On Tue, Mar 24, 2026 at 04:38:07PM -0500, Mario Limonciello wrote:
> 
> 

[..snip..]

> > index cb45fdca27a6..f04561da4518 100644
> > --- a/drivers/cpufreq/amd-pstate.h
> > +++ b/drivers/cpufreq/amd-pstate.h
> > @@ -62,9 +62,12 @@ struct amd_aperf_mperf {
> >    * @cpu: CPU number
> >    * @req: constraint request to apply
> >    * @cppc_req_cached: cached performance request hints
> > + * @cppc_req2_cached: cached value of MSR_AMD_CPPC_REQ2
> >    * @perf: cached performance-related data
> >    * @prefcore_ranking: the preferred core ranking, the higher value indicates a higher
> >    * 		  priority.
> > + * @floor_perf_cnt: Cached value of the number of distinct floor
> > + *                  performance levels supported
> >    * @min_limit_freq: Cached value of policy->min (in khz)
> >    * @max_limit_freq: Cached value of policy->max (in khz)
> >    * @nominal_freq: the frequency (in khz) that mapped to nominal_perf
> > @@ -87,10 +90,13 @@ struct amd_cpudata {
> >   	struct	freq_qos_request req[2];
> >   	u64	cppc_req_cached;
> > +	u64	cppc_req2_cached;
> >   	union perf_cached perf;
> >   	u8	prefcore_ranking;
> > +	u8	floor_perf_cnt;
> > +	u8	bios_floor_perf;
> 
> It looks like you forgot to update doc for bios_floor_perf

Thanks for catching this. I will add the doc for bios_floor_perf in v4.


-- 
Thanks and Regards
gautham.

^ permalink raw reply

* Re: [PATCH v3 08/12] amd-pstate-ut: Add ability to run a single testcase
From: Gautham R. Shenoy @ 2026-03-26 11:29 UTC (permalink / raw)
  To: Mario Limonciello
  Cc: K Prateek Nayak, Rafael J . Wysocki, Viresh Kumar, linux-kernel,
	linux-pm
In-Reply-To: <b69cc187-6ef3-4390-a0cb-f36a2295c6f2@kernel.org>

On Wed, Mar 25, 2026 at 08:45:05AM -0500, Mario Limonciello wrote:
> 
> 
> On 3/24/26 23:28, K Prateek Nayak wrote:
> > Hello Gautham,
> > 
> > On 3/24/2026 9:59 AM, Gautham R. Shenoy wrote:
> > > +static bool test_in_list(const char *list, const char *name)
> > > +{
> > > +       size_t name_len = strlen(name);
> > > +       const char *p = list;
> > > +
> > > +       while (*p) {
> > > +               const char *sep = strchr(p, ';');
> > 
> > Any particular reason for using a ";" as the separator instead of ","?
> > 
> > I personally prefer "," because with ";", I need to explicitly add
> > '' around the test_list otherwise bash thinks the command ends at ";"
> > but with "," that is avoided.
> > 
> > Thoughts?
> > 
> 
> Sure, that sounds like a good reason to use a comma instead.

Sure, I wil change it to a comma-separated list in v4.

-- 
Thanks and Regards
gautham.

^ permalink raw reply

* [PATCH v2 2/2] selftests/bpf: Add tests for wakeup_sources kfuncs
From: Samuel Wu @ 2026-03-26 11:25 UTC (permalink / raw)
  To: Rafael J. Wysocki, Pavel Machek, Len Brown, Greg Kroah-Hartman,
	Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa, Shuah Khan
  Cc: memxor, Samuel Wu, kernel-team, linux-kernel, linux-pm,
	driver-core, bpf, linux-kselftest
In-Reply-To: <20260326112521.2827500-1-wusamuel@google.com>

Introduce a set of BPF selftests to verify the safety and functionality
of wakeup_source kfuncs.

The suite includes:
1. A functional test (test_wakeup_source.c) that iterates over the
   global wakeup_sources list. It uses CO-RE to read timing statistics
   and validates them in user-space via the BPF ring buffer.
2. A negative test suite (wakeup_source_fail.c) ensuring the BPF
   verifier correctly enforces reference tracking and type safety.
3. Enable CONFIG_PM_WAKELOCKS in the test config, allowing creation of
   wakeup sources via /sys/power/wake_lock.

A shared header (wakeup_source.h) is introduced to ensure consistent
memory layout for the Ring Buffer data between BPF and user-space.

Signed-off-by: Samuel Wu <wusamuel@google.com>
---
 tools/testing/selftests/bpf/config            |   3 +-
 .../selftests/bpf/prog_tests/wakeup_source.c  | 101 +++++++++++++++++
 .../selftests/bpf/progs/test_wakeup_source.c  | 107 ++++++++++++++++++
 .../selftests/bpf/progs/wakeup_source.h       |  22 ++++
 .../selftests/bpf/progs/wakeup_source_fail.c  |  63 +++++++++++
 5 files changed, 295 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/wakeup_source.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_wakeup_source.c
 create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source.h
 create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source_fail.c

diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index 24855381290d..bac60b444551 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -130,4 +130,5 @@ CONFIG_INFINIBAND=y
 CONFIG_SMC=y
 CONFIG_SMC_HS_CTRL_BPF=y
 CONFIG_DIBS=y
-CONFIG_DIBS_LO=y
\ No newline at end of file
+CONFIG_DIBS_LO=y
+CONFIG_PM_WAKELOCKS=y
diff --git a/tools/testing/selftests/bpf/prog_tests/wakeup_source.c b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c
new file mode 100644
index 000000000000..ff2899cbf3a8
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/wakeup_source.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Google LLC */
+
+#include <test_progs.h>
+#include <fcntl.h>
+#include "test_wakeup_source.skel.h"
+#include "wakeup_source_fail.skel.h"
+#include "progs/wakeup_source.h"
+
+static int lock_ws(const char *name)
+{
+	int fd;
+	ssize_t bytes;
+
+	fd = open("/sys/power/wake_lock", O_WRONLY);
+	if (!ASSERT_OK_FD(fd, "open /sys/power/wake_lock"))
+		return -1;
+
+	bytes = write(fd, name, strlen(name));
+	close(fd);
+	if (!ASSERT_EQ(bytes, strlen(name), "write to wake_lock"))
+		return -1;
+
+	return 0;
+}
+
+static void unlock_ws(const char *name)
+{
+	int fd;
+
+	fd = open("/sys/power/wake_unlock", O_WRONLY);
+	if (fd < 0)
+		return;
+
+	write(fd, name, strlen(name));
+	close(fd);
+}
+
+struct rb_ctx {
+	const char *name;
+	bool found;
+	long long active_time_ns;
+	long long total_time_ns;
+};
+
+static int process_sample(void *ctx, void *data, size_t len)
+{
+	struct rb_ctx *rb_ctx = ctx;
+	struct wakeup_event_t *e = data;
+
+	if (strcmp(e->name, rb_ctx->name) == 0) {
+		rb_ctx->found = true;
+		rb_ctx->active_time_ns = e->active_time_ns;
+		rb_ctx->total_time_ns = e->total_time_ns;
+	}
+	return 0;
+}
+
+void test_wakeup_source(void)
+{
+	if (test__start_subtest("iterate_and_verify_times")) {
+		struct test_wakeup_source *skel;
+		struct ring_buffer *rb = NULL;
+		struct rb_ctx rb_ctx = {
+			.name = "bpf_selftest_ws_times",
+			.found = false,
+		};
+		int err;
+
+		skel = test_wakeup_source__open_and_load();
+		if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+			return;
+
+		rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), process_sample, &rb_ctx, NULL);
+		if (!ASSERT_OK_PTR(rb, "ring_buffer__new"))
+			goto destroy;
+
+		/* Create a temporary wakeup source */
+		if (!ASSERT_OK(lock_ws(rb_ctx.name), "lock_ws"))
+			goto unlock;
+
+		err = bpf_prog_test_run_opts(bpf_program__fd(
+				skel->progs.iterate_wakeupsources), NULL);
+		ASSERT_OK(err, "bpf_prog_test_run");
+
+		ring_buffer__consume(rb);
+
+		ASSERT_TRUE(rb_ctx.found, "found_test_ws_in_rb");
+		ASSERT_GT(rb_ctx.active_time_ns, 0, "active_time_gt_0");
+		ASSERT_GT(rb_ctx.total_time_ns, 0, "total_time_gt_0");
+
+unlock:
+		unlock_ws(rb_ctx.name);
+destroy:
+		if (rb)
+			ring_buffer__free(rb);
+		test_wakeup_source__destroy(skel);
+	}
+
+	RUN_TESTS(wakeup_source_fail);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_wakeup_source.c b/tools/testing/selftests/bpf/progs/test_wakeup_source.c
new file mode 100644
index 000000000000..fa35156e31d2
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_wakeup_source.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Google LLC */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_experimental.h"
+#include "bpf_misc.h"
+#include "wakeup_source.h"
+
+#define MAX_LOOP_ITER 1000
+#define RB_SIZE (16384 * 4)
+
+struct wakeup_source___local {
+	const char *name;
+	unsigned long active_count;
+	unsigned long event_count;
+	unsigned long wakeup_count;
+	unsigned long expire_count;
+	ktime_t last_time;
+	ktime_t max_time;
+	ktime_t total_time;
+	ktime_t start_prevent_time;
+	ktime_t prevent_sleep_time;
+	struct list_head entry;
+	bool active:1;
+	bool autosleep_enabled:1;
+} __attribute__((preserve_access_index));
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, RB_SIZE);
+} rb SEC(".maps");
+
+struct bpf_ws_lock;
+struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void) __ksym;
+void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock) __ksym;
+struct list_head *bpf_wakeup_sources_get_head(void) __ksym;
+
+SEC("syscall")
+__success __retval(0)
+int iterate_wakeupsources(void *ctx)
+{
+	struct list_head *head = bpf_wakeup_sources_get_head();
+	struct list_head *pos = head;
+	struct bpf_ws_lock *lock;
+	int i;
+
+	lock = bpf_wakeup_sources_read_lock();
+	if (!lock)
+		return 0;
+
+	bpf_for(i, 0, MAX_LOOP_ITER) {
+		if (bpf_core_read(&pos, sizeof(pos), &pos->next) || !pos || pos == head)
+			break;
+
+		struct wakeup_event_t *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
+
+		if (!e)
+			break;
+
+		struct wakeup_source___local *ws = container_of(
+				pos, struct wakeup_source___local, entry);
+		s64 active_time = 0;
+		bool active = BPF_CORE_READ_BITFIELD_PROBED(ws, active);
+		bool autosleep_enable = BPF_CORE_READ_BITFIELD_PROBED(ws, autosleep_enabled);
+		s64 last_time = BPF_CORE_READ(ws, last_time);
+		s64 max_time = BPF_CORE_READ(ws, max_time);
+		s64 prevent_sleep_time = BPF_CORE_READ(ws, prevent_sleep_time);
+		s64 total_time = BPF_CORE_READ(ws, total_time);
+
+		if (active) {
+			s64 curr_time = bpf_ktime_get_ns();
+			s64 prevent_time = BPF_CORE_READ(ws, start_prevent_time);
+
+			if (curr_time > last_time)
+				active_time = curr_time - last_time;
+
+			total_time += active_time;
+			if (active_time > max_time)
+				max_time = active_time;
+			if (autosleep_enable && curr_time > prevent_time)
+				prevent_sleep_time += curr_time - prevent_time;
+		}
+
+		e->active_count = BPF_CORE_READ(ws, active_count);
+		e->active_time_ns = active_time;
+		e->event_count = BPF_CORE_READ(ws, event_count);
+		e->expire_count = BPF_CORE_READ(ws, expire_count);
+		e->last_time_ns = last_time;
+		e->max_time_ns = max_time;
+		e->prevent_sleep_time_ns = prevent_sleep_time;
+		e->total_time_ns = total_time;
+		e->wakeup_count = BPF_CORE_READ(ws, wakeup_count);
+
+		if (bpf_probe_read_kernel_str(
+				e->name, WAKEUP_NAME_LEN, BPF_CORE_READ(ws, name)) < 0)
+			e->name[0] = '\0';
+
+		bpf_ringbuf_submit(e, 0);
+	}
+
+	bpf_wakeup_sources_read_unlock(lock);
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/wakeup_source.h b/tools/testing/selftests/bpf/progs/wakeup_source.h
new file mode 100644
index 000000000000..cd74de92c82f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/wakeup_source.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright 2026 Google LLC */
+
+#ifndef __WAKEUP_SOURCE_H__
+#define __WAKEUP_SOURCE_H__
+
+#define WAKEUP_NAME_LEN 128
+
+struct wakeup_event_t {
+	unsigned long active_count;
+	long long active_time_ns;
+	unsigned long event_count;
+	unsigned long expire_count;
+	long long last_time_ns;
+	long long max_time_ns;
+	long long prevent_sleep_time_ns;
+	long long total_time_ns;
+	unsigned long wakeup_count;
+	char name[WAKEUP_NAME_LEN];
+};
+
+#endif /* __WAKEUP_SOURCE_H__ */
diff --git a/tools/testing/selftests/bpf/progs/wakeup_source_fail.c b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
new file mode 100644
index 000000000000..a569c9d53d21
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/wakeup_source_fail.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright 2026 Google LLC */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+struct bpf_ws_lock;
+
+struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void) __ksym;
+void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock) __ksym;
+
+SEC("syscall")
+__failure __msg("BPF_EXIT instruction in main prog would lead to reference leak")
+int wakeup_source_lock_no_unlock(void *ctx)
+{
+	struct bpf_ws_lock *lock;
+
+	lock = bpf_wakeup_sources_read_lock();
+	if (!lock)
+		return 0;
+
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("access beyond struct")
+int wakeup_source_access_lock_fields(void *ctx)
+{
+	struct bpf_ws_lock *lock;
+	int val;
+
+	lock = bpf_wakeup_sources_read_lock();
+	if (!lock)
+		return 0;
+
+	val = *(int *)lock;
+
+	bpf_wakeup_sources_read_unlock(lock);
+	return val;
+}
+
+SEC("syscall")
+__failure __msg("type=scalar expected=fp")
+int wakeup_source_unlock_no_lock(void *ctx)
+{
+	struct bpf_ws_lock *lock = (void *)0x1;
+
+	bpf_wakeup_sources_read_unlock(lock);
+
+	return 0;
+}
+
+SEC("syscall")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
+int wakeup_source_unlock_null(void *ctx)
+{
+	bpf_wakeup_sources_read_unlock(NULL);
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.53.0.1018.g2bb0e51243-goog


^ permalink raw reply related

* [PATCH v2 1/2] PM: wakeup: Add kfuncs to traverse over wakeup_sources
From: Samuel Wu @ 2026-03-26 11:25 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Pavel Machek, Greg Kroah-Hartman,
	Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa, Shuah Khan
  Cc: memxor, Samuel Wu, kernel-team, linux-kernel, linux-pm,
	driver-core, bpf, linux-kselftest
In-Reply-To: <20260326112521.2827500-1-wusamuel@google.com>

Iterating through wakeup sources via sysfs or debugfs can be inefficient
or restricted. Introduce BPF kfuncs to allow high-performance and safe
in-kernel traversal of the wakeup_sources list.

The new kfuncs include:
- bpf_wakeup_sources_get_head() to obtain the list head.
- bpf_wakeup_sources_read_lock/unlock() to manage the SRCU lock.

For verifier safety, the underlying SRCU index is wrapped in an opaque
'struct bpf_ws_lock' pointer. This enables the use of KF_ACQUIRE and
KF_RELEASE flags, allowing the BPF verifier to strictly enforce paired
lock/unlock cycles and prevent resource leaks.

Signed-off-by: Samuel Wu <wusamuel@google.com>
---
 drivers/base/power/power.h  |  7 ++++
 drivers/base/power/wakeup.c | 72 +++++++++++++++++++++++++++++++++++--
 2 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 922ed457db19..ced563286ac5 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -168,3 +168,10 @@ static inline void device_pm_init(struct device *dev)
 	device_pm_sleep_init(dev);
 	pm_runtime_init(dev);
 }
+
+#ifdef CONFIG_BPF_SYSCALL
+struct bpf_ws_lock { };
+struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void);
+void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock);
+struct list_head *bpf_wakeup_sources_get_head(void);
+#endif
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index b8e48a023bf0..27a26a30b6ee 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -1168,11 +1168,79 @@ static const struct file_operations wakeup_sources_stats_fops = {
 	.release = seq_release_private,
 };
 
-static int __init wakeup_sources_debugfs_init(void)
+#ifdef CONFIG_BPF_SYSCALL
+#include <linux/btf.h>
+
+__bpf_kfunc_start_defs();
+
+/**
+ * bpf_wakeup_sources_read_lock - Acquire the SRCU lock for wakeup sources
+ *
+ * The underlying SRCU lock returns an integer index. However, the BPF verifier
+ * requires a pointer (PTR_TO_BTF_ID) to strictly track the state of acquired
+ * resources using KF_ACQUIRE and KF_RELEASE semantics. We use an opaque
+ * structure pointer (struct bpf_ws_lock *) to satisfy the verifier while
+ * safely encoding the integer index within the pointer address itself.
+ *
+ * Return: An opaque pointer encoding the SRCU lock index + 1 (to avoid NULL).
+ */
+__bpf_kfunc struct bpf_ws_lock *bpf_wakeup_sources_read_lock(void)
+{
+	return (struct bpf_ws_lock *)(long)(wakeup_sources_read_lock() + 1);
+}
+
+/**
+ * bpf_wakeup_sources_read_unlock - Release the SRCU lock for wakeup sources
+ * @lock: The opaque pointer returned by bpf_wakeup_sources_read_lock()
+ *
+ * The BPF verifier guarantees that @lock is a valid, unreleased pointer from
+ * the acquire function. We decode the pointer back into the integer SRCU index
+ * by subtracting 1 and release the lock.
+ */
+__bpf_kfunc void bpf_wakeup_sources_read_unlock(struct bpf_ws_lock *lock)
+{
+	wakeup_sources_read_unlock((int)(long)lock - 1);
+}
+
+/**
+ * bpf_wakeup_sources_get_head - Get the head of the wakeup sources list
+ *
+ * Return: The head of the wakeup sources list.
+ */
+__bpf_kfunc struct list_head *bpf_wakeup_sources_get_head(void)
+{
+	return &wakeup_sources;
+}
+
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(wakeup_source_kfunc_ids)
+BTF_ID_FLAGS(func, bpf_wakeup_sources_read_lock, KF_ACQUIRE)
+BTF_ID_FLAGS(func, bpf_wakeup_sources_read_unlock, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_wakeup_sources_get_head)
+BTF_KFUNCS_END(wakeup_source_kfunc_ids)
+
+static const struct btf_kfunc_id_set wakeup_source_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set   = &wakeup_source_kfunc_ids,
+};
+
+static void __init wakeup_sources_bpf_init(void)
+{
+	if (register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &wakeup_source_kfunc_set))
+		pm_pr_dbg("Wakeup: failed to register BTF kfuncs\n");
+}
+#else
+static inline void wakeup_sources_bpf_init(void) {}
+#endif /* CONFIG_BPF_SYSCALL */
+
+static int __init wakeup_sources_init(void)
 {
 	debugfs_create_file("wakeup_sources", 0444, NULL, NULL,
 			    &wakeup_sources_stats_fops);
+	wakeup_sources_bpf_init();
+
 	return 0;
 }
 
-postcore_initcall(wakeup_sources_debugfs_init);
+postcore_initcall(wakeup_sources_init);
-- 
2.53.0.1018.g2bb0e51243-goog


^ permalink raw reply related

* [PATCH v2 0/2] Support BPF traversal of wakeup sources
From: Samuel Wu @ 2026-03-26 11:25 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Pavel Machek, Greg Kroah-Hartman,
	Danilo Krummrich, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
	Hao Luo, Jiri Olsa, Shuah Khan
  Cc: memxor, Samuel Wu, kernel-team, linux-kernel, linux-pm,
	driver-core, bpf, linux-kselftest

This patchset adds requisite kfuncs for BPF programs to safely traverse
wakeup_sources, and puts a config flag around the sysfs interface.

Currently, a traversal of wakeup sources require going through
/sys/class/wakeup/* or /d/wakeup_sources/*. The repeated syscalls to query
sysfs is inefficient, as there can be hundreds of wakeup_sources, with each
wakeup source also having multiple attributes. debugfs is unstable and
insecure.

Adding kfuncs to lock/unlock wakeup sources allows BPF program to safely
traverse the wakeup sources list. The head address of wakeup_sources can
safely be resolved through BPF helper functions or variable attributes.

On a quiescent Pixel 6 traversing 150 wakeup_sources, I am seeing ~34x
speedup (sampled 75 times in table below). For a device under load, the
speedup is greater.
+-------+----+----------+----------+
|       | n  | AVG (ms) | STD (ms) |
+-------+----+----------+----------+
| sysfs | 75 | 44.9     | 12.6     |
+-------+----+----------+----------+
| BPF   | 75 | 1.3      | 0.7      |
+-------+----+----------+----------+

The initial attempts for BPF traversal of wakeup_sources was with BPF
iterators [1]. However, BPF already allows for traversing of a simple list
with bpf_for(), and this current patchset has the added benefit of being
~2-3x more performant than BPF iterators.

[1]: https://lore.kernel.org/all/20260225210820.177674-1-wusamuel@google.com/

Changes in v2:
- Dropped CONFIG_PM_WAKEUP_STATS_SYSFS patch for future patchset
- Added declarations for kfuncs to .h to fix sparse and checkpatch warnings
- Added kfunc to get address of wakeup_source's head
- Added example bpf prog selftest for traversal of wakeup sources per Kumar
- Added *_fail.c selftest per Kumar
- More concise commit message in patch 1/2
- v1 link: https://lore.kernel.org/all/20260320160055.4114055-1-wusamuel@google.com/

Samuel Wu (2):
  PM: wakeup: Add kfuncs to traverse over wakeup_sources
  selftests/bpf: Add tests for wakeup_sources kfuncs

 drivers/base/power/power.h                    |   7 ++
 drivers/base/power/wakeup.c                   |  72 +++++++++++-
 tools/testing/selftests/bpf/config            |   3 +-
 .../selftests/bpf/prog_tests/wakeup_source.c  | 101 +++++++++++++++++
 .../selftests/bpf/progs/test_wakeup_source.c  | 107 ++++++++++++++++++
 .../selftests/bpf/progs/wakeup_source.h       |  22 ++++
 .../selftests/bpf/progs/wakeup_source_fail.c  |  63 +++++++++++
 7 files changed, 372 insertions(+), 3 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/wakeup_source.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_wakeup_source.c
 create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source.h
 create mode 100644 tools/testing/selftests/bpf/progs/wakeup_source_fail.c

-- 
2.53.0.1018.g2bb0e51243-goog


^ permalink raw reply

* Re: [PATCH v6 20/27] misc: lan966x_pci: Fix dtso nodes ordering
From: Linus Walleij @ 2026-03-26 10:33 UTC (permalink / raw)
  To: Herve Codina
  Cc: Andrew Lunn, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Kalle Niemi, Matti Vaittinen,
	Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Michael Turquette, Stephen Boyd, Andi Shyti, Wolfram Sang,
	Peter Rosin, Arnd Bergmann, Saravana Kannan, Bjorn Helgaas,
	Charles Keepax, Richard Fitzgerald, David Rhodes, Ulf Hansson,
	Mark Brown, Len Brown, Andy Shevchenko, Daniel Scally,
	Heikki Krogerus, Sakari Ailus, Davidlohr Bueso, Jonathan Cameron,
	Dave Jiang, Alison Schofield, Vishal Verma, Ira Weiny,
	Dan Williams, Shawn Guo, Wolfram Sang, linux-kernel, driver-core,
	imx, linux-arm-kernel, linux-clk, linux-i2c, devicetree,
	linux-pci, linux-sound, patches, linux-gpio, linux-pm, linux-spi,
	linux-acpi, linux-cxl, Allan Nielsen, Horatiu Vultur,
	Steen Hegelund, Luca Ceresoli, Thomas Petazzoni
In-Reply-To: <20260325143555.451852-21-herve.codina@bootlin.com>

Hi Herve,

this is nitpicking, but if you respin the series consider the following:

On Wed, Mar 25, 2026 at 3:42 PM Herve Codina <herve.codina@bootlin.com> wrote:
>
> Nodes available in the dtso are not ordered by their unit address.
>
> Fix that re-ordering them according to their unit address.
>
> Signed-off-by: Herve Codina <herve.codina@bootlin.com>
(...)
> +                               switch: switch@e0000000 {

Recommended practice is:

ethernet-switch@...

> +                                       compatible = "microchip,lan966x-switch";
> +                                       reg = <0xe0000000 0x0100000>,
> +                                             <0xe2000000 0x0800000>;
> +                                       reg-names = "cpu", "gcb";
> +
> +                                       interrupt-parent = <&oic>;
> +                                       interrupts = <12 IRQ_TYPE_LEVEL_HIGH>,
> +                                                    <9 IRQ_TYPE_LEVEL_HIGH>;
> +                                       interrupt-names = "xtr", "ana";
> +
> +                                       resets = <&reset 0>;
> +                                       reset-names = "switch";
> +
> +                                       pinctrl-names = "default";
> +                                       pinctrl-0 = <&tod_pins>;
> +
> +                                       ethernet-ports {
> +                                               #address-cells = <1>;
> +                                               #size-cells = <0>;
> +
> +                                               port0: port@0 {

Recommended practice is:
ethernet-port@...

Yours,
Linus Walleij

^ permalink raw reply

* Re: [PATCH v2 06/19] cpufreq: Use trace_call__##name() at guarded tracepoint call sites
From: Rafael J. Wysocki @ 2026-03-26 10:24 UTC (permalink / raw)
  To: Vineeth Pillai (Google)
  Cc: Steven Rostedt, Peter Zijlstra, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, Viresh Kumar,
	Srinivas Pandruvada, Len Brown, linux-pm, linux-kernel,
	linux-trace-kernel
In-Reply-To: <20260323160052.17528-7-vineeth@bitbyteword.org>

On Mon, Mar 23, 2026 at 5:01 PM Vineeth Pillai (Google)
<vineeth@bitbyteword.org> wrote:
>
> Replace trace_foo() with the new trace_call__foo() at sites already
> guarded by trace_foo_enabled(), avoiding a redundant
> static_branch_unlikely() re-evaluation inside the tracepoint.
> trace_call__foo() calls the tracepoint callbacks directly without
> utilizing the static branch again.
>
> Suggested-by: Steven Rostedt <rostedt@goodmis.org>
> Suggested-by: Peter Zijlstra <peterz@infradead.org>
> Signed-off-by: Vineeth Pillai (Google) <vineeth@bitbyteword.org>
> Assisted-by: Claude:claude-sonnet-4-6

Acked-by: Rafael J. Wysocki (Intel) <rafael@kernel.org> # cpufreq core
& intel_pstate

> ---
>  drivers/cpufreq/amd-pstate.c   | 10 +++++-----
>  drivers/cpufreq/cpufreq.c      |  2 +-
>  drivers/cpufreq/intel_pstate.c |  2 +-
>  3 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
> index 5aa9fcd80cf51..4c47324aa2f73 100644
> --- a/drivers/cpufreq/amd-pstate.c
> +++ b/drivers/cpufreq/amd-pstate.c
> @@ -247,7 +247,7 @@ static int msr_update_perf(struct cpufreq_policy *policy, u8 min_perf,
>         if (trace_amd_pstate_epp_perf_enabled()) {
>                 union perf_cached perf = READ_ONCE(cpudata->perf);
>
> -               trace_amd_pstate_epp_perf(cpudata->cpu,
> +               trace_call__amd_pstate_epp_perf(cpudata->cpu,
>                                           perf.highest_perf,
>                                           epp,
>                                           min_perf,
> @@ -298,7 +298,7 @@ static int msr_set_epp(struct cpufreq_policy *policy, u8 epp)
>         if (trace_amd_pstate_epp_perf_enabled()) {
>                 union perf_cached perf = cpudata->perf;
>
> -               trace_amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
> +               trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
>                                           epp,
>                                           FIELD_GET(AMD_CPPC_MIN_PERF_MASK,
>                                                     cpudata->cppc_req_cached),
> @@ -343,7 +343,7 @@ static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp)
>         if (trace_amd_pstate_epp_perf_enabled()) {
>                 union perf_cached perf = cpudata->perf;
>
> -               trace_amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
> +               trace_call__amd_pstate_epp_perf(cpudata->cpu, perf.highest_perf,
>                                           epp,
>                                           FIELD_GET(AMD_CPPC_MIN_PERF_MASK,
>                                                     cpudata->cppc_req_cached),
> @@ -507,7 +507,7 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf,
>         if (trace_amd_pstate_epp_perf_enabled()) {
>                 union perf_cached perf = READ_ONCE(cpudata->perf);
>
> -               trace_amd_pstate_epp_perf(cpudata->cpu,
> +               trace_call__amd_pstate_epp_perf(cpudata->cpu,
>                                           perf.highest_perf,
>                                           epp,
>                                           min_perf,
> @@ -588,7 +588,7 @@ static void amd_pstate_update(struct amd_cpudata *cpudata, u8 min_perf,
>         }
>
>         if (trace_amd_pstate_perf_enabled() && amd_pstate_sample(cpudata)) {
> -               trace_amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq,
> +               trace_call__amd_pstate_perf(min_perf, des_perf, max_perf, cpudata->freq,
>                         cpudata->cur.mperf, cpudata->cur.aperf, cpudata->cur.tsc,
>                                 cpudata->cpu, fast_switch);
>         }
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 277884d91913c..58901047eae5a 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -2222,7 +2222,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy,
>
>         if (trace_cpu_frequency_enabled()) {
>                 for_each_cpu(cpu, policy->cpus)
> -                       trace_cpu_frequency(freq, cpu);
> +                       trace_call__cpu_frequency(freq, cpu);
>         }
>
>         return freq;
> diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
> index 11c58af419006..70be952209144 100644
> --- a/drivers/cpufreq/intel_pstate.c
> +++ b/drivers/cpufreq/intel_pstate.c
> @@ -3132,7 +3132,7 @@ static void intel_cpufreq_trace(struct cpudata *cpu, unsigned int trace_type, in
>                 return;
>
>         sample = &cpu->sample;
> -       trace_pstate_sample(trace_type,
> +       trace_call__pstate_sample(trace_type,
>                 0,
>                 old_pstate,
>                 cpu->pstate.current_pstate,
> --
> 2.53.0
>

^ permalink raw reply

* Re: [RFC][RFT][PATCH 0/3] arm64: Enable asympacking for minor CPPC asymmetry
From: Christian Loehle @ 2026-03-26  9:24 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: arighi, peterz, dietmar.eggemann, valentin.schneider, mingo,
	rostedt, segall, mgorman, catalin.marinas, will, sudeep.holla,
	rafael, linux-pm, linux-kernel, juri.lelli, kobak, fabecassis
In-Reply-To: <CAKfTPtA+rcVCLe5XOKwu+0bsc8_ZXc1TH4qrYo4x5tNwz9ZO5Q@mail.gmail.com>

On 3/26/26 08:24, Vincent Guittot wrote:
> On Thu, 26 Mar 2026 at 09:16, Christian Loehle <christian.loehle@arm.com> wrote:
>>
>> On 3/26/26 07:53, Vincent Guittot wrote:
>>> On Wed, 25 Mar 2026 at 19:13, Christian Loehle <christian.loehle@arm.com> wrote:
>>>>
>>>> The scheduler currently handles CPU performance asymmetry via either:
>>>>
>>>> - SD_ASYM_PACKING: simple priority-based task placement (x86 ITMT)
>>>> - SD_ASYM_CPUCAPACITY: capacity-aware scheduling
>>>>
>>>> On arm64, capacity-aware scheduling is used for any detected capacity
>>>> differences.
>>>>
>>>> Some systems expose small per-CPU performance differences via CPPC
>>>> highest_perf (e.g. due to chip binning), resulting in slightly different
>>>> capacities (<~5%). These differences are sufficient to trigger
>>>> SD_ASYM_CPUCAPACITY, even though the system is otherwise effectively
>>>> symmetric.
>>>>
>>>> For such small deltas, capacity-aware scheduling is unnecessarily
>>>> complex. A simpler priority-based approach, similar to x86 ITMT, is
>>>> sufficient.
>>>
>>> I'm not convinced that moving to  SD_ASYM_PACKING is the right way to
>>> move forward.
>>> t
>>> 1st of all, do you target all kind of system or only SMT? It's not
>>> clear in your cover letter
>>
>> AFAIK only Andrea has access to an unreleased asymmetric SMT system,
>> I haven't done any tests on such a system (as the cover-letter mentions
>> under RFT section).
>>
>>>
>>> Moving on asym pack for !SMT doesn't make sense to me. If you don't
>>> want EAS enabled, you can disable it with
>>> /proc/sys/kernel/sched_energy_aware
>>
>> Sorry, what's EAS got to do with it? The system I care about here
>> (primarily nvidia grace) has no EM.
> 
> I tried to understand the end goal of this patch
> 
> SD_ASYM_CPUCAPACITY works fine with !SMT system so why enabling
> SD_ASYM_PACKING for <5% diff ?
> 
> That doesn't make sense to me
I don't know if "works fine" describes the situation accurately.
I guess I should've included the context in the cover letter, but you
are aware of them (you've replied to them anyway):
https://lore.kernel.org/lkml/20260324005509.1134981-1-arighi@nvidia.com/
https://lore.kernel.org/lkml/20260318092214.130908-1-arighi@nvidia.com/

Andrea sees an improvement even when force-equalizing CPUs to remove
SD_ASYM_CPUCAPACITY, so I'd argue it doesn't "work fine" on these platforms.
To me it seems more reasonable to attempt to get these minor improvements
of minor asymmetries through asympacking and leave SD_ASYM_CPUCAPACITY
to the actual 'true' asymmetry (e.g. different uArch or vastly different
performance levels).
SD_ASYM_CPUCAPACITY handling is also arguably broken if no CPU pair in
the system fulfills capacity_greater(), the call sites in fair.c give
a good overview.
Is $subject the right approach to deal with these platforms instead?
I don't know, that's why it's marked RFC and RFT.

^ permalink raw reply

* Re: [RFC PATCH 1/2] thermal/cpufreq_cooling: remove unused cpu_idx in get_load()
From: Lukasz Luba @ 2026-03-26  9:21 UTC (permalink / raw)
  To: Qais Yousef
  Cc: Xuewen Yan, Viresh Kumar, Xuewen Yan, rui.zhang, rafael, linux-pm,
	amit.kachhap, daniel.lezcano, linux-kernel, ke.wang, di.shen,
	jeson.gao, Peter Zijlstra, Vincent Guittot
In-Reply-To: <20260326090554.jerlaudbe3rkovsi@airbuntu>



On 3/26/26 09:05, Qais Yousef wrote:
> On 03/24/26 10:46, Lukasz Luba wrote:
>>
>> On 3/24/26 02:20, Xuewen Yan wrote:
>>> On Mon, Mar 23, 2026 at 9:25 PM Lukasz Luba <lukasz.luba@arm.com> wrote:
>>>>
>>>>
>>>>
>>>> On 3/23/26 11:06, Viresh Kumar wrote:
>>>>> On 23-03-26, 10:52, Lukasz Luba wrote:
>>>>>>> How is that okay ? What am I missing ?
>>>>>
>>>>> I was missing !SMP :)
>>>>>
>>>>>> Right, there is a mix of two things.
>>>>>> The 'i' left but should be removed as well, since
>>>>>> this is !SMP code with only 1 cpu and i=0.
>>>
>>> That's also why we sent out patch 1/2; after all, it is always 0 on
>>> !SMP systems.
>>>
>>>>>>
>>>>>> The whole split which has been made for getting
>>>>>> the load or utilization from CPU(s) needs to be
>>>>>> cleaned. The compiled code looks different since
>>>>>> it knows there is non-SMP config used.
>>>>>
>>>>> Right, we are allocating that for num_cpus (which should be 1 CPU
>>>>> anyway). The entire thing must be cleaned.
>>>>>
>>>>>> Do you want to clean that or I should do this?
>>>>>
>>>>> It would be helpful if you can do it :)
>>>>>
>>>>
>>>> OK, I will. Thanks for your involvement Viresh!
>>>>
>>>> Xuewen please wait with your v2, I will send
>>>> a redesign of this left code today.
>>>
>>> Okay, and Qais's point is also worth considering: do we actually need
>>> sched_cpu_util()?
>>> The way I see it, generally speaking, the request_power derived from
>>> idle_time might be higher than what we get from sched_cpu_util().
>>> Take this scenario as an example:
>>> Consider a CPU running at the lowest frequency with 50% idle time,
>>> versus one running at the highest frequency with the same 50% idle
>>> time.
>>> In this case, using idle_time yields the same load value for both.
>>> However, sched_cpu_util() would report a lower load when the CPU
>>> frequency is low. This results in a smaller request_power...
> 
> Invariance will cause settling time to stretch longer, but it should settle to
> the correct value eventually. But generally another case against util is that
> it has grown to be a description of compute demand more than true idleness of
> the system.
> 
>>
>> Right, there are 2 things to consider:
>> 1. what is the utilization when the CPU still have idle time, e.g.
>>     this 50% that you mentioned
>> 2. what is the utilization when there is no idle time and CPU
>>     is fully busy (and starts throttling due to heat)
> 
> Hmm I think what you're trying to say here we need to distinguish between two
> cases 50% or fully busy? I think how idle the system is a better question to
> ask rather than what is the utilization (given the ubiquity of the signal
> nowadays)

Yes, these two cases, which are different and util signal is not the
best for that idleness one.


> 
>>
>> In this thermal fwk we are mostly in the 2nd case. In that case the
> 
> But from power allocator perspective (which I think is the context, right?),
> you want to know if you can shift power?

I would like to know the avg power in the last X ms window, then
allocate, shift, set.

> 
>> utilization on CPU's runqueue goes to 1024 no mater the CPU's frequency.
>> We know which highest frequency was allowed to run and we pick the power
>> value from EM for it. That's why the estimation is not that bad (apart
>> from power variation for different flavors of workloads: heavy SIMD vs.
>> normal integer/load).
>>
>> In 1st case scenario we might underestimate the power, but that
>> is not the thermal stress situation anyway, so the max OPP is
>> still allowed.
>>
>> So far it is hard to find the best power model to use and robust CPU
>> load mechanisms. Adding more complexity and creating some
>> over-engineered code in the kernel to maintain might not have sense.
>> The thermal solutions are solved in the Firmware nowadays since the
>> kernel won't react that fast for some rapid changes.
>>
>> We have to balance the complexity here.
> 
> I am not verse in all the details, so not sure what complexity you are
> referring to. IMHO the idle time is a more stable view for how much a breathing
> room the cpu has. It also deals better with long decay of blocked load
> over-estimating the utilization. AFAICS just sample the idle over a window when
> you need to take a decision and you'd solve several problems in one go.

We have issues in estimating power in that X ms window due to fast
frequency changes. You know how often we can change the frequency,
almost per-task enqueue (and e.g. uclamp pushes that even harder).

Simple approach for assuming that the frequency we see now on CPU
has been there for the whole Xms period is 'not the best'.
The util information w/o uclamp information is not helping
much (even we we would try to derive the freq out of it).

Now even more complex - the FW can change the freq way often
than the kernel. So the question is how far we have to push
the whole kernel and those frameworks to deal with those new
platforms.

Then add the power variation due to different computation types
e.g. SIMD heavy vs simple logging task (high power vs. low power
usage at the same OPP).

IMHO we have to find a balance since even more complex models
in kernel won't be able to handle that.

I have been experimenting with the Active Stats patch set for
quite a while, but then FW came into the equation and complicated
the situation. It will be still better for such platform
where FW doesn't change the freq, so this approach based on
idle stats is worth to add IMO.

^ permalink raw reply

* Re: [RFC][RFT][PATCH 0/3] arm64: Enable asympacking for minor CPPC asymmetry
From: Andrea Righi @ 2026-03-26  9:15 UTC (permalink / raw)
  To: Vincent Guittot
  Cc: Christian Loehle, peterz, dietmar.eggemann, valentin.schneider,
	mingo, rostedt, segall, mgorman, catalin.marinas, will,
	sudeep.holla, rafael, linux-pm, linux-kernel, juri.lelli, kobak,
	fabecassis
In-Reply-To: <CAKfTPtDmnnGONJhrQPNh2rhe3-KvMkX1nW8-JYN+otNLQYSU-w@mail.gmail.com>

On Thu, Mar 26, 2026 at 09:20:45AM +0100, Vincent Guittot wrote:
> On Thu, 26 Mar 2026 at 09:12, Andrea Righi <arighi@nvidia.com> wrote:
> >
> > Hi Christian,
> >
> > On Wed, Mar 25, 2026 at 06:13:11PM +0000, Christian Loehle wrote:
> > ...
> > > RFT:
> > > Andrea, please give this a try. This should perform better in particular
> > > for single-threaded workloads and workloads that do not utilize all
> > > cores (all the time anyway).
> > > Capacity-aware scheduling wakeup works very different to the SMP path
> > > used now, some workloads will benefit, some regress, it would be nice
> > > to get some test results for these.
> > > We already discussed DCPerf MediaWiki seems to benefit from
> > > capacity-aware scheduling wakeup behavior, but others (most?) should
> > > benefit from this series.
> > >
> > > I don't know if we can also be clever about ordering amongst SMT siblings.
> > > That would be dependent on the uarch and I don't have a platform to
> > > experiment with this though, so consider this series orthogonal to the
> > > idle-core SMT considerations.
> > > On platforms with SMT though asympacking makes a lot more sense than
> > > capacity-aware scheduling, because arguing about capacity without
> > > considering utilization of the sibling(s) (and the resulting potential
> > > 'stolen' capacity we perceive) isn't theoretically sound.
> >
> > I did some early testing with this patch set. On Vera I'm getting much
> > better performance that SD_ASYM_CPUCAPACITY of course (~1.5x avg speedup),
> > mostly because we avoid using both SMT siblings. It's still not the same
> > improvement that I get equalizing the capacity using the 5% threshold
> > (~1.8x speedup).
> 
> IIRC the tests that you shared in your patch, you get an additonal
> improvement when adding some SMT awarness to SD_ASYM_CPUCAPACITY
> compared to equalizing the capacity

Yes, adding SMT awareness to SD_ASYM_CPUCAPACITY is still the apparoach
that gives me the best performance so far on Vera (~1.9x avg speedup),
among all those that I've tested.

I'll post the updated patch set that I'm using, so we can also elaborate
more on that approach as well.

Thanks,
-Andrea

^ permalink raw reply

* Re: [PATCH v7 2/2] cpufreq: Add boost_freq_req QoS request
From: Zhongqiu Han @ 2026-03-26  9:12 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Pierre Gondois, linux-kernel, Lifeng Zheng, Huang Rui,
	Gautham R. Shenoy, Mario Limonciello, Perry Yuan,
	Rafael J. Wysocki, linux-pm, zhongqiu.han
In-Reply-To: <odpfr3sz5ualy2wnwpnpfbkjjjcmcydwm744aew2vnpbsnajdh@xd7pbkwnkwmu>

On 3/26/2026 4:50 PM, Viresh Kumar wrote:
> On 26-03-26, 16:18, Zhongqiu Han wrote:
>> Would it be reasonable to add a NULL check for policy->boost_freq_req in
>> policy_set_boost() before calling freq_qos_update_request(), even though
>> callers already check policy->boost_supported? Thanks
> 
> Not required. policy_set_boost() should only be called if boost is supported.
> 

Got it, thanks — no NULL check needed since boost_supported is the
precondition.

-- 
Thx and BRs,
Zhongqiu Han

^ permalink raw reply

* Re: [PATCH] thermal: fix error condition for reading st,thermal-flags
From: Lukasz Luba @ 2026-03-26  9:06 UTC (permalink / raw)
  To: Gopi Krishna Menon, rafael, daniel.lezcano, rui.zhang
  Cc: daniel.baluta, simona.toaca, d-gole, m-chawdhry, linux-pm,
	linux-kernel
In-Reply-To: <20260325081844.13829-1-krishnagopi487@gmail.com>



On 3/25/26 08:18, Gopi Krishna Menon wrote:
> of_property_read_u32 returns 0 on success. The current check returns
> -EINVAL if the property is read successfully.
> 
> Fix the check by removing ! from of_property_read_u32
> 
> Suggested-by: Daniel Baluta <daniel.baluta@nxp.com>
> Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
> ---
> Note:
> * This patch is part of the GSoC2026 application process for device tree bindings conversions
> * https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings
> * Changes have only been compile tested
> 
>   drivers/thermal/spear_thermal.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c
> index 603dadcd3df5..5e3e9c1f32f8 100644
> --- a/drivers/thermal/spear_thermal.c
> +++ b/drivers/thermal/spear_thermal.c
> @@ -93,7 +93,7 @@ static int spear_thermal_probe(struct platform_device *pdev)
>   	struct device_node *np = pdev->dev.of_node;
>   	int ret = 0, val;
>   
> -	if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) {
> +	if (!np || of_property_read_u32(np, "st,thermal-flags", &val)) {
>   		dev_err(&pdev->dev, "Failed: DT Pdata not passed\n");
>   		return -EINVAL;
>   	}

Good catch. It deserves the fixes tag, please find the commit
which introduced that. With adding that fixes info, feel free to
add:

Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>

^ permalink raw reply

* Re: [RFC PATCH 1/2] thermal/cpufreq_cooling: remove unused cpu_idx in get_load()
From: Qais Yousef @ 2026-03-26  9:05 UTC (permalink / raw)
  To: Lukasz Luba
  Cc: Xuewen Yan, Viresh Kumar, Xuewen Yan, rui.zhang, rafael, linux-pm,
	amit.kachhap, daniel.lezcano, linux-kernel, ke.wang, di.shen,
	jeson.gao, Peter Zijlstra, Vincent Guittot
In-Reply-To: <35d472ac-8a58-44c5-a0b1-5e1de8ac6cfc@arm.com>

On 03/24/26 10:46, Lukasz Luba wrote:
> 
> On 3/24/26 02:20, Xuewen Yan wrote:
> > On Mon, Mar 23, 2026 at 9:25 PM Lukasz Luba <lukasz.luba@arm.com> wrote:
> > > 
> > > 
> > > 
> > > On 3/23/26 11:06, Viresh Kumar wrote:
> > > > On 23-03-26, 10:52, Lukasz Luba wrote:
> > > > > > How is that okay ? What am I missing ?
> > > > 
> > > > I was missing !SMP :)
> > > > 
> > > > > Right, there is a mix of two things.
> > > > > The 'i' left but should be removed as well, since
> > > > > this is !SMP code with only 1 cpu and i=0.
> > 
> > That's also why we sent out patch 1/2; after all, it is always 0 on
> > !SMP systems.
> > 
> > > > > 
> > > > > The whole split which has been made for getting
> > > > > the load or utilization from CPU(s) needs to be
> > > > > cleaned. The compiled code looks different since
> > > > > it knows there is non-SMP config used.
> > > > 
> > > > Right, we are allocating that for num_cpus (which should be 1 CPU
> > > > anyway). The entire thing must be cleaned.
> > > > 
> > > > > Do you want to clean that or I should do this?
> > > > 
> > > > It would be helpful if you can do it :)
> > > > 
> > > 
> > > OK, I will. Thanks for your involvement Viresh!
> > > 
> > > Xuewen please wait with your v2, I will send
> > > a redesign of this left code today.
> > 
> > Okay, and Qais's point is also worth considering: do we actually need
> > sched_cpu_util()?
> > The way I see it, generally speaking, the request_power derived from
> > idle_time might be higher than what we get from sched_cpu_util().
> > Take this scenario as an example:
> > Consider a CPU running at the lowest frequency with 50% idle time,
> > versus one running at the highest frequency with the same 50% idle
> > time.
> > In this case, using idle_time yields the same load value for both.
> > However, sched_cpu_util() would report a lower load when the CPU
> > frequency is low. This results in a smaller request_power...

Invariance will cause settling time to stretch longer, but it should settle to
the correct value eventually. But generally another case against util is that
it has grown to be a description of compute demand more than true idleness of
the system.

> 
> Right, there are 2 things to consider:
> 1. what is the utilization when the CPU still have idle time, e.g.
>    this 50% that you mentioned
> 2. what is the utilization when there is no idle time and CPU
>    is fully busy (and starts throttling due to heat)

Hmm I think what you're trying to say here we need to distinguish between two
cases 50% or fully busy? I think how idle the system is a better question to
ask rather than what is the utilization (given the ubiquity of the signal
nowadays)

> 
> In this thermal fwk we are mostly in the 2nd case. In that case the

But from power allocator perspective (which I think is the context, right?),
you want to know if you can shift power?

> utilization on CPU's runqueue goes to 1024 no mater the CPU's frequency.
> We know which highest frequency was allowed to run and we pick the power
> value from EM for it. That's why the estimation is not that bad (apart
> from power variation for different flavors of workloads: heavy SIMD vs.
> normal integer/load).
> 
> In 1st case scenario we might underestimate the power, but that
> is not the thermal stress situation anyway, so the max OPP is
> still allowed.
> 
> So far it is hard to find the best power model to use and robust CPU
> load mechanisms. Adding more complexity and creating some
> over-engineered code in the kernel to maintain might not have sense.
> The thermal solutions are solved in the Firmware nowadays since the
> kernel won't react that fast for some rapid changes.
> 
> We have to balance the complexity here.

I am not verse in all the details, so not sure what complexity you are
referring to. IMHO the idle time is a more stable view for how much a breathing
room the cpu has. It also deals better with long decay of blocked load
over-estimating the utilization. AFAICS just sample the idle over a window when
you need to take a decision and you'd solve several problems in one go.

> Let's improve the situation a bit. It would be very much appreciated if
> you could share information if those changes help your platform
> (some older boards might not show any benefit with the new code).
> 
> Regards,
> Lukasz
> 

^ permalink raw reply

* Re: [PATCH v2] dt-bindings: reset: st: convert to dtschema
From: Krzysztof Kozlowski @ 2026-03-26  9:04 UTC (permalink / raw)
  To: Gopi Krishna Menon
  Cc: sre, robh, krzk+dt, lee, conor+dt, daniel.baluta, simona.toaca,
	d-gole, m-chawdhry, linux-pm, devicetree, linux-kernel
In-Reply-To: <20260325130623.36710-1-krishnagopi487@gmail.com>

On Wed, Mar 25, 2026 at 06:36:21PM +0530, Gopi Krishna Menon wrote:
> Convert the STiH4xx reset controller bindings to DT schema.
> 
> Signed-off-by: Gopi Krishna Menon <krishnagopi487@gmail.com>
> ---
> Changes since v1:
> - Changed unevaluatedProperties to additionalProperties
> - Removed the Suggested-by tags

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 7/8] arm64: dts: qcom: sdm845-db845c: describe WiFi/BT properly
From: Konrad Dybcio @ 2026-03-26  9:01 UTC (permalink / raw)
  To: Dmitry Baryshkov, David Heidelberg
  Cc: Bjorn Andersson, Konrad Dybcio, Vinod Koul, linux-arm-msm,
	Liam Girdwood, linux-kernel, devicetree, Luiz Augusto von Dentz,
	Rob Herring, linux-bluetooth, Balakrishna Godavarthi,
	Matthias Kaehlcke, linux-wireless, Jeff Johnson, ath10k, linux-pm,
	Bartosz Golaszewski, Krzysztof Kozlowski, Mark Brown,
	Krzysztof Kozlowski, Bartosz Golaszewski, Marcel Holtmann,
	Conor Dooley, Manivannan Sadhasivam
In-Reply-To: <CAO9ioeVLy_Uzn7L9MyET5wg8CMR132+Dda5JzjdAB=6vz2NEMg@mail.gmail.com>

On 3/26/26 2:59 AM, Dmitry Baryshkov wrote:
> On Thu, 26 Mar 2026 at 02:02, David Heidelberg <david@ixit.cz> wrote:
>>
>> On 19/01/2026 18:08, Dmitry Baryshkov wrote:
>>
>> [...]
>>
>>> +     wcn3990-pmu {
>>> +             compatible = "qcom,wcn3990-pmu";
>>> +
>>> +             pinctrl-0 = <&sw_ctrl_default>;
>>> +             pinctrl-names = "default";
>>> +
>>> +             vddio-supply = <&vreg_s4a_1p8>;
>>> +             vddxo-supply = <&vreg_l7a_1p8>;
>>> +             vddrf-supply = <&vreg_l17a_1p3>;
>>> +             vddch0-supply = <&vreg_l25a_3p3>;
>>> +             vddch1-supply = <&vreg_l23a_3p3>;
>>> +
>>> +             swctrl-gpios = <&pm8998_gpios 3 GPIO_ACTIVE_HIGH>;
>>
>> Do you know if the GPIO is common for whole sdm845, or it's only recommended as
>> reference design, or nothing?
>>
>> I did test defaulting to GPIO 3 on Pixel 3 and WiFi works as before, but since
>> previous downstream kernel didn't touched GPIO 3 at all, I'm worried about
>> toggling unrelated GPIO.
> 
> It is an input-only GPIO, but nevertheless, if you are not sure, just skip it.

I think you should be able to observe its state and deduce based on that

On a sidenote, 99.5% of reference design choices seem to hold true on at
least 90% of devices

Konrad

^ permalink raw reply

* Re: [PATCH v7 2/2] cpufreq: Add boost_freq_req QoS request
From: Pierre Gondois @ 2026-03-26  8:54 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: linux-kernel, Lifeng Zheng, Huang Rui, Gautham R. Shenoy,
	Mario Limonciello, Perry Yuan, Rafael J. Wysocki, linux-pm
In-Reply-To: <hajhezqrujiky7eo4eidogvjomixmhtdxpzfjibrd2xbhxawbd@ptljcesqpyqx>


On 3/26/26 09:48, Viresh Kumar wrote:
> On 25-03-26, 17:52, Pierre Gondois wrote:
>> @@ -1377,6 +1386,8 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
>>   	}
>>   
>>   	freq_qos_remove_request(policy->min_freq_req);
> Since this doesn't check min_freq_req (and depend on the routine to return
> early), shouldn't we do the same for below one ?

Yes it is possible,
there were different views but without the check is ok aswell.

>
>> +	if (policy->boost_freq_req)
>> +		freq_qos_remove_request(policy->boost_freq_req);
>>   	kfree(policy->min_freq_req);
>>   
>>   	cpufreq_policy_put_kobj(policy);

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox