[PATCH v2 7/8] x86,fs/resctrl: Add the resource for Global Slow Memory Bandwidth Allocation

Babu Moger
2026-04-24T01:43:01Z

AMD PQoS Global Slow Memory Bandwidth Enforcement (GLSBE) lets software
specify slow-memory (CXL.memory) bandwidth limits for groups of threads
inside a GLBE control domain. A GLBE control domain is a set of QoS (L3)
domains that are grouped together for global bandwidth allocation; GLSBE
reuses those same control domains.

Add the resource definition for GLSBE in the resctrl filesystem so
administrators can configure and manage slow-memory bandwidth at GLSBE
control domain granularity:

The GLSBE control domain is aligned to the system's NPS (Nodes Per Socket)
configuration. Because RDT_RESOURCE_GSMBA uses the RESCTRL_NPS_NODE scope,
updates routed through resctrl_arch_update_domains() already flow into
resctrl_arch_update_nps(), which programs MSR_IA32_GSMBA_BW_BASE on one CPU
per L3 within the domain.

The schemata identifier is named GSMBA (Global Slow Memory Bandwidth
Allocation) to mirror SMBA; the architecturally documented name GLSBE is
used only in CPUID references.

Signed-off-by: Babu Moger <babu.moger@amd.com>
---
v2: Rewrite commit log to describe GSMBA and NPS scope.
---
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/kernel/cpu/resctrl/core.c | 31 ++++++++++++++++++++++++++++++
fs/resctrl/ctrlmondata.c | 4 ++--
fs/resctrl/rdtgroup.c | 16 +++++++++++----
include/linux/resctrl.h | 1 +
5 files changed, 47 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index f3ff11ca03f2..83e042caa080 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -1294,6 +1294,7 @@
#define MSR_IA32_L3_QOS_EXT_CFG 0xc00003ff
#define MSR_IA32_EVT_CFG_BASE 0xc0000400
#define MSR_IA32_GMBA_BW_BASE 0xc0000600
+#define MSR_IA32_GSMBA_BW_BASE 0xc0000680

/* AMD-V MSRs */
#define MSR_VM_CR 0xc0010114
diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c
index 667ff3eb57f5..9a78a59f1929 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -109,6 +109,15 @@ struct rdt_hw_resource rdt_resources_all[RDT_NUM_RESOURCES] = {
.schema_fmt = RESCTRL_SCHEMA_RANGE,
},
},
+ [RDT_RESOURCE_GSMBA] =
+ {
+ .r_resctrl = {
+ .name = "GSMBA",
+ .ctrl_scope = RESCTRL_NPS_NODE,
+ .ctrl_domains = ctrl_domain_init(RDT_RESOURCE_GSMBA),
+ .schema_fmt = RESCTRL_SCHEMA_RANGE,
+ },
+ },
[RDT_RESOURCE_PERF_PKG] =
{
.r_resctrl = {
@@ -261,6 +270,9 @@ static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r)
case RDT_RESOURCE_GMBA:
subleaf = 7;
break;
+ case RDT_RESOURCE_GSMBA:
+ subleaf = 8;
+ break;
default:
return false;
}
@@ -979,6 +991,19 @@ static __init bool get_slow_mem_config(void)
return false;
}

+static __init bool get_gslow_mem_config(void)
+{
+ struct rdt_hw_resource *hw_res = &rdt_resources_all[RDT_RESOURCE_GSMBA];
+
+ if (!rdt_cpu_has(X86_FEATURE_GSMBA))
+ return false;
+
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ return __rdt_get_mem_config_amd(&hw_res->r_resctrl);
+
+ return false;
+}
+
static __init bool get_rdt_alloc_resources(void)
{
struct rdt_resource *r;
@@ -1017,6 +1042,9 @@ static __init bool get_rdt_alloc_resources(void)
if (get_slow_mem_config())
ret = true;

+ if (get_gslow_mem_config())
+ ret = true;
+
return ret;
}

