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> 11*9d2e1a99SJames 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*9d2e1a99SJames Morse static bool cdp_enabled; 39*9d2e1a99SJames Morse 4009e61dafSJames Morse bool resctrl_arch_alloc_capable(void) 4109e61dafSJames Morse { 4209e61dafSJames Morse struct mpam_resctrl_res *res; 4309e61dafSJames Morse enum resctrl_res_level rid; 4409e61dafSJames Morse 4509e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 4609e61dafSJames Morse if (res->resctrl_res.alloc_capable) 4709e61dafSJames Morse return true; 4809e61dafSJames Morse } 4909e61dafSJames Morse 5009e61dafSJames Morse return false; 5109e61dafSJames Morse } 5209e61dafSJames Morse 5309e61dafSJames Morse /* 5409e61dafSJames Morse * MSC may raise an error interrupt if it sees an out or range partid/pmg, 5509e61dafSJames Morse * and go on to truncate the value. Regardless of what the hardware supports, 5609e61dafSJames Morse * only the system wide safe value is safe to use. 5709e61dafSJames Morse */ 5809e61dafSJames Morse u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored) 5909e61dafSJames Morse { 6009e61dafSJames Morse return mpam_partid_max + 1; 6109e61dafSJames Morse } 6209e61dafSJames Morse 63*9d2e1a99SJames Morse void resctrl_arch_sched_in(struct task_struct *tsk) 64*9d2e1a99SJames Morse { 65*9d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 66*9d2e1a99SJames Morse 67*9d2e1a99SJames Morse mpam_thread_switch(tsk); 68*9d2e1a99SJames Morse } 69*9d2e1a99SJames Morse 70*9d2e1a99SJames Morse void resctrl_arch_set_cpu_default_closid_rmid(int cpu, u32 closid, u32 rmid) 71*9d2e1a99SJames Morse { 72*9d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 73*9d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 74*9d2e1a99SJames Morse 75*9d2e1a99SJames Morse if (!cdp_enabled) { 76*9d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, closid, closid, rmid, rmid); 77*9d2e1a99SJames Morse } else { 78*9d2e1a99SJames Morse /* 79*9d2e1a99SJames Morse * When CDP is enabled, resctrl halves the closid range and we 80*9d2e1a99SJames Morse * use odd/even partid for one closid. 81*9d2e1a99SJames Morse */ 82*9d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 83*9d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 84*9d2e1a99SJames Morse 85*9d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, rmid, rmid); 86*9d2e1a99SJames Morse } 87*9d2e1a99SJames Morse } 88*9d2e1a99SJames Morse 89*9d2e1a99SJames Morse void resctrl_arch_sync_cpu_closid_rmid(void *info) 90*9d2e1a99SJames Morse { 91*9d2e1a99SJames Morse struct resctrl_cpu_defaults *r = info; 92*9d2e1a99SJames Morse 93*9d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 94*9d2e1a99SJames Morse 95*9d2e1a99SJames Morse if (r) { 96*9d2e1a99SJames Morse resctrl_arch_set_cpu_default_closid_rmid(smp_processor_id(), 97*9d2e1a99SJames Morse r->closid, r->rmid); 98*9d2e1a99SJames Morse } 99*9d2e1a99SJames Morse 100*9d2e1a99SJames Morse resctrl_arch_sched_in(current); 101*9d2e1a99SJames Morse } 102*9d2e1a99SJames Morse 103*9d2e1a99SJames Morse void resctrl_arch_set_closid_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 104*9d2e1a99SJames Morse { 105*9d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 106*9d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 107*9d2e1a99SJames Morse 108*9d2e1a99SJames Morse if (!cdp_enabled) { 109*9d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, closid, closid, rmid, rmid); 110*9d2e1a99SJames Morse } else { 111*9d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 112*9d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 113*9d2e1a99SJames Morse 114*9d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, partid_d, partid_i, rmid, rmid); 115*9d2e1a99SJames Morse } 116*9d2e1a99SJames Morse } 117*9d2e1a99SJames Morse 11809e61dafSJames Morse struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) 11909e61dafSJames Morse { 12009e61dafSJames Morse if (l >= RDT_NUM_RESOURCES) 12109e61dafSJames Morse return NULL; 12209e61dafSJames Morse 12309e61dafSJames Morse return &mpam_resctrl_controls[l].resctrl_res; 12409e61dafSJames Morse } 12509e61dafSJames Morse 12652a4edb1SJames Morse static bool cache_has_usable_cpor(struct mpam_class *class) 12752a4edb1SJames Morse { 12852a4edb1SJames Morse struct mpam_props *cprops = &class->props; 12952a4edb1SJames Morse 13052a4edb1SJames Morse if (!mpam_has_feature(mpam_feat_cpor_part, cprops)) 13152a4edb1SJames Morse return false; 13252a4edb1SJames Morse 13352a4edb1SJames Morse /* resctrl uses u32 for all bitmap configurations */ 13452a4edb1SJames Morse return class->props.cpbm_wd <= 32; 13552a4edb1SJames Morse } 13652a4edb1SJames Morse 13752a4edb1SJames Morse /* Test whether we can export MPAM_CLASS_CACHE:{2,3}? */ 13852a4edb1SJames Morse static void mpam_resctrl_pick_caches(void) 13952a4edb1SJames Morse { 14052a4edb1SJames Morse struct mpam_class *class; 14152a4edb1SJames Morse struct mpam_resctrl_res *res; 14252a4edb1SJames Morse 14352a4edb1SJames Morse lockdep_assert_cpus_held(); 14452a4edb1SJames Morse 14552a4edb1SJames Morse guard(srcu)(&mpam_srcu); 14652a4edb1SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 14752a4edb1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 14852a4edb1SJames Morse if (class->type != MPAM_CLASS_CACHE) { 14952a4edb1SJames Morse pr_debug("class %u is not a cache\n", class->level); 15052a4edb1SJames Morse continue; 15152a4edb1SJames Morse } 15252a4edb1SJames Morse 15352a4edb1SJames Morse if (class->level != 2 && class->level != 3) { 15452a4edb1SJames Morse pr_debug("class %u is not L2 or L3\n", class->level); 15552a4edb1SJames Morse continue; 15652a4edb1SJames Morse } 15752a4edb1SJames Morse 15852a4edb1SJames Morse if (!cache_has_usable_cpor(class)) { 15952a4edb1SJames Morse pr_debug("class %u cache misses CPOR\n", class->level); 16052a4edb1SJames Morse continue; 16152a4edb1SJames Morse } 16252a4edb1SJames Morse 16352a4edb1SJames Morse if (!cpumask_equal(&class->affinity, cpu_possible_mask)) { 16452a4edb1SJames Morse pr_debug("class %u has missing CPUs, mask %*pb != %*pb\n", class->level, 16552a4edb1SJames Morse cpumask_pr_args(&class->affinity), 16652a4edb1SJames Morse cpumask_pr_args(cpu_possible_mask)); 16752a4edb1SJames Morse continue; 16852a4edb1SJames Morse } 16952a4edb1SJames Morse 17052a4edb1SJames Morse if (class->level == 2) 17152a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L2]; 17252a4edb1SJames Morse else 17352a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 17452a4edb1SJames Morse res->class = class; 17552a4edb1SJames Morse } 17652a4edb1SJames Morse } 17752a4edb1SJames Morse 17809e61dafSJames Morse static int mpam_resctrl_control_init(struct mpam_resctrl_res *res) 17909e61dafSJames Morse { 18052a4edb1SJames Morse struct mpam_class *class = res->class; 18152a4edb1SJames Morse struct rdt_resource *r = &res->resctrl_res; 18252a4edb1SJames Morse 18352a4edb1SJames Morse switch (r->rid) { 18452a4edb1SJames Morse case RDT_RESOURCE_L2: 18552a4edb1SJames Morse case RDT_RESOURCE_L3: 18652a4edb1SJames Morse r->schema_fmt = RESCTRL_SCHEMA_BITMAP; 18752a4edb1SJames Morse r->cache.arch_has_sparse_bitmasks = true; 18852a4edb1SJames Morse 18952a4edb1SJames Morse r->cache.cbm_len = class->props.cpbm_wd; 19052a4edb1SJames Morse /* mpam_devices will reject empty bitmaps */ 19152a4edb1SJames Morse r->cache.min_cbm_bits = 1; 19252a4edb1SJames Morse 19352a4edb1SJames Morse if (r->rid == RDT_RESOURCE_L2) { 19452a4edb1SJames Morse r->name = "L2"; 19552a4edb1SJames Morse r->ctrl_scope = RESCTRL_L2_CACHE; 19652a4edb1SJames Morse r->cdp_capable = true; 19752a4edb1SJames Morse } else { 19852a4edb1SJames Morse r->name = "L3"; 19952a4edb1SJames Morse r->ctrl_scope = RESCTRL_L3_CACHE; 20052a4edb1SJames Morse r->cdp_capable = true; 20152a4edb1SJames Morse } 20252a4edb1SJames Morse 20352a4edb1SJames Morse /* 20452a4edb1SJames Morse * Which bits are shared with other ...things... Unknown 20552a4edb1SJames Morse * devices use partid-0 which uses all the bitmap fields. Until 20652a4edb1SJames Morse * we have configured the SMMU and GIC not to do this 'all the 20752a4edb1SJames Morse * bits' is the correct answer here. 20852a4edb1SJames Morse */ 20952a4edb1SJames Morse r->cache.shareable_bits = resctrl_get_default_ctrl(r); 21052a4edb1SJames Morse r->alloc_capable = true; 21152a4edb1SJames Morse break; 21252a4edb1SJames Morse default: 21352a4edb1SJames Morse return -EINVAL; 21452a4edb1SJames Morse } 21509e61dafSJames Morse 21609e61dafSJames Morse return 0; 21709e61dafSJames Morse } 21809e61dafSJames Morse 21909e61dafSJames Morse static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp) 22009e61dafSJames Morse { 22109e61dafSJames Morse struct mpam_class *class = comp->class; 22209e61dafSJames Morse 22309e61dafSJames Morse if (class->type == MPAM_CLASS_CACHE) 22409e61dafSJames Morse return comp->comp_id; 22509e61dafSJames Morse 22609e61dafSJames Morse /* TODO: repaint domain ids to match the L3 domain ids */ 22709e61dafSJames Morse /* Otherwise, expose the ID used by the firmware table code. */ 22809e61dafSJames Morse return comp->comp_id; 22909e61dafSJames Morse } 23009e61dafSJames Morse 23102cc6616SJames Morse u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d, 23202cc6616SJames Morse u32 closid, enum resctrl_conf_type type) 23302cc6616SJames Morse { 23402cc6616SJames Morse u32 partid; 23502cc6616SJames Morse struct mpam_config *cfg; 23602cc6616SJames Morse struct mpam_props *cprops; 23702cc6616SJames Morse struct mpam_resctrl_res *res; 23802cc6616SJames Morse struct mpam_resctrl_dom *dom; 23902cc6616SJames Morse enum mpam_device_features configured_by; 24002cc6616SJames Morse 24102cc6616SJames Morse lockdep_assert_cpus_held(); 24202cc6616SJames Morse 24302cc6616SJames Morse if (!mpam_is_enabled()) 24402cc6616SJames Morse return resctrl_get_default_ctrl(r); 24502cc6616SJames Morse 24602cc6616SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 24702cc6616SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 24802cc6616SJames Morse cprops = &res->class->props; 24902cc6616SJames Morse 25002cc6616SJames Morse partid = resctrl_get_config_index(closid, type); 25102cc6616SJames Morse cfg = &dom->ctrl_comp->cfg[partid]; 25202cc6616SJames Morse 25302cc6616SJames Morse switch (r->rid) { 25402cc6616SJames Morse case RDT_RESOURCE_L2: 25502cc6616SJames Morse case RDT_RESOURCE_L3: 25602cc6616SJames Morse configured_by = mpam_feat_cpor_part; 25702cc6616SJames Morse break; 25802cc6616SJames Morse default: 25902cc6616SJames Morse return resctrl_get_default_ctrl(r); 26002cc6616SJames Morse } 26102cc6616SJames Morse 26202cc6616SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r) || 26302cc6616SJames Morse !mpam_has_feature(configured_by, cfg)) 26402cc6616SJames Morse return resctrl_get_default_ctrl(r); 26502cc6616SJames Morse 26602cc6616SJames Morse switch (configured_by) { 26702cc6616SJames Morse case mpam_feat_cpor_part: 26802cc6616SJames Morse return cfg->cpbm; 26902cc6616SJames Morse default: 27002cc6616SJames Morse return resctrl_get_default_ctrl(r); 27102cc6616SJames Morse } 27202cc6616SJames Morse } 27302cc6616SJames Morse 2749cd2b522SJames Morse int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d, 2759cd2b522SJames Morse u32 closid, enum resctrl_conf_type t, u32 cfg_val) 2769cd2b522SJames Morse { 2779cd2b522SJames Morse u32 partid; 2789cd2b522SJames Morse struct mpam_config cfg; 2799cd2b522SJames Morse struct mpam_props *cprops; 2809cd2b522SJames Morse struct mpam_resctrl_res *res; 2819cd2b522SJames Morse struct mpam_resctrl_dom *dom; 2829cd2b522SJames Morse 2839cd2b522SJames Morse lockdep_assert_cpus_held(); 2849cd2b522SJames Morse lockdep_assert_irqs_enabled(); 2859cd2b522SJames Morse 2869cd2b522SJames Morse /* 2879cd2b522SJames Morse * No need to check the CPU as mpam_apply_config() doesn't care, and 2889cd2b522SJames Morse * resctrl_arch_update_domains() relies on this. 2899cd2b522SJames Morse */ 2909cd2b522SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 2919cd2b522SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 2929cd2b522SJames Morse cprops = &res->class->props; 2939cd2b522SJames Morse 2949cd2b522SJames Morse partid = resctrl_get_config_index(closid, t); 2959cd2b522SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r)) { 2969cd2b522SJames Morse pr_debug("Not alloc capable or computed PARTID out of range\n"); 2979cd2b522SJames Morse return -EINVAL; 2989cd2b522SJames Morse } 2999cd2b522SJames Morse 3009cd2b522SJames Morse /* 3019cd2b522SJames Morse * Copy the current config to avoid clearing other resources when the 3029cd2b522SJames Morse * same component is exposed multiple times through resctrl. 3039cd2b522SJames Morse */ 3049cd2b522SJames Morse cfg = dom->ctrl_comp->cfg[partid]; 3059cd2b522SJames Morse 3069cd2b522SJames Morse switch (r->rid) { 3079cd2b522SJames Morse case RDT_RESOURCE_L2: 3089cd2b522SJames Morse case RDT_RESOURCE_L3: 3099cd2b522SJames Morse cfg.cpbm = cfg_val; 3109cd2b522SJames Morse mpam_set_feature(mpam_feat_cpor_part, &cfg); 3119cd2b522SJames Morse break; 3129cd2b522SJames Morse default: 3139cd2b522SJames Morse return -EINVAL; 3149cd2b522SJames Morse } 3159cd2b522SJames Morse 3169cd2b522SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 3179cd2b522SJames Morse } 3189cd2b522SJames Morse 3199cd2b522SJames Morse int resctrl_arch_update_domains(struct rdt_resource *r, u32 closid) 3209cd2b522SJames Morse { 3219cd2b522SJames Morse int err; 3229cd2b522SJames Morse struct rdt_ctrl_domain *d; 3239cd2b522SJames Morse 3249cd2b522SJames Morse lockdep_assert_cpus_held(); 3259cd2b522SJames Morse lockdep_assert_irqs_enabled(); 3269cd2b522SJames Morse 3279cd2b522SJames Morse list_for_each_entry_rcu(d, &r->ctrl_domains, hdr.list) { 3289cd2b522SJames Morse for (enum resctrl_conf_type t = 0; t < CDP_NUM_TYPES; t++) { 3299cd2b522SJames Morse struct resctrl_staged_config *cfg = &d->staged_config[t]; 3309cd2b522SJames Morse 3319cd2b522SJames Morse if (!cfg->have_new_ctrl) 3329cd2b522SJames Morse continue; 3339cd2b522SJames Morse 3349cd2b522SJames Morse err = resctrl_arch_update_one(r, d, closid, t, 3359cd2b522SJames Morse cfg->new_ctrl); 3369cd2b522SJames Morse if (err) 3379cd2b522SJames Morse return err; 3389cd2b522SJames Morse } 3399cd2b522SJames Morse } 3409cd2b522SJames Morse 3419cd2b522SJames Morse return 0; 3429cd2b522SJames Morse } 3439cd2b522SJames Morse 344370d166dSJames Morse void resctrl_arch_reset_all_ctrls(struct rdt_resource *r) 345370d166dSJames Morse { 346370d166dSJames Morse struct mpam_resctrl_res *res; 347370d166dSJames Morse 348370d166dSJames Morse lockdep_assert_cpus_held(); 349370d166dSJames Morse 350370d166dSJames Morse if (!mpam_is_enabled()) 351370d166dSJames Morse return; 352370d166dSJames Morse 353370d166dSJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 354370d166dSJames Morse mpam_reset_class_locked(res->class); 355370d166dSJames Morse } 356370d166dSJames Morse 35709e61dafSJames Morse static void mpam_resctrl_domain_hdr_init(int cpu, struct mpam_component *comp, 35809e61dafSJames Morse enum resctrl_res_level rid, 35909e61dafSJames Morse struct rdt_domain_hdr *hdr) 36009e61dafSJames Morse { 36109e61dafSJames Morse lockdep_assert_cpus_held(); 36209e61dafSJames Morse 36309e61dafSJames Morse INIT_LIST_HEAD(&hdr->list); 36409e61dafSJames Morse hdr->id = mpam_resctrl_pick_domain_id(cpu, comp); 36509e61dafSJames Morse hdr->rid = rid; 36609e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 36709e61dafSJames Morse } 36809e61dafSJames Morse 36909e61dafSJames Morse static void mpam_resctrl_online_domain_hdr(unsigned int cpu, 37009e61dafSJames Morse struct rdt_domain_hdr *hdr) 37109e61dafSJames Morse { 37209e61dafSJames Morse lockdep_assert_cpus_held(); 37309e61dafSJames Morse 37409e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 37509e61dafSJames Morse } 37609e61dafSJames Morse 37709e61dafSJames Morse /** 37809e61dafSJames Morse * mpam_resctrl_offline_domain_hdr() - Update the domain header to remove a CPU. 37909e61dafSJames Morse * @cpu: The CPU to remove from the domain. 38009e61dafSJames Morse * @hdr: The domain's header. 38109e61dafSJames Morse * 38209e61dafSJames Morse * Removes @cpu from the header mask. If this was the last CPU in the domain, 38309e61dafSJames Morse * the domain header is removed from its parent list and true is returned, 38409e61dafSJames Morse * indicating the parent structure can be freed. 38509e61dafSJames Morse * If there are other CPUs in the domain, returns false. 38609e61dafSJames Morse */ 38709e61dafSJames Morse static bool mpam_resctrl_offline_domain_hdr(unsigned int cpu, 38809e61dafSJames Morse struct rdt_domain_hdr *hdr) 38909e61dafSJames Morse { 39009e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 39109e61dafSJames Morse 39209e61dafSJames Morse cpumask_clear_cpu(cpu, &hdr->cpu_mask); 39309e61dafSJames Morse if (cpumask_empty(&hdr->cpu_mask)) { 39409e61dafSJames Morse list_del_rcu(&hdr->list); 39509e61dafSJames Morse synchronize_rcu(); 39609e61dafSJames Morse return true; 39709e61dafSJames Morse } 39809e61dafSJames Morse 39909e61dafSJames Morse return false; 40009e61dafSJames Morse } 40109e61dafSJames Morse 40209e61dafSJames Morse static void mpam_resctrl_domain_insert(struct list_head *list, 40309e61dafSJames Morse struct rdt_domain_hdr *new) 40409e61dafSJames Morse { 40509e61dafSJames Morse struct rdt_domain_hdr *err; 40609e61dafSJames Morse struct list_head *pos = NULL; 40709e61dafSJames Morse 40809e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 40909e61dafSJames Morse 41009e61dafSJames Morse err = resctrl_find_domain(list, new->id, &pos); 41109e61dafSJames Morse if (WARN_ON_ONCE(err)) 41209e61dafSJames Morse return; 41309e61dafSJames Morse 41409e61dafSJames Morse list_add_tail_rcu(&new->list, pos); 41509e61dafSJames Morse } 41609e61dafSJames Morse 41709e61dafSJames Morse static struct mpam_resctrl_dom * 41809e61dafSJames Morse mpam_resctrl_alloc_domain(unsigned int cpu, struct mpam_resctrl_res *res) 41909e61dafSJames Morse { 42009e61dafSJames Morse int err; 42109e61dafSJames Morse struct mpam_resctrl_dom *dom; 42209e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 42309e61dafSJames Morse struct mpam_class *class = res->class; 42409e61dafSJames Morse struct mpam_component *comp_iter, *ctrl_comp; 42509e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 42609e61dafSJames Morse 42709e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 42809e61dafSJames Morse 42909e61dafSJames Morse ctrl_comp = NULL; 43009e61dafSJames Morse guard(srcu)(&mpam_srcu); 43109e61dafSJames Morse list_for_each_entry_srcu(comp_iter, &class->components, class_list, 43209e61dafSJames Morse srcu_read_lock_held(&mpam_srcu)) { 43309e61dafSJames Morse if (cpumask_test_cpu(cpu, &comp_iter->affinity)) { 43409e61dafSJames Morse ctrl_comp = comp_iter; 43509e61dafSJames Morse break; 43609e61dafSJames Morse } 43709e61dafSJames Morse } 43809e61dafSJames Morse 43909e61dafSJames Morse /* class has no component for this CPU */ 44009e61dafSJames Morse if (WARN_ON_ONCE(!ctrl_comp)) 44109e61dafSJames Morse return ERR_PTR(-EINVAL); 44209e61dafSJames Morse 44309e61dafSJames Morse dom = kzalloc_node(sizeof(*dom), GFP_KERNEL, cpu_to_node(cpu)); 44409e61dafSJames Morse if (!dom) 44509e61dafSJames Morse return ERR_PTR(-ENOMEM); 44609e61dafSJames Morse 44709e61dafSJames Morse if (r->alloc_capable) { 44809e61dafSJames Morse dom->ctrl_comp = ctrl_comp; 44909e61dafSJames Morse 45009e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 45109e61dafSJames Morse mpam_resctrl_domain_hdr_init(cpu, ctrl_comp, r->rid, &ctrl_d->hdr); 45209e61dafSJames Morse ctrl_d->hdr.type = RESCTRL_CTRL_DOMAIN; 45309e61dafSJames Morse err = resctrl_online_ctrl_domain(r, ctrl_d); 45409e61dafSJames Morse if (err) 45509e61dafSJames Morse goto free_domain; 45609e61dafSJames Morse 45709e61dafSJames Morse mpam_resctrl_domain_insert(&r->ctrl_domains, &ctrl_d->hdr); 45809e61dafSJames Morse } else { 45909e61dafSJames Morse pr_debug("Skipped control domain online - no controls\n"); 46009e61dafSJames Morse } 46109e61dafSJames Morse return dom; 46209e61dafSJames Morse 46309e61dafSJames Morse free_domain: 46409e61dafSJames Morse kfree(dom); 46509e61dafSJames Morse dom = ERR_PTR(err); 46609e61dafSJames Morse 46709e61dafSJames Morse return dom; 46809e61dafSJames Morse } 46909e61dafSJames Morse 47009e61dafSJames Morse static struct mpam_resctrl_dom * 47109e61dafSJames Morse mpam_resctrl_get_domain_from_cpu(int cpu, struct mpam_resctrl_res *res) 47209e61dafSJames Morse { 47309e61dafSJames Morse struct mpam_resctrl_dom *dom; 47409e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 47509e61dafSJames Morse 47609e61dafSJames Morse lockdep_assert_cpus_held(); 47709e61dafSJames Morse 47809e61dafSJames Morse list_for_each_entry_rcu(dom, &r->ctrl_domains, resctrl_ctrl_dom.hdr.list) { 47909e61dafSJames Morse if (cpumask_test_cpu(cpu, &dom->ctrl_comp->affinity)) 48009e61dafSJames Morse return dom; 48109e61dafSJames Morse } 48209e61dafSJames Morse 48309e61dafSJames Morse return NULL; 48409e61dafSJames Morse } 48509e61dafSJames Morse 48609e61dafSJames Morse int mpam_resctrl_online_cpu(unsigned int cpu) 48709e61dafSJames Morse { 48809e61dafSJames Morse struct mpam_resctrl_res *res; 48909e61dafSJames Morse enum resctrl_res_level rid; 49009e61dafSJames Morse 49109e61dafSJames Morse guard(mutex)(&domain_list_lock); 49209e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 49309e61dafSJames Morse struct mpam_resctrl_dom *dom; 49409e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 49509e61dafSJames Morse 49609e61dafSJames Morse if (!res->class) 49709e61dafSJames Morse continue; // dummy_resource; 49809e61dafSJames Morse 49909e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 50009e61dafSJames Morse if (!dom) { 50109e61dafSJames Morse dom = mpam_resctrl_alloc_domain(cpu, res); 50209e61dafSJames Morse if (IS_ERR(dom)) 50309e61dafSJames Morse return PTR_ERR(dom); 50409e61dafSJames Morse } else { 50509e61dafSJames Morse if (r->alloc_capable) { 50609e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d = &dom->resctrl_ctrl_dom; 50709e61dafSJames Morse 50809e61dafSJames Morse mpam_resctrl_online_domain_hdr(cpu, &ctrl_d->hdr); 50909e61dafSJames Morse } 51009e61dafSJames Morse } 51109e61dafSJames Morse } 51209e61dafSJames Morse 51309e61dafSJames Morse resctrl_online_cpu(cpu); 51409e61dafSJames Morse 51509e61dafSJames Morse return 0; 51609e61dafSJames Morse } 51709e61dafSJames Morse 51809e61dafSJames Morse void mpam_resctrl_offline_cpu(unsigned int cpu) 51909e61dafSJames Morse { 52009e61dafSJames Morse struct mpam_resctrl_res *res; 52109e61dafSJames Morse enum resctrl_res_level rid; 52209e61dafSJames Morse 52309e61dafSJames Morse resctrl_offline_cpu(cpu); 52409e61dafSJames Morse 52509e61dafSJames Morse guard(mutex)(&domain_list_lock); 52609e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 52709e61dafSJames Morse struct mpam_resctrl_dom *dom; 52809e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 52909e61dafSJames Morse bool ctrl_dom_empty; 53009e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 53109e61dafSJames Morse 53209e61dafSJames Morse if (!res->class) 53309e61dafSJames Morse continue; // dummy resource 53409e61dafSJames Morse 53509e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 53609e61dafSJames Morse if (WARN_ON_ONCE(!dom)) 53709e61dafSJames Morse continue; 53809e61dafSJames Morse 53909e61dafSJames Morse if (r->alloc_capable) { 54009e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 54109e61dafSJames Morse ctrl_dom_empty = mpam_resctrl_offline_domain_hdr(cpu, &ctrl_d->hdr); 54209e61dafSJames Morse if (ctrl_dom_empty) 54309e61dafSJames Morse resctrl_offline_ctrl_domain(&res->resctrl_res, ctrl_d); 54409e61dafSJames Morse } else { 54509e61dafSJames Morse ctrl_dom_empty = true; 54609e61dafSJames Morse } 54709e61dafSJames Morse 54809e61dafSJames Morse if (ctrl_dom_empty) 54909e61dafSJames Morse kfree(dom); 55009e61dafSJames Morse } 55109e61dafSJames Morse } 55209e61dafSJames Morse 55309e61dafSJames Morse int mpam_resctrl_setup(void) 55409e61dafSJames Morse { 55509e61dafSJames Morse int err = 0; 55609e61dafSJames Morse struct mpam_resctrl_res *res; 55709e61dafSJames Morse enum resctrl_res_level rid; 55809e61dafSJames Morse 55909e61dafSJames Morse cpus_read_lock(); 56009e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 56109e61dafSJames Morse INIT_LIST_HEAD_RCU(&res->resctrl_res.ctrl_domains); 56209e61dafSJames Morse res->resctrl_res.rid = rid; 56309e61dafSJames Morse } 56409e61dafSJames Morse 56552a4edb1SJames Morse /* Find some classes to use for controls */ 56652a4edb1SJames Morse mpam_resctrl_pick_caches(); 56709e61dafSJames Morse 56809e61dafSJames Morse /* Initialise the resctrl structures from the classes */ 56909e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 57009e61dafSJames Morse if (!res->class) 57109e61dafSJames Morse continue; // dummy resource 57209e61dafSJames Morse 57309e61dafSJames Morse err = mpam_resctrl_control_init(res); 57409e61dafSJames Morse if (err) { 57509e61dafSJames Morse pr_debug("Failed to initialise rid %u\n", rid); 57609e61dafSJames Morse break; 57709e61dafSJames Morse } 57809e61dafSJames Morse } 57909e61dafSJames Morse cpus_read_unlock(); 58009e61dafSJames Morse 58109e61dafSJames Morse if (err) { 58209e61dafSJames Morse pr_debug("Internal error %d - resctrl not supported\n", err); 58309e61dafSJames Morse return err; 58409e61dafSJames Morse } 58509e61dafSJames Morse 58609e61dafSJames Morse if (!resctrl_arch_alloc_capable()) { 58709e61dafSJames Morse pr_debug("No alloc(%u) found - resctrl not supported\n", 58809e61dafSJames Morse resctrl_arch_alloc_capable()); 58909e61dafSJames Morse return -EOPNOTSUPP; 59009e61dafSJames Morse } 59109e61dafSJames Morse 59209e61dafSJames Morse /* TODO: call resctrl_init() */ 59309e61dafSJames Morse 59409e61dafSJames Morse return 0; 59509e61dafSJames Morse } 596