[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