Reading a monitor involves configuring what you want to monitor, and
reading the value. Components made up of multiple MSC may need values
from each MSC. MSCs may take time to configure, returning 'not ready'.
The maximum 'not ready' time should have been provided by firmware.
Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
not ready, then wait the full timeout value before trying again.
CC: Shanker Donthineni <sdonthineni@nvidia.com>
Signed-off-by: James Morse <james.morse@arm.com>
---
Changes since v1:
* Added XCL support.
* Merged FLT/CTL constants.
* a spelling mistake in a comment.
* moved structrues around.
---
drivers/resctrl/mpam_devices.c | 226 ++++++++++++++++++++++++++++++++
drivers/resctrl/mpam_internal.h | 19 +++
2 files changed, 245 insertions(+)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index cf190f896de1..1543c33c5d6a 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -898,6 +898,232 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
return 0;
}
+struct mon_read {
+ struct mpam_msc_ris *ris;
+ struct mon_cfg *ctx;
+ enum mpam_device_features type;
+ u64 *val;
+ int err;
+};
+
+static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
+ u32 *flt_val)
+{
+ struct mon_cfg *ctx = m->ctx;
+
+ /*
+ * For CSU counters its implementation-defined what happens when not
+ * filtering by partid.
+ */
+ *ctl_val |= MSMON_CFG_x_CTL_MATCH_PARTID;
+
+ *flt_val = FIELD_PREP(MSMON_CFG_x_FLT_PARTID, ctx->partid);
+ if (m->ctx->match_pmg) {
+ *ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG;
+ *flt_val |= FIELD_PREP(MSMON_CFG_x_FLT_PMG, ctx->pmg);
+ }
+
+ switch (m->type) {
+ case mpam_feat_msmon_csu:
+ *ctl_val = MSMON_CFG_CSU_CTL_TYPE_CSU;
+
+ if (mpam_has_feature(mpam_feat_msmon_csu_xcl, &m->ris->props))
+ *flt_val |= FIELD_PREP(MSMON_CFG_CSU_FLT_XCL,
+ ctx->csu_exclude_clean);
+
+ break;
+ case mpam_feat_msmon_mbwu:
+ *ctl_val = MSMON_CFG_MBWU_CTL_TYPE_MBWU;
+
+ if (mpam_has_feature(mpam_feat_msmon_mbwu_rwbw, &m->ris->props))
+ *flt_val |= FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts);
+
+ break;
+ default:
+ return;
+ }
+}
+
+static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
+ u32 *flt_val)
+{
+ struct mpam_msc *msc = m->ris->vmsc->msc;
+
+ switch (m->type) {
+ case mpam_feat_msmon_csu:
+ *ctl_val = mpam_read_monsel_reg(msc, CFG_CSU_CTL);
+ *flt_val = mpam_read_monsel_reg(msc, CFG_CSU_FLT);
+ break;
+ case mpam_feat_msmon_mbwu:
+ *ctl_val = mpam_read_monsel_reg(msc, CFG_MBWU_CTL);
+ *flt_val = mpam_read_monsel_reg(msc, CFG_MBWU_FLT);
+ break;
+ default:
+ return;
+ }
+}
+
+/* Remove values set by the hardware to prevent apparent mismatches. */
+static void clean_msmon_ctl_val(u32 *cur_ctl)
+{
+ *cur_ctl &= ~MSMON_CFG_x_CTL_OFLOW_STATUS;
+}
+
+static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
+ u32 flt_val)
+{
+ struct mpam_msc *msc = m->ris->vmsc->msc;
+
+ /*
+ * Write the ctl_val with the enable bit cleared, reset the counter,
+ * then enable counter.
+ */
+ switch (m->type) {
+ case mpam_feat_msmon_csu:
+ mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
+ mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
+ mpam_write_monsel_reg(msc, CSU, 0);
+ mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
+ break;
+ case mpam_feat_msmon_mbwu:
+ mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
+ mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
+ mpam_write_monsel_reg(msc, MBWU, 0);
+ mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
+ break;
+ default:
+ return;
+ }
+}
+
+/* Call with MSC lock held */
+static void __ris_msmon_read(void *arg)
+{
+ u64 now;
+ bool nrdy = false;
+ struct mon_read *m = arg;
+ struct mon_cfg *ctx = m->ctx;
+ struct mpam_msc_ris *ris = m->ris;
+ struct mpam_props *rprops = &ris->props;
+ struct mpam_msc *msc = m->ris->vmsc->msc;
+ u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt;
+
+ if (!mpam_mon_sel_lock(msc)) {
+ m->err = -EIO;
+ return;
+ }
+ mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) |
+ FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
+ mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
+
+ /*
+ * Read the existing configuration to avoid re-writing the same values.
+ * This saves waiting for 'nrdy' on subsequent reads.
+ */
+ read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt);
+ clean_msmon_ctl_val(&cur_ctl);
+ gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val);
+ if (cur_flt != flt_val || cur_ctl != (ctl_val | MSMON_CFG_x_CTL_EN))
+ write_msmon_ctl_flt_vals(m, ctl_val, flt_val);
+
+ switch (m->type) {
+ case mpam_feat_msmon_csu:
+ now = mpam_read_monsel_reg(msc, CSU);
+ if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops))
+ nrdy = now & MSMON___NRDY;
+ break;
+ case mpam_feat_msmon_mbwu:
+ now = mpam_read_monsel_reg(msc, MBWU);
+ if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops))
+ nrdy = now & MSMON___NRDY;
+ break;
+ default:
+ m->err = -EINVAL;
+ break;
+ }
+ mpam_mon_sel_unlock(msc);
+
+ if (nrdy) {
+ m->err = -EBUSY;
+ return;
+ }
+
+ now = FIELD_GET(MSMON___VALUE, now);
+ *m->val += now;
+}
+
+static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
+{
+ int err, idx;
+ struct mpam_msc *msc;
+ struct mpam_vmsc *vmsc;
+ struct mpam_msc_ris *ris;
+
+ idx = srcu_read_lock(&mpam_srcu);
+ list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) {
+ msc = vmsc->msc;
+
+ list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) {
+ arg->ris = ris;
+
+ err = smp_call_function_any(&msc->accessibility,
+ __ris_msmon_read, arg,
+ true);
+ if (!err && arg->err)
+ err = arg->err;
+ if (err)
+ break;
+ }
+ if (err)
+ break;
+ }
+ srcu_read_unlock(&mpam_srcu, idx);
+
+ return err;
+}
+
+int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx,
+ enum mpam_device_features type, u64 *val)
+{
+ int err;
+ struct mon_read arg;
+ u64 wait_jiffies = 0;
+ struct mpam_props *cprops = &comp->class->props;
+
+ might_sleep();
+
+ if (!mpam_is_enabled())
+ return -EIO;
+
+ if (!mpam_has_feature(type, cprops))
+ return -EOPNOTSUPP;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.type = type;
+ arg.val = val;
+ *val = 0;
+
+ err = _msmon_read(comp, &arg);
+ if (err == -EBUSY && comp->class->nrdy_usec)
+ wait_jiffies = usecs_to_jiffies(comp->class->nrdy_usec);
+
+ while (wait_jiffies)
+ wait_jiffies = schedule_timeout_uninterruptible(wait_jiffies);
+
+ if (err == -EBUSY) {
+ memset(&arg, 0, sizeof(arg));
+ arg.ctx = ctx;
+ arg.type = type;
+ arg.val = val;
+ *val = 0;
+
+ err = _msmon_read(comp, &arg);
+ }
+
+ return err;
+}
+
static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
{
u32 num_words, msb;
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 81c4c2bfea3d..bb01e7dbde40 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -196,6 +196,22 @@ static inline void mpam_clear_feature(enum mpam_device_features feat,
*supported &= ~(1 << feat);
}
+/* The values for MSMON_CFG_MBWU_FLT.RWBW */
+enum mon_filter_options {
+ COUNT_BOTH = 0,
+ COUNT_WRITE = 1,
+ COUNT_READ = 2,
+};
+
+struct mon_cfg {
+ u16 mon;
+ u8 pmg;
+ bool match_pmg;
+ bool csu_exclude_clean;
+ u32 partid;
+ enum mon_filter_options opts;
+};
+
struct mpam_class {
/* mpam_components in this class */
struct list_head components;
@@ -343,6 +359,9 @@ void mpam_disable(struct work_struct *work);
int mpam_apply_config(struct mpam_component *comp, u16 partid,
struct mpam_config *cfg);
+int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx,
+ enum mpam_device_features, u64 *val);
+
int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level,
cpumask_t *affinity);
--
2.39.5
On Wed, 10 Sep 2025 20:43:03 +0000
James Morse <james.morse@arm.com> wrote:
> Reading a monitor involves configuring what you want to monitor, and
> reading the value. Components made up of multiple MSC may need values
> from each MSC. MSCs may take time to configure, returning 'not ready'.
> The maximum 'not ready' time should have been provided by firmware.
>
> Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
> not ready, then wait the full timeout value before trying again.
>
> CC: Shanker Donthineni <sdonthineni@nvidia.com>
> Signed-off-by: James Morse <james.morse@arm.com>
Hi James,
A couple of minor comments inline,
Jonathan
> +static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
> + u32 flt_val)
> +{
> + struct mpam_msc *msc = m->ris->vmsc->msc;
> +
> + /*
> + * Write the ctl_val with the enable bit cleared, reset the counter,
> + * then enable counter.
> + */
> + switch (m->type) {
> + case mpam_feat_msmon_csu:
> + mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
> + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
> + mpam_write_monsel_reg(msc, CSU, 0);
> + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
> + break;
> + case mpam_feat_msmon_mbwu:
> + mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
> + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
> + mpam_write_monsel_reg(msc, MBWU, 0);
> + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
> + break;
Given nothing to do later, I'd just return at end of each case.
Entirely up to you though as this is just a personal style preference.
> + default:
> + return;
> + }
> +}
> +
> +static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
> +{
> + int err, idx;
> + struct mpam_msc *msc;
> + struct mpam_vmsc *vmsc;
> + struct mpam_msc_ris *ris;
> +
> + idx = srcu_read_lock(&mpam_srcu);
guard(srcu)(&mpam_srcu);
Then you can do direct returns on errors which looks like it will simplify
things somewhat by letting you just return on err.
> + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) {
> + msc = vmsc->msc;
I'd bring the declaration down here as well.
struct mpam_msc *msc = vmsc->msc;
Could bring ris down here as well.
> +
> + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) {
> + arg->ris = ris;
> +
> + err = smp_call_function_any(&msc->accessibility,
> + __ris_msmon_read, arg,
> + true);
> + if (!err && arg->err)
> + err = arg->err;
> + if (err)
> + break;
> + }
> + if (err)
> + break;
This won't be needed if you returned on error above.
> + }
> + srcu_read_unlock(&mpam_srcu, idx);
> +
> + return err;
And you only reach here with above changes if err == 0 so return 0; appropriate.
> +}
> +
> +int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx,
> + enum mpam_device_features type, u64 *val)
> +{
> + int err;
> + struct mon_read arg;
> + u64 wait_jiffies = 0;
> + struct mpam_props *cprops = &comp->class->props;
> +
> + might_sleep();
> +
> + if (!mpam_is_enabled())
> + return -EIO;
> +
> + if (!mpam_has_feature(type, cprops))
> + return -EOPNOTSUPP;
> +
> + memset(&arg, 0, sizeof(arg));
Either use = { }; at declaration or maybe
arg = (struct mon_read) {
.ctx = ctx,
.type = type,
.val = val,
};
rather than bothering with separate memset.
> + arg.ctx = ctx;
> + arg.type = type;
> + arg.val = val;
> + *val = 0;
> +
> + err = _msmon_read(comp, &arg);
> + if (err == -EBUSY && comp->class->nrdy_usec)
> + wait_jiffies = usecs_to_jiffies(comp->class->nrdy_usec);
> +
> + while (wait_jiffies)
> + wait_jiffies = schedule_timeout_uninterruptible(wait_jiffies);
> +
> + if (err == -EBUSY) {
> + memset(&arg, 0, sizeof(arg));
Same as above.
> + arg.ctx = ctx;
> + arg.type = type;
> + arg.val = val;
> + *val = 0;
> +
> + err = _msmon_read(comp, &arg);
> + }
> +
> + return err;
> +}
Hi Jonathan,
On 12/09/2025 14:21, Jonathan Cameron wrote:
> On Wed, 10 Sep 2025 20:43:03 +0000
> James Morse <james.morse@arm.com> wrote:
>
>> Reading a monitor involves configuring what you want to monitor, and
>> reading the value. Components made up of multiple MSC may need values
>> from each MSC. MSCs may take time to configure, returning 'not ready'.
>> The maximum 'not ready' time should have been provided by firmware.
>>
>> Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
>> not ready, then wait the full timeout value before trying again.
>>
>> CC: Shanker Donthineni <sdonthineni@nvidia.com>
>> Signed-off-by: James Morse <james.morse@arm.com>
>> +static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
>> + u32 flt_val)
>> +{
>> + struct mpam_msc *msc = m->ris->vmsc->msc;
>> +
>> + /*
>> + * Write the ctl_val with the enable bit cleared, reset the counter,
>> + * then enable counter.
>> + */
>> + switch (m->type) {
>> + case mpam_feat_msmon_csu:
>> + mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
>> + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
>> + mpam_write_monsel_reg(msc, CSU, 0);
>> + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
>> + break;
>> + case mpam_feat_msmon_mbwu:
>> + mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
>> + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
>> + mpam_write_monsel_reg(msc, MBWU, 0);
>> + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
>> + break;
>
> Given nothing to do later, I'd just return at end of each case.
> Entirely up to you though as this is just a personal style preference.
Out of habit I try to avoid functions returning from different places, as it makes it
harder to add locking later. Maybe the fancy cleanup c++ thing changes that.
>> + default:
>> + return;
But I'm clearly inconsistent!
I've changes this as you suggest.
>> + }
>> +}
>
>> +
>> +static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
>> +{
>> + int err, idx;
>> + struct mpam_msc *msc;
>> + struct mpam_vmsc *vmsc;
>> + struct mpam_msc_ris *ris;
>> +
>> + idx = srcu_read_lock(&mpam_srcu);
>
> guard(srcu)(&mpam_srcu);
>
> Then you can do direct returns on errors which looks like it will simplify
> things somewhat by letting you just return on err.
>
>
>> + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) {
>> + msc = vmsc->msc;
> I'd bring the declaration down here as well.
> struct mpam_msc *msc = vmsc->msc;
> Could bring ris down here as well.
>
>> +
>> + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) {
>> + arg->ris = ris;
>> +
>> + err = smp_call_function_any(&msc->accessibility,
>> + __ris_msmon_read, arg,
>> + true);
>> + if (!err && arg->err)
>> + err = arg->err;
>> + if (err)
>> + break;
>> + }
>> + if (err)
>> + break;
>
> This won't be needed if you returned on error above.
>
>> + }
>> + srcu_read_unlock(&mpam_srcu, idx);
>> +
>> + return err;
> And you only reach here with above changes if err == 0 so return 0; appropriate.
I've done all of the above.
(I'd even already worked it out from your earlier feedback)
>> +}
>> +
>> +int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx,
>> + enum mpam_device_features type, u64 *val)
>> +{
>> + int err;
>> + struct mon_read arg;
>> + u64 wait_jiffies = 0;
>> + struct mpam_props *cprops = &comp->class->props;
>> +
>> + might_sleep();
>> +
>> + if (!mpam_is_enabled())
>> + return -EIO;
>> +
>> + if (!mpam_has_feature(type, cprops))
>> + return -EOPNOTSUPP;
>> +
>> + memset(&arg, 0, sizeof(arg));
> Either use = { }; at declaration or maybe
> arg = (struct mon_read) {
> .ctx = ctx,
> .type = type,
> .val = val,
> };
>
> rather than bothering with separate memset.
The memset is because arg gets re-used, but there are fields not touched here that get
modified, like 'err'. But this struct assignment works too...
>> + arg.ctx = ctx;
>> + arg.type = type;
>> + arg.val = val;
>> + *val = 0;
>> +
>> + err = _msmon_read(comp, &arg);
>> + if (err == -EBUSY && comp->class->nrdy_usec)
>> + wait_jiffies = usecs_to_jiffies(comp->class->nrdy_usec);
>> +
>> + while (wait_jiffies)
>> + wait_jiffies = schedule_timeout_uninterruptible(wait_jiffies);
>> +
>> + if (err == -EBUSY) {
>> + memset(&arg, 0, sizeof(arg));
> Same as above.
Yup - its like this so that they look the same.
>> + arg.ctx = ctx;
>> + arg.type = type;
>> + arg.val = val;
>> + *val = 0;
>> +
>> + err = _msmon_read(comp, &arg);
>> + }
>> +
>> + return err;
>> +}
>
Thanks,
James
Hi James,
On 9/10/25 21:43, James Morse wrote:
> Reading a monitor involves configuring what you want to monitor, and
> reading the value. Components made up of multiple MSC may need values
> from each MSC. MSCs may take time to configure, returning 'not ready'.
> The maximum 'not ready' time should have been provided by firmware.
>
> Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
> not ready, then wait the full timeout value before trying again.
>
> CC: Shanker Donthineni <sdonthineni@nvidia.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
> Changes since v1:
> * Added XCL support.
> * Merged FLT/CTL constants.
> * a spelling mistake in a comment.
> * moved structrues around.
> ---
> drivers/resctrl/mpam_devices.c | 226 ++++++++++++++++++++++++++++++++
> drivers/resctrl/mpam_internal.h | 19 +++
> 2 files changed, 245 insertions(+)
>
> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index cf190f896de1..1543c33c5d6a 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -898,6 +898,232 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
> return 0;
> }
>
> +struct mon_read {
> + struct mpam_msc_ris *ris;
> + struct mon_cfg *ctx;
> + enum mpam_device_features type;
> + u64 *val;
> + int err;
> +};
> +
> +static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
> + u32 *flt_val)
> +{
> + struct mon_cfg *ctx = m->ctx;
> +
> + /*
> + * For CSU counters its implementation-defined what happens when not
> + * filtering by partid.
> + */
> + *ctl_val |= MSMON_CFG_x_CTL_MATCH_PARTID;
> +
> + *flt_val = FIELD_PREP(MSMON_CFG_x_FLT_PARTID, ctx->partid);
> + if (m->ctx->match_pmg) {
> + *ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG;
> + *flt_val |= FIELD_PREP(MSMON_CFG_x_FLT_PMG, ctx->pmg);
> + }
> +
> + switch (m->type) {
> + case mpam_feat_msmon_csu:
> + *ctl_val = MSMON_CFG_CSU_CTL_TYPE_CSU;
> +
> + if (mpam_has_feature(mpam_feat_msmon_csu_xcl, &m->ris->props))
> + *flt_val |= FIELD_PREP(MSMON_CFG_CSU_FLT_XCL,
> + ctx->csu_exclude_clean);
> +
> + break;
> + case mpam_feat_msmon_mbwu:
> + *ctl_val = MSMON_CFG_MBWU_CTL_TYPE_MBWU;
> +
> + if (mpam_has_feature(mpam_feat_msmon_mbwu_rwbw, &m->ris->props))
> + *flt_val |= FIELD_PREP(MSMON_CFG_MBWU_FLT_RWBW, ctx->opts);
> +
> + break;
> + default:
> + return;
> + }
> +}
> +
> +static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
> + u32 *flt_val)
> +{
> + struct mpam_msc *msc = m->ris->vmsc->msc;
> +
> + switch (m->type) {
> + case mpam_feat_msmon_csu:
> + *ctl_val = mpam_read_monsel_reg(msc, CFG_CSU_CTL);
> + *flt_val = mpam_read_monsel_reg(msc, CFG_CSU_FLT);
> + break;
> + case mpam_feat_msmon_mbwu:
> + *ctl_val = mpam_read_monsel_reg(msc, CFG_MBWU_CTL);
> + *flt_val = mpam_read_monsel_reg(msc, CFG_MBWU_FLT);
> + break;
> + default:
> + return;
> + }
> +}
> +
> +/* Remove values set by the hardware to prevent apparent mismatches. */
> +static void clean_msmon_ctl_val(u32 *cur_ctl)
> +{
> + *cur_ctl &= ~MSMON_CFG_x_CTL_OFLOW_STATUS;
> +}
> +
> +static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
> + u32 flt_val)
> +{
> + struct mpam_msc *msc = m->ris->vmsc->msc;
> +
> + /*
> + * Write the ctl_val with the enable bit cleared, reset the counter,
> + * then enable counter.
> + */
> + switch (m->type) {
> + case mpam_feat_msmon_csu:
> + mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
> + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
> + mpam_write_monsel_reg(msc, CSU, 0);
> + mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
> + break;
> + case mpam_feat_msmon_mbwu:
> + mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
> + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
> + mpam_write_monsel_reg(msc, MBWU, 0);
> + mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
> + break;
> + default:
> + return;
> + }
> +}
> +
> +/* Call with MSC lock held */
> +static void __ris_msmon_read(void *arg)
> +{
> + u64 now;
> + bool nrdy = false;
> + struct mon_read *m = arg;
> + struct mon_cfg *ctx = m->ctx;
> + struct mpam_msc_ris *ris = m->ris;
> + struct mpam_props *rprops = &ris->props;
> + struct mpam_msc *msc = m->ris->vmsc->msc;
> + u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt;
> +
> + if (!mpam_mon_sel_lock(msc)) {
> + m->err = -EIO;
> + return;
> + }
> + mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) |
> + FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
> + mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
> +
> + /*
> + * Read the existing configuration to avoid re-writing the same values.
> + * This saves waiting for 'nrdy' on subsequent reads.
> + */
> + read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt);
> + clean_msmon_ctl_val(&cur_ctl);
> + gen_msmon_ctl_flt_vals(m, &ctl_val, &flt_val);
> + if (cur_flt != flt_val || cur_ctl != (ctl_val | MSMON_CFG_x_CTL_EN))
> + write_msmon_ctl_flt_vals(m, ctl_val, flt_val);
> +
> + switch (m->type) {
> + case mpam_feat_msmon_csu:
> + now = mpam_read_monsel_reg(msc, CSU);
> + if (mpam_has_feature(mpam_feat_msmon_csu_hw_nrdy, rprops))
> + nrdy = now & MSMON___NRDY;
> + break;
> + case mpam_feat_msmon_mbwu:
> + now = mpam_read_monsel_reg(msc, MBWU);
> + if (mpam_has_feature(mpam_feat_msmon_mbwu_hw_nrdy, rprops))
> + nrdy = now & MSMON___NRDY;
> + break;
> + default:
> + m->err = -EINVAL;
> + break;
> + }
> + mpam_mon_sel_unlock(msc);
> +
> + if (nrdy) {
> + m->err = -EBUSY;
> + return;
> + }
> +
> + now = FIELD_GET(MSMON___VALUE, now);
> + *m->val += now;
> +}
> +
> +static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
> +{
> + int err, idx;
> + struct mpam_msc *msc;
> + struct mpam_vmsc *vmsc;
> + struct mpam_msc_ris *ris;
> +
> + idx = srcu_read_lock(&mpam_srcu);
> + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) {
This can be list_for_each_entry_srcu(). (I thought I'd already commented
but turns out that was on another patch.)
> + msc = vmsc->msc;
> +
> + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) {
Also here.
[...]
Thanks,
Ben
Hi Ben,
On 11/09/2025 16:46, Ben Horgan wrote:
> On 9/10/25 21:43, James Morse wrote:
>> Reading a monitor involves configuring what you want to monitor, and
>> reading the value. Components made up of multiple MSC may need values
>> from each MSC. MSCs may take time to configure, returning 'not ready'.
>> The maximum 'not ready' time should have been provided by firmware.
>>
>> Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
>> not ready, then wait the full timeout value before trying again.
>> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
>> index cf190f896de1..1543c33c5d6a 100644
>> --- a/drivers/resctrl/mpam_devices.c
>> +++ b/drivers/resctrl/mpam_devices.c
>> @@ -898,6 +898,232 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
>> +static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
>> +{
>> + int err, idx;
>> + struct mpam_msc *msc;
>> + struct mpam_vmsc *vmsc;
>> + struct mpam_msc_ris *ris;
>> +
>> + idx = srcu_read_lock(&mpam_srcu);
>> + list_for_each_entry_rcu(vmsc, &comp->vmsc, comp_list) {
>
> This can be list_for_each_entry_srcu(). (I thought I'd already commented
> but turns out that was on another patch.)
>
>> + msc = vmsc->msc;
>> +
>> + list_for_each_entry_rcu(ris, &vmsc->ris, vmsc_list) {
>
> Also here.
Yup - thanks I missed these. Fixed.
I bet I went cross-eyed between here and mpam_apply_config() as they are structurally similar.
Thanks,
James
Hi James,
On 9/11/25 16:46, Ben Horgan wrote:
> Hi James,
>
> On 9/10/25 21:43, James Morse wrote:
>> Reading a monitor involves configuring what you want to monitor, and
>> reading the value. Components made up of multiple MSC may need values
>> from each MSC. MSCs may take time to configure, returning 'not ready'.
>> The maximum 'not ready' time should have been provided by firmware.
>>
>> Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
>> not ready, then wait the full timeout value before trying again.
>>
>> CC: Shanker Donthineni <sdonthineni@nvidia.com>
>> Signed-off-by: James Morse <james.morse@arm.com>
>> ---
>> Changes since v1:
>> * Added XCL support.
>> * Merged FLT/CTL constants.
>> * a spelling mistake in a comment.
>> * moved structrues around.
>> ---
>> drivers/resctrl/mpam_devices.c | 226 ++++++++++++++++++++++++++++++++
>> drivers/resctrl/mpam_internal.h | 19 +++
>> 2 files changed, 245 insertions(+)
>>
>> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
>> index cf190f896de1..1543c33c5d6a 100644
>> --- a/drivers/resctrl/mpam_devices.c
>> +++ b/drivers/resctrl/mpam_devices.c
>> +
>> +static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
>> + u32 *flt_val)
>> +{
>> + struct mon_cfg *ctx = m->ctx;
>> +
>> + /*
>> + * For CSU counters its implementation-defined what happens when not
>> + * filtering by partid.
>> + */
>> + *ctl_val |= MSMON_CFG_x_CTL_MATCH_PARTID;
>> +
>> + *flt_val = FIELD_PREP(MSMON_CFG_x_FLT_PARTID, ctx->partid);
>> + if (m->ctx->match_pmg) {
>> + *ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG;
>> + *flt_val |= FIELD_PREP(MSMON_CFG_x_FLT_PMG, ctx->pmg);
>> + }
>> +
>> + switch (m->type) {
>> + case mpam_feat_msmon_csu:
>> + *ctl_val = MSMON_CFG_CSU_CTL_TYPE_CSU;
>> +
>> + if (mpam_has_feature(mpam_feat_msmon_csu_xcl, &m->ris->props))
>> + *flt_val |= FIELD_PREP(MSMON_CFG_CSU_FLT_XCL,
>> + ctx->csu_exclude_clean);
>> +
>> + break;
>> + case mpam_feat_msmon_mbwu:
>> + *ctl_val = MSMON_CFG_MBWU_CTL_TYPE_MBWU;
As you mentioned offline, this zeroes the other bits in *ctl_val.
Thanks,
Ben
Hi Ben,
On 12/09/2025 16:08, Ben Horgan wrote:
> On 9/11/25 16:46, Ben Horgan wrote:
>> On 9/10/25 21:43, James Morse wrote:
>>> Reading a monitor involves configuring what you want to monitor, and
>>> reading the value. Components made up of multiple MSC may need values
>>> from each MSC. MSCs may take time to configure, returning 'not ready'.
>>> The maximum 'not ready' time should have been provided by firmware.
>>>
>>> Add mpam_msmon_read() to hide all this. If (one of) the MSC returns
>>> not ready, then wait the full timeout value before trying again.
>>> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
>>> index cf190f896de1..1543c33c5d6a 100644
>>> --- a/drivers/resctrl/mpam_devices.c
>>> +++ b/drivers/resctrl/mpam_devices.c
>>> +
>>> +static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
>>> + u32 *flt_val)
>>> +{
>>> + struct mon_cfg *ctx = m->ctx;
>>> +
>>> + /*
>>> + * For CSU counters its implementation-defined what happens when not
>>> + * filtering by partid.
>>> + */
>>> + *ctl_val |= MSMON_CFG_x_CTL_MATCH_PARTID;
>>> +
>>> + *flt_val = FIELD_PREP(MSMON_CFG_x_FLT_PARTID, ctx->partid);
>>> + if (m->ctx->match_pmg) {
>>> + *ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG;
>>> + *flt_val |= FIELD_PREP(MSMON_CFG_x_FLT_PMG, ctx->pmg);
>>> + }
>>> +
>>> + switch (m->type) {
>>> + case mpam_feat_msmon_csu:
>>> + *ctl_val = MSMON_CFG_CSU_CTL_TYPE_CSU;
>>> +
>>> + if (mpam_has_feature(mpam_feat_msmon_csu_xcl, &m->ris->props))
>>> + *flt_val |= FIELD_PREP(MSMON_CFG_CSU_FLT_XCL,
>>> + ctx->csu_exclude_clean);
>>> +
>>> + break;
>>> + case mpam_feat_msmon_mbwu:
>>> + *ctl_val = MSMON_CFG_MBWU_CTL_TYPE_MBWU;
>
> As you mentioned offline, this zeroes the other bits in *ctl_val.
Yes - a bug introduced during a late night, I've fixed this up.
Thanks,
James
© 2016 - 2026 Red Hat, Inc.