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


Reply via email to