Re: [PATCH v3 28/47] arm_mpam: resctrl: Add support for csu counters

Ben Horgan
2026-01-30T11:19:36Z

Replying to myself...

On 1/12/26 16:58, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> resctrl exposes a counter via a file named llc_occupancy. This isn't really
> a counter as its value goes up and down, this is a snapshot of the cache
> storage usage monitor.
>
> Add some picking code to find a cache as close as possible to the L3 that
> supports the CSU monitor.
>
> If there is an L3, but it doesn't have any controls, force the L3 resource
> to exist. The existing topology_matches_l3() and
> mpam_resctrl_domain_hdr_init() code will ensure this looks like the L3,
> even if the class belongs to a later cache.
>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Co-developed-by: Dave Martin <dave.martin@arm.com>
> Signed-off-by: Dave Martin <dave.martin@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc:
> Allow csu counters however many partid or pmg there are
> else if -> if
> reduce scope of local variables
> drop has_csu
>
> Changes since v2:
> return -> break so works for mbwu in later patch
> add for_each_mpam_resctrl_mon
> return error from mpam_resctrl_monitor_init(). It may fail when is abmc
> allocation introduced in a later patch.
> Squashed in patch from Dave Martin:
> https://lore.kernel.org/lkml/20250820131621.54983-1-Dave.Martin@arm.com/
> ---
> drivers/resctrl/mpam_internal.h | 6 ++
> drivers/resctrl/mpam_resctrl.c | 173 +++++++++++++++++++++++++++++++-
> 2 files changed, 174 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index f89ceaf7623d..21cc776e57aa 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -349,6 +349,12 @@ struct mpam_resctrl_res {
> struct rdt_resource resctrl_res;
> };
>
> +struct mpam_resctrl_mon {
> + struct mpam_class *class;
> +
> + /* per-class data that resctrl needs will live here */
> +};
> +
> static inline int mpam_alloc_csu_mon(struct mpam_class *class)
> {
> struct mpam_props *cprops = &class->props;
> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
> index 7402bf4293b6..5020a5faed96 100644
> --- a/drivers/resctrl/mpam_resctrl.c
> +++ b/drivers/resctrl/mpam_resctrl.c
> @@ -37,6 +37,21 @@ static struct mpam_resctrl_res mpam_resctrl_controls[RDT_NUM_RESOURCES];
> /* The lock for modifying resctrl's domain lists from cpuhp callbacks. */
> static DEFINE_MUTEX(domain_list_lock);
>
> +/*
> + * The classes we've picked to map to resctrl events.
> + * Resctrl believes all the worlds a Xeon, and these are all on the L3. This
> + * array lets us find the actual class backing the event counters. e.g.
> + * the only memory bandwidth counters may be on the memory controller, but to
> + * make use of them, we pretend they are on L3.
> + * Class pointer may be NULL.
> + */
> +static struct mpam_resctrl_mon mpam_resctrl_counters[QOS_NUM_EVENTS];
> +
> +#define for_each_mpam_resctrl_mon(mon, eventid) \
> + for (eventid = 0, mon = &mpam_resctrl_counters[eventid]; \
> + eventid < QOS_NUM_EVENTS; \
> + eventid++, mon = &mpam_resctrl_counters[eventid])
> +
> static bool exposed_alloc_capable;
> static bool exposed_mon_capable;
>
> @@ -259,6 +274,28 @@ static bool class_has_usable_mba(struct mpam_props *cprops)
> return mba_class_use_mbw_max(cprops);
> }
>
> +static bool cache_has_usable_csu(struct mpam_class *class)
> +{
> + struct mpam_props *cprops;
> +
> + if (!class)
> + return false;
> +
> + cprops = &class->props;
> +
> + if (!mpam_has_feature(mpam_feat_msmon_csu, cprops))
> + return false;
> +
> + /*
> + * CSU counters settle on the value, so we can get away with
> + * having only one.
> + */
> + if (!cprops->num_csu_mon)
> + return false;
> +
> + return true;
> +}
> +
> /*
> * Calculate the worst-case percentage change from each implemented step
> * in the control.
> @@ -507,6 +544,64 @@ static void mpam_resctrl_pick_mba(void)
> }
> }
>
> +static void counter_update_class(enum resctrl_event_id evt_id,
> + struct mpam_class *class)
> +{
> + struct mpam_class *existing_class = mpam_resctrl_counters[evt_id].class;
> +
> + if (existing_class) {
> + if (class->level == 3) {
> + pr_debug("Existing class is L3 - L3 wins\n");
> + return;
> + }
> +
> + if (existing_class->level < class->level) {
> + pr_debug("Existing class is closer to L3, %u versus %u - closer is better\n",
> + existing_class->level, class->level);
> + return;
> + }
> + }
> +
> + mpam_resctrl_counters[evt_id].class = class;
> + exposed_mon_capable = true;
> +}
> +
> +static void mpam_resctrl_pick_counters(void)
> +{
> + struct mpam_class *class;
> +
> + lockdep_assert_cpus_held();
> +
> + guard(srcu)(&mpam_srcu);
> + list_for_each_entry_srcu(class, &mpam_classes, classes_list,
> + srcu_read_lock_held(&mpam_srcu)) {
> + if (class->level < 3) {
> + pr_debug("class %u is before L3", class->level);
> + continue;
> + }
> +
> + if (!cpumask_equal(&class->affinity, cpu_possible_mask)) {
> + pr_debug("class %u does not cover all CPUs",
> + class->level);
> + continue;
> + }
> +
> + if (cache_has_usable_csu(class) && topology_matches_l3(class)) {
> + pr_debug("class %u has usable CSU, and matches L3 topology",
> + class->level);
> +
> + /* CSU counters only make sense on a cache. */
> + switch (class->type) {
> + case MPAM_CLASS_CACHE:
> + counter_update_class(QOS_L3_OCCUP_EVENT_ID, class);

As the counter is named llc_occupancy (llc = last level cache) and we
are naming the resource L3 it would be surprising to have llc_occupancy
on anything other than an L3. Also, that L3 should be a last level
cache. I'll update this as part of a general push to tighten up the
heuristics in this series to make sure that in the future when more
fitting user visible interfaces are added in resctrl we are able to use
them rather than being stuck with something that almost fits.

> + break;
> + default:
> + break;
> + }
> + }
> + }
> +}
> +
[...]

Thanks,

Ben