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]> --- 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), -- 2.43.0
