[PATCH RFC v4 10/18] riscv_cbqri: Add bandwidth controller monitoring device ops
Drew Fustini
fustini at kernel.org
Sun May 10 22:11:06 PDT 2026
Add the BC monitoring primitives. cbqri_init_bc_mon_counters() pre- arms
each MCID with the TOTAL_READ_WRITE event and allocates the per-MCID
software accumulator (struct cbqri_bc_mon_state) so subsequent reads can
extend the 62-bit hardware counter to the 64-bit byte total resctrl
expects. cbqri_bc_mon_overflow() recovers a single-wrap delta. The OVF
bit signals multi-wrap and is the caller's concern.
cbqri_find_only_mon_bc() returns NULL when zero or more than one
mon-capable BC is present. A BC's counter can only accurately back L3
mbm_total_bytes when every memory request flows through that BC.
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 | 75 ++++++++++++++++++++++++++++++++++++++++
drivers/resctrl/cbqri_internal.h | 37 ++++++++++++++++++++
2 files changed, 112 insertions(+)
diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
index 7e5decd7a6b2..0b17a25b009c 100644
--- a/drivers/resctrl/cbqri_devices.c
+++ b/drivers/resctrl/cbqri_devices.c
@@ -820,8 +820,83 @@ int cbqri_init_mon_counters(struct cbqri_controller *ctrl)
return 0;
}
+/*
+ * 62-bit BC counter delta. Inputs must be pre-masked to
+ * CBQRI_BC_MON_CTR_VAL_CTR_MASK. The shift promotes the modular
+ * subtraction into 64-bit so a single wrap (cur < prev) yields the
+ * correct delta. Multi-wrap is handled by the caller via the
+ * hardware OVF bit (CBQRI 4.3). This function only needs to recover
+ * from at most one wrap.
+ */
+u64 cbqri_bc_mon_overflow(u64 prev_ctr, u64 cur_ctr)
+{
+ const unsigned int shift = 64 - 62;
+ u64 chunks = (cur_ctr << shift) - (prev_ctr << shift);
+
+ return chunks >> shift;
+}
+
+/*
+ * Allocate the per-MCID software accumulator and pre-arm every MCID
+ * with TOTAL_READ_WRITE so subsequent reads just snapshot the live
+ * counter.
+ *
+ * Caller responsibility: serialize concurrent invocations on the same
+ * single mon-capable BC (cbqri_resctrl uses cbqri_domain_list_lock for
+ * this).
+ */
+int cbqri_init_bc_mon_counters(struct cbqri_controller *bc)
+{
+ int i, err;
+
+ if (bc->mbm_total_states)
+ return 0;
+
+ bc->mbm_total_states = kcalloc(bc->mcid_count,
+ sizeof(*bc->mbm_total_states),
+ GFP_KERNEL);
+ if (!bc->mbm_total_states)
+ return -ENOMEM;
+
+ for (i = 0; i < bc->mcid_count; i++) {
+ mutex_lock(&bc->lock);
+ err = cbqri_mon_op(bc, CBQRI_BC_MON_CTL_OFF,
+ CBQRI_BC_MON_CTL_OP_CONFIG_EVENT,
+ i, CBQRI_BC_EVT_ID_TOTAL_READ_WRITE, NULL);
+ mutex_unlock(&bc->lock);
+ if (err) {
+ kfree(bc->mbm_total_states);
+ bc->mbm_total_states = NULL;
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return the single mon-capable BC, NULL if zero or more than one. BC
+ * counters can only honestly surface as L3 mbm_total_bytes if every memory
+ * request flows through the same BC.
+ */
+struct cbqri_controller *cbqri_find_only_mon_bc(void)
+{
+ struct cbqri_controller *ctrl, *only_bc = NULL;
+
+ list_for_each_entry(ctrl, &cbqri_controllers, list) {
+ if (ctrl->type != CBQRI_CONTROLLER_TYPE_BANDWIDTH)
+ continue;
+ if (!ctrl->mon_capable)
+ continue;
+ if (only_bc)
+ return NULL;
+ only_bc = ctrl;
+ }
+ return only_bc;
+}
+
void cbqri_controller_destroy(struct cbqri_controller *ctrl)
{
+ kfree(ctrl->mbm_total_states);
kfree(ctrl->rbwb_cache);
kfree(ctrl);
}
diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h
index 11a00f8e7436..1e5dd742273d 100644
--- a/drivers/resctrl/cbqri_internal.h
+++ b/drivers/resctrl/cbqri_internal.h
@@ -64,8 +64,17 @@
#define CBQRI_CC_MON_CTL_OP_CONFIG_EVENT 1
#define CBQRI_CC_MON_CTL_OP_READ_COUNTER 2
+#define CBQRI_BC_MON_CTL_OP_CONFIG_EVENT 1
#define CBQRI_BC_MON_CTL_OP_READ_COUNTER 2
+/* Bandwidth usage monitoring event IDs (CBQRI spec Table 10) */
+#define CBQRI_BC_EVT_ID_TOTAL_READ_WRITE 1
+
+/* bc_mon_ctr_val layout (CBQRI spec section 4.3, Figure 7) */
+#define CBQRI_BC_MON_CTR_VAL_CTR_MASK GENMASK_ULL(61, 0)
+#define CBQRI_BC_MON_CTR_VAL_INVALID BIT_ULL(62)
+#define CBQRI_BC_MON_CTR_VAL_OVF BIT_ULL(63)
+
/* 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)
@@ -91,6 +100,19 @@ struct riscv_cbqri_bandwidth_caps {
bool supports_alloc_at_code;
};
+/**
+ * struct cbqri_bc_mon_state - per-MCID software accumulator for BC bandwidth
+ * @prev_ctr: previous 62-bit hardware snapshot (already masked to CTR field)
+ * @chunks: accumulated 64-bit byte total across hardware wraparounds
+ *
+ * Updated in resctrl_arch_rmid_read() under cbqri_controller::lock and
+ * zeroed by resctrl_arch_reset_rmid().
+ */
+struct cbqri_bc_mon_state {
+ u64 prev_ctr;
+ u64 chunks;
+};
+
/**
* enum cbqri_at - capacity controller access type for CDP
* @CBQRI_AT_DATA: data access (CBQRI Table 1, AT=0)
@@ -158,6 +180,15 @@ struct cbqri_controller {
*/
u16 *rbwb_cache;
+ /*
+ * Per-MCID 64-bit software accumulator for the BC's mbm_total_bytes
+ * event. Allocated by cbqri_init_bc_mon_counters() when this BC is
+ * paired with an L3 monitoring domain, sized by ->mcid_count. NULL
+ * on capacity controllers and on BCs that are not mon-paired.
+ * Protected by ->lock along with the surrounding MMIO sequence.
+ */
+ struct cbqri_bc_mon_state *mbm_total_states;
+
struct list_head list;
struct cache_controller {
@@ -200,4 +231,10 @@ int cbqri_read_rbwb(struct cbqri_controller *ctrl, u32 closid, u64 *rbwb_out);
int cbqri_read_mweight(struct cbqri_controller *ctrl, u32 closid, u64 *mweight_out);
+u64 cbqri_bc_mon_overflow(u64 prev_ctr, u64 cur_ctr);
+
+int cbqri_init_bc_mon_counters(struct cbqri_controller *bc);
+
+struct cbqri_controller *cbqri_find_only_mon_bc(void);
+
#endif /* _DRIVERS_RESCTRL_CBQRI_INTERNAL_H */
--
2.43.0
More information about the linux-riscv
mailing list