[RFC PATCH 16/19] fs/resctrl: Implement rdtgroup_plza_write() to configure PLZA in a group

Babu Moger posted 19 patches 2 weeks, 3 days ago
[RFC PATCH 16/19] fs/resctrl: Implement rdtgroup_plza_write() to configure PLZA in a group
Posted by Babu Moger 2 weeks, 3 days ago
Introduce rdtgroup_plza_write() group which enables per group control of
PLZA through the resctrl filesystem and ensure that enabling or disabling
PLZA is propagated consistently across all CPUs belonging to the group.

Enforce the capability checks, exclude default, pseudo-locked and CTRL_MON
groups with sub monitors. Also, ensure that only one group can have PLZA
enabled at a time.

Signed-off-by: Babu Moger <babu.moger@amd.com>
---
 Documentation/filesystems/resctrl.rst |  5 ++
 fs/resctrl/rdtgroup.c                 | 88 ++++++++++++++++++++++++++-
 2 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/Documentation/filesystems/resctrl.rst b/Documentation/filesystems/resctrl.rst
index 1de55b5cb0e3..8edcc047ffe5 100644
--- a/Documentation/filesystems/resctrl.rst
+++ b/Documentation/filesystems/resctrl.rst
@@ -626,6 +626,11 @@ When control is enabled all CTRL_MON groups will also contain:
 	Available only with debug option. The identifier used by hardware
 	for the control group. On x86 this is the CLOSID.
 
+"plza":
+        When enabled, CPUs or tasks in the resctrl group follow the group's
+        limits while running at Privilege Level 0 (CPL-0). This can only be
+        enabled for CTRL_MON groups.
+
 When monitoring is enabled all MON groups will also contain:
 
 "mon_data":
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index d467b52a0c74..042ae7d63aea 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -896,6 +896,76 @@ static int rdtgroup_plza_show(struct kernfs_open_file *of,
 	return ret;
 }
 
