* [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
@ 2026-08-03 21:05 Christian Loehle
2026-08-03 21:05 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Christian Loehle @ 2026-08-03 21:05 UTC (permalink / raw)
To: Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han, Christian Loehle
cppc-cpufreq reaches cppc_set_perf() from every target callback. For
direct SystemMemory controls, that path currently does several steps
which are unnecessary once the immutable _CPC layout is known:
- a full-width write first reads the access unit and merges the value.
- every write takes the descriptor's RMW lock, even when its access unit
is not shared with another _CPC entry.
- cppc_set_perf() evaluates the same three PCC predicates at each phase
of the transaction.
Remove those costs while retaining the existing conservative paths for
partial fields, overlapping or malformed access units, and PCC controls.
The series was tested on Arm Power-Orion O6 and AmpereOne systems using
cppc-cpufreq and schedutil. An rt-app task pinned to one CPU ran for
500 us every 2 ms with uclamp.min=512, for 5000 periods per run. schedutil
rate_limit_us was 1000. cppc_cpufreq_fast_switch() latency was measured
for 100 ten-second runs. Each sample is the mean callback latency within
one run, and the table reports the median and sample standard deviation
of those samples.
Orion O6 median stdev callbacks
baseline 5703 ns 307 ns 188017
complete series 5023 ns 240 ns 188209
==> 680 ns (11.9%) reduction
AmpereOne median stdev
baseline 2090 ns 157 ns
complete series 1907.5 ns 140 ns
==> 182.5 ns (8.7%) reduction
The cumulative intermediate results on the Orion O6 attribute roughly half
of the gain to each of the first two patches: avoiding the read reduced
the median by 284 ns (5.0%), and avoiding the lock reduced it by another
336 ns (6.2%).
Together they account for 620 ns of the 680 ns total reduction.
With the same arm64 configuration and GCC 11.4, caching the PCC predicate
also reduces cppc_set_perf() from 1124 to 884 bytes. The generated
function has 60 fewer instructions, 17 fewer loads and 20 fewer branches.
This series is based on the CPPC fixes posted at (already queued):
https://lore.kernel.org/lkml/20260722093825.1030594-1-christian.loehle@arm.com/
and the separately posted fix still under review:
https://lore.kernel.org/lkml/20260724104042.1481804-1-christian.loehle@arm.com/
PS:
There's a final optimization that I actually wanted to make but decided to
split it out for now as it somewhat replicated Sumit's series:
Skipping redundant perf ctrl writes in cpufreq-cppc if registers are non-PCC
and !shared (because the values are unchanged, the
!autonomous-common-case), but that requires the driver to have a more
complex caching- and atomic-updating machinery in place, similar to
hotplug. As opposed to this series the optimization would be for the
microcontroller handling the CPPC requests, which may be shared across
many CPUs and therefore redundant requests can increase the dvfs
transition latency.
That patch will follow once Sumit's is queued:
https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
[RESEND] for the new base-commit specifier below
Christian Loehle (3):
ACPI: CPPC: Avoid unnecessary reads for full-width writes
ACPI: CPPC: Avoid locking standalone full-width registers
ACPI: CPPC: Evaluate performance-control PCC use once
drivers/acpi/cppc_acpi.c | 135 +++++++++++++++++++++++++++++++--------
include/acpi/cppc_acpi.h | 5 +-
2 files changed, 113 insertions(+), 27 deletions(-)
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes
2026-08-03 21:05 [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
@ 2026-08-03 21:05 ` Christian Loehle
2026-08-05 13:04 ` Zhongqiu Han
2026-08-03 21:05 ` [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers Christian Loehle
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Christian Loehle @ 2026-08-03 21:05 UTC (permalink / raw)
To: Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han, Christian Loehle
SystemMemory GAS entries may describe a field within a wider access
unit, so cpc_write() reads the access unit before updating the field to
preserve the surrounding bits. It also does this when the field covers
the complete access unit.
When the bit offset is zero and the register bit width equals the
resolved access width, the previous value cannot affect the result. Skip
the MMIO read and mask operation in that case. Retain rmw_lock because
another entry in the same _CPC package may share the access unit.
Signed-off-by: Christian Loehle <christian.loehle@arm.com>
---
drivers/acpi/cppc_acpi.c | 43 ++++++++++++++++++++++++----------------
1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 53d09ca98f06..9b8d68b44ea9 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1162,25 +1162,34 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
return -ENODEV;
}
+ /*
+ * Only partial fields need the previous contents to preserve bits
+ * outside the field. Keep serializing full-width writes because
+ * another _CPC entry may share the access unit and require RMW.
+ */
raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
- switch (size) {
- case 8:
- prev_val = readb_relaxed(vaddr);
- break;
- case 16:
- prev_val = readw_relaxed(vaddr);
- break;
- case 32:
- prev_val = readl_relaxed(vaddr);
- break;
- case 64:
- prev_val = readq_relaxed(vaddr);
- break;
- default:
- raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags);
- return -EFAULT;
+
+ if (reg->bit_offset || reg->bit_width != size) {
+ switch (size) {
+ case 8:
+ prev_val = readb_relaxed(vaddr);
+ break;
+ case 16:
+ prev_val = readw_relaxed(vaddr);
+ break;
+ case 32:
+ prev_val = readl_relaxed(vaddr);
+ break;
+ case 64:
+ prev_val = readq_relaxed(vaddr);
+ break;
+ default:
+ raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
+ flags);
+ return -EFAULT;
+ }
+ val = MASK_VAL_WRITE(reg, prev_val, val);
}
- val = MASK_VAL_WRITE(reg, prev_val, val);
}
switch (size) {
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers
2026-08-03 21:05 [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-08-03 21:05 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
@ 2026-08-03 21:05 ` Christian Loehle
2026-08-03 21:05 ` [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once Christian Loehle
2026-08-03 22:28 ` [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
3 siblings, 0 replies; 10+ messages in thread
From: Christian Loehle @ 2026-08-03 21:05 UTC (permalink / raw)
To: Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han, Christian Loehle
cpc_write() serializes every SystemMemory write with the per-CPU
rmw_lock. The lock is required for read-modify-write fields and for
registers whose access units overlap, but not for a full-width register
in a standalone access unit.
The _CPC layout is immutable after it has been parsed. Classify each
SystemMemory register at probe time and retain locking for partial
fields, invalid access widths, and overlapping access units. Allow
standalone full-width registers to bypass the descriptor lookup and
spinlock.
Store the classification in existing structure padding so that struct
cpc_register_resource does not grow.
Signed-off-by: Christian Loehle <christian.loehle@arm.com>
---
drivers/acpi/cppc_acpi.c | 97 ++++++++++++++++++++++++++++++++++------
include/acpi/cppc_acpi.h | 5 ++-
2 files changed, 88 insertions(+), 14 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 9b8d68b44ea9..2793d6b7d40d 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -200,6 +200,72 @@ show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time);
((((val) & GENMASK(((reg)->bit_width) - 1, 0)) << (reg)->bit_offset) | \
((prev_val) & ~(GENMASK(((reg)->bit_width) - 1, 0) << (reg)->bit_offset))) \
+static u64 cpc_sysmem_access_size(const struct cpc_register_resource *reg)
+{
+ const struct cpc_reg *gas = ®->cpc_entry.reg;
+ unsigned int width;
+
+ if (gas->access_width > 4)
+ return 0;
+
+ width = GET_BIT_WIDTH(gas);
+
+ if (width != 8 && width != 16 && width != 32 && width != 64)
+ return 0;
+
+ return width / 8;
+}
+
+static bool cpc_sysmem_access_units_overlap(const struct cpc_register_resource *a,
+ const struct cpc_register_resource *b)
+{
+ const struct cpc_reg *a_gas = &a->cpc_entry.reg;
+ const struct cpc_reg *b_gas = &b->cpc_entry.reg;
+ u64 a_size = cpc_sysmem_access_size(a);
+ u64 b_size = cpc_sysmem_access_size(b);
+
+ /* Keep the conservative locking path for malformed access widths. */
+ if (!a_size || !b_size)
+ return true;
+
+ if (a_gas->address < b_gas->address)
+ return b_gas->address - a_gas->address < a_size;
+
+ return a_gas->address - b_gas->address < b_size;
+}
+
+static void cpc_mark_rmw_lock_users(struct cpc_desc *cpc_desc)
+{
+ int i, j;
+
+ for (i = 0; i < cpc_desc->num_entries - 2; i++) {
+ struct cpc_register_resource *a = &cpc_desc->cpc_regs[i];
+ struct cpc_reg *gas;
+ u64 access_size;
+
+ if (!CPC_SUPPORTED(a) || !CPC_IN_SYSTEM_MEMORY(a))
+ continue;
+
+ gas = &a->cpc_entry.reg;
+ access_size = cpc_sysmem_access_size(a);
+ if (gas->bit_offset || !access_size ||
+ gas->bit_width != access_size * 8)
+ a->cpc_entry.use_rmw_lock = true;
+
+ for (j = i + 1; j < cpc_desc->num_entries - 2; j++) {
+ struct cpc_register_resource *b = &cpc_desc->cpc_regs[j];
+
+ if (!CPC_SUPPORTED(b) || !CPC_IN_SYSTEM_MEMORY(b))
+ continue;
+ if (!cpc_sysmem_access_units_overlap(a, b))
+ continue;
+
+ a->cpc_entry.use_rmw_lock = true;
+ b->cpc_entry.use_rmw_lock = true;
+ }
+ }
+}
+
static ssize_t show_feedback_ctrs(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -904,6 +970,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
/* Store CPU Logical ID */
cpc_ptr->cpu_id = pr->id;
+ cpc_mark_rmw_lock_users(cpc_ptr);
raw_spin_lock_init(&cpc_ptr->rmw_lock);
/* Parse PSD data for this CPU */
@@ -1123,6 +1190,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
struct cpc_reg *reg = ®_res->cpc_entry.reg;
struct cpc_desc *cpc_desc;
unsigned long flags;
+ bool locked = false;
size = GET_BIT_WIDTH(reg);
@@ -1156,18 +1224,20 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
val, size);
if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
- cpc_desc = per_cpu(cpc_desc_ptr, cpu);
- if (!cpc_desc) {
- pr_debug("No CPC descriptor for CPU:%d\n", cpu);
- return -ENODEV;
- }
-
/*
- * Only partial fields need the previous contents to preserve bits
- * outside the field. Keep serializing full-width writes because
- * another _CPC entry may share the access unit and require RMW.
+ * The _CPC layout is immutable after probe. The precomputed flag
+ * retains serialization for partial fields or overlapping access
+ * units; standalone full-width registers avoid the lock.
*/
- raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
+ locked = reg_res->cpc_entry.use_rmw_lock;
+ if (locked) {
+ cpc_desc = per_cpu(cpc_desc_ptr, cpu);
+ if (!cpc_desc) {
+ pr_debug("No CPC descriptor for CPU:%d\n", cpu);
+ return -ENODEV;
+ }
+ raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
+ }
if (reg->bit_offset || reg->bit_width != size) {
switch (size) {
@@ -1184,8 +1254,9 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
prev_val = readq_relaxed(vaddr);
break;
default:
- raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
- flags);
+ if (locked)
+ raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
+ flags);
return -EFAULT;
}
val = MASK_VAL_WRITE(reg, prev_val, val);
@@ -1217,7 +1288,7 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
break;
}
- if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ if (locked)
raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags);
return ret_val;
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 8c191b9ac18f..19830146c644 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -69,7 +69,10 @@ struct cpc_register_resource {
acpi_object_type type;
u64 __iomem *sys_mem_vaddr;
union {
- struct cpc_reg reg;
+ struct {
+ struct cpc_reg reg;
+ bool use_rmw_lock;
+ };
u64 int_value;
} cpc_entry;
};
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once
2026-08-03 21:05 [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-08-03 21:05 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
2026-08-03 21:05 ` [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers Christian Loehle
@ 2026-08-03 21:05 ` Christian Loehle
2026-08-03 22:28 ` [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
3 siblings, 0 replies; 10+ messages in thread
From: Christian Loehle @ 2026-08-03 21:05 UTC (permalink / raw)
To: Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han, Christian Loehle
cppc_set_perf() evaluates the same immutable address-space predicates
before and after each phase of a performance-control update. This repeats
the three-control PCC test three times for every target request.
Evaluate the predicate once after resolving the control descriptors and
reuse the result throughout the transaction.
Signed-off-by: Christian Loehle <christian.loehle@arm.com>
---
drivers/acpi/cppc_acpi.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 2793d6b7d40d..cd1c4a30eb6f 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -2002,6 +2002,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
struct cpc_register_resource *desired_reg, *min_perf_reg, *max_perf_reg;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, cpu);
struct cppc_pcc_data *pcc_ss_data = NULL;
+ bool regs_in_pcc;
int ret = 0;
if (!cpc_desc) {
@@ -2012,6 +2013,8 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
min_perf_reg = &cpc_desc->cpc_regs[MIN_PERF];
max_perf_reg = &cpc_desc->cpc_regs[MAX_PERF];
+ regs_in_pcc = CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) ||
+ CPC_IN_PCC(max_perf_reg);
/*
* This is Phase-I where we want to write to CPC registers
@@ -2020,7 +2023,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
* Since read_lock can be acquired by multiple CPUs simultaneously we
* achieve that goal here
*/
- if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) {
+ if (regs_in_pcc) {
if (pcc_ss_id < 0) {
pr_debug("Invalid pcc_ss_id\n");
return -ENODEV;
@@ -2056,7 +2059,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg))
cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf);
- if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg))
+ if (regs_in_pcc)
up_read(&pcc_ss_data->pcc_lock); /* END Phase-I */
/*
* This is Phase-II where we transfer the ownership of PCC to Platform
@@ -2104,7 +2107,7 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
* case during a CMD_READ and if there are pending writes it delivers
* the write command before servicing the read command
*/
- if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) {
+ if (regs_in_pcc) {
if (down_write_trylock(&pcc_ss_data->pcc_lock)) {/* BEGIN Phase-II */
/* Update only if there are pending write commands */
if (pcc_ss_data->pending_pcc_write_cmd)
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-03 21:05 [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
` (2 preceding siblings ...)
2026-08-03 21:05 ` [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once Christian Loehle
@ 2026-08-03 22:28 ` Christian Loehle
2026-08-05 13:08 ` Rafael J. Wysocki (Intel)
2026-08-06 10:05 ` Christian Loehle
3 siblings, 2 replies; 10+ messages in thread
From: Christian Loehle @ 2026-08-03 22:28 UTC (permalink / raw)
To: Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han
On 8/3/26 22:05, Christian Loehle wrote:
> cppc-cpufreq reaches cppc_set_perf() from every target callback. For
> direct SystemMemory controls, that path currently does several steps
> which are unnecessary once the immutable _CPC layout is known:
>
> - a full-width write first reads the access unit and merges the value.
> - every write takes the descriptor's RMW lock, even when its access unit
> is not shared with another _CPC entry.
> - cppc_set_perf() evaluates the same three PCC predicates at each phase
> of the transaction.
>
> Remove those costs while retaining the existing conservative paths for
> partial fields, overlapping or malformed access units, and PCC controls.
>
> The series was tested on Arm Power-Orion O6 and AmpereOne systems using
> cppc-cpufreq and schedutil. An rt-app task pinned to one CPU ran for
> 500 us every 2 ms with uclamp.min=512, for 5000 periods per run. schedutil
> rate_limit_us was 1000. cppc_cpufreq_fast_switch() latency was measured
> for 100 ten-second runs. Each sample is the mean callback latency within
> one run, and the table reports the median and sample standard deviation
> of those samples.
>
> Orion O6 median stdev callbacks
> baseline 5703 ns 307 ns 188017
> complete series 5023 ns 240 ns 188209
> ==> 680 ns (11.9%) reduction
>
> AmpereOne median stdev
> baseline 2090 ns 157 ns
> complete series 1907.5 ns 140 ns
> ==> 182.5 ns (8.7%) reduction
>
> The cumulative intermediate results on the Orion O6 attribute roughly half
> of the gain to each of the first two patches: avoiding the read reduced
> the median by 284 ns (5.0%), and avoiding the lock reduced it by another
> 336 ns (6.2%).
> Together they account for 620 ns of the 680 ns total reduction.
>
> With the same arm64 configuration and GCC 11.4, caching the PCC predicate
> also reduces cppc_set_perf() from 1124 to 884 bytes. The generated
> function has 60 fewer instructions, 17 fewer loads and 20 fewer branches.
>
> This series is based on the CPPC fixes posted at (already queued):
> https://lore.kernel.org/lkml/20260722093825.1030594-1-christian.loehle@arm.com/
>
> and the separately posted fix still under review:
> https://lore.kernel.org/lkml/20260724104042.1481804-1-christian.loehle@arm.com/
>
> PS:
> There's a final optimization that I actually wanted to make but decided to
> split it out for now as it somewhat replicated Sumit's series:
> Skipping redundant perf ctrl writes in cpufreq-cppc if registers are non-PCC
> and !shared (because the values are unchanged, the
> !autonomous-common-case), but that requires the driver to have a more
> complex caching- and atomic-updating machinery in place, similar to
> hotplug. As opposed to this series the optimization would be for the
> microcontroller handling the CPPC requests, which may be shared across
> many CPUs and therefore redundant requests can increase the dvfs
> transition latency.
> That patch will follow once Sumit's is queued:
> https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
>
> [RESEND] for the new base-commit specifier below
>
> Christian Loehle (3):
> ACPI: CPPC: Avoid unnecessary reads for full-width writes
> ACPI: CPPC: Avoid locking standalone full-width registers
> ACPI: CPPC: Evaluate performance-control PCC use once
>
> drivers/acpi/cppc_acpi.c | 135 +++++++++++++++++++++++++++++++--------
> include/acpi/cppc_acpi.h | 5 +-
> 2 files changed, 113 insertions(+), 27 deletions(-)
>
>
> base-commit: 075b74841bd0065a3bda3440873c747938e69b68
Nice, so that worked:
https://sashiko.dev/#/patchset/20260803210527.1285229-1-christian.loehle@arm.com
Seems no findings on $SUBJECT but the rest of the comments it has
look legit to me? I'll go take another look tomorrow:
1. Using per-CPU cpc_desc->rmw_lock for SYSTEM_MEMORY CPPC control register that
may be shared.
2. MASK_VAL_WRITE() truncation on 32bit architectures.
3. acpi_cppc_processor_exit() calls kfree(cpc_ptr) unconditionally (UAF with sysfs?)
4. cppc_set_reg_val_in_pcc() calls cpc_write() before down_write(&pcc_ss_data->pcc_lock)
5 & 6 are sanitization of values from FW.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes
2026-08-03 21:05 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
@ 2026-08-05 13:04 ` Zhongqiu Han
0 siblings, 0 replies; 10+ messages in thread
From: Zhongqiu Han @ 2026-08-05 13:04 UTC (permalink / raw)
To: Christian Loehle, Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han
On 8/4/2026 5:05 AM, Christian Loehle wrote:
> SystemMemory GAS entries may describe a field within a wider access
> unit, so cpc_write() reads the access unit before updating the field to
> preserve the surrounding bits. It also does this when the field covers
> the complete access unit.
>
> When the bit offset is zero and the register bit width equals the
> resolved access width, the previous value cannot affect the result. Skip
> the MMIO read and mask operation in that case. Retain rmw_lock because
> another entry in the same _CPC package may share the access unit.
>
> Signed-off-by: Christian Loehle <christian.loehle@arm.com>
Nice optimization. Looks good to me.
Reviewed-by: Zhongqiu Han <zhongqiu.han@oss.qualcomm.com>
> ---
> drivers/acpi/cppc_acpi.c | 43 ++++++++++++++++++++++++----------------
> 1 file changed, 26 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index 53d09ca98f06..9b8d68b44ea9 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1162,25 +1162,34 @@ static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
> return -ENODEV;
> }
>
> + /*
> + * Only partial fields need the previous contents to preserve bits
> + * outside the field. Keep serializing full-width writes because
> + * another _CPC entry may share the access unit and require RMW.
> + */
> raw_spin_lock_irqsave(&cpc_desc->rmw_lock, flags);
> - switch (size) {
> - case 8:
> - prev_val = readb_relaxed(vaddr);
> - break;
> - case 16:
> - prev_val = readw_relaxed(vaddr);
> - break;
> - case 32:
> - prev_val = readl_relaxed(vaddr);
> - break;
> - case 64:
> - prev_val = readq_relaxed(vaddr);
> - break;
> - default:
> - raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock, flags);
> - return -EFAULT;
> +
> + if (reg->bit_offset || reg->bit_width != size) {
> + switch (size) {
> + case 8:
> + prev_val = readb_relaxed(vaddr);
> + break;
> + case 16:
> + prev_val = readw_relaxed(vaddr);
> + break;
> + case 32:
> + prev_val = readl_relaxed(vaddr);
> + break;
> + case 64:
> + prev_val = readq_relaxed(vaddr);
> + break;
> + default:
> + raw_spin_unlock_irqrestore(&cpc_desc->rmw_lock,
> + flags);
> + return -EFAULT;
> + }
> + val = MASK_VAL_WRITE(reg, prev_val, val);
> }
> - val = MASK_VAL_WRITE(reg, prev_val, val);
> }
>
> switch (size) {
--
Thx and BRs,
Zhongqiu Han
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-03 22:28 ` [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
@ 2026-08-05 13:08 ` Rafael J. Wysocki (Intel)
2026-08-06 10:05 ` Christian Loehle
1 sibling, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-08-05 13:08 UTC (permalink / raw)
To: Christian Loehle
Cc: Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-acpi,
linux-kernel, Len Brown, Jie Zhan, Lifeng Zheng, Pierre Gondois,
Sumit Gupta, Sudeep Holla, Ionela Voinescu, zhongqiu.han
On Tue, Aug 4, 2026 at 12:28 AM Christian Loehle
<christian.loehle@arm.com> wrote:
>
> On 8/3/26 22:05, Christian Loehle wrote:
> > cppc-cpufreq reaches cppc_set_perf() from every target callback. For
> > direct SystemMemory controls, that path currently does several steps
> > which are unnecessary once the immutable _CPC layout is known:
> >
> > - a full-width write first reads the access unit and merges the value.
> > - every write takes the descriptor's RMW lock, even when its access unit
> > is not shared with another _CPC entry.
> > - cppc_set_perf() evaluates the same three PCC predicates at each phase
> > of the transaction.
> >
> > Remove those costs while retaining the existing conservative paths for
> > partial fields, overlapping or malformed access units, and PCC controls.
> >
> > The series was tested on Arm Power-Orion O6 and AmpereOne systems using
> > cppc-cpufreq and schedutil. An rt-app task pinned to one CPU ran for
> > 500 us every 2 ms with uclamp.min=512, for 5000 periods per run. schedutil
> > rate_limit_us was 1000. cppc_cpufreq_fast_switch() latency was measured
> > for 100 ten-second runs. Each sample is the mean callback latency within
> > one run, and the table reports the median and sample standard deviation
> > of those samples.
> >
> > Orion O6 median stdev callbacks
> > baseline 5703 ns 307 ns 188017
> > complete series 5023 ns 240 ns 188209
> > ==> 680 ns (11.9%) reduction
> >
> > AmpereOne median stdev
> > baseline 2090 ns 157 ns
> > complete series 1907.5 ns 140 ns
> > ==> 182.5 ns (8.7%) reduction
> >
> > The cumulative intermediate results on the Orion O6 attribute roughly half
> > of the gain to each of the first two patches: avoiding the read reduced
> > the median by 284 ns (5.0%), and avoiding the lock reduced it by another
> > 336 ns (6.2%).
> > Together they account for 620 ns of the 680 ns total reduction.
> >
> > With the same arm64 configuration and GCC 11.4, caching the PCC predicate
> > also reduces cppc_set_perf() from 1124 to 884 bytes. The generated
> > function has 60 fewer instructions, 17 fewer loads and 20 fewer branches.
> >
> > This series is based on the CPPC fixes posted at (already queued):
> > https://lore.kernel.org/lkml/20260722093825.1030594-1-christian.loehle@arm.com/
> >
> > and the separately posted fix still under review:
> > https://lore.kernel.org/lkml/20260724104042.1481804-1-christian.loehle@arm.com/
> >
> > PS:
> > There's a final optimization that I actually wanted to make but decided to
> > split it out for now as it somewhat replicated Sumit's series:
> > Skipping redundant perf ctrl writes in cpufreq-cppc if registers are non-PCC
> > and !shared (because the values are unchanged, the
> > !autonomous-common-case), but that requires the driver to have a more
> > complex caching- and atomic-updating machinery in place, similar to
> > hotplug. As opposed to this series the optimization would be for the
> > microcontroller handling the CPPC requests, which may be shared across
> > many CPUs and therefore redundant requests can increase the dvfs
> > transition latency.
> > That patch will follow once Sumit's is queued:
> > https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
> >
> > [RESEND] for the new base-commit specifier below
> >
> > Christian Loehle (3):
> > ACPI: CPPC: Avoid unnecessary reads for full-width writes
> > ACPI: CPPC: Avoid locking standalone full-width registers
> > ACPI: CPPC: Evaluate performance-control PCC use once
> >
> > drivers/acpi/cppc_acpi.c | 135 +++++++++++++++++++++++++++++++--------
> > include/acpi/cppc_acpi.h | 5 +-
> > 2 files changed, 113 insertions(+), 27 deletions(-)
> >
> >
> > base-commit: 075b74841bd0065a3bda3440873c747938e69b68
>
> Nice, so that worked:
> https://sashiko.dev/#/patchset/20260803210527.1285229-1-christian.loehle@arm.com
> Seems no findings on $SUBJECT
Yes, so applied as 7.3 material.
> but the rest of the comments it has look legit to me?
Yes, but please address those on top of the $subject series, thanks!
> I'll go take another look tomorrow:
>
> 1. Using per-CPU cpc_desc->rmw_lock for SYSTEM_MEMORY CPPC control register that
> may be shared.
> 2. MASK_VAL_WRITE() truncation on 32bit architectures.
> 3. acpi_cppc_processor_exit() calls kfree(cpc_ptr) unconditionally (UAF with sysfs?)
> 4. cppc_set_reg_val_in_pcc() calls cpc_write() before down_write(&pcc_ss_data->pcc_lock)
> 5 & 6 are sanitization of values from FW.
Sounds good to me.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-03 22:28 ` [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-08-05 13:08 ` Rafael J. Wysocki (Intel)
@ 2026-08-06 10:05 ` Christian Loehle
2026-08-06 10:36 ` Rafael J. Wysocki (Intel)
1 sibling, 1 reply; 10+ messages in thread
From: Christian Loehle @ 2026-08-06 10:05 UTC (permalink / raw)
To: Rafael J . Wysocki, Viresh Kumar
Cc: linux-pm, linux-acpi, linux-kernel, Len Brown, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han
On 8/3/26 23:28, Christian Loehle wrote:
> On 8/3/26 22:05, Christian Loehle wrote:
>> cppc-cpufreq reaches cppc_set_perf() from every target callback. For
>> direct SystemMemory controls, that path currently does several steps
>> which are unnecessary once the immutable _CPC layout is known:
>>
>> - a full-width write first reads the access unit and merges the value.
>> - every write takes the descriptor's RMW lock, even when its access unit
>> is not shared with another _CPC entry.
>> - cppc_set_perf() evaluates the same three PCC predicates at each phase
>> of the transaction.
>>
>> Remove those costs while retaining the existing conservative paths for
>> partial fields, overlapping or malformed access units, and PCC controls.
>>
>> The series was tested on Arm Power-Orion O6 and AmpereOne systems using
>> cppc-cpufreq and schedutil. An rt-app task pinned to one CPU ran for
>> 500 us every 2 ms with uclamp.min=512, for 5000 periods per run. schedutil
>> rate_limit_us was 1000. cppc_cpufreq_fast_switch() latency was measured
>> for 100 ten-second runs. Each sample is the mean callback latency within
>> one run, and the table reports the median and sample standard deviation
>> of those samples.
>>
>> Orion O6 median stdev callbacks
>> baseline 5703 ns 307 ns 188017
>> complete series 5023 ns 240 ns 188209
>> ==> 680 ns (11.9%) reduction
>>
>> AmpereOne median stdev
>> baseline 2090 ns 157 ns
>> complete series 1907.5 ns 140 ns
>> ==> 182.5 ns (8.7%) reduction
>>
>> The cumulative intermediate results on the Orion O6 attribute roughly half
>> of the gain to each of the first two patches: avoiding the read reduced
>> the median by 284 ns (5.0%), and avoiding the lock reduced it by another
>> 336 ns (6.2%).
>> Together they account for 620 ns of the 680 ns total reduction.
>>
>> With the same arm64 configuration and GCC 11.4, caching the PCC predicate
>> also reduces cppc_set_perf() from 1124 to 884 bytes. The generated
>> function has 60 fewer instructions, 17 fewer loads and 20 fewer branches.
>>
>> This series is based on the CPPC fixes posted at (already queued):
>> https://lore.kernel.org/lkml/20260722093825.1030594-1-christian.loehle@arm.com/
>>
>> and the separately posted fix still under review:
>> https://lore.kernel.org/lkml/20260724104042.1481804-1-christian.loehle@arm.com/
>>
>> PS:
>> There's a final optimization that I actually wanted to make but decided to
>> split it out for now as it somewhat replicated Sumit's series:
>> Skipping redundant perf ctrl writes in cpufreq-cppc if registers are non-PCC
>> and !shared (because the values are unchanged, the
>> !autonomous-common-case), but that requires the driver to have a more
>> complex caching- and atomic-updating machinery in place, similar to
>> hotplug. As opposed to this series the optimization would be for the
>> microcontroller handling the CPPC requests, which may be shared across
>> many CPUs and therefore redundant requests can increase the dvfs
>> transition latency.
>> That patch will follow once Sumit's is queued:
>> https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
>>
>> [RESEND] for the new base-commit specifier below
>>
>> Christian Loehle (3):
>> ACPI: CPPC: Avoid unnecessary reads for full-width writes
>> ACPI: CPPC: Avoid locking standalone full-width registers
>> ACPI: CPPC: Evaluate performance-control PCC use once
>>
>> drivers/acpi/cppc_acpi.c | 135 +++++++++++++++++++++++++++++++--------
>> include/acpi/cppc_acpi.h | 5 +-
>> 2 files changed, 113 insertions(+), 27 deletions(-)
>>
>>
>> base-commit: 075b74841bd0065a3bda3440873c747938e69b68
>
> Nice, so that worked:
> https://sashiko.dev/#/patchset/20260803210527.1285229-1-christian.loehle@arm.com
> Seems no findings on $SUBJECT but the rest of the comments it has
> look legit to me? I'll go take another look tomorrow:
So I have patches for these all and will post them in a minute.
>
> 1. Using per-CPU cpc_desc->rmw_lock for SYSTEM_MEMORY CPPC control register that
> may be shared.
The solution here sucks unfortunately.
Obviously expanding the per-CPU rmw_lock to per-policy is reasonable, but unfortunately
that doesn't prevent something like:
Policy 0 Desired: address X, bits 7:0, access size 32
Policy 1 Desired: address X, bits 15:8, access size 32
which would be a compliant GAS description and would require a global lock for rmw.
And of course even worse, now that Desired is write-only rmw isn't possible at all
anymore.
I'm leaning to just reject these edge-cases that hopefully don't actually exist
(and require _CPC rev4 to be full access-unit width)
Opinions?
> 2. MASK_VAL_WRITE() truncation on 32bit architectures.
> 3. acpi_cppc_processor_exit() calls kfree(cpc_ptr) unconditionally (UAF with sysfs?)
> 4. cppc_set_reg_val_in_pcc() calls cpc_write() before down_write(&pcc_ss_data->pcc_lock)
> 5 & 6 are sanitization of values from FW.
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-06 10:05 ` Christian Loehle
@ 2026-08-06 10:36 ` Rafael J. Wysocki (Intel)
2026-08-06 14:25 ` Christian Loehle
0 siblings, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-08-06 10:36 UTC (permalink / raw)
To: Christian Loehle
Cc: Rafael J . Wysocki, Viresh Kumar, linux-pm, linux-acpi,
linux-kernel, Len Brown, Jie Zhan, Lifeng Zheng, Pierre Gondois,
Sumit Gupta, Sudeep Holla, Ionela Voinescu, zhongqiu.han
On Thu, Aug 6, 2026 at 12:05 PM Christian Loehle
<christian.loehle@arm.com> wrote:
>
> On 8/3/26 23:28, Christian Loehle wrote:
> > On 8/3/26 22:05, Christian Loehle wrote:
> >> cppc-cpufreq reaches cppc_set_perf() from every target callback. For
> >> direct SystemMemory controls, that path currently does several steps
> >> which are unnecessary once the immutable _CPC layout is known:
> >>
> >> - a full-width write first reads the access unit and merges the value.
> >> - every write takes the descriptor's RMW lock, even when its access unit
> >> is not shared with another _CPC entry.
> >> - cppc_set_perf() evaluates the same three PCC predicates at each phase
> >> of the transaction.
> >>
> >> Remove those costs while retaining the existing conservative paths for
> >> partial fields, overlapping or malformed access units, and PCC controls.
> >>
> >> The series was tested on Arm Power-Orion O6 and AmpereOne systems using
> >> cppc-cpufreq and schedutil. An rt-app task pinned to one CPU ran for
> >> 500 us every 2 ms with uclamp.min=512, for 5000 periods per run. schedutil
> >> rate_limit_us was 1000. cppc_cpufreq_fast_switch() latency was measured
> >> for 100 ten-second runs. Each sample is the mean callback latency within
> >> one run, and the table reports the median and sample standard deviation
> >> of those samples.
> >>
> >> Orion O6 median stdev callbacks
> >> baseline 5703 ns 307 ns 188017
> >> complete series 5023 ns 240 ns 188209
> >> ==> 680 ns (11.9%) reduction
> >>
> >> AmpereOne median stdev
> >> baseline 2090 ns 157 ns
> >> complete series 1907.5 ns 140 ns
> >> ==> 182.5 ns (8.7%) reduction
> >>
> >> The cumulative intermediate results on the Orion O6 attribute roughly half
> >> of the gain to each of the first two patches: avoiding the read reduced
> >> the median by 284 ns (5.0%), and avoiding the lock reduced it by another
> >> 336 ns (6.2%).
> >> Together they account for 620 ns of the 680 ns total reduction.
> >>
> >> With the same arm64 configuration and GCC 11.4, caching the PCC predicate
> >> also reduces cppc_set_perf() from 1124 to 884 bytes. The generated
> >> function has 60 fewer instructions, 17 fewer loads and 20 fewer branches.
> >>
> >> This series is based on the CPPC fixes posted at (already queued):
> >> https://lore.kernel.org/lkml/20260722093825.1030594-1-christian.loehle@arm.com/
> >>
> >> and the separately posted fix still under review:
> >> https://lore.kernel.org/lkml/20260724104042.1481804-1-christian.loehle@arm.com/
> >>
> >> PS:
> >> There's a final optimization that I actually wanted to make but decided to
> >> split it out for now as it somewhat replicated Sumit's series:
> >> Skipping redundant perf ctrl writes in cpufreq-cppc if registers are non-PCC
> >> and !shared (because the values are unchanged, the
> >> !autonomous-common-case), but that requires the driver to have a more
> >> complex caching- and atomic-updating machinery in place, similar to
> >> hotplug. As opposed to this series the optimization would be for the
> >> microcontroller handling the CPPC requests, which may be shared across
> >> many CPUs and therefore redundant requests can increase the dvfs
> >> transition latency.
> >> That patch will follow once Sumit's is queued:
> >> https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
> >>
> >> [RESEND] for the new base-commit specifier below
> >>
> >> Christian Loehle (3):
> >> ACPI: CPPC: Avoid unnecessary reads for full-width writes
> >> ACPI: CPPC: Avoid locking standalone full-width registers
> >> ACPI: CPPC: Evaluate performance-control PCC use once
> >>
> >> drivers/acpi/cppc_acpi.c | 135 +++++++++++++++++++++++++++++++--------
> >> include/acpi/cppc_acpi.h | 5 +-
> >> 2 files changed, 113 insertions(+), 27 deletions(-)
> >>
> >>
> >> base-commit: 075b74841bd0065a3bda3440873c747938e69b68
> >
> > Nice, so that worked:
> > https://sashiko.dev/#/patchset/20260803210527.1285229-1-christian.loehle@arm.com
> > Seems no findings on $SUBJECT but the rest of the comments it has
> > look legit to me? I'll go take another look tomorrow:
>
> So I have patches for these all and will post them in a minute.
>
> >
> > 1. Using per-CPU cpc_desc->rmw_lock for SYSTEM_MEMORY CPPC control register that
> > may be shared.
>
> The solution here sucks unfortunately.
> Obviously expanding the per-CPU rmw_lock to per-policy is reasonable, but unfortunately
> that doesn't prevent something like:
> Policy 0 Desired: address X, bits 7:0, access size 32
> Policy 1 Desired: address X, bits 15:8, access size 32
> which would be a compliant GAS description and would require a global lock for rmw.
>
> And of course even worse, now that Desired is write-only rmw isn't possible at all
> anymore.
> I'm leaning to just reject these edge-cases that hopefully don't actually exist
> (and require _CPC rev4 to be full access-unit width)
> Opinions?
Fail initialization on insane platform setups I'd say.
There's no promise that Linux will support compliant setups that are
too much pain to deal with.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-06 10:36 ` Rafael J. Wysocki (Intel)
@ 2026-08-06 14:25 ` Christian Loehle
0 siblings, 0 replies; 10+ messages in thread
From: Christian Loehle @ 2026-08-06 14:25 UTC (permalink / raw)
To: Rafael J. Wysocki (Intel)
Cc: Viresh Kumar, linux-pm, linux-acpi, linux-kernel, Len Brown,
Jie Zhan, Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, zhongqiu.han
On 8/6/26 11:36, Rafael J. Wysocki (Intel) wrote:
> On Thu, Aug 6, 2026 at 12:05 PM Christian Loehle
> <christian.loehle@arm.com> wrote:
>>
>> On 8/3/26 23:28, Christian Loehle wrote:
>>> On 8/3/26 22:05, Christian Loehle wrote:
>>>> cppc-cpufreq reaches cppc_set_perf() from every target callback. For
>>>> direct SystemMemory controls, that path currently does several steps
>>>> which are unnecessary once the immutable _CPC layout is known:
>>>>
>>>> - a full-width write first reads the access unit and merges the value.
>>>> - every write takes the descriptor's RMW lock, even when its access unit
>>>> is not shared with another _CPC entry.
>>>> - cppc_set_perf() evaluates the same three PCC predicates at each phase
>>>> of the transaction.
>>>>
>>>> Remove those costs while retaining the existing conservative paths for
>>>> partial fields, overlapping or malformed access units, and PCC controls.
>>>>
>>>> The series was tested on Arm Power-Orion O6 and AmpereOne systems using
>>>> cppc-cpufreq and schedutil. An rt-app task pinned to one CPU ran for
>>>> 500 us every 2 ms with uclamp.min=512, for 5000 periods per run. schedutil
>>>> rate_limit_us was 1000. cppc_cpufreq_fast_switch() latency was measured
>>>> for 100 ten-second runs. Each sample is the mean callback latency within
>>>> one run, and the table reports the median and sample standard deviation
>>>> of those samples.
>>>>
>>>> Orion O6 median stdev callbacks
>>>> baseline 5703 ns 307 ns 188017
>>>> complete series 5023 ns 240 ns 188209
>>>> ==> 680 ns (11.9%) reduction
>>>>
>>>> AmpereOne median stdev
>>>> baseline 2090 ns 157 ns
>>>> complete series 1907.5 ns 140 ns
>>>> ==> 182.5 ns (8.7%) reduction
>>>>
>>>> The cumulative intermediate results on the Orion O6 attribute roughly half
>>>> of the gain to each of the first two patches: avoiding the read reduced
>>>> the median by 284 ns (5.0%), and avoiding the lock reduced it by another
>>>> 336 ns (6.2%).
>>>> Together they account for 620 ns of the 680 ns total reduction.
>>>>
>>>> With the same arm64 configuration and GCC 11.4, caching the PCC predicate
>>>> also reduces cppc_set_perf() from 1124 to 884 bytes. The generated
>>>> function has 60 fewer instructions, 17 fewer loads and 20 fewer branches.
>>>>
>>>> This series is based on the CPPC fixes posted at (already queued):
>>>> https://lore.kernel.org/lkml/20260722093825.1030594-1-christian.loehle@arm.com/
>>>>
>>>> and the separately posted fix still under review:
>>>> https://lore.kernel.org/lkml/20260724104042.1481804-1-christian.loehle@arm.com/
>>>>
>>>> PS:
>>>> There's a final optimization that I actually wanted to make but decided to
>>>> split it out for now as it somewhat replicated Sumit's series:
>>>> Skipping redundant perf ctrl writes in cpufreq-cppc if registers are non-PCC
>>>> and !shared (because the values are unchanged, the
>>>> !autonomous-common-case), but that requires the driver to have a more
>>>> complex caching- and atomic-updating machinery in place, similar to
>>>> hotplug. As opposed to this series the optimization would be for the
>>>> microcontroller handling the CPPC requests, which may be shared across
>>>> many CPUs and therefore redundant requests can increase the dvfs
>>>> transition latency.
>>>> That patch will follow once Sumit's is queued:
>>>> https://lore.kernel.org/lkml/20260716153820.2007095-1-sumitg@nvidia.com/
>>>>
>>>> [RESEND] for the new base-commit specifier below
>>>>
>>>> Christian Loehle (3):
>>>> ACPI: CPPC: Avoid unnecessary reads for full-width writes
>>>> ACPI: CPPC: Avoid locking standalone full-width registers
>>>> ACPI: CPPC: Evaluate performance-control PCC use once
>>>>
>>>> drivers/acpi/cppc_acpi.c | 135 +++++++++++++++++++++++++++++++--------
>>>> include/acpi/cppc_acpi.h | 5 +-
>>>> 2 files changed, 113 insertions(+), 27 deletions(-)
>>>>
>>>>
>>>> base-commit: 075b74841bd0065a3bda3440873c747938e69b68
>>>
>>> Nice, so that worked:
>>> https://sashiko.dev/#/patchset/20260803210527.1285229-1-christian.loehle@arm.com
>>> Seems no findings on $SUBJECT but the rest of the comments it has
>>> look legit to me? I'll go take another look tomorrow:
>>
>> So I have patches for these all and will post them in a minute.
>>
>>>
>>> 1. Using per-CPU cpc_desc->rmw_lock for SYSTEM_MEMORY CPPC control register that
>>> may be shared.
>>
>> The solution here sucks unfortunately.
>> Obviously expanding the per-CPU rmw_lock to per-policy is reasonable, but unfortunately
>> that doesn't prevent something like:
>> Policy 0 Desired: address X, bits 7:0, access size 32
>> Policy 1 Desired: address X, bits 15:8, access size 32
>> which would be a compliant GAS description and would require a global lock for rmw.
>>
>> And of course even worse, now that Desired is write-only rmw isn't possible at all
>> anymore.
>> I'm leaning to just reject these edge-cases that hopefully don't actually exist
>> (and require _CPC rev4 to be full access-unit width)
>> Opinions?
>
> Fail initialization on insane platform setups I'd say.
>
> There's no promise that Linux will support compliant setups that are
> too much pain to deal with.
Ok done, I would delay posting until the your bleeding-edge hits linux-next, in the
hopes of being able to supply a valid base-commit for Sashiko.
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-06 14:25 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-03 21:05 [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-08-03 21:05 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
2026-08-05 13:04 ` Zhongqiu Han
2026-08-03 21:05 ` [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers Christian Loehle
2026-08-03 21:05 ` [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once Christian Loehle
2026-08-03 22:28 ` [RESEND][PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-08-05 13:08 ` Rafael J. Wysocki (Intel)
2026-08-06 10:05 ` Christian Loehle
2026-08-06 10:36 ` Rafael J. Wysocki (Intel)
2026-08-06 14:25 ` Christian Loehle
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox