[PATCH RFC v3 06/11] RISC-V: QoS: add resctrl setup and domain management

Drew Fustini <[email protected]>
Newsgroups dev.linux.lists.acpica-devel,org.infradead.lists.linux-riscv,org.kernel.vger.linux-acpi,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Add the setup and domain management layer: domain allocation
(qos_new_domain), controller value initialization
(qos_init_domain_ctrlval), resource struct initialization for cache and
bandwidth resources, domain registration with the resctrl filesystem
(qos_resctrl_add_controller_domain), and the top-level setup function
(qos_resctrl_setup) that probes all controllers and calls resctrl_init().

Also add qos_resctrl_online_cpu() and qos_resctrl_offline_cpu() for CPU
hotplug integration.

Co-developed-by: Adrien Ricciardi <[email protected]>
Signed-off-by: Adrien Ricciardi <[email protected]>
Signed-off-by: Drew Fustini <[email protected]>
---
 arch/riscv/kernel/qos/qos_resctrl.c | 295 +++++++++++++++++++++++++++++++++++-
 1 file changed, 294 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kernel/qos/qos_resctrl.c b/arch/riscv/kernel/qos/qos_resctrl.c
index a4a120f89840..8d7e3b0abb75 100644
--- a/arch/riscv/kernel/qos/qos_resctrl.c
+++ b/arch/riscv/kernel/qos/qos_resctrl.c
@@ -675,7 +675,23 @@ void resctrl_arch_reset_rmid_all(struct rdt_resource *r, struct rdt_l3_mon_domai
 
 void resctrl_arch_reset_all_ctrls(struct rdt_resource *r)
 {
-	/* not implemented for the RISC-V resctrl implementation */
+	struct cbqri_resctrl_res *hw_res;
+	struct rdt_ctrl_domain *d;
+	enum resctrl_conf_type t;
+	u32 default_ctrl;
+	int i;
+
+	lockdep_assert_cpus_held();
+
+	hw_res = container_of(r, struct cbqri_resctrl_res, resctrl_res);
+	default_ctrl = resctrl_get_default_ctrl(r);
+
+	list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
+		for (i = 0; i < hw_res->max_rcid; i++) {
+			for (t = 0; t < CDP_NUM_TYPES; t++)
+				resctrl_arch_update_one(r, d, i, t, default_ctrl);
+		}
+	}
 }
 
 void resctrl_arch_pre_mount(void)
@@ -797,3 +813,280 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
 	spin_unlock(&ctrl->lock);
 	return val;
 }
