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

Babu Moger
2026-04-24T01:42:31Z

AMD PQoS Global Bandwidth Enforcement (GLBE) feature provides a mechanism
for software to specify bandwidth limits for groups of threads inside a
GLBE control domain. GLBE control domain is a set of participating QoS
domains that are grouped together for global bandwidth allocation.

Add the resource definition for GLBE in resctrl filesystem. Resource allows
users to configure and manage the global memory bandwidth allocation
settings for GLBE control domain. GLBE control domain is a set of
participating QoS domains that are grouped together for global bandwidth
allocation.

By default the GLBE control domain is aligned to system NPS (Nodes Per
Socket) configuration. However, MSR_IA32_GMBA_BW_BASE must be programmed
in L3 domains.

Signed-off-by: Babu Moger <babu.moger@amd.com>
---
v2: Updated the change log about NPS scope.
---
arch/x86/include/asm/msr-index.h | 1 +
arch/x86/kernel/cpu/resctrl/core.c | 46 ++++++++++++++++++++++++++++--
fs/resctrl/ctrlmondata.c | 5 ++--
fs/resctrl/rdtgroup.c | 13 +++++++--
include/linux/resctrl.h | 1 +
5 files changed, 59 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index a14a0f43e04a..f3ff11ca03f2 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -1293,6 +1293,7 @@
#define MSR_IA32_L3_QOS_ABMC_CFG 0xc00003fd
#define MSR_IA32_L3_QOS_EXT_CFG 0xc00003ff
#define MSR_IA32_EVT_CFG_BASE 0xc0000400
+#define MSR_IA32_GMBA_BW_BASE 0xc0000600

/* 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 0f58f5e3b853..22114ff84bfa 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -91,6 +91,15 @@ struct rdt_hw_resource rdt_resources_all[RDT_NUM_RESOURCES] = {
.schema_fmt = RESCTRL_SCHEMA_RANGE,
},
},
+ [RDT_RESOURCE_GMBA] =
+ {
+ .r_resctrl = {
+ .name = "GMB",
+ .ctrl_scope = RESCTRL_NPS_NODE,
+ .ctrl_domains = ctrl_domain_init(RDT_RESOURCE_GMBA),
+ .schema_fmt = RESCTRL_SCHEMA_RANGE,
+ },
+ },
[RDT_RESOURCE_SMBA] =
{
.r_resctrl = {
@@ -239,10 +248,22 @@ static __init bool __rdt_get_mem_config_amd(struct rdt_resource *r)
u32 eax, ebx, ecx, edx, subleaf;

/*
- * Query CPUID_Fn80000020_EDX_x01 for MBA and
- * CPUID_Fn80000020_EDX_x02 for SMBA
+ * Query CPUID function 0x80000020 to obtain num_closid and max_bw values.
+ * Use subleaf 1 for MBA, subleaf 2 for SMBA, and subleaf 7 for GMBA.
*/
- subleaf = (r->rid == RDT_RESOURCE_SMBA) ? 2 : 1;
+ switch (r->rid) {
+ case RDT_RESOURCE_MBA:
+ subleaf = 1;
+ break;
+ case RDT_RESOURCE_SMBA:
+ subleaf = 2;
+ break;
+ case RDT_RESOURCE_GMBA:
+ subleaf = 7;
+ break;
+ default:
+ return false;
+ }

cpuid_count(0x80000020, subleaf, &eax, &ebx, &ecx, &edx);
hw_res->num_closid = edx + 1;
@@ -930,6 +951,19 @@ static __init bool get_mem_config(void)
return false;
}

