[PATCH v10 07/12] x86/resctrl: Parse ACPI CMRC table
From: Chen Yu
Date: Thu Nov 05 2026 - 05:22:38 EST
The CMRC (Cache Monitoring Registers for CPU Agents Description) sub-table of
ERDT describes the MMIO registers used to read the LLC occupancy counters of an
ERDT domain.
Reading the LLC occupancy of an RMID via MMIO, rather than via the
IA32_QM_EVTSEL/IA32_QM_CTR MSRs, requires the following data from the CMRC
sub-table:
- Where the counters are: the base address and size of the domain's
monitoring register block.
- How the counters are laid out: the index function, clump size and clump
stride, which locate the counter of a given RMID within that block.
- How to interpret a counter value: the flags, which indicate whether a
counter can report that its data is unavailable, and up_scale, which
converts a counter value to bytes.
The LLC occupancy read path needs all of this data on every read. The limbo
handler also needs up_scale to round its occupancy threshold.
Parse each CMRC sub-table, validate the data above, ioremap the monitoring
register block and save a copy of the CMRC sub-table in the corresponding ERDT
domain entry.
Suggested-by: Tony Luck <tony.luck@xxxxxxxxx>
Signed-off-by: Chen Yu <yu.c.chen@xxxxxxxxx>
Tested-by: Hongyu Ning <hongyu.ning@xxxxxxxxxxxxxxx>
---
Changes since v9:
- Rebased on the kernel-doc enum erdt_mmio_type from "x86/resctrl:
Parse ACPI ERDT table and save CACD cpumask per RMD". No code change.
arch/x86/kernel/cpu/resctrl/erdt.c | 53 ++++++++++++++++++++++++++
arch/x86/kernel/cpu/resctrl/internal.h | 6 ++-
2 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kernel/cpu/resctrl/erdt.c b/arch/x86/kernel/cpu/resctrl/erdt.c
index 1b07dd734b9b..ef7f51fde838 100644
--- a/arch/x86/kernel/cpu/resctrl/erdt.c
+++ b/arch/x86/kernel/cpu/resctrl/erdt.c
@@ -23,6 +23,7 @@ static LIST_HEAD(domain_info_list);
static bool erdt_enabled;
#define ERDT_VALID_REVISION 1
+#define CMRC_SUPPORTED_INDEX_FN 1
#define RMDD_FLAG_CPU_L3_DOMAIN BIT(0)
#define ERDT_DOMAIN_ID_UNSET -1
@@ -65,6 +66,7 @@ static void erdt_iounmap_domain(struct erdt_domain_info *domain)
static void cleanup_one_domain(struct erdt_domain_info *d)
{
erdt_iounmap_domain(d);
+ kfree(d->cmrc);
kfree(d);
}
@@ -98,6 +100,47 @@ static __init bool cacd_init(struct acpi_subtbl_hdr_16 *subtbl,
return true;
}
+static __init bool cmrc_init(struct acpi_subtbl_hdr_16 *subtbl,
+ struct erdt_domain_info *domain_info)
+{
+ struct acpi_erdt_cmrc *cmrc = (struct acpi_erdt_cmrc *)subtbl;
+
+ if (cmrc->header.length < sizeof(*cmrc)) {
+ pr_warn(FW_BUG "Truncated CMRC sub-table\n");
+ return false;
+ }
+
+ if (cmrc->index_fn != CMRC_SUPPORTED_INDEX_FN) {
+ pr_info("Unsupported CMRC index function %u\n", cmrc->index_fn);
+ return false;
+ }
+
+ if (!cmrc->clump_size) {
+ pr_warn(FW_BUG "CMRC clump_size is zero\n");
+ return false;
+ }
+
+ /* resctrl scales monitoring values with an unsigned int. */
+ if (cmrc->up_scale > UINT_MAX) {
+ pr_warn(FW_BUG "Insane CMRC up_scale value 0x%llx\n", cmrc->up_scale);
+ return false;
+ }
+
+ domain_info->base[ERDT_MMIO_CMRC_BASE] =
+ erdt_ioremap(cmrc->cmt_reg_base, cmrc->cmt_reg_size, "CMRC base");
+ if (!domain_info->base[ERDT_MMIO_CMRC_BASE])
+ return false;
+
+ domain_info->cmrc = kmemdup(cmrc, cmrc->header.length, GFP_KERNEL);
+ if (!domain_info->cmrc) {
+ iounmap(domain_info->base[ERDT_MMIO_CMRC_BASE]);
+ domain_info->base[ERDT_MMIO_CMRC_BASE] = NULL;
+ return false;
+ }
+
+ return true;
+}
+
static inline struct acpi_subtbl_hdr_16 *next_subtbl(struct acpi_subtbl_hdr_16 *subtbl)
{
return (void *)subtbl + subtbl->length;
@@ -154,6 +197,16 @@ static __init bool parse_rmdd_table(struct acpi_subtbl_hdr_16 *rmdd_hdr)
subtbl_mask |= BIT(ACPI_ERDT_TYPE_CACD);
break;
+ /* An RMDD table supports at most one CMRC sub-table */
+ case ACPI_ERDT_TYPE_CMRC:
+ if (subtbl_mask & BIT(ACPI_ERDT_TYPE_CMRC))
+ break;
+
+ if (!cmrc_init(subtbl, domain_info))
+ goto cleanup;
+
+ subtbl_mask |= BIT(ACPI_ERDT_TYPE_CMRC);
+ break;
default:
break;
}
diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h
index deb033155f31..527e3ac91b26 100644
--- a/arch/x86/kernel/cpu/resctrl/internal.h
+++ b/arch/x86/kernel/cpu/resctrl/internal.h
@@ -24,11 +24,13 @@
/**
* enum erdt_mmio_type - Index into erdt_domain_info::base[] for each MMIO region
* @ERDT_MMIO_RMDD_CREG: RMDD control register base address
+ * @ERDT_MMIO_CMRC_BASE: CMRC monitoring register base address
* @ERDT_MMIO_LAST: Last MMIO region type
*/
enum erdt_mmio_type {
ERDT_MMIO_RMDD_CREG,
- ERDT_MMIO_LAST = ERDT_MMIO_RMDD_CREG
+ ERDT_MMIO_CMRC_BASE,
+ ERDT_MMIO_LAST = ERDT_MMIO_CMRC_BASE
};
#define ERDT_MMIO_NUM_TYPES (ERDT_MMIO_LAST + 1)
@@ -36,6 +38,7 @@ enum erdt_mmio_type {
/**
* struct erdt_domain_info - L3 domain ERDT information
* @base: Array of ioremapped MMIO region base addresses, indexed by ERDT_MMIO_*
+ * @cmrc: Copy of the ACPI CMRC sub-table for this domain
* @cpu_mask: CPUs belonging to this resource management domain
* @dom_id: L3 cache ID shared by all CPUs in this domain
* (ERDT_DOMAIN_ID_UNSET if unset)
@@ -43,6 +46,7 @@ enum erdt_mmio_type {
*/
struct erdt_domain_info {
void __iomem *base[ERDT_MMIO_NUM_TYPES];
+ struct acpi_erdt_cmrc *cmrc;
struct cpumask cpu_mask;
int dom_id;
struct list_head entry;
--
2.25.1