+
+static struct rdt_ctrl_domain *qos_new_domain(struct cbqri_controller *ctrl)
+{
+	struct cbqri_resctrl_dom *hw_dom;
+	struct rdt_ctrl_domain *domain;
+
+	hw_dom = kzalloc_obj(*hw_dom, GFP_KERNEL);
+	if (!hw_dom)
+		return NULL;
+
+	/* associate this cbqri_controller with the domain */
+	hw_dom->hw_ctrl = ctrl;
+
+	/* the rdt_domain struct from inside the cbqri_resctrl_dom struct */
+	domain = &hw_dom->resctrl_ctrl_dom;
+
+	INIT_LIST_HEAD(&domain->hdr.list);
+
+	return domain;
+}
+
+static int qos_init_domain_ctrlval(struct rdt_resource *r, struct rdt_ctrl_domain *d)
+{
+	struct cbqri_resctrl_res *hw_res;
+	int err = 0;
+	int i;
+
+	hw_res = container_of(r, struct cbqri_resctrl_res, resctrl_res);
+
+	for (i = 0; i < hw_res->max_rcid; i++) {
+		err = resctrl_arch_update_one(r, d, i, 0, resctrl_get_default_ctrl(r));
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+static int qos_init_cache_resource(struct cbqri_controller *ctrl,
+				   struct cbqri_resctrl_res *cbqri_res,
+				   enum resctrl_res_level rid, char *name,
+				   enum resctrl_scope scope)
+{
+	struct rdt_resource *res = &cbqri_res->resctrl_res;
+
+	/* Already initialized by a previous controller at this cache level */
+	if (res->name) {
+		if (cbqri_res->max_rcid != ctrl->rcid_count ||
+		    res->cache.cbm_len != ctrl->cc.ncblks) {
+			pr_err("%s controllers have mismatched capabilities\n",
+			       name);
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	cbqri_res->max_rcid = ctrl->rcid_count;
+	cbqri_res->max_mcid = ctrl->mcid_count;
+	res->rid = rid;
+	res->name = name;
+	res->alloc_capable = ctrl->alloc_capable;
+	res->schema_fmt = RESCTRL_SCHEMA_BITMAP;
+	res->ctrl_scope = scope;
+	res->cache.cbm_len = ctrl->cc.ncblks;
+	res->cache.shareable_bits = resctrl_get_default_ctrl(res);
+	res->cache.min_cbm_bits = 1;
+	return 0;
+}
+
+static int qos_init_membw_resource(struct cbqri_controller *ctrl,
+				   struct cbqri_resctrl_res *cbqri_res)
+{
+	struct rdt_resource *res = &cbqri_res->resctrl_res;
+
+	if (res->name) {
+		if (cbqri_res->max_rcid != ctrl->rcid_count ||
+		    res->membw.max_bw != DIV_ROUND_UP(ctrl->bc.mrbwb * 100,
+						      ctrl->bc.nbwblks)) {
+			pr_err("MB controllers have mismatched capabilities\n");
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	cbqri_res->max_rcid = ctrl->rcid_count;
+	cbqri_res->max_mcid = ctrl->mcid_count;
+	res->rid = RDT_RESOURCE_MBA;
+	res->name = "MB";
+	res->alloc_capable = ctrl->alloc_capable;
+	res->schema_fmt = RESCTRL_SCHEMA_RANGE;
+	/*
+	 * resctrl requires a cache scope for MBA domains. Use L3 as a
+	 * proxy until the framework supports non-cache scopes for
+	 * bandwidth resources.
+	 */
+	res->ctrl_scope = RESCTRL_L3_CACHE;
+	res->membw.delay_linear = true;
+	res->membw.arch_needs_linear = true;
+	res->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED;
+	res->membw.min_bw = 1;
+	res->membw.max_bw = DIV_ROUND_UP(ctrl->bc.mrbwb * 100, ctrl->bc.nbwblks);
+	res->membw.bw_gran = 1;
+	return 0;
+}
+
+static int qos_resctrl_add_controller_domain(struct cbqri_controller *ctrl)
+{
+	struct rdt_ctrl_domain *domain;
+	struct cbqri_resctrl_res *cbqri_res = NULL;
+	struct rdt_resource *res = NULL;
+	struct list_head *pos = NULL;
+	int err;
+
+	domain = qos_new_domain(ctrl);
+	if (!domain)
+		return -ENOSPC;
+
+	switch (ctrl->type) {
+	case CBQRI_CONTROLLER_TYPE_CAPACITY:
+		cpumask_copy(&domain->hdr.cpu_mask, &ctrl->cache.cpu_mask);
+		domain->hdr.id = ctrl->cache.cache_id;
+
+		if (ctrl->cache.cache_level == 2) {
+			cbqri_res = &cbqri_resctrl_resources[RDT_RESOURCE_L2];
+			err = qos_init_cache_resource(ctrl, cbqri_res,
+						      RDT_RESOURCE_L2, "L2",
+						      RESCTRL_L2_CACHE);
+		} else if (ctrl->cache.cache_level == 3) {
+			cbqri_res = &cbqri_resctrl_resources[RDT_RESOURCE_L3];
+			err = qos_init_cache_resource(ctrl, cbqri_res,
+						      RDT_RESOURCE_L3, "L3",
+						      RESCTRL_L3_CACHE);
+		} else {
+			pr_err("unknown cache level %d\n", ctrl->cache.cache_level);
+			err = -ENODEV;
+		}
+		if (err)
+			goto err_free_domain;
+		res = &cbqri_res->resctrl_res;
+		break;
+
+	case CBQRI_CONTROLLER_TYPE_BANDWIDTH:
+		cpumask_copy(&domain->hdr.cpu_mask, &ctrl->mem.cpu_mask);
+		domain->hdr.id = ctrl->mem.prox_dom;
+		if (ctrl->alloc_capable) {
+			cbqri_res = &cbqri_resctrl_resources[RDT_RESOURCE_MBA];
+			err = qos_init_membw_resource(ctrl, cbqri_res);
+			if (err)
+				goto err_free_domain;
+			res = &cbqri_res->resctrl_res;
+		}
+		break;
+
+	default:
+		pr_err("unknown controller type %d\n", ctrl->type);
+		err = -ENODEV;
+		goto err_free_domain;
+	}
+
+	if (!res)
+		goto out;
+
+	err = qos_init_domain_ctrlval(res, domain);
+	if (err)
+		goto err_free_domain;
+
+	if (resctrl_find_domain(&res->ctrl_domains, domain->hdr.id, &pos)) {
+		pr_err("duplicate domain id %d for resource %s\n",
+		       domain->hdr.id, res->name);
+		err = -EEXIST;
+		goto err_free_domain;
+	}
+	if (pos)
+		list_add_tail(&domain->hdr.list, pos);
+	else
+		list_add_tail(&domain->hdr.list, &res->ctrl_domains);
+
+	err = resctrl_online_ctrl_domain(res, domain);
+	if (err) {
+		pr_err("failed to online domain %d\n", domain->hdr.id);
+		list_del(&domain->hdr.list);
+		goto err_free_domain;
+	}
+
+out:
+	return 0;
+
+err_free_domain:
+	kfree(container_of(domain, struct cbqri_resctrl_dom, resctrl_ctrl_dom));
+	return err;
+}
+
+int qos_resctrl_setup(void)
+{
+	struct rdt_ctrl_domain *domain, *domain_temp;
+	struct cbqri_controller *ctrl;
+	struct cbqri_resctrl_res *res;
+	int err = 0;
+	int i = 0;
+
+	max_rmid = U32_MAX;
+
+	for (i = 0; i < RDT_NUM_RESOURCES; i++) {
+		res = &cbqri_resctrl_resources[i];
+		INIT_LIST_HEAD(&res->resctrl_res.ctrl_domains);
+		INIT_LIST_HEAD(&res->resctrl_res.mon_domains);
+		res->resctrl_res.rid = i;
+	}
+
+	list_for_each_entry(ctrl, &cbqri_controllers, list) {
+		err = cbqri_probe_controller(ctrl);
+		if (err) {
+			pr_err("%s(): failed (%d)\n", __func__, err);
+			goto err_free_controllers_list;
+		}
+
+		err = qos_resctrl_add_controller_domain(ctrl);
+		if (err) {
+			pr_err("%s(): failed to add controller domain (%d)\n", __func__, err);
+			goto err_free_controllers_list;
+		}
+
+		/*
+		 * CDP (code data prioritization) on x86 is similar to
+		 * the AT (access type) field in CBQRI. CDP only supports
+		 * caches so this must be a CBQRI capacity controller.
+		 */
+		if (ctrl->type == CBQRI_CONTROLLER_TYPE_CAPACITY &&
+		    ctrl->cc.supports_alloc_at_code) {
+			if (ctrl->cache.cache_level == 2)
+				exposed_cdp_l2_capable = true;
+			else
+				exposed_cdp_l3_capable = true;
+		}
+	}
+	pr_debug("alloc=%d cdp_l2=%d cdp_l3=%d\n",
+		 exposed_alloc_capable,
+		 exposed_cdp_l2_capable, exposed_cdp_l3_capable);
+
+	err = resctrl_init();
+	if (err)
+		goto err_free_controllers_list;
+
+	return 0;
+
+err_free_controllers_list:
+	for (i = 0; i < RDT_NUM_RESOURCES; i++) {
+		res = &cbqri_resctrl_resources[i];
+		list_for_each_entry_safe(domain, domain_temp, &res->resctrl_res.ctrl_domains,
+					 hdr.list) {
+			resctrl_offline_ctrl_domain(&res->resctrl_res, domain);
+			list_del(&domain->hdr.list);
+			kfree(container_of(domain, struct cbqri_resctrl_dom, resctrl_ctrl_dom));
+		}
+	}
+
+	list_for_each_entry(ctrl, &cbqri_controllers, list) {
+		if (!ctrl->base)
+			break;
+		iounmap(ctrl->base);
+		ctrl->base = NULL;
+		release_mem_region(ctrl->addr, ctrl->size);
+	}
+
+	return err;
+}
+
+int qos_resctrl_online_cpu(unsigned int cpu)
+{
+	resctrl_online_cpu(cpu);
+	return 0;
+}
+
+int qos_resctrl_offline_cpu(unsigned int cpu)
+{
+	resctrl_offline_cpu(cpu);
+	return 0;
+}

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.