109e61dafSJames Morse // SPDX-License-Identifier: GPL-2.0 209e61dafSJames Morse // Copyright (C) 2025 Arm Ltd. 309e61dafSJames Morse 409e61dafSJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 509e61dafSJames Morse 609e61dafSJames Morse #include <linux/arm_mpam.h> 709e61dafSJames Morse #include <linux/cacheinfo.h> 809e61dafSJames Morse #include <linux/cpu.h> 909e61dafSJames Morse #include <linux/cpumask.h> 1009e61dafSJames Morse #include <linux/errno.h> 119d2e1a99SJames Morse #include <linux/limits.h> 1209e61dafSJames Morse #include <linux/list.h> 1309e61dafSJames Morse #include <linux/printk.h> 1409e61dafSJames Morse #include <linux/rculist.h> 1509e61dafSJames Morse #include <linux/resctrl.h> 1609e61dafSJames Morse #include <linux/slab.h> 1709e61dafSJames Morse #include <linux/types.h> 1809e61dafSJames Morse 1909e61dafSJames Morse #include <asm/mpam.h> 2009e61dafSJames Morse 2109e61dafSJames Morse #include "mpam_internal.h" 2209e61dafSJames Morse 2309e61dafSJames Morse /* 2409e61dafSJames Morse * The classes we've picked to map to resctrl resources, wrapped 2509e61dafSJames Morse * in with their resctrl structure. 2609e61dafSJames Morse * Class pointer may be NULL. 2709e61dafSJames Morse */ 2809e61dafSJames Morse static struct mpam_resctrl_res mpam_resctrl_controls[RDT_NUM_RESOURCES]; 2909e61dafSJames Morse 3009e61dafSJames Morse #define for_each_mpam_resctrl_control(res, rid) \ 3109e61dafSJames Morse for (rid = 0, res = &mpam_resctrl_controls[rid]; \ 3209e61dafSJames Morse rid < RDT_NUM_RESOURCES; \ 3309e61dafSJames Morse rid++, res = &mpam_resctrl_controls[rid]) 3409e61dafSJames Morse 3509e61dafSJames Morse /* The lock for modifying resctrl's domain lists from cpuhp callbacks. */ 3609e61dafSJames Morse static DEFINE_MUTEX(domain_list_lock); 3709e61dafSJames Morse 38*6789fb99SJames Morse /* 39*6789fb99SJames Morse * MPAM emulates CDP by setting different PARTID in the I/D fields of MPAM0_EL1. 40*6789fb99SJames Morse * This applies globally to all traffic the CPU generates. 41*6789fb99SJames Morse */ 429d2e1a99SJames Morse static bool cdp_enabled; 439d2e1a99SJames Morse 4409e61dafSJames Morse bool resctrl_arch_alloc_capable(void) 4509e61dafSJames Morse { 4609e61dafSJames Morse struct mpam_resctrl_res *res; 4709e61dafSJames Morse enum resctrl_res_level rid; 4809e61dafSJames Morse 4909e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 5009e61dafSJames Morse if (res->resctrl_res.alloc_capable) 5109e61dafSJames Morse return true; 5209e61dafSJames Morse } 5309e61dafSJames Morse 5409e61dafSJames Morse return false; 5509e61dafSJames Morse } 5609e61dafSJames Morse 57*6789fb99SJames Morse bool resctrl_arch_get_cdp_enabled(enum resctrl_res_level rid) 58*6789fb99SJames Morse { 59*6789fb99SJames Morse return mpam_resctrl_controls[rid].cdp_enabled; 60*6789fb99SJames Morse } 61*6789fb99SJames Morse 62*6789fb99SJames Morse /** 63*6789fb99SJames Morse * resctrl_reset_task_closids() - Reset the PARTID/PMG values for all tasks. 64*6789fb99SJames Morse * 65*6789fb99SJames Morse * At boot, all existing tasks use partid zero for D and I. 66*6789fb99SJames Morse * To enable/disable CDP emulation, all these tasks need relabelling. 67*6789fb99SJames Morse */ 68*6789fb99SJames Morse static void resctrl_reset_task_closids(void) 69*6789fb99SJames Morse { 70*6789fb99SJames Morse struct task_struct *p, *t; 71*6789fb99SJames Morse 72*6789fb99SJames Morse read_lock(&tasklist_lock); 73*6789fb99SJames Morse for_each_process_thread(p, t) { 74*6789fb99SJames Morse resctrl_arch_set_closid_rmid(t, RESCTRL_RESERVED_CLOSID, 75*6789fb99SJames Morse RESCTRL_RESERVED_RMID); 76*6789fb99SJames Morse } 77*6789fb99SJames Morse read_unlock(&tasklist_lock); 78*6789fb99SJames Morse } 79*6789fb99SJames Morse 80*6789fb99SJames Morse int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable) 81*6789fb99SJames Morse { 82*6789fb99SJames Morse u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID; 83*6789fb99SJames Morse int cpu; 84*6789fb99SJames Morse 85*6789fb99SJames Morse /* 86*6789fb99SJames Morse * resctrl_arch_set_cdp_enabled() is only called with enable set to 87*6789fb99SJames Morse * false on error and unmount. 88*6789fb99SJames Morse */ 89*6789fb99SJames Morse cdp_enabled = enable; 90*6789fb99SJames Morse mpam_resctrl_controls[rid].cdp_enabled = enable; 91*6789fb99SJames Morse 92*6789fb99SJames Morse /* The mbw_max feature can't hide cdp as it's a per-partid maximum. */ 93*6789fb99SJames Morse if (cdp_enabled && !mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled) 94*6789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = false; 95*6789fb99SJames Morse 96*6789fb99SJames Morse if (mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled && 97*6789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].class) 98*6789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = true; 99*6789fb99SJames Morse 100*6789fb99SJames Morse if (enable) { 101*6789fb99SJames Morse if (mpam_partid_max < 1) 102*6789fb99SJames Morse return -EINVAL; 103*6789fb99SJames Morse 104*6789fb99SJames Morse partid_d = resctrl_get_config_index(RESCTRL_RESERVED_CLOSID, CDP_DATA); 105*6789fb99SJames Morse partid_i = resctrl_get_config_index(RESCTRL_RESERVED_CLOSID, CDP_CODE); 106*6789fb99SJames Morse } 107*6789fb99SJames Morse 108*6789fb99SJames Morse mpam_set_task_partid_pmg(current, partid_d, partid_i, 0, 0); 109*6789fb99SJames Morse WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current)); 110*6789fb99SJames Morse 111*6789fb99SJames Morse resctrl_reset_task_closids(); 112*6789fb99SJames Morse 113*6789fb99SJames Morse for_each_possible_cpu(cpu) 114*6789fb99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0); 115*6789fb99SJames Morse on_each_cpu(resctrl_arch_sync_cpu_closid_rmid, NULL, 1); 116*6789fb99SJames Morse 117*6789fb99SJames Morse return 0; 118*6789fb99SJames Morse } 119*6789fb99SJames Morse 120*6789fb99SJames Morse static bool mpam_resctrl_hide_cdp(enum resctrl_res_level rid) 121*6789fb99SJames Morse { 122*6789fb99SJames Morse return cdp_enabled && !resctrl_arch_get_cdp_enabled(rid); 123*6789fb99SJames Morse } 124*6789fb99SJames Morse 12509e61dafSJames Morse /* 12609e61dafSJames Morse * MSC may raise an error interrupt if it sees an out or range partid/pmg, 12709e61dafSJames Morse * and go on to truncate the value. Regardless of what the hardware supports, 12809e61dafSJames Morse * only the system wide safe value is safe to use. 12909e61dafSJames Morse */ 13009e61dafSJames Morse u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored) 13109e61dafSJames Morse { 13209e61dafSJames Morse return mpam_partid_max + 1; 13309e61dafSJames Morse } 13409e61dafSJames Morse 1359d2e1a99SJames Morse void resctrl_arch_sched_in(struct task_struct *tsk) 1369d2e1a99SJames Morse { 1379d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 1389d2e1a99SJames Morse 1399d2e1a99SJames Morse mpam_thread_switch(tsk); 1409d2e1a99SJames Morse } 1419d2e1a99SJames Morse 1429d2e1a99SJames Morse void resctrl_arch_set_cpu_default_closid_rmid(int cpu, u32 closid, u32 rmid) 1439d2e1a99SJames Morse { 1449d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 1459d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 1469d2e1a99SJames Morse 1479d2e1a99SJames Morse if (!cdp_enabled) { 1489d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, closid, closid, rmid, rmid); 1499d2e1a99SJames Morse } else { 1509d2e1a99SJames Morse /* 1519d2e1a99SJames Morse * When CDP is enabled, resctrl halves the closid range and we 1529d2e1a99SJames Morse * use odd/even partid for one closid. 1539d2e1a99SJames Morse */ 1549d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 1559d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 1569d2e1a99SJames Morse 1579d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, rmid, rmid); 1589d2e1a99SJames Morse } 1599d2e1a99SJames Morse } 1609d2e1a99SJames Morse 1619d2e1a99SJames Morse void resctrl_arch_sync_cpu_closid_rmid(void *info) 1629d2e1a99SJames Morse { 1639d2e1a99SJames Morse struct resctrl_cpu_defaults *r = info; 1649d2e1a99SJames Morse 1659d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 1669d2e1a99SJames Morse 1679d2e1a99SJames Morse if (r) { 1689d2e1a99SJames Morse resctrl_arch_set_cpu_default_closid_rmid(smp_processor_id(), 1699d2e1a99SJames Morse r->closid, r->rmid); 1709d2e1a99SJames Morse } 1719d2e1a99SJames Morse 1729d2e1a99SJames Morse resctrl_arch_sched_in(current); 1739d2e1a99SJames Morse } 1749d2e1a99SJames Morse 1759d2e1a99SJames Morse void resctrl_arch_set_closid_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 1769d2e1a99SJames Morse { 1779d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 1789d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 1799d2e1a99SJames Morse 1809d2e1a99SJames Morse if (!cdp_enabled) { 1819d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, closid, closid, rmid, rmid); 1829d2e1a99SJames Morse } else { 1839d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 1849d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 1859d2e1a99SJames Morse 1869d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, partid_d, partid_i, rmid, rmid); 1879d2e1a99SJames Morse } 1889d2e1a99SJames Morse } 1899d2e1a99SJames Morse 190*6789fb99SJames Morse bool resctrl_arch_match_closid(struct task_struct *tsk, u32 closid) 191*6789fb99SJames Morse { 192*6789fb99SJames Morse u64 regval = mpam_get_regval(tsk); 193*6789fb99SJames Morse u32 tsk_closid = FIELD_GET(MPAM0_EL1_PARTID_D, regval); 194*6789fb99SJames Morse 195*6789fb99SJames Morse if (cdp_enabled) 196*6789fb99SJames Morse tsk_closid >>= 1; 197*6789fb99SJames Morse 198*6789fb99SJames Morse return tsk_closid == closid; 199*6789fb99SJames Morse } 200*6789fb99SJames Morse 201*6789fb99SJames Morse /* The task's pmg is not unique, the partid must be considered too */ 202*6789fb99SJames Morse bool resctrl_arch_match_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 203*6789fb99SJames Morse { 204*6789fb99SJames Morse u64 regval = mpam_get_regval(tsk); 205*6789fb99SJames Morse u32 tsk_closid = FIELD_GET(MPAM0_EL1_PARTID_D, regval); 206*6789fb99SJames Morse u32 tsk_rmid = FIELD_GET(MPAM0_EL1_PMG_D, regval); 207*6789fb99SJames Morse 208*6789fb99SJames Morse if (cdp_enabled) 209*6789fb99SJames Morse tsk_closid >>= 1; 210*6789fb99SJames Morse 211*6789fb99SJames Morse return (tsk_closid == closid) && (tsk_rmid == rmid); 212*6789fb99SJames Morse } 213*6789fb99SJames Morse 21409e61dafSJames Morse struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) 21509e61dafSJames Morse { 21609e61dafSJames Morse if (l >= RDT_NUM_RESOURCES) 21709e61dafSJames Morse return NULL; 21809e61dafSJames Morse 21909e61dafSJames Morse return &mpam_resctrl_controls[l].resctrl_res; 22009e61dafSJames Morse } 22109e61dafSJames Morse 22252a4edb1SJames Morse static bool cache_has_usable_cpor(struct mpam_class *class) 22352a4edb1SJames Morse { 22452a4edb1SJames Morse struct mpam_props *cprops = &class->props; 22552a4edb1SJames Morse 22652a4edb1SJames Morse if (!mpam_has_feature(mpam_feat_cpor_part, cprops)) 22752a4edb1SJames Morse return false; 22852a4edb1SJames Morse 22952a4edb1SJames Morse /* resctrl uses u32 for all bitmap configurations */ 23052a4edb1SJames Morse return class->props.cpbm_wd <= 32; 23152a4edb1SJames Morse } 23252a4edb1SJames Morse 23352a4edb1SJames Morse /* Test whether we can export MPAM_CLASS_CACHE:{2,3}? */ 23452a4edb1SJames Morse static void mpam_resctrl_pick_caches(void) 23552a4edb1SJames Morse { 23652a4edb1SJames Morse struct mpam_class *class; 23752a4edb1SJames Morse struct mpam_resctrl_res *res; 23852a4edb1SJames Morse 23952a4edb1SJames Morse lockdep_assert_cpus_held(); 24052a4edb1SJames Morse 24152a4edb1SJames Morse guard(srcu)(&mpam_srcu); 24252a4edb1SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 24352a4edb1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 24452a4edb1SJames Morse if (class->type != MPAM_CLASS_CACHE) { 24552a4edb1SJames Morse pr_debug("class %u is not a cache\n", class->level); 24652a4edb1SJames Morse continue; 24752a4edb1SJames Morse } 24852a4edb1SJames Morse 24952a4edb1SJames Morse if (class->level != 2 && class->level != 3) { 25052a4edb1SJames Morse pr_debug("class %u is not L2 or L3\n", class->level); 25152a4edb1SJames Morse continue; 25252a4edb1SJames Morse } 25352a4edb1SJames Morse 25452a4edb1SJames Morse if (!cache_has_usable_cpor(class)) { 25552a4edb1SJames Morse pr_debug("class %u cache misses CPOR\n", class->level); 25652a4edb1SJames Morse continue; 25752a4edb1SJames Morse } 25852a4edb1SJames Morse 25952a4edb1SJames Morse if (!cpumask_equal(&class->affinity, cpu_possible_mask)) { 26052a4edb1SJames Morse pr_debug("class %u has missing CPUs, mask %*pb != %*pb\n", class->level, 26152a4edb1SJames Morse cpumask_pr_args(&class->affinity), 26252a4edb1SJames Morse cpumask_pr_args(cpu_possible_mask)); 26352a4edb1SJames Morse continue; 26452a4edb1SJames Morse } 26552a4edb1SJames Morse 26652a4edb1SJames Morse if (class->level == 2) 26752a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L2]; 26852a4edb1SJames Morse else 26952a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 27052a4edb1SJames Morse res->class = class; 27152a4edb1SJames Morse } 27252a4edb1SJames Morse } 27352a4edb1SJames Morse 27409e61dafSJames Morse static int mpam_resctrl_control_init(struct mpam_resctrl_res *res) 27509e61dafSJames Morse { 27652a4edb1SJames Morse struct mpam_class *class = res->class; 27752a4edb1SJames Morse struct rdt_resource *r = &res->resctrl_res; 27852a4edb1SJames Morse 27952a4edb1SJames Morse switch (r->rid) { 28052a4edb1SJames Morse case RDT_RESOURCE_L2: 28152a4edb1SJames Morse case RDT_RESOURCE_L3: 28252a4edb1SJames Morse r->schema_fmt = RESCTRL_SCHEMA_BITMAP; 28352a4edb1SJames Morse r->cache.arch_has_sparse_bitmasks = true; 28452a4edb1SJames Morse 28552a4edb1SJames Morse r->cache.cbm_len = class->props.cpbm_wd; 28652a4edb1SJames Morse /* mpam_devices will reject empty bitmaps */ 28752a4edb1SJames Morse r->cache.min_cbm_bits = 1; 28852a4edb1SJames Morse 28952a4edb1SJames Morse if (r->rid == RDT_RESOURCE_L2) { 29052a4edb1SJames Morse r->name = "L2"; 29152a4edb1SJames Morse r->ctrl_scope = RESCTRL_L2_CACHE; 29252a4edb1SJames Morse r->cdp_capable = true; 29352a4edb1SJames Morse } else { 29452a4edb1SJames Morse r->name = "L3"; 29552a4edb1SJames Morse r->ctrl_scope = RESCTRL_L3_CACHE; 29652a4edb1SJames Morse r->cdp_capable = true; 29752a4edb1SJames Morse } 29852a4edb1SJames Morse 29952a4edb1SJames Morse /* 30052a4edb1SJames Morse * Which bits are shared with other ...things... Unknown 30152a4edb1SJames Morse * devices use partid-0 which uses all the bitmap fields. Until 30252a4edb1SJames Morse * we have configured the SMMU and GIC not to do this 'all the 30352a4edb1SJames Morse * bits' is the correct answer here. 30452a4edb1SJames Morse */ 30552a4edb1SJames Morse r->cache.shareable_bits = resctrl_get_default_ctrl(r); 30652a4edb1SJames Morse r->alloc_capable = true; 30752a4edb1SJames Morse break; 30852a4edb1SJames Morse default: 30952a4edb1SJames Morse return -EINVAL; 31052a4edb1SJames Morse } 31109e61dafSJames Morse 31209e61dafSJames Morse return 0; 31309e61dafSJames Morse } 31409e61dafSJames Morse 31509e61dafSJames Morse static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp) 31609e61dafSJames Morse { 31709e61dafSJames Morse struct mpam_class *class = comp->class; 31809e61dafSJames Morse 31909e61dafSJames Morse if (class->type == MPAM_CLASS_CACHE) 32009e61dafSJames Morse return comp->comp_id; 32109e61dafSJames Morse 32209e61dafSJames Morse /* TODO: repaint domain ids to match the L3 domain ids */ 32309e61dafSJames Morse /* Otherwise, expose the ID used by the firmware table code. */ 32409e61dafSJames Morse return comp->comp_id; 32509e61dafSJames Morse } 32609e61dafSJames Morse 32702cc6616SJames Morse u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d, 32802cc6616SJames Morse u32 closid, enum resctrl_conf_type type) 32902cc6616SJames Morse { 33002cc6616SJames Morse u32 partid; 33102cc6616SJames Morse struct mpam_config *cfg; 33202cc6616SJames Morse struct mpam_props *cprops; 33302cc6616SJames Morse struct mpam_resctrl_res *res; 33402cc6616SJames Morse struct mpam_resctrl_dom *dom; 33502cc6616SJames Morse enum mpam_device_features configured_by; 33602cc6616SJames Morse 33702cc6616SJames Morse lockdep_assert_cpus_held(); 33802cc6616SJames Morse 33902cc6616SJames Morse if (!mpam_is_enabled()) 34002cc6616SJames Morse return resctrl_get_default_ctrl(r); 34102cc6616SJames Morse 34202cc6616SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 34302cc6616SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 34402cc6616SJames Morse cprops = &res->class->props; 34502cc6616SJames Morse 346*6789fb99SJames Morse /* 347*6789fb99SJames Morse * When CDP is enabled, but the resource doesn't support it, 348*6789fb99SJames Morse * the control is cloned across both partids. 349*6789fb99SJames Morse * Pick one at random to read: 350*6789fb99SJames Morse */ 351*6789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) 352*6789fb99SJames Morse type = CDP_DATA; 353*6789fb99SJames Morse 35402cc6616SJames Morse partid = resctrl_get_config_index(closid, type); 35502cc6616SJames Morse cfg = &dom->ctrl_comp->cfg[partid]; 35602cc6616SJames Morse 35702cc6616SJames Morse switch (r->rid) { 35802cc6616SJames Morse case RDT_RESOURCE_L2: 35902cc6616SJames Morse case RDT_RESOURCE_L3: 36002cc6616SJames Morse configured_by = mpam_feat_cpor_part; 36102cc6616SJames Morse break; 36202cc6616SJames Morse default: 36302cc6616SJames Morse return resctrl_get_default_ctrl(r); 36402cc6616SJames Morse } 36502cc6616SJames Morse 36602cc6616SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r) || 36702cc6616SJames Morse !mpam_has_feature(configured_by, cfg)) 36802cc6616SJames Morse return resctrl_get_default_ctrl(r); 36902cc6616SJames Morse 37002cc6616SJames Morse switch (configured_by) { 37102cc6616SJames Morse case mpam_feat_cpor_part: 37202cc6616SJames Morse return cfg->cpbm; 37302cc6616SJames Morse default: 37402cc6616SJames Morse return resctrl_get_default_ctrl(r); 37502cc6616SJames Morse } 37602cc6616SJames Morse } 37702cc6616SJames Morse 3789cd2b522SJames Morse int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d, 3799cd2b522SJames Morse u32 closid, enum resctrl_conf_type t, u32 cfg_val) 3809cd2b522SJames Morse { 381*6789fb99SJames Morse int err; 3829cd2b522SJames Morse u32 partid; 3839cd2b522SJames Morse struct mpam_config cfg; 3849cd2b522SJames Morse struct mpam_props *cprops; 3859cd2b522SJames Morse struct mpam_resctrl_res *res; 3869cd2b522SJames Morse struct mpam_resctrl_dom *dom; 3879cd2b522SJames Morse 3889cd2b522SJames Morse lockdep_assert_cpus_held(); 3899cd2b522SJames Morse lockdep_assert_irqs_enabled(); 3909cd2b522SJames Morse 3919cd2b522SJames Morse /* 3929cd2b522SJames Morse * No need to check the CPU as mpam_apply_config() doesn't care, and 3939cd2b522SJames Morse * resctrl_arch_update_domains() relies on this. 3949cd2b522SJames Morse */ 3959cd2b522SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 3969cd2b522SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 3979cd2b522SJames Morse cprops = &res->class->props; 3989cd2b522SJames Morse 399*6789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) 400*6789fb99SJames Morse t = CDP_DATA; 401*6789fb99SJames Morse 4029cd2b522SJames Morse partid = resctrl_get_config_index(closid, t); 4039cd2b522SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r)) { 4049cd2b522SJames Morse pr_debug("Not alloc capable or computed PARTID out of range\n"); 4059cd2b522SJames Morse return -EINVAL; 4069cd2b522SJames Morse } 4079cd2b522SJames Morse 4089cd2b522SJames Morse /* 4099cd2b522SJames Morse * Copy the current config to avoid clearing other resources when the 4109cd2b522SJames Morse * same component is exposed multiple times through resctrl. 4119cd2b522SJames Morse */ 4129cd2b522SJames Morse cfg = dom->ctrl_comp->cfg[partid]; 4139cd2b522SJames Morse 4149cd2b522SJames Morse switch (r->rid) { 4159cd2b522SJames Morse case RDT_RESOURCE_L2: 4169cd2b522SJames Morse case RDT_RESOURCE_L3: 4179cd2b522SJames Morse cfg.cpbm = cfg_val; 4189cd2b522SJames Morse mpam_set_feature(mpam_feat_cpor_part, &cfg); 4199cd2b522SJames Morse break; 4209cd2b522SJames Morse default: 4219cd2b522SJames Morse return -EINVAL; 4229cd2b522SJames Morse } 4239cd2b522SJames Morse 424*6789fb99SJames Morse /* 425*6789fb99SJames Morse * When CDP is enabled, but the resource doesn't support it, we need to 426*6789fb99SJames Morse * apply the same configuration to the other partid. 427*6789fb99SJames Morse */ 428*6789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) { 429*6789fb99SJames Morse partid = resctrl_get_config_index(closid, CDP_CODE); 430*6789fb99SJames Morse err = mpam_apply_config(dom->ctrl_comp, partid, &cfg); 431*6789fb99SJames Morse if (err) 432*6789fb99SJames Morse return err; 433*6789fb99SJames Morse 434*6789fb99SJames Morse partid = resctrl_get_config_index(closid, CDP_DATA); 435*6789fb99SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 436*6789fb99SJames Morse } 437*6789fb99SJames Morse 4389cd2b522SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 4399cd2b522SJames Morse } 4409cd2b522SJames Morse 4419cd2b522SJames Morse int resctrl_arch_update_domains(struct rdt_resource *r, u32 closid) 4429cd2b522SJames Morse { 4439cd2b522SJames Morse int err; 4449cd2b522SJames Morse struct rdt_ctrl_domain *d; 4459cd2b522SJames Morse 4469cd2b522SJames Morse lockdep_assert_cpus_held(); 4479cd2b522SJames Morse lockdep_assert_irqs_enabled(); 4489cd2b522SJames Morse 4499cd2b522SJames Morse list_for_each_entry_rcu(d, &r->ctrl_domains, hdr.list) { 4509cd2b522SJames Morse for (enum resctrl_conf_type t = 0; t < CDP_NUM_TYPES; t++) { 4519cd2b522SJames Morse struct resctrl_staged_config *cfg = &d->staged_config[t]; 4529cd2b522SJames Morse 4539cd2b522SJames Morse if (!cfg->have_new_ctrl) 4549cd2b522SJames Morse continue; 4559cd2b522SJames Morse 4569cd2b522SJames Morse err = resctrl_arch_update_one(r, d, closid, t, 4579cd2b522SJames Morse cfg->new_ctrl); 4589cd2b522SJames Morse if (err) 4599cd2b522SJames Morse return err; 4609cd2b522SJames Morse } 4619cd2b522SJames Morse } 4629cd2b522SJames Morse 4639cd2b522SJames Morse return 0; 4649cd2b522SJames Morse } 4659cd2b522SJames Morse 466370d166dSJames Morse void resctrl_arch_reset_all_ctrls(struct rdt_resource *r) 467370d166dSJames Morse { 468370d166dSJames Morse struct mpam_resctrl_res *res; 469370d166dSJames Morse 470370d166dSJames Morse lockdep_assert_cpus_held(); 471370d166dSJames Morse 472370d166dSJames Morse if (!mpam_is_enabled()) 473370d166dSJames Morse return; 474370d166dSJames Morse 475370d166dSJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 476370d166dSJames Morse mpam_reset_class_locked(res->class); 477370d166dSJames Morse } 478370d166dSJames Morse 47909e61dafSJames Morse static void mpam_resctrl_domain_hdr_init(int cpu, struct mpam_component *comp, 48009e61dafSJames Morse enum resctrl_res_level rid, 48109e61dafSJames Morse struct rdt_domain_hdr *hdr) 48209e61dafSJames Morse { 48309e61dafSJames Morse lockdep_assert_cpus_held(); 48409e61dafSJames Morse 48509e61dafSJames Morse INIT_LIST_HEAD(&hdr->list); 48609e61dafSJames Morse hdr->id = mpam_resctrl_pick_domain_id(cpu, comp); 48709e61dafSJames Morse hdr->rid = rid; 48809e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 48909e61dafSJames Morse } 49009e61dafSJames Morse 49109e61dafSJames Morse static void mpam_resctrl_online_domain_hdr(unsigned int cpu, 49209e61dafSJames Morse struct rdt_domain_hdr *hdr) 49309e61dafSJames Morse { 49409e61dafSJames Morse lockdep_assert_cpus_held(); 49509e61dafSJames Morse 49609e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 49709e61dafSJames Morse } 49809e61dafSJames Morse 49909e61dafSJames Morse /** 50009e61dafSJames Morse * mpam_resctrl_offline_domain_hdr() - Update the domain header to remove a CPU. 50109e61dafSJames Morse * @cpu: The CPU to remove from the domain. 50209e61dafSJames Morse * @hdr: The domain's header. 50309e61dafSJames Morse * 50409e61dafSJames Morse * Removes @cpu from the header mask. If this was the last CPU in the domain, 50509e61dafSJames Morse * the domain header is removed from its parent list and true is returned, 50609e61dafSJames Morse * indicating the parent structure can be freed. 50709e61dafSJames Morse * If there are other CPUs in the domain, returns false. 50809e61dafSJames Morse */ 50909e61dafSJames Morse static bool mpam_resctrl_offline_domain_hdr(unsigned int cpu, 51009e61dafSJames Morse struct rdt_domain_hdr *hdr) 51109e61dafSJames Morse { 51209e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 51309e61dafSJames Morse 51409e61dafSJames Morse cpumask_clear_cpu(cpu, &hdr->cpu_mask); 51509e61dafSJames Morse if (cpumask_empty(&hdr->cpu_mask)) { 51609e61dafSJames Morse list_del_rcu(&hdr->list); 51709e61dafSJames Morse synchronize_rcu(); 51809e61dafSJames Morse return true; 51909e61dafSJames Morse } 52009e61dafSJames Morse 52109e61dafSJames Morse return false; 52209e61dafSJames Morse } 52309e61dafSJames Morse 52409e61dafSJames Morse static void mpam_resctrl_domain_insert(struct list_head *list, 52509e61dafSJames Morse struct rdt_domain_hdr *new) 52609e61dafSJames Morse { 52709e61dafSJames Morse struct rdt_domain_hdr *err; 52809e61dafSJames Morse struct list_head *pos = NULL; 52909e61dafSJames Morse 53009e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 53109e61dafSJames Morse 53209e61dafSJames Morse err = resctrl_find_domain(list, new->id, &pos); 53309e61dafSJames Morse if (WARN_ON_ONCE(err)) 53409e61dafSJames Morse return; 53509e61dafSJames Morse 53609e61dafSJames Morse list_add_tail_rcu(&new->list, pos); 53709e61dafSJames Morse } 53809e61dafSJames Morse 53909e61dafSJames Morse static struct mpam_resctrl_dom * 54009e61dafSJames Morse mpam_resctrl_alloc_domain(unsigned int cpu, struct mpam_resctrl_res *res) 54109e61dafSJames Morse { 54209e61dafSJames Morse int err; 54309e61dafSJames Morse struct mpam_resctrl_dom *dom; 54409e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 54509e61dafSJames Morse struct mpam_class *class = res->class; 54609e61dafSJames Morse struct mpam_component *comp_iter, *ctrl_comp; 54709e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 54809e61dafSJames Morse 54909e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 55009e61dafSJames Morse 55109e61dafSJames Morse ctrl_comp = NULL; 55209e61dafSJames Morse guard(srcu)(&mpam_srcu); 55309e61dafSJames Morse list_for_each_entry_srcu(comp_iter, &class->components, class_list, 55409e61dafSJames Morse srcu_read_lock_held(&mpam_srcu)) { 55509e61dafSJames Morse if (cpumask_test_cpu(cpu, &comp_iter->affinity)) { 55609e61dafSJames Morse ctrl_comp = comp_iter; 55709e61dafSJames Morse break; 55809e61dafSJames Morse } 55909e61dafSJames Morse } 56009e61dafSJames Morse 56109e61dafSJames Morse /* class has no component for this CPU */ 56209e61dafSJames Morse if (WARN_ON_ONCE(!ctrl_comp)) 56309e61dafSJames Morse return ERR_PTR(-EINVAL); 56409e61dafSJames Morse 56509e61dafSJames Morse dom = kzalloc_node(sizeof(*dom), GFP_KERNEL, cpu_to_node(cpu)); 56609e61dafSJames Morse if (!dom) 56709e61dafSJames Morse return ERR_PTR(-ENOMEM); 56809e61dafSJames Morse 56909e61dafSJames Morse if (r->alloc_capable) { 57009e61dafSJames Morse dom->ctrl_comp = ctrl_comp; 57109e61dafSJames Morse 57209e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 57309e61dafSJames Morse mpam_resctrl_domain_hdr_init(cpu, ctrl_comp, r->rid, &ctrl_d->hdr); 57409e61dafSJames Morse ctrl_d->hdr.type = RESCTRL_CTRL_DOMAIN; 57509e61dafSJames Morse err = resctrl_online_ctrl_domain(r, ctrl_d); 57609e61dafSJames Morse if (err) 57709e61dafSJames Morse goto free_domain; 57809e61dafSJames Morse 57909e61dafSJames Morse mpam_resctrl_domain_insert(&r->ctrl_domains, &ctrl_d->hdr); 58009e61dafSJames Morse } else { 58109e61dafSJames Morse pr_debug("Skipped control domain online - no controls\n"); 58209e61dafSJames Morse } 58309e61dafSJames Morse return dom; 58409e61dafSJames Morse 58509e61dafSJames Morse free_domain: 58609e61dafSJames Morse kfree(dom); 58709e61dafSJames Morse dom = ERR_PTR(err); 58809e61dafSJames Morse 58909e61dafSJames Morse return dom; 59009e61dafSJames Morse } 59109e61dafSJames Morse 59209e61dafSJames Morse static struct mpam_resctrl_dom * 59309e61dafSJames Morse mpam_resctrl_get_domain_from_cpu(int cpu, struct mpam_resctrl_res *res) 59409e61dafSJames Morse { 59509e61dafSJames Morse struct mpam_resctrl_dom *dom; 59609e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 59709e61dafSJames Morse 59809e61dafSJames Morse lockdep_assert_cpus_held(); 59909e61dafSJames Morse 60009e61dafSJames Morse list_for_each_entry_rcu(dom, &r->ctrl_domains, resctrl_ctrl_dom.hdr.list) { 60109e61dafSJames Morse if (cpumask_test_cpu(cpu, &dom->ctrl_comp->affinity)) 60209e61dafSJames Morse return dom; 60309e61dafSJames Morse } 60409e61dafSJames Morse 60509e61dafSJames Morse return NULL; 60609e61dafSJames Morse } 60709e61dafSJames Morse 60809e61dafSJames Morse int mpam_resctrl_online_cpu(unsigned int cpu) 60909e61dafSJames Morse { 61009e61dafSJames Morse struct mpam_resctrl_res *res; 61109e61dafSJames Morse enum resctrl_res_level rid; 61209e61dafSJames Morse 61309e61dafSJames Morse guard(mutex)(&domain_list_lock); 61409e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 61509e61dafSJames Morse struct mpam_resctrl_dom *dom; 61609e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 61709e61dafSJames Morse 61809e61dafSJames Morse if (!res->class) 61909e61dafSJames Morse continue; // dummy_resource; 62009e61dafSJames Morse 62109e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 62209e61dafSJames Morse if (!dom) { 62309e61dafSJames Morse dom = mpam_resctrl_alloc_domain(cpu, res); 62409e61dafSJames Morse if (IS_ERR(dom)) 62509e61dafSJames Morse return PTR_ERR(dom); 62609e61dafSJames Morse } else { 62709e61dafSJames Morse if (r->alloc_capable) { 62809e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d = &dom->resctrl_ctrl_dom; 62909e61dafSJames Morse 63009e61dafSJames Morse mpam_resctrl_online_domain_hdr(cpu, &ctrl_d->hdr); 63109e61dafSJames Morse } 63209e61dafSJames Morse } 63309e61dafSJames Morse } 63409e61dafSJames Morse 63509e61dafSJames Morse resctrl_online_cpu(cpu); 63609e61dafSJames Morse 63709e61dafSJames Morse return 0; 63809e61dafSJames Morse } 63909e61dafSJames Morse 64009e61dafSJames Morse void mpam_resctrl_offline_cpu(unsigned int cpu) 64109e61dafSJames Morse { 64209e61dafSJames Morse struct mpam_resctrl_res *res; 64309e61dafSJames Morse enum resctrl_res_level rid; 64409e61dafSJames Morse 64509e61dafSJames Morse resctrl_offline_cpu(cpu); 64609e61dafSJames Morse 64709e61dafSJames Morse guard(mutex)(&domain_list_lock); 64809e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 64909e61dafSJames Morse struct mpam_resctrl_dom *dom; 65009e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 65109e61dafSJames Morse bool ctrl_dom_empty; 65209e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 65309e61dafSJames Morse 65409e61dafSJames Morse if (!res->class) 65509e61dafSJames Morse continue; // dummy resource 65609e61dafSJames Morse 65709e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 65809e61dafSJames Morse if (WARN_ON_ONCE(!dom)) 65909e61dafSJames Morse continue; 66009e61dafSJames Morse 66109e61dafSJames Morse if (r->alloc_capable) { 66209e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 66309e61dafSJames Morse ctrl_dom_empty = mpam_resctrl_offline_domain_hdr(cpu, &ctrl_d->hdr); 66409e61dafSJames Morse if (ctrl_dom_empty) 66509e61dafSJames Morse resctrl_offline_ctrl_domain(&res->resctrl_res, ctrl_d); 66609e61dafSJames Morse } else { 66709e61dafSJames Morse ctrl_dom_empty = true; 66809e61dafSJames Morse } 66909e61dafSJames Morse 67009e61dafSJames Morse if (ctrl_dom_empty) 67109e61dafSJames Morse kfree(dom); 67209e61dafSJames Morse } 67309e61dafSJames Morse } 67409e61dafSJames Morse 67509e61dafSJames Morse int mpam_resctrl_setup(void) 67609e61dafSJames Morse { 67709e61dafSJames Morse int err = 0; 67809e61dafSJames Morse struct mpam_resctrl_res *res; 67909e61dafSJames Morse enum resctrl_res_level rid; 68009e61dafSJames Morse 68109e61dafSJames Morse cpus_read_lock(); 68209e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 68309e61dafSJames Morse INIT_LIST_HEAD_RCU(&res->resctrl_res.ctrl_domains); 68409e61dafSJames Morse res->resctrl_res.rid = rid; 68509e61dafSJames Morse } 68609e61dafSJames Morse 68752a4edb1SJames Morse /* Find some classes to use for controls */ 68852a4edb1SJames Morse mpam_resctrl_pick_caches(); 68909e61dafSJames Morse 69009e61dafSJames Morse /* Initialise the resctrl structures from the classes */ 69109e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 69209e61dafSJames Morse if (!res->class) 69309e61dafSJames Morse continue; // dummy resource 69409e61dafSJames Morse 69509e61dafSJames Morse err = mpam_resctrl_control_init(res); 69609e61dafSJames Morse if (err) { 69709e61dafSJames Morse pr_debug("Failed to initialise rid %u\n", rid); 69809e61dafSJames Morse break; 69909e61dafSJames Morse } 70009e61dafSJames Morse } 70109e61dafSJames Morse cpus_read_unlock(); 70209e61dafSJames Morse 70309e61dafSJames Morse if (err) { 70409e61dafSJames Morse pr_debug("Internal error %d - resctrl not supported\n", err); 70509e61dafSJames Morse return err; 70609e61dafSJames Morse } 70709e61dafSJames Morse 70809e61dafSJames Morse if (!resctrl_arch_alloc_capable()) { 70909e61dafSJames Morse pr_debug("No alloc(%u) found - resctrl not supported\n", 71009e61dafSJames Morse resctrl_arch_alloc_capable()); 71109e61dafSJames Morse return -EOPNOTSUPP; 71209e61dafSJames Morse } 71309e61dafSJames Morse 71409e61dafSJames Morse /* TODO: call resctrl_init() */ 71509e61dafSJames Morse 71609e61dafSJames Morse return 0; 71709e61dafSJames Morse } 718