Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

arm_mpam: resctrl: Add boilerplate cpuhp and domain allocation

resctrl has its own data structures to describe its resources. We can't use
these directly as we play tricks with the 'MBA' resource, picking the MPAM
controls or monitors that best apply. We may export the same component as
both L3 and MBA.

Add mpam_resctrl_res[] as the array of class->resctrl mappings we are
exporting, and add the cpuhp hooks that allocated and free the resctrl
domain structures. Only the mpam control feature are considered here and
monitor support will be added later.

While we're here, plumb in a few other obvious things.

CONFIG_ARM_CPU_RESCTRL is used to allow this code to be built even though
it can't yet be linked against resctrl.

Tested-by: Gavin Shan <gshan@redhat.com>
Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Tested-by: Peter Newman <peternewman@google.com>
Tested-by: Zeng Heng <zengheng4@huawei.com>
Tested-by: Punit Agrawal <punit.agrawal@oss.qualcomm.com>
Tested-by: Jesse Chick <jessechick@os.amperecomputing.com>
Reviewed-by: Zeng Heng <zengheng4@huawei.com>
Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Co-developed-by: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: James Morse <james.morse@arm.com>

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