[PATCH RFC v6 14/18] riscv_cbqri: resctrl: Add MB_WGHT bandwidth allocation via Mweight
From: Drew Fustini
Date: Mon Jun 01 2026 - 16:39:26 EST
Add bandwidth allocation through Mweight (shared weight for unreserved
bandwidth) exposed as the MB_WGHT resource. Mweight has no MBA
equivalent, so it lands as a new RDT_RESOURCE_*.
Mweight is an integer in [0, 255]. A value of 0 disables work-
conserving sharing for the group, capping its bandwidth at the
MB_MIN reservation. Values 1..255 compete for the leftover pool in
proportion to the weight.
The same BC backs both MB_MIN and MB_WGHT and bc_bw_alloc packs Rbwb and
Mweight in one register. cbqri_attach_cpu_to_bw_ctrl() attaches both
rids to the picked BC.
Reset gives every RCID the new-group default (max_bw = 255) for
equal opportunistic shares.
Assisted-by: Claude:claude-opus-4-7
Co-developed-by: Adrien Ricciardi <aricciardi@xxxxxxxxxxxx>
Signed-off-by: Adrien Ricciardi <aricciardi@xxxxxxxxxxxx>
Signed-off-by: Drew Fustini <fustini@xxxxxxxxxx>
---
drivers/resctrl/cbqri_resctrl.c | 90 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 86 insertions(+), 4 deletions(-)
diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctrl.c
index 1d312004b07d..14b955eb7949 100644
--- a/drivers/resctrl/cbqri_resctrl.c
+++ b/drivers/resctrl/cbqri_resctrl.c
@@ -366,6 +366,8 @@ int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d,
case RDT_RESOURCE_MB_MIN:
/* sum(Rbwb) <= MRBWB validation runs inside cbqri_apply_rbwb(). */
return cbqri_apply_rbwb(dom->hw_ctrl, closid, cfg_val, true);
+ case RDT_RESOURCE_MB_WGHT:
+ return cbqri_apply_mweight_config(dom->hw_ctrl, closid, cfg_val);
default:
return -EINVAL;
}
@@ -426,6 +428,14 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
val = (u32)rbwb;
break;
}
+ case RDT_RESOURCE_MB_WGHT: {
+ u64 mweight;
+
+ err = cbqri_read_mweight(ctrl, closid, &mweight);
+ if (err == 0)
+ val = (u32)mweight;
+ break;
+ }
default:
break;
}
@@ -485,6 +495,18 @@ void resctrl_arch_reset_all_ctrls(struct rdt_resource *r)
rcid, rerr);
}
break;
+ case RDT_RESOURCE_MB_WGHT:
+ /* All RCIDs start at max weight (the new-group default). */
+ for (i = 0; i < hw_res->ctrl->rcid_count; i++) {
+ int rerr;
+
+ rerr = cbqri_apply_mweight_config(dom->hw_ctrl, i,
+ default_ctrl);
+ if (rerr)
+ pr_err_ratelimited("Mweight reset RCID %u failed (%d)\n",
+ i, rerr);
+ }
+ break;
default:
for (i = 0; i < hw_res->ctrl->rcid_count; i++) {
for (t = 0; t < CDP_NUM_TYPES; t++) {
@@ -549,6 +571,11 @@ static int cbqri_init_domain_ctrlval(struct rdt_resource *r, struct rdt_ctrl_dom
err = cbqri_apply_rbwb(dom->hw_ctrl, rcid, rbwb, false);
break;
+ case RDT_RESOURCE_MB_WGHT:
+ /* Match the new-group default: equal weights across RCIDs. */
+ err = cbqri_apply_mweight_config(dom->hw_ctrl, i,
+ resctrl_get_default_ctrl(r));
+ break;
default:
/*
* Seed both DATA and CODE staged slots so a later
@@ -690,6 +717,25 @@ static int cbqri_resctrl_control_init(struct cbqri_resctrl_res *cbqri_res)
INIT_LIST_HEAD(&res->mon_domains);
break;
+ case RDT_RESOURCE_MB_WGHT:
+ res->name = "MB_WGHT";
+ res->schema_fmt = RESCTRL_SCHEMA_RANGE;
+ res->ctrl_scope = RESCTRL_L3_CACHE;
+ /* Mweight is a dimensionless ratio. No delay/linear concept. */
+ res->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
+ /*
+ * CBQRI section 4.5: Mweight is 0-255 (0 disables
+ * work-conserving). No sum constraint, so leave
+ * default_to_min false. Groups default to max_bw.
+ */
+ res->membw.min_bw = 0;
+ res->membw.max_bw = 255;
+ res->membw.bw_gran = 1;
+ res->alloc_capable = ctrl->alloc_capable;
+ INIT_LIST_HEAD(&res->ctrl_domains);
+ INIT_LIST_HEAD(&res->mon_domains);
+ break;
+
default:
break;
}
@@ -698,13 +744,12 @@ static int cbqri_resctrl_control_init(struct cbqri_resctrl_res *cbqri_res)
}
/*
- * Pick one BC to back MB_MIN. Multiple BCs must agree on rcid_count
- * and mrbwb. Mismatch is fatal because resctrl exposes a single set
- * of caps per rid.
+ * Pick one BC to back both MB_MIN and MB_WGHT.
*/
static int cbqri_resctrl_pick_bw_alloc(void)
{
struct cbqri_resctrl_res *mb_min = &cbqri_resctrl_resources[RDT_RESOURCE_MB_MIN];
+ struct cbqri_resctrl_res *mb_wght = &cbqri_resctrl_resources[RDT_RESOURCE_MB_WGHT];
struct cbqri_controller *ctrl;
list_for_each_entry(ctrl, &cbqri_controllers, list) {
@@ -723,6 +768,7 @@ static int cbqri_resctrl_pick_bw_alloc(void)
}
mb_min->ctrl = ctrl;
+ mb_wght->ctrl = ctrl;
}
return 0;
@@ -961,10 +1007,46 @@ static int cbqri_attach_cpu_to_one_bw_res(struct cbqri_controller *ctrl,
return 0;
}
+static void cbqri_detach_cpu_from_one_bw_res(struct cbqri_controller *ctrl,
+ enum resctrl_res_level rid,
+ unsigned int cpu)
+{
+ struct cbqri_resctrl_res *hw_res = &cbqri_resctrl_resources[rid];
+ struct rdt_resource *res = &hw_res->resctrl_res;
+ struct rdt_ctrl_domain *domain;
+ int dom_id = ctrl->mem.prox_dom;
+
+ lockdep_assert_held(&cbqri_domain_list_lock);
+
+ if (!hw_res->ctrl)
+ return;
+
+ domain = cbqri_find_ctrl_domain(&res->ctrl_domains, dom_id);
+ if (!domain || !cpumask_test_cpu(cpu, &domain->hdr.cpu_mask))
+ return;
+
+ cpumask_clear_cpu(cpu, &domain->hdr.cpu_mask);
+ if (cpumask_empty(&domain->hdr.cpu_mask)) {
+ resctrl_offline_ctrl_domain(res, domain);
+ list_del(&domain->hdr.list);
+ kfree(container_of(domain, struct cbqri_resctrl_dom,
+ resctrl_ctrl_dom));
+ }
+}
+
static int cbqri_attach_cpu_to_bw_ctrl(struct cbqri_controller *ctrl,
unsigned int cpu)
{
- return cbqri_attach_cpu_to_one_bw_res(ctrl, RDT_RESOURCE_MB_MIN, cpu);
+ int err;
+
+ err = cbqri_attach_cpu_to_one_bw_res(ctrl, RDT_RESOURCE_MB_MIN, cpu);
+ if (err)
+ return err;
+
+ err = cbqri_attach_cpu_to_one_bw_res(ctrl, RDT_RESOURCE_MB_WGHT, cpu);
+ if (err)
+ cbqri_detach_cpu_from_one_bw_res(ctrl, RDT_RESOURCE_MB_MIN, cpu);
+ return err;
}
static void cbqri_detach_cpu_from_l3_mon(struct rdt_resource *res,
--
2.43.0