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


Reply via email to