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: Add the class and component structures for firmware described ris

An MSC is a container of resources, each identified by their RIS index.
Some RIS are described by firmware to provide their position in the system.
Others are discovered when the driver probes the hardware.

To configure a resource it needs to be found by its class, e.g. 'L2'.
There are two kinds of grouping, a class is a set of components, which
are visible to user-space as there are likely to be multiple instances
of the L2 cache. (e.g. one per cluster or package)

Add support for creating and destroying structures to allow a hierarchy
of resources to be created.

Reviewed-by: Gavin Shan <gshan@redhat.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Reviewed-by: Fenghua Yu <fenghuay@nvidia.com>
Tested-by: Fenghua Yu <fenghuay@nvidia.com>
Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
Tested-by: Peter Newman <peternewman@google.com>
Tested-by: Carl Worth <carl@os.amperecomputing.com>
Tested-by: Gavin Shan <gshan@redhat.com>
Tested-by: Zeng Heng <zengheng4@huawei.com>
Tested-by: Hanjun Guo <guohanjun@huawei.com>
Signed-off-by: James Morse <james.morse@arm.com>
Signed-off-by: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

James Morse and committed by
Catalin Marinas
01fb4b82 f04046f2

+490 -1
+391 -1
drivers/resctrl/mpam_devices.c
··· 37 37 static atomic_t mpam_num_msc; 38 38 39 39 /* 40 + * An MSC is a physical container for controls and monitors, each identified by 41 + * their RIS index. These share a base-address, interrupts and some MMIO 42 + * registers. A vMSC is a virtual container for RIS in an MSC that control or 43 + * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but 44 + * not all RIS in an MSC share a vMSC. 45 + * 46 + * Components are a group of vMSC that control or monitor the same thing but 47 + * are from different MSC, so have different base-address, interrupts etc. 48 + * Classes are the set components of the same type. 49 + * 50 + * The features of a vMSC is the union of the RIS it contains. 51 + * The features of a Class and Component are the common subset of the vMSC 52 + * they contain. 53 + * 54 + * e.g. The system cache may have bandwidth controls on multiple interfaces, 55 + * for regulating traffic from devices independently of traffic from CPUs. 56 + * If these are two RIS in one MSC, they will be treated as controlling 57 + * different things, and will not share a vMSC/component/class. 58 + * 59 + * e.g. The L2 may have one MSC and two RIS, one for cache-controls another 60 + * for bandwidth. These two RIS are members of the same vMSC. 61 + * 62 + * e.g. The set of RIS that make up the L2 are grouped as a component. These 63 + * are sometimes termed slices. They should be configured the same, as if there 64 + * were only one. 65 + * 66 + * e.g. The SoC probably has more than one L2, each attached to a distinct set 67 + * of CPUs. All the L2 components are grouped as a class. 68 + * 69 + * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, 70 + * then linked via struct mpam_ris to a vmsc, component and class. 71 + * The same MSC may exist under different class->component->vmsc paths, but the 72 + * RIS index will be unique. 73 + */ 74 + LIST_HEAD(mpam_classes); 75 + 76 + /* List of all objects that can be free()d after synchronise_srcu() */ 77 + static LLIST_HEAD(mpam_garbage); 78 + 79 + static inline void init_garbage(struct mpam_garbage *garbage) 80 + { 81 + init_llist_node(&garbage->llist); 82 + } 83 + 84 + #define add_to_garbage(x) \ 85 + do { \ 86 + __typeof__(x) _x = (x); \ 87 + _x->garbage.to_free = _x; \ 88 + llist_add(&_x->garbage.llist, &mpam_garbage); \ 89 + } while (0) 90 + 91 + static void mpam_free_garbage(void) 92 + { 93 + struct mpam_garbage *iter, *tmp; 94 + struct llist_node *to_free = llist_del_all(&mpam_garbage); 95 + 96 + if (!to_free) 97 + return; 98 + 99 + synchronize_srcu(&mpam_srcu); 100 + 101 + llist_for_each_entry_safe(iter, tmp, to_free, llist) { 102 + if (iter->pdev) 103 + devm_kfree(&iter->pdev->dev, iter->to_free); 104 + else 105 + kfree(iter->to_free); 106 + } 107 + } 108 + 109 + static struct mpam_class * 110 + mpam_class_alloc(u8 level_idx, enum mpam_class_types type) 111 + { 112 + struct mpam_class *class; 113 + 114 + lockdep_assert_held(&mpam_list_lock); 115 + 116 + class = kzalloc(sizeof(*class), GFP_KERNEL); 117 + if (!class) 118 + return ERR_PTR(-ENOMEM); 119 + init_garbage(&class->garbage); 120 + 121 + INIT_LIST_HEAD_RCU(&class->components); 122 + /* Affinity is updated when ris are added */ 123 + class->level = level_idx; 124 + class->type = type; 125 + INIT_LIST_HEAD_RCU(&class->classes_list); 126 + 127 + list_add_rcu(&class->classes_list, &mpam_classes); 128 + 129 + return class; 130 + } 131 + 132 + static void mpam_class_destroy(struct mpam_class *class) 133 + { 134 + lockdep_assert_held(&mpam_list_lock); 135 + 136 + list_del_rcu(&class->classes_list); 137 + add_to_garbage(class); 138 + } 139 + 140 + static struct mpam_class * 141 + mpam_class_find(u8 level_idx, enum mpam_class_types type) 142 + { 143 + struct mpam_class *class; 144 + 145 + lockdep_assert_held(&mpam_list_lock); 146 + 147 + list_for_each_entry(class, &mpam_classes, classes_list) { 148 + if (class->type == type && class->level == level_idx) 149 + return class; 150 + } 151 + 152 + return mpam_class_alloc(level_idx, type); 153 + } 154 + 155 + static struct mpam_component * 156 + mpam_component_alloc(struct mpam_class *class, int id) 157 + { 158 + struct mpam_component *comp; 159 + 160 + lockdep_assert_held(&mpam_list_lock); 161 + 162 + comp = kzalloc(sizeof(*comp), GFP_KERNEL); 163 + if (!comp) 164 + return ERR_PTR(-ENOMEM); 165 + init_garbage(&comp->garbage); 166 + 167 + comp->comp_id = id; 168 + INIT_LIST_HEAD_RCU(&comp->vmsc); 169 + /* Affinity is updated when RIS are added */ 170 + INIT_LIST_HEAD_RCU(&comp->class_list); 171 + comp->class = class; 172 + 173 + list_add_rcu(&comp->class_list, &class->components); 174 + 175 + return comp; 176 + } 177 + 178 + static void mpam_component_destroy(struct mpam_component *comp) 179 + { 180 + struct mpam_class *class = comp->class; 181 + 182 + lockdep_assert_held(&mpam_list_lock); 183 + 184 + list_del_rcu(&comp->class_list); 185 + add_to_garbage(comp); 186 + 187 + if (list_empty(&class->components)) 188 + mpam_class_destroy(class); 189 + } 190 + 191 + static struct mpam_component * 192 + mpam_component_find(struct mpam_class *class, int id) 193 + { 194 + struct mpam_component *comp; 195 + 196 + lockdep_assert_held(&mpam_list_lock); 197 + 198 + list_for_each_entry(comp, &class->components, class_list) { 199 + if (comp->comp_id == id) 200 + return comp; 201 + } 202 + 203 + return mpam_component_alloc(class, id); 204 + } 205 + 206 + static struct mpam_vmsc * 207 + mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc) 208 + { 209 + struct mpam_vmsc *vmsc; 210 + 211 + lockdep_assert_held(&mpam_list_lock); 212 + 213 + vmsc = kzalloc(sizeof(*vmsc), GFP_KERNEL); 214 + if (!vmsc) 215 + return ERR_PTR(-ENOMEM); 216 + init_garbage(&vmsc->garbage); 217 + 218 + INIT_LIST_HEAD_RCU(&vmsc->ris); 219 + INIT_LIST_HEAD_RCU(&vmsc->comp_list); 220 + vmsc->comp = comp; 221 + vmsc->msc = msc; 222 + 223 + list_add_rcu(&vmsc->comp_list, &comp->vmsc); 224 + 225 + return vmsc; 226 + } 227 + 228 + static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) 229 + { 230 + struct mpam_component *comp = vmsc->comp; 231 + 232 + lockdep_assert_held(&mpam_list_lock); 233 + 234 + list_del_rcu(&vmsc->comp_list); 235 + add_to_garbage(vmsc); 236 + 237 + if (list_empty(&comp->vmsc)) 238 + mpam_component_destroy(comp); 239 + } 240 + 241 + static struct mpam_vmsc * 242 + mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) 243 + { 244 + struct mpam_vmsc *vmsc; 245 + 246 + lockdep_assert_held(&mpam_list_lock); 247 + 248 + list_for_each_entry(vmsc, &comp->vmsc, comp_list) { 249 + if (vmsc->msc->id == msc->id) 250 + return vmsc; 251 + } 252 + 253 + return mpam_vmsc_alloc(comp, msc); 254 + } 255 + 256 + /* 257 + * The cacheinfo structures are only populated when CPUs are online. 258 + * This helper walks the acpi tables to include offline CPUs too. 259 + */ 260 + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 261 + cpumask_t *affinity) 262 + { 263 + return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); 264 + } 265 + 266 + /* 267 + * cpumask_of_node() only knows about online CPUs. This can't tell us whether 268 + * a class is represented on all possible CPUs. 269 + */ 270 + static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) 271 + { 272 + int cpu; 273 + 274 + for_each_possible_cpu(cpu) { 275 + if (node_id == cpu_to_node(cpu)) 276 + cpumask_set_cpu(cpu, affinity); 277 + } 278 + } 279 + 280 + static int mpam_ris_get_affinity(struct mpam_msc *msc, cpumask_t *affinity, 281 + enum mpam_class_types type, 282 + struct mpam_class *class, 283 + struct mpam_component *comp) 284 + { 285 + int err; 286 + 287 + switch (type) { 288 + case MPAM_CLASS_CACHE: 289 + err = mpam_get_cpumask_from_cache_id(comp->comp_id, class->level, 290 + affinity); 291 + if (err) { 292 + dev_warn_once(&msc->pdev->dev, 293 + "Failed to determine CPU affinity\n"); 294 + return err; 295 + } 296 + 297 + if (cpumask_empty(affinity)) 298 + dev_warn_once(&msc->pdev->dev, "no CPUs associated with cache node\n"); 299 + 300 + break; 301 + case MPAM_CLASS_MEMORY: 302 + get_cpumask_from_node_id(comp->comp_id, affinity); 303 + /* affinity may be empty for CPU-less memory nodes */ 304 + break; 305 + case MPAM_CLASS_UNKNOWN: 306 + return 0; 307 + } 308 + 309 + cpumask_and(affinity, affinity, &msc->accessibility); 310 + 311 + return 0; 312 + } 313 + 314 + static int mpam_ris_create_locked(struct mpam_msc *msc, u8 ris_idx, 315 + enum mpam_class_types type, u8 class_id, 316 + int component_id) 317 + { 318 + int err; 319 + struct mpam_vmsc *vmsc; 320 + struct mpam_msc_ris *ris; 321 + struct mpam_class *class; 322 + struct mpam_component *comp; 323 + struct platform_device *pdev = msc->pdev; 324 + 325 + lockdep_assert_held(&mpam_list_lock); 326 + 327 + if (ris_idx > MPAM_MSC_MAX_NUM_RIS) 328 + return -EINVAL; 329 + 330 + if (test_and_set_bit(ris_idx, &msc->ris_idxs)) 331 + return -EBUSY; 332 + 333 + ris = devm_kzalloc(&msc->pdev->dev, sizeof(*ris), GFP_KERNEL); 334 + if (!ris) 335 + return -ENOMEM; 336 + init_garbage(&ris->garbage); 337 + ris->garbage.pdev = pdev; 338 + 339 + class = mpam_class_find(class_id, type); 340 + if (IS_ERR(class)) 341 + return PTR_ERR(class); 342 + 343 + comp = mpam_component_find(class, component_id); 344 + if (IS_ERR(comp)) { 345 + if (list_empty(&class->components)) 346 + mpam_class_destroy(class); 347 + return PTR_ERR(comp); 348 + } 349 + 350 + vmsc = mpam_vmsc_find(comp, msc); 351 + if (IS_ERR(vmsc)) { 352 + if (list_empty(&comp->vmsc)) 353 + mpam_component_destroy(comp); 354 + return PTR_ERR(vmsc); 355 + } 356 + 357 + err = mpam_ris_get_affinity(msc, &ris->affinity, type, class, comp); 358 + if (err) { 359 + if (list_empty(&vmsc->ris)) 360 + mpam_vmsc_destroy(vmsc); 361 + return err; 362 + } 363 + 364 + ris->ris_idx = ris_idx; 365 + INIT_LIST_HEAD_RCU(&ris->msc_list); 366 + INIT_LIST_HEAD_RCU(&ris->vmsc_list); 367 + ris->vmsc = vmsc; 368 + 369 + cpumask_or(&comp->affinity, &comp->affinity, &ris->affinity); 370 + cpumask_or(&class->affinity, &class->affinity, &ris->affinity); 371 + list_add_rcu(&ris->vmsc_list, &vmsc->ris); 372 + list_add_rcu(&ris->msc_list, &msc->ris); 373 + 374 + return 0; 375 + } 376 + 377 + static void mpam_ris_destroy(struct mpam_msc_ris *ris) 378 + { 379 + struct mpam_vmsc *vmsc = ris->vmsc; 380 + struct mpam_msc *msc = vmsc->msc; 381 + struct mpam_component *comp = vmsc->comp; 382 + struct mpam_class *class = comp->class; 383 + 384 + lockdep_assert_held(&mpam_list_lock); 385 + 386 + /* 387 + * It is assumed affinities don't overlap. If they do the class becomes 388 + * unusable immediately. 389 + */ 390 + cpumask_andnot(&class->affinity, &class->affinity, &ris->affinity); 391 + cpumask_andnot(&comp->affinity, &comp->affinity, &ris->affinity); 392 + clear_bit(ris->ris_idx, &msc->ris_idxs); 393 + list_del_rcu(&ris->msc_list); 394 + list_del_rcu(&ris->vmsc_list); 395 + add_to_garbage(ris); 396 + 397 + if (list_empty(&vmsc->ris)) 398 + mpam_vmsc_destroy(vmsc); 399 + } 400 + 401 + int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 402 + enum mpam_class_types type, u8 class_id, int component_id) 403 + { 404 + int err; 405 + 406 + mutex_lock(&mpam_list_lock); 407 + err = mpam_ris_create_locked(msc, ris_idx, type, class_id, 408 + component_id); 409 + mutex_unlock(&mpam_list_lock); 410 + if (err) 411 + mpam_free_garbage(); 412 + 413 + return err; 414 + } 415 + 416 + /* 40 417 * An MSC can control traffic from a set of CPUs, but may only be accessible 41 418 * from a (hopefully wider) set of CPUs. The common reason for this is power 42 419 * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the ··· 433 56 acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 434 57 } 435 58 59 + /* 60 + * There are two ways of reaching a struct mpam_msc_ris. Via the 61 + * class->component->vmsc->ris, or via the msc. 62 + * When destroying the msc, the other side needs unlinking and cleaning up too. 63 + */ 436 64 static void mpam_msc_destroy(struct mpam_msc *msc) 437 65 { 438 66 struct platform_device *pdev = msc->pdev; 67 + struct mpam_msc_ris *ris, *tmp; 439 68 440 69 lockdep_assert_held(&mpam_list_lock); 441 70 71 + list_for_each_entry_safe(ris, tmp, &msc->ris, msc_list) 72 + mpam_ris_destroy(ris); 73 + 442 74 list_del_rcu(&msc->all_msc_list); 443 75 platform_set_drvdata(pdev, NULL); 76 + 77 + add_to_garbage(msc); 444 78 } 445 79 446 80 static void mpam_msc_drv_remove(struct platform_device *pdev) ··· 462 74 mpam_msc_destroy(msc); 463 75 mutex_unlock(&mpam_list_lock); 464 76 465 - synchronize_srcu(&mpam_srcu); 77 + mpam_free_garbage(); 466 78 } 467 79 468 80 static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) ··· 478 90 msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 479 91 if (!msc) 480 92 return ERR_PTR(-ENOMEM); 93 + init_garbage(&msc->garbage); 94 + msc->garbage.pdev = pdev; 481 95 482 96 err = devm_mutex_init(dev, &msc->probe_lock); 483 97 if (err)
+94
drivers/resctrl/mpam_internal.h
··· 7 7 #include <linux/arm_mpam.h> 8 8 #include <linux/cpumask.h> 9 9 #include <linux/io.h> 10 + #include <linux/llist.h> 10 11 #include <linux/mutex.h> 12 + #include <linux/srcu.h> 11 13 #include <linux/types.h> 12 14 15 + #define MPAM_MSC_MAX_NUM_RIS 16 16 + 13 17 struct platform_device; 18 + 19 + /* 20 + * Structures protected by SRCU may not be freed for a surprising amount of 21 + * time (especially if perf is running). To ensure the MPAM error interrupt can 22 + * tear down all the structures, build a list of objects that can be garbage 23 + * collected once synchronize_srcu() has returned. 24 + * If pdev is non-NULL, use devm_kfree(). 25 + */ 26 + struct mpam_garbage { 27 + /* member of mpam_garbage */ 28 + struct llist_node llist; 29 + 30 + void *to_free; 31 + struct platform_device *pdev; 32 + }; 14 33 15 34 struct mpam_msc { 16 35 /* member of mpam_all_msc */ ··· 64 45 65 46 void __iomem *mapped_hwpage; 66 47 size_t mapped_hwpage_sz; 48 + 49 + struct mpam_garbage garbage; 67 50 }; 51 + 52 + struct mpam_class { 53 + /* mpam_components in this class */ 54 + struct list_head components; 55 + 56 + cpumask_t affinity; 57 + 58 + u8 level; 59 + enum mpam_class_types type; 60 + 61 + /* member of mpam_classes */ 62 + struct list_head classes_list; 63 + 64 + struct mpam_garbage garbage; 65 + }; 66 + 67 + struct mpam_component { 68 + u32 comp_id; 69 + 70 + /* mpam_vmsc in this component */ 71 + struct list_head vmsc; 72 + 73 + cpumask_t affinity; 74 + 75 + /* member of mpam_class:components */ 76 + struct list_head class_list; 77 + 78 + /* parent: */ 79 + struct mpam_class *class; 80 + 81 + struct mpam_garbage garbage; 82 + }; 83 + 84 + struct mpam_vmsc { 85 + /* member of mpam_component:vmsc_list */ 86 + struct list_head comp_list; 87 + 88 + /* mpam_msc_ris in this vmsc */ 89 + struct list_head ris; 90 + 91 + /* All RIS in this vMSC are members of this MSC */ 92 + struct mpam_msc *msc; 93 + 94 + /* parent: */ 95 + struct mpam_component *comp; 96 + 97 + struct mpam_garbage garbage; 98 + }; 99 + 100 + struct mpam_msc_ris { 101 + u8 ris_idx; 102 + 103 + cpumask_t affinity; 104 + 105 + /* member of mpam_vmsc:ris */ 106 + struct list_head vmsc_list; 107 + 108 + /* member of mpam_msc:ris */ 109 + struct list_head msc_list; 110 + 111 + /* parent: */ 112 + struct mpam_vmsc *vmsc; 113 + 114 + struct mpam_garbage garbage; 115 + }; 116 + 117 + /* List of all classes - protected by srcu*/ 118 + extern struct srcu_struct mpam_srcu; 119 + extern struct list_head mpam_classes; 120 + 121 + int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 122 + cpumask_t *affinity); 123 + 68 124 #endif /* MPAM_INTERNAL_H */
+5
include/linux/arm_mpam.h
··· 37 37 static inline int acpi_mpam_count_msc(void) { return -EINVAL; } 38 38 #endif 39 39 40 + #ifdef CONFIG_ARM64_MPAM_DRIVER 41 + int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 42 + enum mpam_class_types type, u8 class_id, int component_id); 43 + #else 40 44 static inline int mpam_ris_create(struct mpam_msc *msc, u8 ris_idx, 41 45 enum mpam_class_types type, u8 class_id, 42 46 int component_id) 43 47 { 44 48 return -EINVAL; 45 49 } 50 + #endif 46 51 47 52 #endif /* __LINUX_ARM_MPAM_H */