[RFC PATCH 23/31] x86/resctrl: Add region aware MBA controllers
From: Chen Yu
Date: Sun Aug 02 2026 - 12:18:50 EST
Add region aware MBA controllers for ERDT platforms that have the MARC
(Memory-bandwidth Allocation Registers for CPU Agents) ACPI subtable.
For each memory region reported by MRRM, create OPT, MIN, and MAX
controllers (as indicated by MARC flags) using
resctrl_ctrl_name_region(). The controller properties (bandwidth range,
granularity, unit) are derived from the MARC subtable fields.
The hw_update callback is a stub for now; the actual MMIO write
implementation will follow in a subsequent patch.
Signed-off-by: Chen Yu <yu.c.chen@xxxxxxxxx>
---
arch/x86/kernel/cpu/resctrl/core.c | 3 ++
arch/x86/kernel/cpu/resctrl/erdt.c | 67 ++++++++++++++++++++++++++
arch/x86/kernel/cpu/resctrl/internal.h | 5 ++
3 files changed, 75 insertions(+)
diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c
index 0bde098db41e..b2c53eab7d8f 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -1063,6 +1063,9 @@ static __init bool get_rdt_alloc_resources(void)
if (get_mem_config())
ret = true;
+ if (erdt_get_mem_config(&rdt_resources_all[RDT_RESOURCE_MBA].r_resctrl))
+ ret = true;
+
if (get_slow_mem_config())
ret = true;
diff --git a/arch/x86/kernel/cpu/resctrl/erdt.c b/arch/x86/kernel/cpu/resctrl/erdt.c
index c44e1d00b89f..b61f75b63f37 100644
--- a/arch/x86/kernel/cpu/resctrl/erdt.c
+++ b/arch/x86/kernel/cpu/resctrl/erdt.c
@@ -21,6 +21,7 @@ static LIST_HEAD(domain_info_list);
/* True when the ERDT ACPI table describes at least one domain with at least one CPU. */
static bool erdt_enabled;
+static u32 erdt_max_clos;
#define ERDT_VALID_VERSION 1
#define CMRC_SUPPORTED_INDEX_FN 1
@@ -57,6 +58,9 @@ bool erdt_support(int flag)
if (flag == X86_FEATURE_CQM_MBM_TOTAL)
return valid_subtbl_mask & BIT(ACPI_ERDT_TYPE_MMRC);
+ if (flag == X86_FEATURE_MBA)
+ return valid_subtbl_mask & BIT(ACPI_ERDT_TYPE_MARC);
+
return false;
}
@@ -257,6 +261,68 @@ int erdt_mon_read(struct rdt_domain_hdr *hdr, enum resctrl_event_id evtid, u32 r
return -EIO;
}
+static void marc_hw_update(struct hw_param *m)
+{
+}
+
+__init bool erdt_get_mem_config(struct rdt_resource *r)
+{
+ struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r);
+ struct resctrl_hw_ctrl *hw_ctrl;
+ struct erdt_domain_info *d;
+ struct acpi_erdt_marc *marc;
+ int max_regions, region;
+ unsigned int type;
+
+ if (!erdt_cpu_has(X86_FEATURE_MBA))
+ return false;
+
+ /* Use the first domain's MARC to discover control properties. */
+ d = list_first_entry_or_null(&domain_info_list, struct erdt_domain_info, entry);
+ if (!d || !d->marc)
+ return false;
+
+ marc = d->marc;
+ max_regions = acpi_mrrm_max_mem_region();
+ hw_res->num_closid = max(hw_res->num_closid, erdt_max_clos + 1);
+
+ for (region = 0; region < max_regions; region++) {
+ for (type = RESCTRL_CTRL_REGION_TYPE_OPT; type < RESCTRL_CTRL_REGION_NR_CTRLS; type++) {
+ if (type == RESCTRL_CTRL_REGION_TYPE_OPT && !(marc->flags & MARC_FLAG_OPT))
+ continue;
+ if (type == RESCTRL_CTRL_REGION_TYPE_MIN && !(marc->flags & MARC_FLAG_MIN))
+ continue;
+ if (type == RESCTRL_CTRL_REGION_TYPE_MAX && !(marc->flags & MARC_FLAG_MAX))
+ continue;
+
+ hw_ctrl = kzalloc_obj(*hw_ctrl);
+ if (!hw_ctrl)
+ return false;
+
+ hw_ctrl->r_ctrl.scope = RESCTRL_L3_CACHE;
+ hw_ctrl->r_ctrl.type = RESCTRL_CTRL_SCALAR;
+ hw_ctrl->r_ctrl.name = resctrl_ctrl_name_region(region, type);
+ INIT_LIST_HEAD(&hw_ctrl->r_ctrl.domains);
+ INIT_LIST_HEAD(&hw_ctrl->r_ctrl.emul);
+
+ hw_ctrl->r_ctrl.scalar.max_bw = marc->mba_ctrl_range;
+ hw_ctrl->r_ctrl.scalar.min_bw = 1;
+ hw_ctrl->r_ctrl.scalar.bw_gran = 1;
+ hw_ctrl->r_ctrl.scalar.resolution = 1;
+ hw_ctrl->r_ctrl.scalar.tolerance = 0;
+ hw_ctrl->r_ctrl.scalar.scale = 1;
+ hw_ctrl->r_ctrl.scalar.unit = RESCTRL_CTRL_UNIT_ALL;
+
+ hw_ctrl->hw_update = marc_hw_update;
+ list_add_tail(&hw_ctrl->r_ctrl.entry, &r->controls);
+ }
+ }
+
+ r->alloc_capable = true;
+
+ return true;
+}
+
static void __iomem *erdt_ioremap(phys_addr_t base, u32 num_pages, const char *desc)
{
void __iomem *addr;
@@ -699,6 +765,7 @@ static __init int enumerate_erdt_table(struct acpi_table_header *table_hdr)
if (list_empty(&domain_info_list))
goto cleanup;
+ erdt_max_clos = erdt->max_clos;
erdt_enabled = true;
return 0;
diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h
index 6855022bf07e..f09583c95a62 100644
--- a/arch/x86/kernel/cpu/resctrl/internal.h
+++ b/arch/x86/kernel/cpu/resctrl/internal.h
@@ -30,6 +30,10 @@
* @ERDT_MMIO_MARC_MIN: MARC minimum BW register base address
* @ERDT_MMIO_MARC_MAX: MARC maximum BW register base address
*/
+#define RESCTRL_CTRL_REGION_TYPE_OPT 0
+#define RESCTRL_CTRL_REGION_TYPE_MIN 1
+#define RESCTRL_CTRL_REGION_TYPE_MAX 2
+
enum erdt_mmio_type {
ERDT_MMIO_RMDD_CREG,
ERDT_MMIO_CMRC_BASE,
@@ -324,6 +328,7 @@ static inline bool intel_handle_aet_option(bool force_off, char *tok) { return f
bool erdt_support(int flag);
bool erdt_cpu_has(int flag);
bool erdt_enable_mon(void);
+bool __init erdt_get_mem_config(struct rdt_resource *r);
int erdt_get_max_rmid(void);
int erdt_mon_read(struct rdt_domain_hdr *hdr, enum resctrl_event_id evtid, u32 rmid,
u64 *val, bool first);
--
2.43.0