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 cpuhp callbacks to probe MSC hardware

Because an MSC can only by accessed from the CPUs in its cpu-affinity
set we need to be running on one of those CPUs to probe the MSC
hardware.

Do this work in the cpuhp callback. Probing the hardware will only
happen before MPAM is enabled, walk all the MSCs and probe those we can
reach that haven't already been probed as each CPU's online call is made.

This adds the low-level MSC register read accessors.

Once all MSCs reported by the firmware have been probed from a CPU in
their respective cpu-affinity set, the probe-time cpuhp callbacks are
replaced. The replacement callbacks will ultimately need to handle
save/restore of the runtime MSC state across power transitions, but for
now there is nothing to do in them: so do nothing.

The architecture's context switch code will be enabled by a static-key,
this can be set by mpam_enable(), but must be done from process context,
not a cpuhp callback because both take the cpuhp lock.
Whenever a new MSC has been probed, the mpam_enable() work is scheduled
to test if all the MSCs have been probed. If probing fails, mpam_disable()
is scheduled to unregister the cpuhp callbacks and free memory.

CC: Lecopzer Chen <lecopzerc@nvidia.com>
Signed-off-by: James Morse <james.morse@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Fenghua Yu <fenghuay@nvidia.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.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: Ben Horgan <ben.horgan@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

James Morse and committed by
Catalin Marinas
8f8d0ac1 aa64b9e1

