The Resource Director Technology Monitoring Interface: How the CPU Counts Cache Lines Per Process

2026-06-13

You've seen Cache Allocation Technology (CAT) partition L3 between workloads and Memory Bandwidth Allocation (MBA) throttle cores. But before you can allocate, you have to measure. That's what Resource Director Technology Monitoring (RDT-M) does: it tags every cache line with a small identifier so the CPU can tell you, in hardware, exactly how many bytes of L3 a specific process is occupying and how much memory bandwidth it's consuming — right now, this microsecond.

The mechanism is a Resource Monitoring ID (RMID). The kernel writes an RMID into a per-logical-core MSR (IA32_PQR_ASSOC, MSR 0xC8F) at context-switch time. From that point on, every cache fill, every eviction, and every memory request issued by that core is tagged with the RMID. Hardware counters indexed by RMID accumulate:

You read them by writing {RMID, event_id} into IA32_QM_EVTSEL (0xC8D) and reading the value from IA32_QM_CTR (0xC8E). One MSR write, one MSR read — about 200 cycles, no IPI required.

The catch: RMIDs are scarce. A Skylake-SP has ~143 of them per socket. The kernel's resctrl filesystem (/sys/fs/resctrl/) manages this pool, recycling RMIDs when monitoring groups are destroyed — but a recycled RMID's occupancy counter is meaningless until enough cache turnover has happened to flush the old tags. The kernel implements a "limbo list" that holds freed RMIDs until their occupancy drops below a threshold.

Real-world example: Database operators running Postgres next to a batch analytics job on the same socket saw p99 latency spikes whenever the batch job started. Using pqos -m all:0-15 (the Intel CMT tool), they observed the batch job's L3 occupancy climbing from 2 MB to 35 MB out of a 38 MB cache in under 200 ms. Postgres was being evicted in real time. The fix was CAT to cap the batch job at 16 ways — but they only knew to apply CAT because RDT-M let them see the eviction happening.

Rule of thumb: One RMID consumes hardware state proportional to (cache size / line size) bits of tag storage. On a 38 MB LLC with 64-byte lines, that's ~600K cache lines × 8 bits of RMID = ~600 KB of dedicated SRAM just to track tags. This is why RMIDs aren't free — and why the kernel rations them.

See it in action: Check out How To Set The CPU Affinity Of A Running Process In Linux by Liv4IT to see this theory applied.
Key Takeaway: RDT-M tags every cache line with an RMID at fill time, letting the kernel read per-process L3 occupancy and memory bandwidth in ~200 cycles via two MSRs — but RMIDs are a scarce hardware resource the kernel must ration.

All newsletters