[PATCH RFC v4 08/18] riscv_cbqri: Add capacity controller monitoring device ops
Drew Fustini
fustini at kernel.org
Sun May 10 22:11:04 PDT 2026
Add the CC monitoring primitives. cbqri_init_mon_counters() pre-arms
every MCID with the Occupancy event so a subsequent READ_COUNTER
just snapshots the live counter without re-configuring the slot.
cbqri_probe_cc() leaves ctrl->mon_capable false when cacheinfo has
not given a non-zero cache_size, since the byte conversion would be
meaningless. cbqri_mon_op() takes a reg_offset and serves both CC
and BC mon_ctl registers, which share an identical layout.
Assisted-by: Claude:claude-opus-4-7
Co-developed-by: Adrien Ricciardi <aricciardi at baylibre.com>
Signed-off-by: Adrien Ricciardi <aricciardi at baylibre.com>
Signed-off-by: Drew Fustini <fustini at kernel.org>
---
drivers/resctrl/cbqri_devices.c | 85 ++++++++++++++++++++++++++++++++++++++++
drivers/resctrl/cbqri_internal.h | 21 ++++++++++
2 files changed, 106 insertions(+)
diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
index dc76a146e34d..e46b02d2c50d 100644
--- a/drivers/resctrl/cbqri_devices.c
+++ b/drivers/resctrl/cbqri_devices.c
@@ -105,6 +105,46 @@ static int cbqri_cc_alloc_op(struct cbqri_controller *ctrl, int operation,
return 0;
}
+/*
+ * Issue a monitoring op on a CC or BC controller's mon_ctl register at
+ * reg_offset (CBQRI_CC_MON_CTL_OFF or CBQRI_BC_MON_CTL_OFF). The CC and
+ * BC mon_ctl registers share an identical OP/MCID/EVT_ID/STATUS layout, so
+ * one helper covers both. Caller must hold ctrl->lock.
+ */
+int cbqri_mon_op(struct cbqri_controller *ctrl, int reg_offset,
+ int operation, int mcid, int evt_id, u64 *out_reg)
+{
+ u64 reg;
+
+ lockdep_assert_held(&ctrl->lock);
+
+ if (ctrl->faulted)
+ return -EIO;
+
+ if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) {
+ pr_err_ratelimited("BUSY timeout before starting operation\n");
+ return -EIO;
+ }
+ FIELD_MODIFY(CBQRI_MON_CTL_OP_MASK, ®, operation);
+ FIELD_MODIFY(CBQRI_MON_CTL_MCID_MASK, ®, mcid);
+ FIELD_MODIFY(CBQRI_MON_CTL_EVT_ID_MASK, ®, evt_id);
+ iowrite64(reg, ctrl->base + reg_offset);
+
+ if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) {
+ pr_err_ratelimited("BUSY timeout\n");
+ return -EIO;
+ }
+
+ if (FIELD_GET(CBQRI_MON_CTL_STATUS_MASK, reg) !=
+ CBQRI_MON_CTL_STATUS_SUCCESS)
+ return -EIO;
+
+ if (out_reg)
+ *out_reg = reg;
+
+ return 0;
+}
+
/*
* Apply a capacity block mask and verify via CONFIG_LIMIT + READ_LIMIT.
*
@@ -310,6 +350,7 @@ static int cbqri_probe_feature(struct cbqri_controller *ctrl, int reg_offset,
static int cbqri_probe_cc(struct cbqri_controller *ctrl)
{
+ bool has_mon_at_code = false;
int err, status;
u64 reg;
@@ -361,6 +402,28 @@ static int cbqri_probe_cc(struct cbqri_controller *ctrl)
}
cpus_read_unlock();
+ /* Probe monitoring features */
+ err = cbqri_probe_feature(ctrl, CBQRI_CC_MON_CTL_OFF,
+ CBQRI_CC_MON_CTL_OP_READ_COUNTER, &status,
+ &has_mon_at_code);
+ if (err)
+ return err;
+
+ if (status == CBQRI_MON_CTL_STATUS_SUCCESS) {
+ /*
+ * Occupancy is reported to userspace in bytes, computed as
+ * cache_size * counter / ncblks by the resctrl glue. If
+ * cacheinfo has no cache_size, leave mon_capable false so
+ * the file is not exposed at all rather than silently
+ * returning 0.
+ */
+ if (!ctrl->cache.cache_size)
+ pr_debug("CC @%pa: cache_size unknown, occupancy monitoring disabled\n",
+ &ctrl->addr);
+ else
+ ctrl->mon_capable = true;
+ }
+
/* Probe allocation features */
err = cbqri_probe_feature(ctrl, CBQRI_CC_ALLOC_CTL_OFF,
CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT,
@@ -428,6 +491,28 @@ static int cbqri_probe_controller(struct cbqri_controller *ctrl)
return err;
}
+/*
+ * Pre-arm every MCID with the Occupancy event so a subsequent READ_COUNTER
+ * just snapshots the live counter rather than re-configuring the slot.
+ * Called once per CC during resctrl-side cpuhp online for the L3 monitoring
+ * domain.
+ */
+int cbqri_init_mon_counters(struct cbqri_controller *ctrl)
+{
+ int i, err;
+
+ for (i = 0; i < ctrl->mcid_count; i++) {
+ mutex_lock(&ctrl->lock);
+ err = cbqri_mon_op(ctrl, CBQRI_CC_MON_CTL_OFF,
+ CBQRI_CC_MON_CTL_OP_CONFIG_EVENT,
+ i, CBQRI_CC_EVT_ID_OCCUPANCY, NULL);
+ mutex_unlock(&ctrl->lock);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
void cbqri_controller_destroy(struct cbqri_controller *ctrl)
{
kfree(ctrl);
diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h
index 6a581a7e417b..b1169ffc599f 100644
--- a/drivers/resctrl/cbqri_internal.h
+++ b/drivers/resctrl/cbqri_internal.h
@@ -11,6 +11,8 @@
/* Capacity Controller (CC) MMIO register offsets. */
#define CBQRI_CC_CAPABILITIES_OFF 0
+#define CBQRI_CC_MON_CTL_OFF 8
+#define CBQRI_CC_MON_CTL_VAL_OFF 16
#define CBQRI_CC_ALLOC_CTL_OFF 24
#define CBQRI_CC_BLOCK_MASK_OFF 32
@@ -39,6 +41,20 @@
#define CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT 2
#define CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS 1
+#define CBQRI_CC_MON_CTL_OP_CONFIG_EVENT 1
+#define CBQRI_CC_MON_CTL_OP_READ_COUNTER 2
+
+/* mon_ctl field masks (CC and BC share an identical OP/MCID/EVT_ID/STATUS layout) */
+#define CBQRI_MON_CTL_OP_MASK GENMASK(4, 0)
+#define CBQRI_MON_CTL_MCID_MASK GENMASK(19, 8)
+#define CBQRI_MON_CTL_EVT_ID_MASK GENMASK(27, 20)
+#define CBQRI_MON_CTL_STATUS_MASK GENMASK_ULL(38, 32)
+#define CBQRI_MON_CTL_STATUS_SUCCESS 1
+
+/* Capacity usage monitoring event IDs (CBQRI spec Table 4) */
+#define CBQRI_CC_EVT_ID_NONE 0
+#define CBQRI_CC_EVT_ID_OCCUPANCY 1
+
/* Capacity Controller hardware capabilities */
struct riscv_cbqri_capacity_caps {
u16 ncblks;
@@ -122,4 +138,9 @@ int cbqri_apply_cache_config(struct cbqri_controller *ctrl, u32 closid,
int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid,
enum cbqri_at at, u32 *cbm_out);
+int cbqri_mon_op(struct cbqri_controller *ctrl, int reg_offset,
+ int operation, int mcid, int evt_id, u64 *out_reg);
+
+int cbqri_init_mon_counters(struct cbqri_controller *ctrl);
+
#endif /* _DRIVERS_RESCTRL_CBQRI_INTERNAL_H */
--
2.43.0
More information about the linux-riscv
mailing list