[PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register

Sumit Gupta posted 8 patches 1 month, 2 weeks ago
[PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
Posted by Sumit Gupta 1 month, 2 weeks ago
Add sysfs interface to read/write the Performance Limited register.

The Performance Limited register indicates to the OS that an
unpredictable event (like thermal throttling) has limited processor
performance. This register is sticky and remains set until reset or
OS clears it by writing 0.

The interface is exposed as:
 /sys/devices/system/cpu/cpuX/cpufreq/perf_limited

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
---
 drivers/acpi/cppc_acpi.c       | 26 ++++++++++++++++++++++++++
 drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
 include/acpi/cppc_acpi.h       | 10 ++++++++++
 3 files changed, 48 insertions(+)

diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index ef53eb8a1feb..9b8da3ef06db 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
 }
 EXPORT_SYMBOL_GPL(cppc_set_max_perf);
 
+/**
+ * cppc_get_perf_limited - Get the Performance Limited register value.
+ * @cpu: CPU from which to get Performance Limited register.
+ * @perf_limited: Pointer to store the Performance Limited value.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+	return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
+
+/**
+ * cppc_set_perf_limited() - Write the Performance Limited register.
+ * @cpu: CPU on which to write register.
+ * @perf_limited: Value to write to the perf_limited register.
+ *
+ * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
+ */
+int cppc_set_perf_limited(int cpu, u64 perf_limited)
+{
+	return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
+}
+EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
+
 /**
  * cppc_get_perf - Get a CPU's performance controls.
  * @cpu: CPU for which to get performance controls.
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index cde6202e9c51..a425ad575aa6 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -1043,12 +1043,23 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
 	return count;
 }
 
+static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
+{
+	return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf);
+}
+
+static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count)
+{
+	return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, count);
+}
+
 cpufreq_freq_attr_ro(freqdomain_cpus);
 cpufreq_freq_attr_rw(auto_select);
 cpufreq_freq_attr_rw(auto_act_window);
 cpufreq_freq_attr_rw(energy_performance_preference_val);
 cpufreq_freq_attr_rw(min_perf);
 cpufreq_freq_attr_rw(max_perf);
+cpufreq_freq_attr_rw(perf_limited);
 
 static struct freq_attr *cppc_cpufreq_attr[] = {
 	&freqdomain_cpus,
@@ -1057,6 +1068,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
 	&energy_performance_preference_val,
 	&min_perf,
 	&max_perf,
+	&perf_limited,
 	NULL,
 };
 
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index be7de1222eee..8baff46f2ac7 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
 extern int cppc_set_min_perf(int cpu, u64 min_perf);
 extern int cppc_get_max_perf(int cpu, u64 *max_perf);
 extern int cppc_set_max_perf(int cpu, u64 max_perf);
+extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
+extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
 extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
 extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
 extern int amd_detect_prefcore(bool *detected);
@@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
 {
 	return -EOPNOTSUPP;
 }
+static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
+{
+	return -EOPNOTSUPP;
+}
+static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
+{
+	return -EOPNOTSUPP;
+}
 static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
 {
 	return -ENODEV;
-- 
2.34.1
Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
Posted by Pierre Gondois 3 weeks ago
On 11/5/25 12:38, Sumit Gupta wrote:
> Add sysfs interface to read/write the Performance Limited register.
>
> The Performance Limited register indicates to the OS that an
> unpredictable event (like thermal throttling) has limited processor
> performance. This register is sticky and remains set until reset or
> OS clears it by writing 0.
>
> The interface is exposed as:
>   /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
>
> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
> ---
>   drivers/acpi/cppc_acpi.c       | 26 ++++++++++++++++++++++++++
>   drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
>   include/acpi/cppc_acpi.h       | 10 ++++++++++
>   3 files changed, 48 insertions(+)
>
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index ef53eb8a1feb..9b8da3ef06db 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
>   }
>   EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>   
> +/**
> + * cppc_get_perf_limited - Get the Performance Limited register value.
> + * @cpu: CPU from which to get Performance Limited register.
> + * @perf_limited: Pointer to store the Performance Limited value.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> +	return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
> +
> +/**
> + * cppc_set_perf_limited() - Write the Performance Limited register.
> + * @cpu: CPU on which to write register.
> + * @perf_limited: Value to write to the perf_limited register.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> +	return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}

There are currently only 2 bits used:
- 0 Desired_Excursion
- 1 Minimum_Excursion
It might be worth defining these bits and mask the values when trying to 
set the register.

------

Also NIT:

The spec. says:
" All accesses to the Performance Limited Register must be made using 
interlocked operations, by both accessing entities."

I am not sure I understand which synchronization issues are faced.
It's just to report the comment from the spec.


> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
> +
>   /**
>    * cppc_get_perf - Get a CPU's performance controls.
>    * @cpu: CPU for which to get performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index cde6202e9c51..a425ad575aa6 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -1043,12 +1043,23 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
>   	return count;
>   }
>   
> +static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
> +{
> +	return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf);
> +}
> +
> +static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> +	return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, count);
> +}
> +
>   cpufreq_freq_attr_ro(freqdomain_cpus);
>   cpufreq_freq_attr_rw(auto_select);
>   cpufreq_freq_attr_rw(auto_act_window);
>   cpufreq_freq_attr_rw(energy_performance_preference_val);
>   cpufreq_freq_attr_rw(min_perf);
>   cpufreq_freq_attr_rw(max_perf);
> +cpufreq_freq_attr_rw(perf_limited);
>   
>   static struct freq_attr *cppc_cpufreq_attr[] = {
>   	&freqdomain_cpus,
> @@ -1057,6 +1068,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>   	&energy_performance_preference_val,
>   	&min_perf,
>   	&max_perf,
> +	&perf_limited,
>   	NULL,
>   };
>   
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index be7de1222eee..8baff46f2ac7 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
>   extern int cppc_set_min_perf(int cpu, u64 min_perf);
>   extern int cppc_get_max_perf(int cpu, u64 *max_perf);
>   extern int cppc_set_max_perf(int cpu, u64 max_perf);
> +extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
> +extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
>   extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
>   extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
>   extern int amd_detect_prefcore(bool *detected);
> @@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
>   {
>   	return -EOPNOTSUPP;
>   }
> +static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> +	return -EOPNOTSUPP;
> +}
>   static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
>   {
>   	return -ENODEV;
Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
Posted by Sumit Gupta 1 week, 2 days ago
On 27/11/25 20:24, Pierre Gondois wrote:
> External email: Use caution opening links or attachments
>
>
> On 11/5/25 12:38, Sumit Gupta wrote:
>> Add sysfs interface to read/write the Performance Limited register.
>>
>> The Performance Limited register indicates to the OS that an
>> unpredictable event (like thermal throttling) has limited processor
>> performance. This register is sticky and remains set until reset or
>> OS clears it by writing 0.
>>
>> The interface is exposed as:
>>   /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
>>
>> Signed-off-by: Sumit Gupta<sumitg@nvidia.com>
>> ---
>>   drivers/acpi/cppc_acpi.c       | 26 ++++++++++++++++++++++++++
>>   drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
>>   include/acpi/cppc_acpi.h       | 10 ++++++++++
>>   3 files changed, 48 insertions(+)
>>
>> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
>> index ef53eb8a1feb..9b8da3ef06db 100644
>> --- a/drivers/acpi/cppc_acpi.c
>> +++ b/drivers/acpi/cppc_acpi.c
>> @@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
>>   }
>>   EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>>
>> +/**
>> + * cppc_get_perf_limited - Get the Performance Limited register value.
>> + * @cpu: CPU from which to get Performance Limited register.
>> + * @perf_limited: Pointer to store the Performance Limited value.
>> + *
>> + * Return: 0 for success, -EIO on register access failure, 
>> -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_get_perf_limited(int cpu, u64 *perf_limited)
>> +{
>> +     return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
>> +}
>> +EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
>> +
>> +/**
>> + * cppc_set_perf_limited() - Write the Performance Limited register.
>> + * @cpu: CPU on which to write register.
>> + * @perf_limited: Value to write to the perf_limited register.
>> + *
>> + * Return: 0 for success, -EIO on register access failure, 
>> -EOPNOTSUPP if not supported.
>> + */
>> +int cppc_set_perf_limited(int cpu, u64 perf_limited)
>> +{
>> +     return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
>> +}
>
> There are currently only 2 bits used:
> - 0 Desired_Excursion
> - 1 Minimum_Excursion
> It might be worth defining these bits and mask the values when trying to
> set the register.
>

Will do the change to allow only the clearing of these two bits and not 
setting.


> ------
>
> Also NIT:
>
> The spec. says:
> " All accesses to the Performance Limited Register must be made using
> interlocked operations, by both accessing entities."
>
> I am not sure I understand which synchronization issues are faced.
> It's just to report the comment from the spec.
>

cpc_write() already has locking. So, two writes will complete without 
interference.
As you suggested above, only the required register bit will be cleared 
by an operation.

Thank you,
Sumit Gupta


....


Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
Posted by Ionela Voinescu 1 month ago
Hi,

On Wednesday 05 Nov 2025 at 17:08:41 (+0530), Sumit Gupta wrote:
> Add sysfs interface to read/write the Performance Limited register.
> 
> The Performance Limited register indicates to the OS that an
> unpredictable event (like thermal throttling) has limited processor
> performance. This register is sticky and remains set until reset or
> OS clears it by writing 0.
> 
> The interface is exposed as:
>  /sys/devices/system/cpu/cpuX/cpufreq/perf_limited

What is the intended use of this interface? The performance limited
register has a specific format of status bits with feedback about
performance being limited temporarily and the user can only clear it.

"Contains a resource descriptor with a single Register() descriptor
that describes the register to read to determine if performance was
limited. A nonzero value indicates performance was limited. This
register is sticky, and will remain set until reset or OSPM clears
it by writing 0. See the section “Performance Limiting” for more
details." Also, "The performance limited register should only be used
to report short term, unpredictable events (e.g., PROCHOT being
asserted)."

Therefore, I'm not seeing the value of exposing this via sysfs.

Thanks,
Ionela.

> 
> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
> ---
>  drivers/acpi/cppc_acpi.c       | 26 ++++++++++++++++++++++++++
>  drivers/cpufreq/cppc_cpufreq.c | 12 ++++++++++++
>  include/acpi/cppc_acpi.h       | 10 ++++++++++
>  3 files changed, 48 insertions(+)
> 
> diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
> index ef53eb8a1feb..9b8da3ef06db 100644
> --- a/drivers/acpi/cppc_acpi.c
> +++ b/drivers/acpi/cppc_acpi.c
> @@ -1810,6 +1810,32 @@ int cppc_set_max_perf(int cpu, u64 max_perf)
>  }
>  EXPORT_SYMBOL_GPL(cppc_set_max_perf);
>  
> +/**
> + * cppc_get_perf_limited - Get the Performance Limited register value.
> + * @cpu: CPU from which to get Performance Limited register.
> + * @perf_limited: Pointer to store the Performance Limited value.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> +	return cppc_get_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
> +EXPORT_SYMBOL_GPL(cppc_get_perf_limited);
> +
> +/**
> + * cppc_set_perf_limited() - Write the Performance Limited register.
> + * @cpu: CPU on which to write register.
> + * @perf_limited: Value to write to the perf_limited register.
> + *
> + * Return: 0 for success, -EIO on register access failure, -EOPNOTSUPP if not supported.
> + */
> +int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> +	return cppc_set_reg_val(cpu, PERF_LIMITED, perf_limited);
> +}
> +EXPORT_SYMBOL_GPL(cppc_set_perf_limited);
> +
>  /**
>   * cppc_get_perf - Get a CPU's performance controls.
>   * @cpu: CPU for which to get performance controls.
> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
> index cde6202e9c51..a425ad575aa6 100644
> --- a/drivers/cpufreq/cppc_cpufreq.c
> +++ b/drivers/cpufreq/cppc_cpufreq.c
> @@ -1043,12 +1043,23 @@ static ssize_t store_max_perf(struct cpufreq_policy *policy, const char *buf, si
>  	return count;
>  }
>  
> +static ssize_t show_perf_limited(struct cpufreq_policy *policy, char *buf)
> +{
> +	return cppc_cpufreq_sysfs_show_u64(policy->cpu, cppc_get_perf_limited, buf);
> +}
> +
> +static ssize_t store_perf_limited(struct cpufreq_policy *policy, const char *buf, size_t count)
> +{
> +	return cppc_cpufreq_sysfs_store_u64(policy->cpu, cppc_set_perf_limited, buf, count);
> +}
> +
>  cpufreq_freq_attr_ro(freqdomain_cpus);
>  cpufreq_freq_attr_rw(auto_select);
>  cpufreq_freq_attr_rw(auto_act_window);
>  cpufreq_freq_attr_rw(energy_performance_preference_val);
>  cpufreq_freq_attr_rw(min_perf);
>  cpufreq_freq_attr_rw(max_perf);
> +cpufreq_freq_attr_rw(perf_limited);
>  
>  static struct freq_attr *cppc_cpufreq_attr[] = {
>  	&freqdomain_cpus,
> @@ -1057,6 +1068,7 @@ static struct freq_attr *cppc_cpufreq_attr[] = {
>  	&energy_performance_preference_val,
>  	&min_perf,
>  	&max_perf,
> +	&perf_limited,
>  	NULL,
>  };
>  
> diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
> index be7de1222eee..8baff46f2ac7 100644
> --- a/include/acpi/cppc_acpi.h
> +++ b/include/acpi/cppc_acpi.h
> @@ -177,6 +177,8 @@ extern int cppc_get_min_perf(int cpu, u64 *min_perf);
>  extern int cppc_set_min_perf(int cpu, u64 min_perf);
>  extern int cppc_get_max_perf(int cpu, u64 *max_perf);
>  extern int cppc_set_max_perf(int cpu, u64 max_perf);
> +extern int cppc_get_perf_limited(int cpu, u64 *perf_limited);
> +extern int cppc_set_perf_limited(int cpu, u64 perf_limited);
>  extern int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf);
>  extern int amd_get_boost_ratio_numerator(unsigned int cpu, u64 *numerator);
>  extern int amd_detect_prefcore(bool *detected);
> @@ -285,6 +287,14 @@ static inline int cppc_set_max_perf(int cpu, u64 max_perf)
>  {
>  	return -EOPNOTSUPP;
>  }
> +static inline int cppc_get_perf_limited(int cpu, u64 *perf_limited)
> +{
> +	return -EOPNOTSUPP;
> +}
> +static inline int cppc_set_perf_limited(int cpu, u64 perf_limited)
> +{
> +	return -EOPNOTSUPP;
> +}
>  static inline int amd_get_highest_perf(unsigned int cpu, u32 *highest_perf)
>  {
>  	return -ENODEV;
> -- 
> 2.34.1
> 
Re: [PATCH v4 5/8] ACPI: CPPC: add APIs and sysfs interface for perf_limited register
Posted by Sumit Gupta 1 month ago
On 13/11/25 17:05, Ionela Voinescu wrote:
> External email: Use caution opening links or attachments
>
>
> Hi,
>
> On Wednesday 05 Nov 2025 at 17:08:41 (+0530), Sumit Gupta wrote:
>> Add sysfs interface to read/write the Performance Limited register.
>>
>> The Performance Limited register indicates to the OS that an
>> unpredictable event (like thermal throttling) has limited processor
>> performance. This register is sticky and remains set until reset or
>> OS clears it by writing 0.
>>
>> The interface is exposed as:
>>   /sys/devices/system/cpu/cpuX/cpufreq/perf_limited
> What is the intended use of this interface? The performance limited
> register has a specific format of status bits with feedback about
> performance being limited temporarily and the user can only clear it.
>
> "Contains a resource descriptor with a single Register() descriptor
> that describes the register to read to determine if performance was
> limited. A nonzero value indicates performance was limited. This
> register is sticky, and will remain set until reset or OSPM clears
> it by writing 0. See the section “Performance Limiting” for more
> details." Also, "The performance limited register should only be used
> to report short term, unpredictable events (e.g., PROCHOT being
> asserted)."
>
> Therefore, I'm not seeing the value of exposing this via sysfs.
>
> Thanks,
> Ionela.
>

This enables users to detect if platform throttling impacted a workload.
Users clear the register before execution, run the workload, then check
afterward if set, hardware throttling (thermal/power/current limits)
occurred during that time window. The write operation can be restricted
to only accept 0 (clearing) to prevent writing arbitrary values.

Thank you,
Sumit Gupta

....