+static __init bool get_gmem_config(void)
+{
+ struct rdt_hw_resource *hw_res = &rdt_resources_all[RDT_RESOURCE_GMBA];
+
+ if (!rdt_cpu_has(X86_FEATURE_GMBA))
+ 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_slow_mem_config(void)
{
struct rdt_hw_resource *hw_res = &rdt_resources_all[RDT_RESOURCE_SMBA];
@@ -975,6 +1009,9 @@ static __init bool get_rdt_alloc_resources(void)
if (get_mem_config())
ret = true;

+ if (get_gmem_config())
+ ret = true;
+
if (get_slow_mem_config())
ret = true;

@@ -1075,6 +1112,9 @@ static __init void rdt_init_res_defs_amd(void)
} else if (r->rid == RDT_RESOURCE_MBA) {
hw_res->msr_base = MSR_IA32_MBA_BW_BASE;
hw_res->msr_update = mba_wrmsr_amd;
+ } else if (r->rid == RDT_RESOURCE_GMBA) {
+ hw_res->msr_base = MSR_IA32_GMBA_BW_BASE;
+ hw_res->msr_update = mba_wrmsr_amd;
} else if (r->rid == RDT_RESOURCE_SMBA) {
hw_res->msr_base = MSR_IA32_SMBA_BW_BASE;
hw_res->msr_update = mba_wrmsr_amd;
diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c
index 9a7dfc48cb2e..22bbc5fa520d 100644
--- a/fs/resctrl/ctrlmondata.c
+++ b/fs/resctrl/ctrlmondata.c
@@ -246,8 +246,9 @@ static int parse_line(char *line, struct resctrl_schema *s,
return -EINVAL;

if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
- (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_SMBA)) {
- rdt_last_cmd_puts("Cannot pseudo-lock MBA resource\n");
+ (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");
return -EINVAL;
}

diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 5dfdaa6f9d8f..cc14c04314fe 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -1412,7 +1412,8 @@ 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_SMBA)
+ if (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_GMBA ||
+ r->rid == RDT_RESOURCE_SMBA)
continue;
has_cache = true;
list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
@@ -1617,6 +1618,7 @@ static int rdtgroup_size_show(struct kernfs_open_file *of,
closid,
type);
if (r->rid == RDT_RESOURCE_MBA ||
+ r->rid == RDT_RESOURCE_GMBA ||
r->rid == RDT_RESOURCE_SMBA)
size = ctrl;
else
@@ -2170,13 +2172,18 @@ 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_smba;
+ struct rdt_resource *r_mba, *r_gmba, *r_smba;

r_mba = resctrl_arch_get_resource(RDT_RESOURCE_MBA);
if (r_mba->alloc_capable &&
r_mba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
throttle_mode = r_mba->membw.throttle_mode;

+ r_gmba = resctrl_arch_get_resource(RDT_RESOURCE_GMBA);
+ if (r_gmba->alloc_capable &&
+ r_gmba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
+ throttle_mode = r_gmba->membw.throttle_mode;
+
r_smba = resctrl_arch_get_resource(RDT_RESOURCE_SMBA);
if (r_smba->alloc_capable &&
r_smba->membw.throttle_mode != THREAD_THROTTLE_UNDEFINED)
@@ -2396,6 +2403,7 @@ static unsigned long fflags_from_resource(struct rdt_resource *r)
case RDT_RESOURCE_L2:
return RFTYPE_RES_CACHE;
case RDT_RESOURCE_MBA:
+ case RDT_RESOURCE_GMBA:
case RDT_RESOURCE_SMBA:
return RFTYPE_RES_MB;
case RDT_RESOURCE_PERF_PKG:
@@ -3649,6 +3657,7 @@ static int rdtgroup_init_alloc(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) {
rdtgroup_init_mba(r, rdtgrp->closid);
if (is_mba_sc(r))
diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h
index 80290ded0b82..ed09ed2e0477 100644
--- a/include/linux/resctrl.h
+++ b/include/linux/resctrl.h
@@ -52,6 +52,7 @@ enum resctrl_res_level {
RDT_RESOURCE_L3,
RDT_RESOURCE_L2,
RDT_RESOURCE_MBA,
+ RDT_RESOURCE_GMBA,
RDT_RESOURCE_SMBA,
RDT_RESOURCE_PERF_PKG,

--
2.43.0