* [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes
2026-07-24 13:42 [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
@ 2026-07-24 13:42 ` Christian Loehle
2026-07-24 13:42 ` [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers Christian Loehle
` (2 subsequent siblings)
3 siblings, 0 replies; 8+ messages in thread
From: Christian Loehle @ 2026-07-24 13:42 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, 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] 8+ messages in thread* [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers
2026-07-24 13:42 [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-07-24 13:42 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
@ 2026-07-24 13:42 ` Christian Loehle
2026-07-24 13:42 ` [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once Christian Loehle
2026-08-03 14:07 ` [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
3 siblings, 0 replies; 8+ messages in thread
From: Christian Loehle @ 2026-07-24 13:42 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, 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] 8+ messages in thread* [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once
2026-07-24 13:42 [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
2026-07-24 13:42 ` [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes Christian Loehle
2026-07-24 13:42 ` [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers Christian Loehle
@ 2026-07-24 13:42 ` Christian Loehle
2026-08-03 14:07 ` [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
3 siblings, 0 replies; 8+ messages in thread
From: Christian Loehle @ 2026-07-24 13:42 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, 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] 8+ messages in thread* Re: [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-07-24 13:42 [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
` (2 preceding siblings ...)
2026-07-24 13:42 ` [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once Christian Loehle
@ 2026-08-03 14:07 ` Christian Loehle
2026-08-03 14:54 ` Rafael J. Wysocki (Intel)
3 siblings, 1 reply; 8+ messages in thread
From: Christian Loehle @ 2026-08-03 14:07 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 7/24/26 14:42, 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/
>
> 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: 1590cf0329716306e948a8fc29f1d3ee87d3989f
> prerequisite-patch-id: 4c1f4063800e3658717eb77e43af13dad52d55cc
> prerequisite-patch-id: 5c40d9f099543123c9b904d18ce651e740fbefb8
> prerequisite-patch-id: 7202391dca8f31f9eb8f6d59fc753b3ce665e103
I was about the rebase and resend this series but noticed it still applies cleanly
on 7.2-rc6 + ACPI: CPPC: Avoid Desired Performance reads on ACPI 6.6+
https://lore.kernel.org/all/cover.1785749175.git.christian.loehle@arm.com/
Any comments on this?
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-03 14:07 ` [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead Christian Loehle
@ 2026-08-03 14:54 ` Rafael J. Wysocki (Intel)
2026-08-03 20:46 ` Christian Loehle
0 siblings, 1 reply; 8+ messages in thread
From: Rafael J. Wysocki (Intel) @ 2026-08-03 14:54 UTC (permalink / raw)
To: Christian Loehle
Cc: Viresh Kumar, linux-pm, linux-acpi, linux-kernel, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, Zhongqiu Han
On Mon, Aug 3, 2026 at 4:07 PM Christian Loehle
<christian.loehle@arm.com> wrote:
>
> On 7/24/26 14:42, 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/
> >
> > 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: 1590cf0329716306e948a8fc29f1d3ee87d3989f
> > prerequisite-patch-id: 4c1f4063800e3658717eb77e43af13dad52d55cc
> > prerequisite-patch-id: 5c40d9f099543123c9b904d18ce651e740fbefb8
> > prerequisite-patch-id: 7202391dca8f31f9eb8f6d59fc753b3ce665e103
>
>
> I was about the rebase and resend this series but noticed it still applies cleanly
> on 7.2-rc6 + ACPI: CPPC: Avoid Desired Performance reads on ACPI 6.6+
> https://lore.kernel.org/all/cover.1785749175.git.christian.loehle@arm.com/
>
> Any comments on this?
Sashiko says that it couldn't apply this series:
https://sashiko.dev/#/patchset/20260724134251.1632824-1-christian.loehle%40arm.com
Would it apply on top of plain 7.2-rc6? If so, can you please rebase
and resend it? Or is it linux-next-only?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-03 14:54 ` Rafael J. Wysocki (Intel)
@ 2026-08-03 20:46 ` Christian Loehle
2026-08-03 21:01 ` Christian Loehle
0 siblings, 1 reply; 8+ messages in thread
From: Christian Loehle @ 2026-08-03 20:46 UTC (permalink / raw)
To: Rafael J. Wysocki (Intel)
Cc: Viresh Kumar, linux-pm, linux-acpi, linux-kernel, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, Zhongqiu Han
On 8/3/26 15:54, Rafael J. Wysocki (Intel) wrote:
> On Mon, Aug 3, 2026 at 4:07 PM Christian Loehle
> <christian.loehle@arm.com> wrote:
>>
>> On 7/24/26 14:42, 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/
>>>
>>> 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: 1590cf0329716306e948a8fc29f1d3ee87d3989f
>>> prerequisite-patch-id: 4c1f4063800e3658717eb77e43af13dad52d55cc
>>> prerequisite-patch-id: 5c40d9f099543123c9b904d18ce651e740fbefb8
>>> prerequisite-patch-id: 7202391dca8f31f9eb8f6d59fc753b3ce665e103
>>
>>
>> I was about the rebase and resend this series but noticed it still applies cleanly
>> on 7.2-rc6 + ACPI: CPPC: Avoid Desired Performance reads on ACPI 6.6+
>> https://lore.kernel.org/all/cover.1785749175.git.christian.loehle@arm.com/
>>
>> Any comments on this?
>
> Sashiko says that it couldn't apply this series:
>
> https://sashiko.dev/#/patchset/20260724134251.1632824-1-christian.loehle%40arm.com
>
> Would it apply on top of plain 7.2-rc6? If so, can you please rebase
> and resend it? Or is it linux-next-only?
Strange, it still works for me, anything I'm missing?
Anyway I can rebase on v7.2-rc6 and rebase, the desired_perf changes are unrelated,
but here's my log FWIW:
# git checkout v7.2-rc6
# git switch -c cloehle/cppc-target-optimizations-7.2-rc6-test
# b4 am -c -o - 20260724134251.1632824-1-christian.loehle@arm.com | git am
Grabbing thread from lore.kernel.org/all/20260724134251.1632824-1-christian.loehle@arm.com/t.mbox.gz
Checking for newer revisions
Grabbing search results from lore.kernel.org
Analyzing 6 messages in the thread
Looking for additional code-review trailers on lore.kernel.org
Analyzing 0 code-review messages
Checking attestation on all messages, may take a moment...
---
✓ [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes
✓ [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers
✓ [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once
---
✓ Signed: DKIM/arm.com
---
Total patches: 3
---
Link: https://lore.kernel.org/r/20260724134251.1632824-1-christian.loehle@arm.com
Base: using specified base-commit 1590cf0329716306e948a8fc29f1d3ee87d3989f
git checkout -b 20260724_christian_loehle_arm_com 1590cf0329716306e948a8fc29f1d3ee87d3989f
Applying: ACPI: CPPC: Avoid unnecessary reads for full-width writes
Applying: ACPI: CPPC: Avoid locking standalone full-width registers
Applying: ACPI: CPPC: Evaluate performance-control PCC use once
# git log --oneline
86f53dfeddb6 (HEAD -> cloehle/cppc-target-optimizations-7.2-rc6-test) ACPI: CPPC: Evaluate performance-control PCC use once
61378f475006 ACPI: CPPC: Avoid locking standalone full-width registers
0c5b12480412 ACPI: CPPC: Avoid unnecessary reads for full-width writes
075b74841bd0 (tag: v7.2-rc6, origin/master, origin/HEAD) Linux 7.2-rc6
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH 0/3] ACPI: CPPC: Reduce .target() callback overhead
2026-08-03 20:46 ` Christian Loehle
@ 2026-08-03 21:01 ` Christian Loehle
0 siblings, 0 replies; 8+ messages in thread
From: Christian Loehle @ 2026-08-03 21:01 UTC (permalink / raw)
To: Rafael J. Wysocki (Intel)
Cc: Viresh Kumar, linux-pm, linux-acpi, linux-kernel, Jie Zhan,
Lifeng Zheng, Pierre Gondois, Sumit Gupta, Sudeep Holla,
Ionela Voinescu, Zhongqiu Han
On 8/3/26 21:46, Christian Loehle wrote:
> On 8/3/26 15:54, Rafael J. Wysocki (Intel) wrote:
>> On Mon, Aug 3, 2026 at 4:07 PM Christian Loehle
>> <christian.loehle@arm.com> wrote:
>>>
>>> On 7/24/26 14:42, 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/
>>>>
>>>> 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: 1590cf0329716306e948a8fc29f1d3ee87d3989f
>>>> prerequisite-patch-id: 4c1f4063800e3658717eb77e43af13dad52d55cc
>>>> prerequisite-patch-id: 5c40d9f099543123c9b904d18ce651e740fbefb8
>>>> prerequisite-patch-id: 7202391dca8f31f9eb8f6d59fc753b3ce665e103
>>>
>>>
>>> I was about the rebase and resend this series but noticed it still applies cleanly
>>> on 7.2-rc6 + ACPI: CPPC: Avoid Desired Performance reads on ACPI 6.6+
>>> https://lore.kernel.org/all/cover.1785749175.git.christian.loehle@arm.com/
>>>
>>> Any comments on this?
>>
>> Sashiko says that it couldn't apply this series:
>>
>> https://sashiko.dev/#/patchset/20260724134251.1632824-1-christian.loehle%40arm.com
>>
>> Would it apply on top of plain 7.2-rc6? If so, can you please rebase
>> and resend it? Or is it linux-next-only?
>
> Strange, it still works for me, anything I'm missing?
> Anyway I can rebase on v7.2-rc6 and rebase, the desired_perf changes are unrelated,
> but here's my log FWIW:
>
> # git checkout v7.2-rc6
> # git switch -c cloehle/cppc-target-optimizations-7.2-rc6-test
> # b4 am -c -o - 20260724134251.1632824-1-christian.loehle@arm.com | git am
> Grabbing thread from lore.kernel.org/all/20260724134251.1632824-1-christian.loehle@arm.com/t.mbox.gz
> Checking for newer revisions
> Grabbing search results from lore.kernel.org
> Analyzing 6 messages in the thread
> Looking for additional code-review trailers on lore.kernel.org
> Analyzing 0 code-review messages
> Checking attestation on all messages, may take a moment...
> ---
> ✓ [PATCH 1/3] ACPI: CPPC: Avoid unnecessary reads for full-width writes
> ✓ [PATCH 2/3] ACPI: CPPC: Avoid locking standalone full-width registers
> ✓ [PATCH 3/3] ACPI: CPPC: Evaluate performance-control PCC use once
> ---
> ✓ Signed: DKIM/arm.com
> ---
> Total patches: 3
> ---
> Link: https://lore.kernel.org/r/20260724134251.1632824-1-christian.loehle@arm.com
> Base: using specified base-commit 1590cf0329716306e948a8fc29f1d3ee87d3989f
> git checkout -b 20260724_christian_loehle_arm_com 1590cf0329716306e948a8fc29f1d3ee87d3989f
> Applying: ACPI: CPPC: Avoid unnecessary reads for full-width writes
> Applying: ACPI: CPPC: Avoid locking standalone full-width registers
> Applying: ACPI: CPPC: Evaluate performance-control PCC use once
>
> # git log --oneline
> 86f53dfeddb6 (HEAD -> cloehle/cppc-target-optimizations-7.2-rc6-test) ACPI: CPPC: Evaluate performance-control PCC use once
> 61378f475006 ACPI: CPPC: Avoid locking standalone full-width registers
> 0c5b12480412 ACPI: CPPC: Avoid unnecessary reads for full-width writes
> 075b74841bd0 (tag: v7.2-rc6, origin/master, origin/HEAD) Linux 7.2-rc6
>
Ah looks like sashiko doesn't understand prerequisite-patch-id yet:
https://github.com/sashiko-dev/sashiko/issues/49
^ permalink raw reply [flat|nested] 8+ messages in thread