[RFC PATCH 05/38] arm64: mpam: Add helpers to change a task or cpu's MPAM PARTID/PMG values

James Morse posted 38 patches 2 months ago
There is a newer version of this series
[RFC PATCH 05/38] arm64: mpam: Add helpers to change a task or cpu's MPAM PARTID/PMG values
Posted by James Morse 2 months ago
Care must be taken when modifying the PARTID and PMG of a task in any
per-task structure as writing these values may race with the task being
scheduled in, and reading the modified values.

Add helpers to set the task properties, and the CPU default value.
These use WRITE_ONCE() that pairs with the READ_ONCE() in mpam_get_regval()
to avoid causing torn values.

CC: Dave Martin <Dave.Martin@arm.com>
CC: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: James Morse <james.morse@arm.com>
---
 arch/arm64/include/asm/mpam.h | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/arm64/include/asm/mpam.h b/arch/arm64/include/asm/mpam.h
index 86a55176f884..2960ffaf6574 100644
--- a/arch/arm64/include/asm/mpam.h
+++ b/arch/arm64/include/asm/mpam.h
@@ -5,6 +5,7 @@
 #define __ASM__MPAM_H
 
 #include <linux/bitops.h>
+#include <linux/bitfield.h>
 #include <linux/init.h>
 #include <linux/jump_label.h>
 #include <linux/percpu.h>
@@ -37,6 +38,35 @@ extern u64 arm64_mpam_global_default;
  * A value in struct thread_info is used instead of struct task_struct as the
  * cpu's u64 register format is used, but struct task_struct has two u32'.
  */
+static inline void mpam_set_cpu_defaults(int cpu, u16 partid_d, u16 partid_i,
+					 u8 pmg_d, u8 pmg_i)
+{
+	u64 default_val;
+
+	default_val = FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d);
+	default_val |= FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i);
+	default_val |= FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d);
+	default_val |= FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);
+
+	WRITE_ONCE(per_cpu(arm64_mpam_default, cpu), default_val);
+}
+
+static inline void mpam_set_task_partid_pmg(struct task_struct *tsk,
+					    u16 partid_d, u16 partid_i,
+					    u8 pmg_d, u8 pmg_i)
+{
+#ifdef CONFIG_ARM64_MPAM
+	u64 regval;
+
+	regval = FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d);
+	regval |= FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i);
+	regval |= FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d);
+	regval |= FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);
+
+	WRITE_ONCE(task_thread_info(tsk)->mpam_partid_pmg, regval);
+#endif
+}
+
 static inline u64 mpam_get_regval(struct task_struct *tsk)
 {
 #ifdef CONFIG_ARM64_MPAM
-- 
2.39.5
Re: [RFC PATCH 05/38] arm64: mpam: Add helpers to change a task or cpu's MPAM PARTID/PMG values
Posted by Jonathan Cameron 1 month, 3 weeks ago
On Fri, 5 Dec 2025 21:58:28 +0000
James Morse <james.morse@arm.com> wrote:

> Care must be taken when modifying the PARTID and PMG of a task in any
> per-task structure as writing these values may race with the task being
> scheduled in, and reading the modified values.
> 
> Add helpers to set the task properties, and the CPU default value.
> These use WRITE_ONCE() that pairs with the READ_ONCE() in mpam_get_regval()
> to avoid causing torn values.
> 
> CC: Dave Martin <Dave.Martin@arm.com>
> CC: Ben Horgan <ben.horgan@arm.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
>  arch/arm64/include/asm/mpam.h | 30 ++++++++++++++++++++++++++++++
>  1 file changed, 30 insertions(+)
> 
> diff --git a/arch/arm64/include/asm/mpam.h b/arch/arm64/include/asm/mpam.h
> index 86a55176f884..2960ffaf6574 100644
> --- a/arch/arm64/include/asm/mpam.h
> +++ b/arch/arm64/include/asm/mpam.h
> @@ -5,6 +5,7 @@
>  #define __ASM__MPAM_H
>  
>  #include <linux/bitops.h>
> +#include <linux/bitfield.h>
>  #include <linux/init.h>
>  #include <linux/jump_label.h>
>  #include <linux/percpu.h>
> @@ -37,6 +38,35 @@ extern u64 arm64_mpam_global_default;
>   * A value in struct thread_info is used instead of struct task_struct as the
>   * cpu's u64 register format is used, but struct task_struct has two u32'.
>   */
I'd be tempted to reorder this so the comment ends up near at least
one use of task_thread_info(tsk)->mpam_partid_pmg. Otherwise it is even
less obvious what it is referring to.

Easiest might be to put the mpam_get_regval() before the mpam_set_task_partid_pmg()


> +static inline void mpam_set_cpu_defaults(int cpu, u16 partid_d, u16 partid_i,
> +					 u8 pmg_d, u8 pmg_i)
> +{
> +	u64 default_val;
> +
> +	default_val = FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d);
> +	default_val |= FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i);
> +	default_val |= FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d);
> +	default_val |= FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);
> +
> +	WRITE_ONCE(per_cpu(arm64_mpam_default, cpu), default_val);
> +}
> +
> +static inline void mpam_set_task_partid_pmg(struct task_struct *tsk,
> +					    u16 partid_d, u16 partid_i,
> +					    u8 pmg_d, u8 pmg_i)
> +{
> +#ifdef CONFIG_ARM64_MPAM
> +	u64 regval;
> +
> +	regval = FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d);
> +	regval |= FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i);
> +	regval |= FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d);
> +	regval |= FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);

