[RFC PATCH 21/31] x86/resctrl: Parse ACPI MARC table
From: Chen Yu
Date: Sun Aug 02 2026 - 12:19:53 EST
The MARC table is used to provide Memory Bandwidth Allocation Register
information. Its register addresses are mapped, and the MARC pointer
is saved in the corresponding domain information entry.
Signed-off-by: Chen Yu <yu.c.chen@xxxxxxxxx>
---
arch/x86/kernel/cpu/resctrl/erdt.c | 70 ++++++++++++++++++++++++++
arch/x86/kernel/cpu/resctrl/internal.h | 10 +++-
2 files changed, 79 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/cpu/resctrl/erdt.c b/arch/x86/kernel/cpu/resctrl/erdt.c
index d16cd6af6648..c44e1d00b89f 100644
--- a/arch/x86/kernel/cpu/resctrl/erdt.c
+++ b/arch/x86/kernel/cpu/resctrl/erdt.c
@@ -25,8 +25,13 @@ static bool erdt_enabled;
#define ERDT_VALID_VERSION 1
#define CMRC_SUPPORTED_INDEX_FN 1
#define MMRC_SUPPORTED_INDEX_FN 1
+#define MARC_SUPPORTED_INDEX_FN 1
#define RMDD_FLAG_CPU_L3_DOMAIN BIT(0)
+#define MARC_FLAG_OPT BIT(0)
+#define MARC_FLAG_MIN BIT(1)
+#define MARC_FLAG_MAX BIT(2)
+
/* Set in a monitoring counter when it holds no valid data to report. */
#define UNAVAILABLE_COUNTER BIT_ULL(63)
#define CMRC_FLAG_UNAVAILABLE_BIT BIT(0)
@@ -283,6 +288,7 @@ static void cleanup_one_domain(struct erdt_domain_info *d)
erdt_iounmap_domain(d);
kfree(d->cmrc);
kfree(d->mmrc);
+ kfree(d->marc);
kfree(d);
}
@@ -396,6 +402,64 @@ static __init int mmrc_init(struct acpi_subtbl_hdr_16 *subtbl,
return 0;
}
+static __init int marc_init(struct acpi_subtbl_hdr_16 *subtbl,
+ struct erdt_domain_info *domain_info)
+{
+ struct acpi_erdt_marc *marc = (struct acpi_erdt_marc *)subtbl;
+
+ if (subtbl->length < sizeof(*marc)) {
+ pr_warn(FW_BUG "Truncated MARC subtable\n");
+ return -EIO;
+ }
+
+ if (marc->index_fn != MARC_SUPPORTED_INDEX_FN) {
+ pr_info("Unsupported MARC index function %d\n", marc->index_fn);
+ return -EIO;
+ }
+
+ if (marc->flags & MARC_FLAG_OPT) {
+ domain_info->base[ERDT_MMIO_MARC_OPT] =
+ erdt_ioremap(marc->reg_base_opt, marc->mba_reg_size, "MARC OPT base");
+ if (!domain_info->base[ERDT_MMIO_MARC_OPT])
+ return -EIO;
+ }
+
+ if (marc->flags & MARC_FLAG_MIN) {
+ domain_info->base[ERDT_MMIO_MARC_MIN] =
+ erdt_ioremap(marc->reg_base_min, marc->mba_reg_size, "MARC MIN base");
+ if (!domain_info->base[ERDT_MMIO_MARC_MIN])
+ goto unmap;
+ }
+
+ if (marc->flags & MARC_FLAG_MAX) {
+ domain_info->base[ERDT_MMIO_MARC_MAX] =
+ erdt_ioremap(marc->reg_base_max, marc->mba_reg_size, "MARC MAX base");
+ if (!domain_info->base[ERDT_MMIO_MARC_MAX])
+ goto unmap;
+ }
+
+ domain_info->marc = kmemdup(marc, subtbl->length, GFP_KERNEL);
+ if (!domain_info->marc)
+ goto unmap;
+
+ return 0;
+
+unmap:
+ if (domain_info->base[ERDT_MMIO_MARC_OPT]) {
+ iounmap(domain_info->base[ERDT_MMIO_MARC_OPT]);
+ domain_info->base[ERDT_MMIO_MARC_OPT] = NULL;
+ }
+ if (domain_info->base[ERDT_MMIO_MARC_MIN]) {
+ iounmap(domain_info->base[ERDT_MMIO_MARC_MIN]);
+ domain_info->base[ERDT_MMIO_MARC_MIN] = NULL;
+ }
+ if (domain_info->base[ERDT_MMIO_MARC_MAX]) {
+ iounmap(domain_info->base[ERDT_MMIO_MARC_MAX]);
+ domain_info->base[ERDT_MMIO_MARC_MAX] = NULL;
+ }
+ return -EIO;
+}
+
static inline struct acpi_subtbl_hdr_16 *rmdd_subtbl(struct acpi_erdt_rmdd *rmdd)
{
return (void *)rmdd + sizeof(*rmdd);
@@ -478,6 +542,12 @@ static __init bool parse_rmdd_table(struct acpi_subtbl_hdr_16 *rmdd_hdr)
!mmrc_init(subtbl, domain_info))
subtbl_mask |= BIT(ACPI_ERDT_TYPE_MMRC);
+ break;
+ case ACPI_ERDT_TYPE_MARC:
+ if (!(subtbl_mask & BIT(ACPI_ERDT_TYPE_MARC)) &&
+ !marc_init(subtbl, domain_info))
+ subtbl_mask |= BIT(ACPI_ERDT_TYPE_MARC);
+
break;
default:
break;
diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h
index ca9124e9aea5..6855022bf07e 100644
--- a/arch/x86/kernel/cpu/resctrl/internal.h
+++ b/arch/x86/kernel/cpu/resctrl/internal.h
@@ -26,12 +26,18 @@
* @ERDT_MMIO_RMDD_CREG: RMDD control register base address
* @ERDT_MMIO_CMRC_BASE: CMRC monitoring register base address
* @ERDT_MMIO_MMRC_BASE: MMRC monitoring register base address
+ * @ERDT_MMIO_MARC_OPT: MARC optimal BW register base address
+ * @ERDT_MMIO_MARC_MIN: MARC minimum BW register base address
+ * @ERDT_MMIO_MARC_MAX: MARC maximum BW register base address
*/
enum erdt_mmio_type {
ERDT_MMIO_RMDD_CREG,
ERDT_MMIO_CMRC_BASE,
ERDT_MMIO_MMRC_BASE,
- ERDT_MMIO_LAST = ERDT_MMIO_MMRC_BASE
+ ERDT_MMIO_MARC_OPT,
+ ERDT_MMIO_MARC_MIN,
+ ERDT_MMIO_MARC_MAX,
+ ERDT_MMIO_LAST = ERDT_MMIO_MARC_MAX
};
#define ERDT_MMIO_NUM_TYPES (ERDT_MMIO_LAST + 1)
@@ -41,6 +47,7 @@ enum erdt_mmio_type {
* @base: Array of ioremapped MMIO region base addresses, indexed by ERDT_MMIO_* type
* @cmrc: Copy of the ACPI CMRC sub-table for this domain
* @mmrc: Copy of the ACPI MMRC sub-table for this domain
+ * @marc: Copy of the ACPI MARC sub-table for this domain
* @cpu_mask: CPUs belonging to this resource management domain
* @max_rmid: Maximum RMID supported by this domain
* @dom_id: L3 cache ID shared by all CPUs in this domain (-1 if unset)
@@ -50,6 +57,7 @@ struct erdt_domain_info {
void __iomem *base[ERDT_MMIO_NUM_TYPES];
struct acpi_erdt_cmrc *cmrc;
struct acpi_erdt_mmrc *mmrc;
+ struct acpi_erdt_marc *marc;
struct cpumask cpu_mask;
u32 max_rmid;
int dom_id;
--
2.43.0