+static ssize_t rdtgroup_plza_write(struct kernfs_open_file *of, char *buf,
+				   size_t nbytes, loff_t off)
+{
+	struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
+	struct rdtgroup *rdtgrp, *prgrp;
+	int cpu, ret = 0;
+	bool enable;
+
+	ret = kstrtobool(buf, &enable);
+	if (ret)
+		return ret;
+
+	rdtgrp = rdtgroup_kn_lock_live(of->kn);
+	if (!rdtgrp) {
+		rdtgroup_kn_unlock(of->kn);
+		return -ENOENT;
+	}
+
+	rdt_last_cmd_clear();
+
+	if (!r->plza_capable) {
+		rdt_last_cmd_puts("PLZA is not supported in the system\n");
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (rdtgrp == &rdtgroup_default) {
+		rdt_last_cmd_puts("Cannot set PLZA on a default group\n");
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
+		rdt_last_cmd_puts("Resource group is pseudo-locked\n");
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	if (!list_empty(&rdtgrp->mon.crdtgrp_list)) {
+		rdt_last_cmd_puts("Cannot change CTRL_MON group with sub monitor groups\n");
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	list_for_each_entry(prgrp, &rdt_all_groups, rdtgroup_list) {
+		if (prgrp == rdtgrp)
+			continue;
+		if (enable && prgrp->plza) {
+			rdt_last_cmd_puts("PLZA is already configured on another group\n");
+			ret = -EINVAL;
+			goto unlock;
+		}
+	}
+
+	/* Enable or disable PLZA state and update per CPU state if there is a change */
+	if (enable != rdtgrp->plza) {
+		resctrl_arch_plza_setup(r, rdtgrp->closid, rdtgrp->mon.rmid);
+
+		for_each_cpu(cpu, &rdtgrp->cpu_mask)
+			resctrl_arch_set_cpu_plza(cpu, rdtgrp->closid,
+						  rdtgrp->mon.rmid, enable);
+		rdtgrp->plza = enable;
+	}
+
+unlock:
+	rdtgroup_kn_unlock(of->kn);
+
+	return ret ?: nbytes;
+}
+
 #ifdef CONFIG_PROC_CPU_RESCTRL
 /*
  * A task can only be part of one resctrl control group and of one monitor
@@ -2171,8 +2241,9 @@ static struct rftype res_common_files[] = {
 	},
 	{
 		.name		= "plza",
-		.mode		= 0444,
+		.mode		= 0644,
 		.kf_ops		= &rdtgroup_kf_single_ops,
+		.write		= rdtgroup_plza_write,
 		.seq_show	= rdtgroup_plza_show,
 	},
 };
@@ -3126,11 +3197,19 @@ static void free_all_child_rdtgrp(struct rdtgroup *rdtgrp)
 static void rmdir_all_sub(void)
 {
 	struct rdtgroup *rdtgrp, *tmp;
+	int cpu;
 
 	/* Move all tasks to the default resource group */
 	rdt_move_group_tasks(NULL, &rdtgroup_default, NULL);
 
 	list_for_each_entry_safe(rdtgrp, tmp, &rdt_all_groups, rdtgroup_list) {
+		if (rdtgrp->plza) {
+			for_each_cpu(cpu, &rdtgrp->cpu_mask)
+				resctrl_arch_set_cpu_plza(cpu, rdtgrp->closid,
+							  rdtgrp->mon.rmid, false);
+			rdtgrp->plza = 0;
+		}
+
 		/* Free any child rmids */
 		free_all_child_rdtgrp(rdtgrp);
 
@@ -4090,6 +4169,13 @@ static int rdtgroup_rmdir_ctrl(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask)
 	u32 closid, rmid;
 	int cpu;
 
+	if (rdtgrp->plza) {
+		for_each_cpu(cpu, &rdtgrp->cpu_mask)
+			resctrl_arch_set_cpu_plza(cpu, rdtgrp->closid,
+						  rdtgrp->mon.rmid, false);
+		rdtgrp->plza = 0;
+	}
+
 	/* Give any tasks back to the default group */
 	rdt_move_group_tasks(rdtgrp, &rdtgroup_default, tmpmask);
 
-- 
2.34.1
Re: [RFC PATCH 16/19] fs/resctrl: Implement rdtgroup_plza_write() to configure PLZA in a group
Posted by Luck, Tony 1 week, 3 days ago
On Wed, Jan 21, 2026 at 03:12:54PM -0600, Babu Moger wrote:
> Introduce rdtgroup_plza_write() group which enables per group control of
> PLZA through the resctrl filesystem and ensure that enabling or disabling
> PLZA is propagated consistently across all CPUs belonging to the group.
> 
> Enforce the capability checks, exclude default, pseudo-locked and CTRL_MON
> groups with sub monitors. Also, ensure that only one group can have PLZA
> enabled at a time.
> 
...

> +static ssize_t rdtgroup_plza_write(struct kernfs_open_file *of, char *buf,
> +				   size_t nbytes, loff_t off)
> +{
> +	struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
> +	struct rdtgroup *rdtgrp, *prgrp;
> +	int cpu, ret = 0;
> +	bool enable;

...

> +	/* Enable or disable PLZA state and update per CPU state if there is a change */
> +	if (enable != rdtgrp->plza) {
> +		resctrl_arch_plza_setup(r, rdtgrp->closid, rdtgrp->mon.rmid);

What is this for? If I've just created a group with no tasks, and empty
CPU mask ... it seems that this writes the MSR_IA32_PQR_PLZA_ASSOC on
every CPU in every domain.

> +		for_each_cpu(cpu, &rdtgrp->cpu_mask)
> +			resctrl_arch_set_cpu_plza(cpu, rdtgrp->closid,
> +						  rdtgrp->mon.rmid, enable);
> +		rdtgrp->plza = enable;
> +	}
> +
> +unlock:
> +	rdtgroup_kn_unlock(of->kn);
> +
> +	return ret ?: nbytes;
> +}

It also appears that marking a task as PLZA is permanent. Moving it to
another group doesn't unmark it. Is this intentional?

# mkdir group1 group2 plza_group
# echo 1 > plza_group/plza
# echo $$ > group1/tasks
# echo $$ > plza_group/tasks

My shell is now in group1 and in the plza_group
# grep $$ */tasks
group1/tasks:4125
plza_group/tasks:4125

Move shell to group2
# echo $$ > group2/tasks
# grep $$ */tasks
group2/tasks:4125
plza_group/tasks:4125

Succcess in moving to group2, but still in plza_group

-Tony

N.B. I don't have a PLZA enabled system. So I faked it with this
patch.

From 1655fea0049947218fa5400916d57109be8521ef Mon Sep 17 00:00:00 2001
From: Tony Luck <tony.luck@intel.com>
Date: Wed, 28 Jan 2026 13:02:51 -0800
Subject: [PATCH] fake PLZA

---
 arch/x86/include/asm/resctrl.h            | 10 ++++++----
 arch/x86/kernel/cpu/resctrl/core.c        |  4 ++--
 arch/x86/kernel/cpu/resctrl/ctrlmondata.c |  3 ++-
 3 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/resctrl.h b/arch/x86/include/asm/resctrl.h
index 2c11787c5253..7ee35bebb64c 100644
--- a/arch/x86/include/asm/resctrl.h
+++ b/arch/x86/include/asm/resctrl.h
@@ -90,14 +90,16 @@ static inline void resctrl_arch_disable_mon(void)
 
 static inline void resctrl_arch_enable_plza(void)
 {
-	static_branch_enable_cpuslocked(&rdt_plza_enable_key);
-	static_branch_inc_cpuslocked(&rdt_enable_key);
+	pr_info("resctrl_arch_enable_plza\n");
+	//static_branch_enable_cpuslocked(&rdt_plza_enable_key);
+	//static_branch_inc_cpuslocked(&rdt_enable_key);
 }
 
 static inline void resctrl_arch_disable_plza(void)
 {
-	static_branch_disable_cpuslocked(&rdt_plza_enable_key);
-	static_branch_dec_cpuslocked(&rdt_enable_key);
+	pr_info("resctrl_arch_disable_plza\n");
+	//static_branch_disable_cpuslocked(&rdt_plza_enable_key);
+	//static_branch_dec_cpuslocked(&rdt_enable_key);
 }
 
 /*
diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c
index e41fe5fa3f30..780cdfb0e7cd 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -295,7 +295,7 @@ static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r)
 
 	r->alloc_capable = true;
 
-	if (rdt_cpu_has(X86_FEATURE_PLZA))
+	if (1 || rdt_cpu_has(X86_FEATURE_PLZA))
 		r->plza_capable = true;
 
 	return true;
@@ -318,7 +318,7 @@ static void rdt_get_cache_alloc_cfg(int idx, struct rdt_resource *r)
 		r->cache.arch_has_sparse_bitmasks = ecx.split.noncont;
 	r->alloc_capable = true;
 
-	if (rdt_cpu_has(X86_FEATURE_PLZA))
+	if (1 || rdt_cpu_has(X86_FEATURE_PLZA))
 		r->plza_capable = true;
 }
 
diff --git a/arch/x86/kernel/cpu/resctrl/ctrlmondata.c b/arch/x86/kernel/cpu/resctrl/ctrlmondata.c
index 79ed41bde810..24a37ebed13a 100644
--- a/arch/x86/kernel/cpu/resctrl/ctrlmondata.c
+++ b/arch/x86/kernel/cpu/resctrl/ctrlmondata.c
@@ -136,7 +136,8 @@ static void resctrl_plza_set_one_amd(void *arg)
 {
 	union qos_pqr_plza_assoc *plza = arg;
 
-	wrmsrl(MSR_IA32_PQR_PLZA_ASSOC, plza->full);
+	pr_info("wrmsr(MSR_IA32_PQR_PLZA_ASSOC, 0x%lx)\n", plza->full);
+	//wrmsrl(MSR_IA32_PQR_PLZA_ASSOC, plza->full);
 }
 
 void resctrl_arch_plza_setup(struct rdt_resource *r, u32 closid, u32 rmid)
-- 
2.52.0
Re: [RFC PATCH 16/19] fs/resctrl: Implement rdtgroup_plza_write() to configure PLZA in a group
Posted by Babu Moger 1 week, 2 days ago
Hi Tony,

On 1/28/26 16:03, Luck, Tony wrote:
> On Wed, Jan 21, 2026 at 03:12:54PM -0600, Babu Moger wrote:
>> Introduce rdtgroup_plza_write() group which enables per group control of
>> PLZA through the resctrl filesystem and ensure that enabling or disabling
>> PLZA is propagated consistently across all CPUs belonging to the group.
>>
>> Enforce the capability checks, exclude default, pseudo-locked and CTRL_MON
>> groups with sub monitors. Also, ensure that only one group can have PLZA
>> enabled at a time.
>>
> ...
>
>> +static ssize_t rdtgroup_plza_write(struct kernfs_open_file *of, char *buf,
>> +				   size_t nbytes, loff_t off)
>> +{
>> +	struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
>> +	struct rdtgroup *rdtgrp, *prgrp;
>> +	int cpu, ret = 0;
>> +	bool enable;
> ...
>
>> +	/* Enable or disable PLZA state and update per CPU state if there is a change */
>> +	if (enable != rdtgrp->plza) {
>> +		resctrl_arch_plza_setup(r, rdtgrp->closid, rdtgrp->mon.rmid);
> What is this for? If I've just created a group with no tasks, and empty
> CPU mask ... it seems that this writes the MSR_IA32_PQR_PLZA_ASSOC on
> every CPU in every domain.
>
>> +		for_each_cpu(cpu, &rdtgrp->cpu_mask)
>> +			resctrl_arch_set_cpu_plza(cpu, rdtgrp->closid,
>> +						  rdtgrp->mon.rmid, enable);
>> +		rdtgrp->plza = enable;
>> +	}
>> +
>> +unlock:
>> +	rdtgroup_kn_unlock(of->kn);
>> +
>> +	return ret ?: nbytes;
>> +}
> It also appears that marking a task as PLZA is permanent. Moving it to
> another group doesn't unmark it. Is this intentional?
>
> # mkdir group1 group2 plza_group
> # echo 1 > plza_group/plza
> # echo $$ > group1/tasks
> # echo $$ > plza_group/tasks
>
> My shell is now in group1 and in the plza_group
> # grep $$ */tasks
> group1/tasks:4125
> plza_group/tasks:4125
>
> Move shell to group2
> # echo $$ > group2/tasks
> # grep $$ */tasks
> group2/tasks:4125
> plza_group/tasks:4125
>
> Succcess in moving to group2, but still in plza_group

You are moving the task from group1 to group2. This basically changes 
the association in

MSR_IA32_PQR_ASSOC register,  It does not change the PLZA association.

To change it:

a. You either remove task from plza group which triggers task update (tsk->plza = 0)
    
    echo >> /sys/fs/resctrl/plza_group/tasks

b. Or you can change the group as regular group.

   echo 0 > /sys/fs/resctrl/plza_group/plza


Thanks for the trying it out.

- Babu


Re: [RFC PATCH 16/19] fs/resctrl: Implement rdtgroup_plza_write() to configure PLZA in a group
Posted by Luck, Tony 1 week, 2 days ago
On Wed, Jan 28, 2026 at 02:03:31PM -0800, Luck, Tony wrote:
> On Wed, Jan 21, 2026 at 03:12:54PM -0600, Babu Moger wrote:
> > Introduce rdtgroup_plza_write() group which enables per group control of
> > PLZA through the resctrl filesystem and ensure that enabling or disabling
> > PLZA is propagated consistently across all CPUs belonging to the group.
> > 
> > Enforce the capability checks, exclude default, pseudo-locked and CTRL_MON
> > groups with sub monitors. Also, ensure that only one group can have PLZA
> > enabled at a time.
> > 
> ...
> 
> > +static ssize_t rdtgroup_plza_write(struct kernfs_open_file *of, char *buf,
> > +				   size_t nbytes, loff_t off)
> > +{
> > +	struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
> > +	struct rdtgroup *rdtgrp, *prgrp;
> > +	int cpu, ret = 0;
> > +	bool enable;
> 
> ...
> 
> > +	/* Enable or disable PLZA state and update per CPU state if there is a change */
> > +	if (enable != rdtgrp->plza) {
> > +		resctrl_arch_plza_setup(r, rdtgrp->closid, rdtgrp->mon.rmid);
> 
> What is this for? If I've just created a group with no tasks, and empty
> CPU mask ... it seems that this writes the MSR_IA32_PQR_PLZA_ASSOC on
> every CPU in every domain.

I think I see now. There are THREE enable bits in your
MSR_IA32_PQR_PLZA_ASSOC.
One each for CLOSID and RMID, and an overall PLZA_EN in the high bit.

At this step you setup the CLOSID/RMID with their enable bits, but
leaving the PLZA_EN off.

Is this a subtle optimzation for the context switch? Is the WRMSR
faster if it only toggle PLZA_EN leaving all the other bits unchanged?

This might not be working as expected. The context switch code does:

		wrmsr(MSR_IA32_PQR_PLZA_ASSOC,
		      RMID_EN | state->plza_rmid,
		      (plza ? PLZA_EN : 0) | CLOSID_EN | state->plza_closid);

This doesn't just clear the PLZA_EN bit, it zeroes the high dword of the MSR.

> It also appears that marking a task as PLZA is permanent. Moving it to
> another group doesn't unmark it. Is this intentional?

Ditto assigning a CPU to the PLZA group. Once done it can't be undone
(except by turing off PLZA?).

-Tony

[More comments about this coming against patch 16]
Re: [RFC PATCH 16/19] fs/resctrl: Implement rdtgroup_plza_write() to configure PLZA in a group
Posted by Babu Moger 1 week, 2 days ago
Hi Tony,


On 1/29/26 12:54, Luck, Tony wrote:
> On Wed, Jan 28, 2026 at 02:03:31PM -0800, Luck, Tony wrote:
>> On Wed, Jan 21, 2026 at 03:12:54PM -0600, Babu Moger wrote:
>>> Introduce rdtgroup_plza_write() group which enables per group control of
>>> PLZA through the resctrl filesystem and ensure that enabling or disabling
>>> PLZA is propagated consistently across all CPUs belonging to the group.
>>>
>>> Enforce the capability checks, exclude default, pseudo-locked and CTRL_MON
>>> groups with sub monitors. Also, ensure that only one group can have PLZA
>>> enabled at a time.
>>>
>> ...
>>
>>> +static ssize_t rdtgroup_plza_write(struct kernfs_open_file *of, char *buf,
>>> +				   size_t nbytes, loff_t off)
>>> +{
>>> +	struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
>>> +	struct rdtgroup *rdtgrp, *prgrp;
>>> +	int cpu, ret = 0;
>>> +	bool enable;
>> ...
>>
>>> +	/* Enable or disable PLZA state and update per CPU state if there is a change */
>>> +	if (enable != rdtgrp->plza) {
>>> +		resctrl_arch_plza_setup(r, rdtgrp->closid, rdtgrp->mon.rmid);
>> What is this for? If I've just created a group with no tasks, and empty
>> CPU mask ... it seems that this writes the MSR_IA32_PQR_PLZA_ASSOC on
>> every CPU in every domain.

Here is the reason.

Some fields of PQR_PLZA_ASSOC must be set to the same value for all HW 
threads in the QOS domain for consistent operation (Per-QosDomain).

  The user should use the following sequence to set these values to a 
consistent state.

 1.

    Set PQR_PLZA_ASSOC[PLZA_EN]=0 for all HW threads in the QOS Domain

 2.

    Set the COS_EN, COS, RMID_EN, and RMID fields of PQR_PLZA_ASSOC to
    the desired configuration on all HW threads in the QOS Domain

 3.

    Set PQR_PLZA_ASSOC[PLZA_EN]=1 for all HW threads in the QOS Domain
    where PLZA should be enabled.

      *

        The user should perform this as a read-modify-write to avoid
        changing the value of COS_EN, COS, RMID_EN, and RMID fields of
        PQR_PLZA_ASSOC.


Basically, we have to set all the fields to consistent state to setup 
the PLZA first.   Then setup PLZA_EN bit on each thread based on current 
association.

> I think I see now. There are THREE enable bits in your
> MSR_IA32_PQR_PLZA_ASSOC.
> One each for CLOSID and RMID, and an overall PLZA_EN in the high bit.
>
> At this step you setup the CLOSID/RMID with their enable bits, but
> leaving the PLZA_EN off.
>
> Is this a subtle optimzation for the context switch? Is the WRMSR
> faster if it only toggle PLZA_EN leaving all the other bits unchanged?


I really did not think of optimization here. Mostly followed the spec.


>
> This might not be working as expected. The context switch code does:
>
> 		wrmsr(MSR_IA32_PQR_PLZA_ASSOC,
> 		      RMID_EN | state->plza_rmid,
> 		      (plza ? PLZA_EN : 0) | CLOSID_EN | state->plza_closid);
>
> This doesn't just clear the PLZA_EN bit, it zeroes the high dword of the MSR.
>
>> It also appears that marking a task as PLZA is permanent. Moving it to
>> another group doesn't unmark it. Is this intentional?
> Ditto assigning a CPU to the PLZA group. Once done it can't be undone
> (except by turing off PLZA?).
>
> -Tony
>
> [More comments about this coming against patch 16]
>