+180 -1
+175 -1
drivers/resctrl/mpam_devices.c
··· 4 4 #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 5 5 6 6 #include <linux/acpi.h> 7 + #include <linux/atomic.h> 7 8 #include <linux/arm_mpam.h> 8 9 #include <linux/cacheinfo.h> 10 + #include <linux/cpu.h> 9 11 #include <linux/cpumask.h> 10 12 #include <linux/device.h> 11 13 #include <linux/errno.h> ··· 19 17 #include <linux/printk.h> 20 18 #include <linux/srcu.h> 21 19 #include <linux/types.h> 20 + #include <linux/workqueue.h> 22 21 23 22 #include "mpam_internal.h" 24 23 ··· 38 35 * can be enabled. 39 36 */ 40 37 static atomic_t mpam_num_msc; 38 + 39 + static int mpam_cpuhp_state; 40 + static DEFINE_MUTEX(mpam_cpuhp_state_lock); 41 + 42 + /* 43 + * mpam is enabled once all devices have been probed from CPU online callbacks, 44 + * scheduled via this work_struct. If access to an MSC depends on a CPU that 45 + * was not brought online at boot, this can happen surprisingly late. 46 + */ 47 + static DECLARE_WORK(mpam_enable_work, &mpam_enable); 48 + 49 + /* 50 + * All mpam error interrupts indicate a software bug. On receipt, disable the 51 + * driver. 52 + */ 53 + static DECLARE_WORK(mpam_broken_work, &mpam_disable); 54 + 55 + /* When mpam is disabled, the printed reason to aid debugging */ 56 + static char *mpam_disable_reason; 41 57 42 58 /* 43 59 * An MSC is a physical container for controls and monitors, each identified by ··· 127 105 kfree(iter->to_free); 128 106 } 129 107 } 108 + 109 + static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) 110 + { 111 + WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); 112 + 113 + return readl_relaxed(msc->mapped_hwpage + reg); 114 + } 115 + 116 + static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) 117 + { 118 + lockdep_assert_held_once(&msc->part_sel_lock); 119 + return __mpam_read_reg(msc, reg); 120 + } 121 + 122 + #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg) 130 123 131 124 static struct mpam_class * 132 125 mpam_class_alloc(u8 level_idx, enum mpam_class_types type) ··· 450 413 return err; 451 414 } 452 415 416 + static int mpam_msc_hw_probe(struct mpam_msc *msc) 417 + { 418 + u64 idr; 419 + struct device *dev = &msc->pdev->dev; 420 + 421 + lockdep_assert_held(&msc->probe_lock); 422 + 423 + idr = __mpam_read_reg(msc, MPAMF_AIDR); 424 + if ((idr & MPAMF_AIDR_ARCH_MAJOR_REV) != MPAM_ARCHITECTURE_V1) { 425 + dev_err_once(dev, "MSC does not match MPAM architecture v1.x\n"); 426 + return -EIO; 427 + } 428 + 429 + msc->probed = true; 430 + 431 + return 0; 432 + } 433 + 434 + static int mpam_cpu_online(unsigned int cpu) 435 + { 436 + return 0; 437 + } 438 + 439 + /* Before mpam is enabled, try to probe new MSC */ 440 + static int mpam_discovery_cpu_online(unsigned int cpu) 441 + { 442 + int err = 0; 443 + struct mpam_msc *msc; 444 + bool new_device_probed = false; 445 + 446 + guard(srcu)(&mpam_srcu); 447 + list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 448 + srcu_read_lock_held(&mpam_srcu)) { 449 + if (!cpumask_test_cpu(cpu, &msc->accessibility)) 450 + continue; 451 + 452 + mutex_lock(&msc->probe_lock); 453 + if (!msc->probed) 454 + err = mpam_msc_hw_probe(msc); 455 + mutex_unlock(&msc->probe_lock); 456 + 457 + if (err) 458 + break; 459 + new_device_probed = true; 460 + } 461 + 462 + if (new_device_probed && !err) 463 + schedule_work(&mpam_enable_work); 464 + if (err) { 465 + mpam_disable_reason = "error during probing"; 466 + schedule_work(&mpam_broken_work); 467 + } 468 + 469 + return err; 470 + } 471 + 472 + static int mpam_cpu_offline(unsigned int cpu) 473 + { 474 + return 0; 475 + } 476 + 477 + static void mpam_register_cpuhp_callbacks(int (*online)(unsigned int online), 478 + int (*offline)(unsigned int offline), 479 + char *name) 480 + { 481 + mutex_lock(&mpam_cpuhp_state_lock); 482 + if (mpam_cpuhp_state) { 483 + cpuhp_remove_state(mpam_cpuhp_state); 484 + mpam_cpuhp_state = 0; 485 + } 486 + 487 + mpam_cpuhp_state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, name, online, 488 + offline); 489 + if (mpam_cpuhp_state <= 0) { 490 + pr_err("Failed to register cpuhp callbacks"); 491 + mpam_cpuhp_state = 0; 492 + } 493 + mutex_unlock(&mpam_cpuhp_state_lock); 494 + } 495 + 453 496 /* 454 497 * An MSC can control traffic from a set of CPUs, but may only be accessible 455 498 * from a (hopefully wider) set of CPUs. The common reason for this is power ··· 666 549 } 667 550 668 551 if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 669 - pr_info("Discovered all MSCs\n"); 552 + mpam_register_cpuhp_callbacks(mpam_discovery_cpu_online, NULL, 553 + "mpam:drv_probe"); 670 554 671 555 return 0; 672 556 } ··· 679 561 .probe = mpam_msc_drv_probe, 680 562 .remove = mpam_msc_drv_remove, 681 563 }; 564 + 565 + static void mpam_enable_once(void) 566 + { 567 + mpam_register_cpuhp_callbacks(mpam_cpu_online, mpam_cpu_offline, 568 + "mpam:online"); 569 + 570 + pr_info("MPAM enabled\n"); 571 + } 572 + 573 + void mpam_disable(struct work_struct *ignored) 574 + { 575 + struct mpam_msc *msc, *tmp; 576 + 577 + mutex_lock(&mpam_cpuhp_state_lock); 578 + if (mpam_cpuhp_state) { 579 + cpuhp_remove_state(mpam_cpuhp_state); 580 + mpam_cpuhp_state = 0; 581 + } 582 + mutex_unlock(&mpam_cpuhp_state_lock); 583 + 584 + mutex_lock(&mpam_list_lock); 585 + list_for_each_entry_safe(msc, tmp, &mpam_all_msc, all_msc_list) 586 + mpam_msc_destroy(msc); 587 + mutex_unlock(&mpam_list_lock); 588 + mpam_free_garbage(); 589 + 590 + pr_err_once("MPAM disabled due to %s\n", mpam_disable_reason); 591 + } 592 + 593 + /* 594 + * Enable mpam once all devices have been probed. 595 + * Scheduled by mpam_discovery_cpu_online() once all devices have been created. 596 + * Also scheduled when new devices are probed when new CPUs come online. 597 + */ 598 + void mpam_enable(struct work_struct *work) 599 + { 600 + static atomic_t once; 601 + struct mpam_msc *msc; 602 + bool all_devices_probed = true; 603 + 604 + /* Have we probed all the hw devices? */ 605 + guard(srcu)(&mpam_srcu); 606 + list_for_each_entry_srcu(msc, &mpam_all_msc, all_msc_list, 607 + srcu_read_lock_held(&mpam_srcu)) { 608 + mutex_lock(&msc->probe_lock); 609 + if (!msc->probed) 610 + all_devices_probed = false; 611 + mutex_unlock(&msc->probe_lock); 612 + 613 + if (!all_devices_probed) 614 + break; 615 + } 616 + 617 + if (all_devices_probed && !atomic_fetch_inc(&once)) 618 + mpam_enable_once(); 619 + } 682 620 683 621 static int __init mpam_msc_driver_init(void) 684 622 {
+5
drivers/resctrl/mpam_internal.h
··· 48 48 * properties become read-only and the lists are protected by SRCU. 49 49 */ 50 50 struct mutex probe_lock; 51 + bool probed; 51 52 unsigned long ris_idxs; 52 53 u32 ris_max; 53 54 ··· 137 136 /* List of all classes - protected by srcu*/ 138 137 extern struct srcu_struct mpam_srcu; 139 138 extern struct list_head mpam_classes; 139 + 140 + /* Scheduled work callback to enable mpam once all MSC have been probed */ 141 + void mpam_enable(struct work_struct *work); 142 + void mpam_disable(struct work_struct *work); 140 143 141 144 int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, 142 145 cpumask_t *affinity);