Maybe a macro or helper to build regval given this replicates the block in
mpam_set_cpu_defaults.  Obviously ignore if this changes in later patches!

> +
> +	WRITE_ONCE(task_thread_info(tsk)->mpam_partid_pmg, regval);
> +#endif
> +}
> +
>  static inline u64 mpam_get_regval(struct task_struct *tsk)
>  {
>  #ifdef CONFIG_ARM64_MPAM
Re: [RFC PATCH 05/38] arm64: mpam: Add helpers to change a task or cpu's MPAM PARTID/PMG values
Posted by Ben Horgan 1 month, 3 weeks ago
Hi Jonathan,

On 12/18/25 10:44, Jonathan Cameron wrote:
> On Fri, 5 Dec 2025 21:58:28 +0000
> James Morse <james.morse@arm.com> wrote:
> 
>> Care must be taken when modifying the PARTID and PMG of a task in any
>> per-task structure as writing these values may race with the task being
>> scheduled in, and reading the modified values.
>>
>> Add helpers to set the task properties, and the CPU default value.
>> These use WRITE_ONCE() that pairs with the READ_ONCE() in mpam_get_regval()
>> to avoid causing torn values.
>>
>> CC: Dave Martin <Dave.Martin@arm.com>
>> CC: Ben Horgan <ben.horgan@arm.com>
>> Signed-off-by: James Morse <james.morse@arm.com>
>> ---
>>  arch/arm64/include/asm/mpam.h | 30 ++++++++++++++++++++++++++++++
>>  1 file changed, 30 insertions(+)
>>
>> diff --git a/arch/arm64/include/asm/mpam.h b/arch/arm64/include/asm/mpam.h
>> index 86a55176f884..2960ffaf6574 100644
>> --- a/arch/arm64/include/asm/mpam.h
>> +++ b/arch/arm64/include/asm/mpam.h
>> @@ -5,6 +5,7 @@
>>  #define __ASM__MPAM_H
>>  
>>  #include <linux/bitops.h>
>> +#include <linux/bitfield.h>
>>  #include <linux/init.h>
>>  #include <linux/jump_label.h>
>>  #include <linux/percpu.h>
>> @@ -37,6 +38,35 @@ extern u64 arm64_mpam_global_default;
>>   * A value in struct thread_info is used instead of struct task_struct as the
>>   * cpu's u64 register format is used, but struct task_struct has two u32'.
>>   */
> I'd be tempted to reorder this so the comment ends up near at least
> one use of task_thread_info(tsk)->mpam_partid_pmg. Otherwise it is even
> less obvious what it is referring to.
> 
> Easiest might be to put the mpam_get_regval() before the mpam_set_task_partid_pmg()

Yes, this comment positioning was confusing.

> 
> 
>> +static inline void mpam_set_cpu_defaults(int cpu, u16 partid_d, u16 partid_i,
>> +					 u8 pmg_d, u8 pmg_i)
>> +{
>> +	u64 default_val;
>> +
>> +	default_val = FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d);
>> +	default_val |= FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i);
>> +	default_val |= FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d);
>> +	default_val |= FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);
>> +
>> +	WRITE_ONCE(per_cpu(arm64_mpam_default, cpu), default_val);
>> +}
>> +
>> +static inline void mpam_set_task_partid_pmg(struct task_struct *tsk,
>> +					    u16 partid_d, u16 partid_i,
>> +					    u8 pmg_d, u8 pmg_i)
>> +{
>> +#ifdef CONFIG_ARM64_MPAM
>> +	u64 regval;
>> +
>> +	regval = FIELD_PREP(MPAM0_EL1_PARTID_D, partid_d);
>> +	regval |= FIELD_PREP(MPAM0_EL1_PARTID_I, partid_i);
>> +	regval |= FIELD_PREP(MPAM0_EL1_PMG_D, pmg_d);
>> +	regval |= FIELD_PREP(MPAM0_EL1_PMG_I, pmg_i);
> 
> Maybe a macro or helper to build regval given this replicates the block in
> mpam_set_cpu_defaults.  Obviously ignore if this changes in later patches!

I've added a helper.

> 
>> +
>> +	WRITE_ONCE(task_thread_info(tsk)->mpam_partid_pmg, regval);
>> +#endif
>> +}
>> +
>>  static inline u64 mpam_get_regval(struct task_struct *tsk)
>>  {
>>  #ifdef CONFIG_ARM64_MPAM
> 

Thanks,

Ben