[PATCH v2 08/15] ACPI: CPPC: Release PCC data after probe failures

From: Christian Loehle

Date: Sat Aug 08 2026 - 04:31:48 EST


pcc_data_alloc() takes a per-processor reference while parsing the first
PCC register. Every later probe failure currently frees the CPC descriptor
without dropping that reference. Depending on where probe fails, this
leaks an unacquired PCC object, an acquired mailbox channel, or an extra
reference to a shared channel.

Factor the teardown into pcc_data_put() so it handles both acquired and
unacquired state, and invoke it on every failure after allocation.

Do not store the temporary pcc_data_alloc() result in the eventual probe
return value. A successful allocation must not make a later parsing failure
run cleanup and then return success.

The per-CPU PCC subspace index is zero-initialized. If probe returns before
assigning it, a later processor exit can consequently drop the reference
for subspace 0 even though this CPU never acquired one. Initialize the
index to -1 before any probe return and only release it from exit after a
CPC descriptor has been published.

Fixes: 85b1407bf6d2 ("ACPI / CPPC: Make CPPC ACPI driver aware of PCC subspace IDs")
Signed-off-by: Christian Loehle <christian.loehle@xxxxxxx>
---
drivers/acpi/cppc_acpi.c | 61 +++++++++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 19 deletions(-)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 7589d7ea7745..dca6f74c2cd2 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -807,6 +807,24 @@ static int pcc_data_alloc(int pcc_ss_id)
return 0;
}

+static void pcc_data_put(int pcc_ss_id)
+{
+ struct cppc_pcc_data *data;
+
+ if (pcc_ss_id < 0 || pcc_ss_id >= MAX_PCC_SUBSPACES)
+ return;
+
+ data = pcc_data[pcc_ss_id];
+ if (!data || --data->refcount)
+ return;
+
+ if (data->pcc_channel_acquired)
+ pcc_mbox_free_channel(data->pcc_channel);
+
+ kfree(data);
+ pcc_data[pcc_ss_id] = NULL;
+}
+
/*
* An example CPC table looks like the following.
*
@@ -852,8 +870,12 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
acpi_handle handle = pr->handle;
unsigned int num_ent, i, cpc_rev;
int pcc_subspace_id = -1;
+ bool pcc_data_ref = false;
acpi_status status;
int ret = -ENODATA;
+ int err;
+
+ per_cpu(cpu_pcc_subspace_idx, pr->id) = -1;

if (!osc_sb_cppc2_support_acked) {
pr_debug("CPPC v2 _OSC not acked\n");
@@ -985,8 +1007,12 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
if (pcc_subspace_id < 0) {
pcc_subspace_id = gas_t->access_width;
- if (pcc_data_alloc(pcc_subspace_id))
+ err = pcc_data_alloc(pcc_subspace_id);
+ if (err) {
+ ret = err;
goto out_free;
+ }
+ pcc_data_ref = true;
} else if (pcc_subspace_id != gas_t->access_width) {
pr_debug("Mismatched PCC ids in _CPC for CPU:%d\n",
pr->id);
@@ -1139,7 +1165,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
if (ret) {
per_cpu(cpc_desc_ptr, pr->id) = NULL;
kobject_put(&cpc_ptr->kobj);
- goto out_buf_free;
+ goto out_pcc_put;
}

kfree(output.pointer);
@@ -1149,6 +1175,11 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
pr_err("CPU%d: failed to initialize _CPC: %d\n", pr->id, ret);
cppc_free_desc(cpc_ptr);

+out_pcc_put:
+ if (pcc_data_ref)
+ pcc_data_put(pcc_subspace_id);
+ per_cpu(cpu_pcc_subspace_idx, pr->id) = -1;
+
out_buf_free:
kfree(output.pointer);
return ret;
@@ -1164,28 +1195,20 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
void acpi_cppc_processor_exit(struct acpi_processor *pr)
{
struct cpc_desc *cpc_ptr;
- int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id);
+ int pcc_ss_id;

cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
- if (cpc_ptr) {
- per_cpu(cpc_desc_ptr, pr->id) = NULL;
- kobject_del(&cpc_ptr->kobj);
+ if (!cpc_ptr) {
+ per_cpu(cpu_pcc_subspace_idx, pr->id) = -1;
+ return;
}

- if (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) {
- if (pcc_data[pcc_ss_id]->pcc_channel_acquired) {
- pcc_data[pcc_ss_id]->refcount--;
- if (!pcc_data[pcc_ss_id]->refcount) {
- pcc_mbox_free_channel(pcc_data[pcc_ss_id]->pcc_channel);
- kfree(pcc_data[pcc_ss_id]);
- pcc_data[pcc_ss_id] = NULL;
- }
- }
- }
- per_cpu(cpu_pcc_subspace_idx, pr->id) = -1;
+ pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->id);
+ per_cpu(cpc_desc_ptr, pr->id) = NULL;
+ kobject_del(&cpc_ptr->kobj);

- if (!cpc_ptr)
- return;
+ pcc_data_put(pcc_ss_id);
+ per_cpu(cpu_pcc_subspace_idx, pr->id) = -1;

kobject_put(&cpc_ptr->kobj);
}
--
2.34.1