On Fri, 16 Oct 2026 18:10:48 +0800 Jinjie Ruan <[email protected]> wrote:
> From: Jinjie Ruan <[email protected]> > > Add the CSU monitor: MSMON_CFG_CSU_FLT/CTL window configuration > (replacing the unimplemented-monitor placeholder), the MSMON_CSU > and MSMON_CSU_CAPTURE reads, and the per-RIS monitor array with > the num-csu-mon and csu-capacity-bytes properties. Occupancy is > modelled as a snapshot: csu-capacity-bytes scaled by the CPBM > portion fraction of the filtered PARTID, so cache-usage readings > track the configured CBM. Writes to the CSU counters are ignored > (they probe NRDY; the snapshot is always ready) and CSU > monitoring is advertised in MSMON_IDR/CSUMON_IDR on the cache > subtype, which lets the driver pick QOS_L2/L3_OCCUP > (llc_occupancy). > > Signed-off-by: Jinjie Ruan <[email protected]> Looks good to me. Probably need some masking though on the writes where there are resv bits. > --- > hw/arm/mpam.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 97 insertions(+), 2 deletions(-) > > diff --git a/hw/arm/mpam.c b/hw/arm/mpam.c > index 50c8a8e..6c93ead 100644 > --- a/hw/arm/mpam.c > +++ b/hw/arm/mpam.c > @@ -11,6 +11,7 @@ > #include "qemu/timer.h" > #include "qemu/host-utils.h" > #include "qemu/bitops.h" > +#include "qemu/units.h" > #include "hw/core/irq.h" > #include "hw/core/qdev-properties.h" > #include "hw/core/registerfields.h" > @@ -139,8 +140,20 @@ REG32(MSMON_MBWU_L, 0x0880) /* 64-bit counter */ > REG32(MSMON_MBWU_CAPTURE, 0x0868) > REG32(MSMON_MBWU_CAPTURE_L, 0x0890) > REG32(MSMON_CAPT_EVNT, 0x0808) > + > REG32(MSMON_CFG_CSU_FLT, 0x0810) > + FIELD(MSMON_CFG_CSU_FLT, PARTID, 0, 16) > + FIELD(MSMON_CFG_CSU_FLT, PMG, 16, 8) > + FIELD(MSMON_CFG_CSU_FLT, XCL, 31, 1) > + > REG32(MSMON_CFG_CSU_CTL, 0x0818) > + FIELD(MSMON_CFG_CSU_CTL, TYPE, 0, 8) > + FIELD(MSMON_CFG_CSU_CTL, MATCH_PARTID, 16, 1) > + FIELD(MSMON_CFG_CSU_CTL, MATCH_PMG, 17, 1) > + FIELD(MSMON_CFG_CSU_CTL, EN, 31, 1) > + > +REG32(MSMON_CSU, 0x0840) /* 31-bit value, bit31 NRDY */ > +REG32(MSMON_CSU_CAPTURE, 0x0848) > > REG32(MPAMF_ERR_MSI_MPAM, 0x00dc) > REG32(MPAMF_ERR_MSI_ADDR_L, 0x00e0) > @@ -282,6 +295,16 @@ typedef struct MpamMbwuMon { > bool oflow; > } MpamMbwuMon; > > +/* > + * One CSU monitor instance. Occupancy is modelled as a snapshot: the > + * cache capacity scaled by the CPBM portion fraction of the filtered > + * PARTID, so cache-usage readings track the configured CBM. > + */ > +typedef struct MpamCsuMon { > + uint32_t flt; > + uint32_t ctl; > +} MpamCsuMon; > + > typedef struct Mpamf { > uint64_t idr; > uint32_t iidr; > @@ -308,6 +331,7 @@ typedef struct Mpamf { > MpamfPerNrwId *per_nrw_id; > > MpamMbwuMon *mbwu_mons; > + MpamCsuMon *csu_mons; > > } Mpamf; > > @@ -326,6 +350,8 @@ typedef struct MPAMMSCState { > uint8_t num_ris; > uint32_t num_mbwu_mon; > uint64_t bw_nominal_bps; > + uint32_t num_csu_mon; > + uint64_t csu_capacity; > > /* > * Wired-interrupt GSIVs the board assigned (INTID = SPI + 32). Zero > @@ -468,6 +494,36 @@ static uint64_t mpam_mbwu_value(MPAMMSCState *s, Mpamf > *mf, MpamMbwuMon *m) > return v; > } > > +static MpamCsuMon *mpam_csu_mon(MPAMMSCState *s, Mpamf *mf) > +{ > + unsigned mon = FIELD_EX32(s->mon_sel, MSMON_CFG_MON_SEL, MON_SEL); > + > + if (mon >= s->num_csu_mon) { > + qemu_log_mask(LOG_GUEST_ERROR, > + "MPAM: MON_SEL %d out of range (num-csu-mon %d)\n", > + mon, s->num_csu_mon); > + mon = 0; > + } > + return &mf->csu_mons[mon]; > +} > + > +/* > + * Snapshot occupancy: the cache capacity scaled by the fraction of CPBM > + * portions the filtered PARTID is allowed to occupy. > + */ > +static uint32_t mpam_csu_value(MPAMMSCState *s, Mpamf *mf, MpamCsuMon *m) > +{ > + if (FIELD_EX32(m->ctl, MSMON_CFG_CSU_CTL, EN) && > + FIELD_EX32(m->ctl, MSMON_CFG_CSU_CTL, MATCH_PARTID)) { > + uint32_t partid = FIELD_EX32(m->flt, MSMON_CFG_CSU_FLT, PARTID); > + uint32_t nrw = mpam_nrw_id_for(s, mf, partid); > + uint32_t cpbm = mf->per_nrw_id[nrw].cfg_cpbm[0]; > + > + return muldiv64(s->csu_capacity, ctpop32(cpbm), MPAM_CACHE_PART); > + } > + return 0; > +} > + > typedef struct MPAMMSCMemState { > MPAMMSCState parent; > } MPAMMSCMemState; > @@ -563,6 +619,21 @@ static uint64_t mpam_msc_read_reg(void *opaque, hwaddr > offset, > return mpamf->mbwumon_idr; > case A_MSMON_CFG_MON_SEL: > return s->mon_sel; > + case A_MSMON_CFG_CSU_FLT: > + return mpam_csu_mon(s, mpam_mon_ris(s))->flt; > + case A_MSMON_CFG_CSU_CTL: > + return mpam_csu_mon(s, mpam_mon_ris(s))->ctl; > + case A_MSMON_CSU ... (A_MSMON_CSU + 3): > + case A_MSMON_CSU_CAPTURE ... (A_MSMON_CSU_CAPTURE + 3): > + { > + uint32_t v = mpam_csu_value(s, mpam_mon_ris(s), > + mpam_csu_mon(s, mpam_mon_ris(s))); > + hwaddr off = (offset >= A_MSMON_CSU_CAPTURE) ? > + offset - A_MSMON_CSU_CAPTURE : offset - A_MSMON_CSU; > + > + /* NRDY (bit 31) is never set: the snapshot is always ready */ > + return extract32(v, off * 8, 32 - off * 8); > + } > case A_MSMON_CFG_MBWU_FLT: > return mpam_mbwu_mon(s, mpam_mon_ris(s))->flt; > case A_MSMON_CFG_MBWU_CTL: > @@ -879,11 +950,23 @@ static void mpam_msc_write_reg(void *opaque, hwaddr > offset, uint64_t value, > m->t0 = qemu_clock_get_ns(QEMU_CLOCK_HOST); > return; > } > - case A_MSMON_CAPT_EVNT: > case A_MSMON_CFG_CSU_FLT: > + mpam_csu_mon(s, mpam_mon_ris(s))->flt = value; > + return; > case A_MSMON_CFG_CSU_CTL: > + mpam_csu_mon(s, mpam_mon_ris(s))->ctl = value; > + return; > + case A_MSMON_CSU: > + case A_MSMON_CSU_CAPTURE: > + /* > + * The driver pokes these with MSMON___NRDY to probe whether the > + * counter needs polling. Ignoring the write leaves the snapshot > + * permanently ready, which is what the emulation models. > + */ > + return; > + case A_MSMON_CAPT_EVNT: > qemu_log_mask(LOG_UNIMP, > - "MPAM: cache-usage monitoring not implemented (write > %lx)\n", > + "MPAM: capture events not implemented (write %lx)\n", > offset); > return; > case A_MPAMF_ECR: > @@ -941,6 +1024,7 @@ static void mpam_msc_realize(DeviceState *dev, Error > **errp) > s->mpamf[i].per_nrw_id = g_new0(MpamfPerNrwId, s->num_int_partid); > s->mpamf[i].cfg_intpartid = g_new0(uint32_t, s->num_partid); > s->mpamf[i].mbwu_mons = g_new0(MpamMbwuMon, s->num_mbwu_mon); > + s->mpamf[i].csu_mons = g_new0(MpamCsuMon, s->num_csu_mon); > /* > * Until resctrl programs the map, wrap each request PARTID onto an > * internal PARTID so the narrowing indirection is always in range. > @@ -1088,6 +1172,14 @@ static void mpam_msc_cache_realize(DeviceState *dev, > Error **errp) > mpamf->mbwumon_idr = FIELD_DP32(mpamf->mbwumon_idr, > MPAMF_MBWUMON_IDR, HAS_RWBW, 1); > > + if (s->num_csu_mon) { > + mpamf->msmon_idr = FIELD_DP32(mpamf->msmon_idr, > + MPAMF_MSMON_IDR, MSMON_CSU, 1); > + mpamf->csumon_idr = FIELD_DP32(mpamf->csumon_idr, > + MPAMF_CSUMON_IDR, NUM_MON, > + s->num_csu_mon); > + } > + > /* Cache specific bit */ > mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_CPOR_PART, 1); > mpamf->idr = FIELD_DP64(mpamf->idr, MPAMF_IDR, HAS_CCAP_PART, 1); > @@ -1148,6 +1240,9 @@ static const Property mpam_msc_props[] = { > DEFINE_PROP_UINT32("num-mbwu-mon", MPAMMSCState, num_mbwu_mon, 8), > DEFINE_PROP_UINT64("bw-nominal-bps", MPAMMSCState, bw_nominal_bps, > 10000000000ULL), /* 10 GB/s default bandwidth */ > + DEFINE_PROP_UINT32("num-csu-mon", MPAMMSCState, num_csu_mon, 4), > + DEFINE_PROP_UINT64("csu-capacity-bytes", MPAMMSCState, csu_capacity, > + 32 * MiB), > DEFINE_PROP_UINT32("err-gsiv", MPAMMSCState, err_gsiv, 0), > DEFINE_PROP_UINT32("oflow-gsiv", MPAMMSCState, oflow_gsiv, 0), > DEFINE_PROP_UINT16("node", MPAMMSCState, node, UINT16_MAX),
