[PATCH 8/8] cpufreq: cppc: Expose Resource Priority attributes via sysfs
Lifeng Zheng <[email protected]> Fri, 17 Jul 2026 10:45:02 +0800
| Newsgroups | dev.linux.lists.acpica-devel,org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
Add a "resource_priority" kobject directory under each cpufreq policy that has CPPC v4 Resource Priority entries. Inside it, create a sub-directory per resource priority group (indexed 0..N-1) with the following sysfs attributes: controlled_resources (RO) -- resource type IDs as human-readable names enable (RW) -- enable/disable the priority group priority_count (RO) -- number of priority levels priority (RW) -- current priority value Create the sysfs hierarchy in cppc_cpufreq_cpu_init() and tear it down in cppc_cpufreq_cpu_exit(). A void *res_prio_data pointer in cppc_cpudata tracks the allocation. These attributes allow administrators and power-management daemons to inspect and tune Resource Priority settings at runtime without requiring platform-specific tools. Signed-off-by: Lifeng Zheng <[email protected]> --- drivers/cpufreq/cppc_cpufreq.c | 270 +++++++++++++++++++++++++++++++++ include/acpi/cppc_acpi.h | 1 + 2 files changed, 271 insertions(+) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index f6cea0c54dd9..ed88f4e55ced 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -640,6 +640,9 @@ static void cppc_cpufreq_put_cpu_data(struct cpufreq_policy *policy) policy->driver_data = NULL; } +static int cppc_create_res_prio_sysfs(struct cpufreq_policy *policy); +static void cppc_remove_res_prio_sysfs(struct cpufreq_policy *policy); + static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) { unsigned int cpu = policy->cpu; @@ -715,6 +718,11 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) } cppc_cpufreq_cpu_fie_init(policy); + + ret = cppc_create_res_prio_sysfs(policy); + if (ret) + goto out; + return 0; out: @@ -729,6 +737,7 @@ static void cppc_cpufreq_cpu_exit(struct cpufreq_policy *policy) unsigned int cpu = policy->cpu; int ret; + cppc_remove_res_prio_sysfs(policy); cppc_cpufreq_cpu_fie_exit(policy); cpu_data->perf_ctrls.desired_perf = caps->lowest_perf; @@ -1026,6 +1035,267 @@ static struct freq_attr *cppc_cpufreq_attr[] = { NULL, }; +/* ====== Resource Priority sysfs interface (CPPC v4) ====== */ + +struct cppc_res_prio_group { + struct kobject kobj; + int index; + unsigned int cpu; +}; + +struct cppc_res_prio_data { + struct kobject kobj; + int num_groups; + struct cppc_res_prio_group groups[]; +}; + +static const char * const resource_type_names[] = { + NULL, + "processor_boost", + "processor_throttle", + "l2_cache", + "l3_cache", + "memory_bandwidth", +}; + +#define RESOURCE_TYPE_MAX 5 + +static const char *resource_type_to_name(u32 id) +{ + if (id >= 1 && id <= RESOURCE_TYPE_MAX) + return resource_type_names[id]; + return NULL; +} + +static ssize_t controlled_resources_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u32 resources[32]; + int num = ARRAY_SIZE(resources); + int ret, i, pos = 0; + + ret = cppc_get_resource_priority_resources(grp->cpu, grp->index, + resources, &num); + if (ret) + return ret; + + if (num == 0) + return sysfs_emit(buf, "\n"); + + for (i = 0; i < num; i++) { + const char *name = resource_type_to_name(resources[i]); + + if (name) + pos += sysfs_emit_at(buf, pos, "%s", name); + else + pos += sysfs_emit_at(buf, pos, "unknown(0x%02x)", resources[i]); + + if (i < num - 1) + pos += sysfs_emit_at(buf, pos, " "); + } + + pos += sysfs_emit_at(buf, pos, "\n"); + return pos; +} + +static ssize_t enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + bool val; + int ret; + + ret = cppc_get_res_priority_enable(grp->cpu, grp->index, &val); + if (ret == -EOPNOTSUPP) + return sysfs_emit(buf, "<unsupported>\n"); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", val); +} + +static ssize_t enable_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + ret = cppc_set_res_priority_enable(grp->cpu, grp->index, val); + if (ret) + return ret; + + return count; +} + +static ssize_t priority_count_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u64 val; + int ret; + + ret = cppc_get_res_priority_count(grp->cpu, grp->index, &val); + if (ret == -EOPNOTSUPP) + return sysfs_emit(buf, "<unsupported>\n"); + if (ret) + return ret; + + return sysfs_emit(buf, "%llu\n", val); +} + +static ssize_t priority_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u64 val; + int ret; + + ret = cppc_get_res_priority(grp->cpu, grp->index, &val); + if (ret == -EOPNOTSUPP) + return sysfs_emit(buf, "<unsupported>\n"); + if (ret) + return ret; + + return sysfs_emit(buf, "%llu\n", val); +} + +static ssize_t priority_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct cppc_res_prio_group *grp = + container_of(kobj, struct cppc_res_prio_group, kobj); + u64 val; + int ret; + + ret = kstrtou64(buf, 0, &val); + if (ret) + return ret; + + ret = cppc_set_res_priority(grp->cpu, grp->index, val); + if (ret) + return ret; + + return count; +} + +static struct kobj_attribute attr_controlled_resources = + __ATTR(controlled_resources, 0444, controlled_resources_show, NULL); +static struct kobj_attribute attr_enable = + __ATTR(enable, 0644, enable_show, enable_store); +static struct kobj_attribute attr_priority_count = + __ATTR(priority_count, 0444, priority_count_show, NULL); +static struct kobj_attribute attr_priority = + __ATTR(priority, 0644, priority_show, priority_store); + +static struct attribute *res_prio_group_attrs[] = { + &attr_controlled_resources.attr, + &attr_enable.attr, + &attr_priority_count.attr, + &attr_priority.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(res_prio_group); + +static void cppc_res_prio_group_release(struct kobject *kobj) +{ + /* + * cppc_res_prio_group is embedded in the flexible array of + * cppc_res_prio_data and freed when the parent is released. + */ +} + +static const struct kobj_type cppc_res_prio_group_ktype = { + .release = cppc_res_prio_group_release, + .sysfs_ops = &kobj_sysfs_ops, + .default_groups = res_prio_group_groups, +}; + +static void cppc_res_prio_release(struct kobject *kobj) +{ + struct cppc_res_prio_data *data = + container_of(kobj, struct cppc_res_prio_data, kobj); + + kfree(data); +} + +static const struct kobj_type cppc_res_prio_ktype = { + .release = cppc_res_prio_release, + .sysfs_ops = &kobj_sysfs_ops, +}; + +static void cppc_remove_res_prio_sysfs(struct cpufreq_policy *policy) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + struct cppc_res_prio_data *data = cpu_data->res_prio_data; + int i; + + if (!data) + return; + + for (i = 0; i < data->num_groups; i++) + kobject_put(&data->groups[i].kobj); + + kobject_put(&data->kobj); + cpu_data->res_prio_data = NULL; +} + +static int cppc_create_res_prio_sysfs(struct cpufreq_policy *policy) +{ + struct cppc_cpudata *cpu_data = policy->driver_data; + struct cppc_res_prio_data *data; + int num, i, ret; + + ret = cppc_get_resource_priority_count(policy->cpu, &num); + if (ret || num <= 0) + return 0; + + data = kzalloc(struct_size(data, groups, num), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num_groups = num; + + ret = kobject_init_and_add(&data->kobj, &cppc_res_prio_ktype, + &policy->kobj, "resource_priority"); + if (ret) { + kfree(data); + return ret; + } + + for (i = 0; i < num; i++) { + data->groups[i].index = i; + data->groups[i].cpu = policy->cpu; + + ret = kobject_init_and_add(&data->groups[i].kobj, + &cppc_res_prio_group_ktype, + &data->kobj, "%d", i); + if (ret) { + while (--i >= 0) + kobject_put(&data->groups[i].kobj); + kobject_put(&data->kobj); + return ret; + } + } + + cpu_data->res_prio_data = data; + return 0; +} + static struct cpufreq_driver cppc_cpufreq_driver = { .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS, .verify = cppc_verify_policy, diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 86a4764c504d..481fca627389 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -175,6 +175,7 @@ struct cppc_cpudata { struct cppc_perf_fb_ctrs perf_fb_ctrs; unsigned int shared_type; cpumask_var_t shared_cpu_map; + void *res_prio_data; }; #ifdef CONFIG_ACPI_CPPC_LIB -- 2.33.0