@@ -1120,6 +1148,9 @@ static __init void rdt_init_res_defs_amd(void)
} else if (r->rid == RDT_RESOURCE_SMBA) {
hw_res->msr_base = MSR_IA32_SMBA_BW_BASE;
hw_res->msr_update = mba_wrmsr_amd;
+ } else if (r->rid == RDT_RESOURCE_GSMBA) {
+ hw_res->msr_base = MSR_IA32_GSMBA_BW_BASE;
+ hw_res->msr_update = mba_wrmsr_amd;
}
}
}
diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c
index 22bbc5fa520d..217055aaf5c7 100644
--- a/fs/resctrl/ctrlmondata.c
+++ b/fs/resctrl/ctrlmondata.c
@@ -247,8 +247,8 @@ static int parse_line(char *line, struct resctrl_schema *s,

if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
(r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_GMBA ||
- r->rid == RDT_RESOURCE_SMBA)) {
- rdt_last_cmd_puts("Cannot pseudo-lock MBA/SMBA/GMBA resource\n");
+ r->rid == RDT_RESOURCE_SMBA || r->rid == RDT_RESOURCE_GSMBA)) {
+ rdt_last_cmd_puts("Cannot pseudo-lock MBA/SMBA/GMBA/GSMBA resource\n");
return -EINVAL;
}

diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index cc14c04314fe..761cc9cff4db 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -1413,7 +1413,7 @@ static bool rdtgroup_mode_test_exclusive(struct rdtgroup *rdtgrp)
list_for_each_entry(s, &resctrl_schema_all, list) {
r = s->res;
if (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_GMBA ||
- r->rid == RDT_RESOURCE_SMBA)
+ r->rid == RDT_RESOURCE_SMBA || r->rid == RDT_RESOURCE_GSMBA)
continue;
has_cache = true;
list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
@@ -1619,7 +1619,8 @@ static int rdtgroup_size_show(struct kernfs_open_file *of,
type);
if (r->rid == RDT_RESOURCE_MBA ||
r->rid == RDT_RESOURCE_GMBA ||
- r->rid == RDT_RESOURCE_SMBA)
+ r->rid == RDT_RESOURCE_SMBA ||
+ r->rid == RDT_RESOURCE_GSMBA)
size = ctrl;
else
size = rdtgroup_cbm_to_size(r, d, ctrl);
@@ -2172,7 +2173,7 @@ static struct rftype *rdtgroup_get_rftype_by_name(const char *name)
static void thread_throttle_mode_init(void)
{
enum membw_throttle_mode throttle_mode = THREAD_THROTTLE_UNDEFINED;
- struct rdt_resource *r_mba, *r_gmba, *r_smba;
+ struct rdt_resource *r_mba, *r_gmba, *r_smba, *r_gsmba;

r_mba = resctrl_arch_get_resource(RDT_RESOURCE_MBA);
if (r_mba->alloc_capable &&
@@ -2189,6 +2190,11 @@ static void thread_throttle_mode_init(void)
r_smba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
throttle_mode = r_smba->membw.throttle_mode;

+ r_gsmba = resctrl_arch_get_resource(RDT_RESOURCE_GSMBA);
+ if (r_gsmba->alloc_capable &&
+ r_gsmba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
+ throttle_mode = r_gsmba->membw.throttle_mode;
+
if (throttle_mode == THREAD_THROTTLE_UNDEFINED)
return;

@@ -2405,6 +2411,7 @@ static unsigned long fflags_from_resource(struct rdt_resource *r)
case RDT_RESOURCE_MBA:
case RDT_RESOURCE_GMBA:
case RDT_RESOURCE_SMBA:
+ case RDT_RESOURCE_GSMBA:
return RFTYPE_RES_MB;
case RDT_RESOURCE_PERF_PKG:
return RFTYPE_RES_PERF_PKG;
@@ -3658,7 +3665,8 @@ static int rdtgroup_init_alloc(struct rdtgroup *rdtgrp)
r = s->res;
if (r->rid == RDT_RESOURCE_MBA ||
r->rid == RDT_RESOURCE_GMBA ||
- r->rid == RDT_RESOURCE_SMBA) {
+ r->rid == RDT_RESOURCE_SMBA ||
+ r->rid == RDT_RESOURCE_GSMBA) {
rdtgroup_init_mba(r, rdtgrp->closid);
if (is_mba_sc(r))
continue;
diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index ed09ed2e0477..cd374f0d31db 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -54,6 +54,7 @@ enum resctrl_res_level {
RDT_RESOURCE_MBA,
RDT_RESOURCE_GMBA,
RDT_RESOURCE_SMBA,
+ RDT_RESOURCE_GSMBA,
RDT_RESOURCE_PERF_PKG,

/* Must be the last */
--
2.43.0