[PATCH v2 07/15] ACPI: CPPC: Release CPC descriptors through kobject
From: Christian Loehle
Date: Sat Aug 08 2026 - 04:31:18 EST
The CPC descriptor embeds the kobject used for its sysfs directory, but
it has no release callback and processor exit frees the descriptor
immediately after kobject_put(). It also unmaps register resources and
releases PCC state before sysfs removal has drained active attribute
callbacks.
Provide a release callback which unmaps and frees the descriptor, and use
the same cleanup helper before kobject initialization. Once initialized,
let kobject_put() own descriptor cleanup so its error path cannot free the
object twice.
On processor exit, first unpublish the per-CPU descriptor and remove its
sysfs directory, then release PCC state and drop the kobject reference.
This keeps all resources valid while sysfs callbacks are active and also
works with delayed kobject release.
Fixes: 158c998ea44b ("ACPI / CPPC: add sysfs support to compute delivered performance")
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Link: https://sashiko.dev/#/patchset/20260724134251.1632824-1-christian.loehle%40arm.com
Signed-off-by: Christian Loehle <christian.loehle@xxxxxxx>
---
drivers/acpi/cppc_acpi.c | 50 +++++++++++++++++++++++-----------------
1 file changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index f0f3820200ea..7589d7ea7745 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -337,7 +337,27 @@ static struct attribute *cppc_attrs[] = {
};
ATTRIBUTE_GROUPS(cppc);
+static void cppc_free_desc(struct cpc_desc *cpc_ptr)
+{
+ unsigned int i;
+
+ for (i = 2; i < cpc_ptr->num_entries; i++) {
+ void __iomem *addr = cpc_ptr->cpc_regs[i - 2].sys_mem_vaddr;
+
+ if (addr)
+ iounmap(addr);
+ }
+
+ kfree(cpc_ptr);
+}
+
+static void cppc_kobj_release(struct kobject *kobj)
+{
+ cppc_free_desc(to_cpc_desc(kobj));
+}
+
static const struct kobj_type cppc_ktype = {
+ .release = cppc_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
.default_groups = cppc_groups,
};
@@ -1119,7 +1139,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_free;
+ goto out_buf_free;
}
kfree(output.pointer);
@@ -1127,15 +1147,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
out_free:
pr_err("CPU%d: failed to initialize _CPC: %d\n", pr->id, ret);
-
- /* Free all the mapped sys mem areas for this CPU */
- for (i = 2; i < cpc_ptr->num_entries; i++) {
- void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
-
- if (addr)
- iounmap(addr);
- }
- kfree(cpc_ptr);
+ cppc_free_desc(cpc_ptr);
out_buf_free:
kfree(output.pointer);
@@ -1152,10 +1164,14 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
void acpi_cppc_processor_exit(struct acpi_processor *pr)
{
struct cpc_desc *cpc_ptr;
- unsigned int i;
- void __iomem *addr;
int pcc_ss_id = per_cpu(cpu_pcc_subspace_idx, pr->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 (pcc_ss_id >= 0 && pcc_data[pcc_ss_id]) {
if (pcc_data[pcc_ss_id]->pcc_channel_acquired) {
pcc_data[pcc_ss_id]->refcount--;
@@ -1166,20 +1182,12 @@ void acpi_cppc_processor_exit(struct acpi_processor *pr)
}
}
}
+ per_cpu(cpu_pcc_subspace_idx, pr->id) = -1;
- cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
if (!cpc_ptr)
return;
- /* Free all the mapped sys mem areas for this CPU */
- for (i = 2; i < cpc_ptr->num_entries; i++) {
- addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
- if (addr)
- iounmap(addr);
- }
-
kobject_put(&cpc_ptr->kobj);
- kfree(cpc_ptr);
}
EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
--
2.34.1