1*09e61dafSJames Morse // SPDX-License-Identifier: GPL-2.0 2*09e61dafSJames Morse // Copyright (C) 2025 Arm Ltd. 3*09e61dafSJames Morse 4*09e61dafSJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 5*09e61dafSJames Morse 6*09e61dafSJames Morse #include <linux/arm_mpam.h> 7*09e61dafSJames Morse #include <linux/cacheinfo.h> 8*09e61dafSJames Morse #include <linux/cpu.h> 9*09e61dafSJames Morse #include <linux/cpumask.h> 10*09e61dafSJames Morse #include <linux/errno.h> 11*09e61dafSJames Morse #include <linux/list.h> 12*09e61dafSJames Morse #include <linux/printk.h> 13*09e61dafSJames Morse #include <linux/rculist.h> 14*09e61dafSJames Morse #include <linux/resctrl.h> 15*09e61dafSJames Morse #include <linux/slab.h> 16*09e61dafSJames Morse #include <linux/types.h> 17*09e61dafSJames Morse 18*09e61dafSJames Morse #include <asm/mpam.h> 19*09e61dafSJames Morse 20*09e61dafSJames Morse #include "mpam_internal.h" 21*09e61dafSJames Morse 22*09e61dafSJames Morse /* 23*09e61dafSJames Morse * The classes we've picked to map to resctrl resources, wrapped 24*09e61dafSJames Morse * in with their resctrl structure. 25*09e61dafSJames Morse * Class pointer may be NULL. 26*09e61dafSJames Morse */ 27*09e61dafSJames Morse static struct mpam_resctrl_res mpam_resctrl_controls[RDT_NUM_RESOURCES]; 28*09e61dafSJames Morse 29*09e61dafSJames Morse #define for_each_mpam_resctrl_control(res, rid) \ 30*09e61dafSJames Morse for (rid = 0, res = &mpam_resctrl_controls[rid]; \ 31*09e61dafSJames Morse rid < RDT_NUM_RESOURCES; \ 32*09e61dafSJames Morse rid++, res = &mpam_resctrl_controls[rid]) 33*09e61dafSJames Morse 34*09e61dafSJames Morse /* The lock for modifying resctrl's domain lists from cpuhp callbacks. */ 35*09e61dafSJames Morse static DEFINE_MUTEX(domain_list_lock); 36*09e61dafSJames Morse 37*09e61dafSJames Morse bool resctrl_arch_alloc_capable(void) 38*09e61dafSJames Morse { 39*09e61dafSJames Morse struct mpam_resctrl_res *res; 40*09e61dafSJames Morse enum resctrl_res_level rid; 41*09e61dafSJames Morse 42*09e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 43*09e61dafSJames Morse if (res->resctrl_res.alloc_capable) 44*09e61dafSJames Morse return true; 45*09e61dafSJames Morse } 46*09e61dafSJames Morse 47*09e61dafSJames Morse return false; 48*09e61dafSJames Morse } 49*09e61dafSJames Morse 50*09e61dafSJames Morse /* 51*09e61dafSJames Morse * MSC may raise an error interrupt if it sees an out or range partid/pmg, 52*09e61dafSJames Morse * and go on to truncate the value. Regardless of what the hardware supports, 53*09e61dafSJames Morse * only the system wide safe value is safe to use. 54*09e61dafSJames Morse */ 55*09e61dafSJames Morse u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored) 56*09e61dafSJames Morse { 57*09e61dafSJames Morse return mpam_partid_max + 1; 58*09e61dafSJames Morse } 59*09e61dafSJames Morse 60*09e61dafSJames Morse struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) 61*09e61dafSJames Morse { 62*09e61dafSJames Morse if (l >= RDT_NUM_RESOURCES) 63*09e61dafSJames Morse return NULL; 64*09e61dafSJames Morse 65*09e61dafSJames Morse return &mpam_resctrl_controls[l].resctrl_res; 66*09e61dafSJames Morse } 67*09e61dafSJames Morse 68*09e61dafSJames Morse static int mpam_resctrl_control_init(struct mpam_resctrl_res *res) 69*09e61dafSJames Morse { 70*09e61dafSJames Morse /* TODO: initialise the resctrl resources */ 71*09e61dafSJames Morse 72*09e61dafSJames Morse return 0; 73*09e61dafSJames Morse } 74*09e61dafSJames Morse 75*09e61dafSJames Morse static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp) 76*09e61dafSJames Morse { 77*09e61dafSJames Morse struct mpam_class *class = comp->class; 78*09e61dafSJames Morse 79*09e61dafSJames Morse if (class->type == MPAM_CLASS_CACHE) 80*09e61dafSJames Morse return comp->comp_id; 81*09e61dafSJames Morse 82*09e61dafSJames Morse /* TODO: repaint domain ids to match the L3 domain ids */ 83*09e61dafSJames Morse /* Otherwise, expose the ID used by the firmware table code. */ 84*09e61dafSJames Morse return comp->comp_id; 85*09e61dafSJames Morse } 86*09e61dafSJames Morse 87*09e61dafSJames Morse static void mpam_resctrl_domain_hdr_init(int cpu, struct mpam_component *comp, 88*09e61dafSJames Morse enum resctrl_res_level rid, 89*09e61dafSJames Morse struct rdt_domain_hdr *hdr) 90*09e61dafSJames Morse { 91*09e61dafSJames Morse lockdep_assert_cpus_held(); 92*09e61dafSJames Morse 93*09e61dafSJames Morse INIT_LIST_HEAD(&hdr->list); 94*09e61dafSJames Morse hdr->id = mpam_resctrl_pick_domain_id(cpu, comp); 95*09e61dafSJames Morse hdr->rid = rid; 96*09e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 97*09e61dafSJames Morse } 98*09e61dafSJames Morse 99*09e61dafSJames Morse static void mpam_resctrl_online_domain_hdr(unsigned int cpu, 100*09e61dafSJames Morse struct rdt_domain_hdr *hdr) 101*09e61dafSJames Morse { 102*09e61dafSJames Morse lockdep_assert_cpus_held(); 103*09e61dafSJames Morse 104*09e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 105*09e61dafSJames Morse } 106*09e61dafSJames Morse 107*09e61dafSJames Morse /** 108*09e61dafSJames Morse * mpam_resctrl_offline_domain_hdr() - Update the domain header to remove a CPU. 109*09e61dafSJames Morse * @cpu: The CPU to remove from the domain. 110*09e61dafSJames Morse * @hdr: The domain's header. 111*09e61dafSJames Morse * 112*09e61dafSJames Morse * Removes @cpu from the header mask. If this was the last CPU in the domain, 113*09e61dafSJames Morse * the domain header is removed from its parent list and true is returned, 114*09e61dafSJames Morse * indicating the parent structure can be freed. 115*09e61dafSJames Morse * If there are other CPUs in the domain, returns false. 116*09e61dafSJames Morse */ 117*09e61dafSJames Morse static bool mpam_resctrl_offline_domain_hdr(unsigned int cpu, 118*09e61dafSJames Morse struct rdt_domain_hdr *hdr) 119*09e61dafSJames Morse { 120*09e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 121*09e61dafSJames Morse 122*09e61dafSJames Morse cpumask_clear_cpu(cpu, &hdr->cpu_mask); 123*09e61dafSJames Morse if (cpumask_empty(&hdr->cpu_mask)) { 124*09e61dafSJames Morse list_del_rcu(&hdr->list); 125*09e61dafSJames Morse synchronize_rcu(); 126*09e61dafSJames Morse return true; 127*09e61dafSJames Morse } 128*09e61dafSJames Morse 129*09e61dafSJames Morse return false; 130*09e61dafSJames Morse } 131*09e61dafSJames Morse 132*09e61dafSJames Morse static void mpam_resctrl_domain_insert(struct list_head *list, 133*09e61dafSJames Morse struct rdt_domain_hdr *new) 134*09e61dafSJames Morse { 135*09e61dafSJames Morse struct rdt_domain_hdr *err; 136*09e61dafSJames Morse struct list_head *pos = NULL; 137*09e61dafSJames Morse 138*09e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 139*09e61dafSJames Morse 140*09e61dafSJames Morse err = resctrl_find_domain(list, new->id, &pos); 141*09e61dafSJames Morse if (WARN_ON_ONCE(err)) 142*09e61dafSJames Morse return; 143*09e61dafSJames Morse 144*09e61dafSJames Morse list_add_tail_rcu(&new->list, pos); 145*09e61dafSJames Morse } 146*09e61dafSJames Morse 147*09e61dafSJames Morse static struct mpam_resctrl_dom * 148*09e61dafSJames Morse mpam_resctrl_alloc_domain(unsigned int cpu, struct mpam_resctrl_res *res) 149*09e61dafSJames Morse { 150*09e61dafSJames Morse int err; 151*09e61dafSJames Morse struct mpam_resctrl_dom *dom; 152*09e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 153*09e61dafSJames Morse struct mpam_class *class = res->class; 154*09e61dafSJames Morse struct mpam_component *comp_iter, *ctrl_comp; 155*09e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 156*09e61dafSJames Morse 157*09e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 158*09e61dafSJames Morse 159*09e61dafSJames Morse ctrl_comp = NULL; 160*09e61dafSJames Morse guard(srcu)(&mpam_srcu); 161*09e61dafSJames Morse list_for_each_entry_srcu(comp_iter, &class->components, class_list, 162*09e61dafSJames Morse srcu_read_lock_held(&mpam_srcu)) { 163*09e61dafSJames Morse if (cpumask_test_cpu(cpu, &comp_iter->affinity)) { 164*09e61dafSJames Morse ctrl_comp = comp_iter; 165*09e61dafSJames Morse break; 166*09e61dafSJames Morse } 167*09e61dafSJames Morse } 168*09e61dafSJames Morse 169*09e61dafSJames Morse /* class has no component for this CPU */ 170*09e61dafSJames Morse if (WARN_ON_ONCE(!ctrl_comp)) 171*09e61dafSJames Morse return ERR_PTR(-EINVAL); 172*09e61dafSJames Morse 173*09e61dafSJames Morse dom = kzalloc_node(sizeof(*dom), GFP_KERNEL, cpu_to_node(cpu)); 174*09e61dafSJames Morse if (!dom) 175*09e61dafSJames Morse return ERR_PTR(-ENOMEM); 176*09e61dafSJames Morse 177*09e61dafSJames Morse if (r->alloc_capable) { 178*09e61dafSJames Morse dom->ctrl_comp = ctrl_comp; 179*09e61dafSJames Morse 180*09e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 181*09e61dafSJames Morse mpam_resctrl_domain_hdr_init(cpu, ctrl_comp, r->rid, &ctrl_d->hdr); 182*09e61dafSJames Morse ctrl_d->hdr.type = RESCTRL_CTRL_DOMAIN; 183*09e61dafSJames Morse err = resctrl_online_ctrl_domain(r, ctrl_d); 184*09e61dafSJames Morse if (err) 185*09e61dafSJames Morse goto free_domain; 186*09e61dafSJames Morse 187*09e61dafSJames Morse mpam_resctrl_domain_insert(&r->ctrl_domains, &ctrl_d->hdr); 188*09e61dafSJames Morse } else { 189*09e61dafSJames Morse pr_debug("Skipped control domain online - no controls\n"); 190*09e61dafSJames Morse } 191*09e61dafSJames Morse return dom; 192*09e61dafSJames Morse 193*09e61dafSJames Morse free_domain: 194*09e61dafSJames Morse kfree(dom); 195*09e61dafSJames Morse dom = ERR_PTR(err); 196*09e61dafSJames Morse 197*09e61dafSJames Morse return dom; 198*09e61dafSJames Morse } 199*09e61dafSJames Morse 200*09e61dafSJames Morse static struct mpam_resctrl_dom * 201*09e61dafSJames Morse mpam_resctrl_get_domain_from_cpu(int cpu, struct mpam_resctrl_res *res) 202*09e61dafSJames Morse { 203*09e61dafSJames Morse struct mpam_resctrl_dom *dom; 204*09e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 205*09e61dafSJames Morse 206*09e61dafSJames Morse lockdep_assert_cpus_held(); 207*09e61dafSJames Morse 208*09e61dafSJames Morse list_for_each_entry_rcu(dom, &r->ctrl_domains, resctrl_ctrl_dom.hdr.list) { 209*09e61dafSJames Morse if (cpumask_test_cpu(cpu, &dom->ctrl_comp->affinity)) 210*09e61dafSJames Morse return dom; 211*09e61dafSJames Morse } 212*09e61dafSJames Morse 213*09e61dafSJames Morse return NULL; 214*09e61dafSJames Morse } 215*09e61dafSJames Morse 216*09e61dafSJames Morse int mpam_resctrl_online_cpu(unsigned int cpu) 217*09e61dafSJames Morse { 218*09e61dafSJames Morse struct mpam_resctrl_res *res; 219*09e61dafSJames Morse enum resctrl_res_level rid; 220*09e61dafSJames Morse 221*09e61dafSJames Morse guard(mutex)(&domain_list_lock); 222*09e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 223*09e61dafSJames Morse struct mpam_resctrl_dom *dom; 224*09e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 225*09e61dafSJames Morse 226*09e61dafSJames Morse if (!res->class) 227*09e61dafSJames Morse continue; // dummy_resource; 228*09e61dafSJames Morse 229*09e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 230*09e61dafSJames Morse if (!dom) { 231*09e61dafSJames Morse dom = mpam_resctrl_alloc_domain(cpu, res); 232*09e61dafSJames Morse if (IS_ERR(dom)) 233*09e61dafSJames Morse return PTR_ERR(dom); 234*09e61dafSJames Morse } else { 235*09e61dafSJames Morse if (r->alloc_capable) { 236*09e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d = &dom->resctrl_ctrl_dom; 237*09e61dafSJames Morse 238*09e61dafSJames Morse mpam_resctrl_online_domain_hdr(cpu, &ctrl_d->hdr); 239*09e61dafSJames Morse } 240*09e61dafSJames Morse } 241*09e61dafSJames Morse } 242*09e61dafSJames Morse 243*09e61dafSJames Morse resctrl_online_cpu(cpu); 244*09e61dafSJames Morse 245*09e61dafSJames Morse return 0; 246*09e61dafSJames Morse } 247*09e61dafSJames Morse 248*09e61dafSJames Morse void mpam_resctrl_offline_cpu(unsigned int cpu) 249*09e61dafSJames Morse { 250*09e61dafSJames Morse struct mpam_resctrl_res *res; 251*09e61dafSJames Morse enum resctrl_res_level rid; 252*09e61dafSJames Morse 253*09e61dafSJames Morse resctrl_offline_cpu(cpu); 254*09e61dafSJames Morse 255*09e61dafSJames Morse guard(mutex)(&domain_list_lock); 256*09e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 257*09e61dafSJames Morse struct mpam_resctrl_dom *dom; 258*09e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 259*09e61dafSJames Morse bool ctrl_dom_empty; 260*09e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 261*09e61dafSJames Morse 262*09e61dafSJames Morse if (!res->class) 263*09e61dafSJames Morse continue; // dummy resource 264*09e61dafSJames Morse 265*09e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 266*09e61dafSJames Morse if (WARN_ON_ONCE(!dom)) 267*09e61dafSJames Morse continue; 268*09e61dafSJames Morse 269*09e61dafSJames Morse if (r->alloc_capable) { 270*09e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 271*09e61dafSJames Morse ctrl_dom_empty = mpam_resctrl_offline_domain_hdr(cpu, &ctrl_d->hdr); 272*09e61dafSJames Morse if (ctrl_dom_empty) 273*09e61dafSJames Morse resctrl_offline_ctrl_domain(&res->resctrl_res, ctrl_d); 274*09e61dafSJames Morse } else { 275*09e61dafSJames Morse ctrl_dom_empty = true; 276*09e61dafSJames Morse } 277*09e61dafSJames Morse 278*09e61dafSJames Morse if (ctrl_dom_empty) 279*09e61dafSJames Morse kfree(dom); 280*09e61dafSJames Morse } 281*09e61dafSJames Morse } 282*09e61dafSJames Morse 283*09e61dafSJames Morse int mpam_resctrl_setup(void) 284*09e61dafSJames Morse { 285*09e61dafSJames Morse int err = 0; 286*09e61dafSJames Morse struct mpam_resctrl_res *res; 287*09e61dafSJames Morse enum resctrl_res_level rid; 288*09e61dafSJames Morse 289*09e61dafSJames Morse cpus_read_lock(); 290*09e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 291*09e61dafSJames Morse INIT_LIST_HEAD_RCU(&res->resctrl_res.ctrl_domains); 292*09e61dafSJames Morse res->resctrl_res.rid = rid; 293*09e61dafSJames Morse } 294*09e61dafSJames Morse 295*09e61dafSJames Morse /* TODO: pick MPAM classes to map to resctrl resources */ 296*09e61dafSJames Morse 297*09e61dafSJames Morse /* Initialise the resctrl structures from the classes */ 298*09e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 299*09e61dafSJames Morse if (!res->class) 300*09e61dafSJames Morse continue; // dummy resource 301*09e61dafSJames Morse 302*09e61dafSJames Morse err = mpam_resctrl_control_init(res); 303*09e61dafSJames Morse if (err) { 304*09e61dafSJames Morse pr_debug("Failed to initialise rid %u\n", rid); 305*09e61dafSJames Morse break; 306*09e61dafSJames Morse } 307*09e61dafSJames Morse } 308*09e61dafSJames Morse cpus_read_unlock(); 309*09e61dafSJames Morse 310*09e61dafSJames Morse if (err) { 311*09e61dafSJames Morse pr_debug("Internal error %d - resctrl not supported\n", err); 312*09e61dafSJames Morse return err; 313*09e61dafSJames Morse } 314*09e61dafSJames Morse 315*09e61dafSJames Morse if (!resctrl_arch_alloc_capable()) { 316*09e61dafSJames Morse pr_debug("No alloc(%u) found - resctrl not supported\n", 317*09e61dafSJames Morse resctrl_arch_alloc_capable()); 318*09e61dafSJames Morse return -EOPNOTSUPP; 319*09e61dafSJames Morse } 320*09e61dafSJames Morse 321*09e61dafSJames Morse /* TODO: call resctrl_init() */ 322*09e61dafSJames Morse 323*09e61dafSJames Morse return 0; 324*09e61dafSJames Morse } 325