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

arm_mpam: Add probe/remove for mpam msc driver and kbuild boiler plate

Probing MPAM is convoluted. MSCs that are integrated with a CPU may
only be accessible from those CPUs, and they may not be online.
Touching the hardware early is pointless as MPAM can't be used until
the system-wide common values for num_partid and num_pmg have been
discovered.

Start with driver probe/remove and mapping the MSC.

Cc: Carl Worth <carl@os.amperecomputing.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
Reviewed-by: Fenghua Yu <fenghuay@nvidia.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: 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
f04046f2 115c5325

+262
+1
arch/arm64/Kconfig
··· 2025 2025 2026 2026 config ARM64_MPAM 2027 2027 bool "Enable support for MPAM" 2028 + select ARM64_MPAM_DRIVER if EXPERT # does nothing yet 2028 2029 select ACPI_MPAM if ACPI 2029 2030 help 2030 2031 Memory System Resource Partitioning and Monitoring (MPAM) is an
+2
drivers/Kconfig
··· 251 251 252 252 source "drivers/cdx/Kconfig" 253 253 254 + source "drivers/resctrl/Kconfig" 255 + 254 256 endmenu
+1
drivers/Makefile
··· 194 194 obj-$(CONFIG_DRM_ACCEL) += accel/ 195 195 obj-$(CONFIG_CDX_BUS) += cdx/ 196 196 obj-$(CONFIG_DPLL) += dpll/ 197 + obj-y += resctrl/ 197 198 198 199 obj-$(CONFIG_DIBS) += dibs/ 199 200 obj-$(CONFIG_S390) += s390/
+15
drivers/resctrl/Kconfig
··· 1 + menuconfig ARM64_MPAM_DRIVER 2 + bool "MPAM driver" 3 + depends on ARM64 && ARM64_MPAM && EXPERT 4 + help 5 + Memory System Resource Partitioning and Monitoring (MPAM) driver for 6 + System IP, e.g. caches and memory controllers. 7 + 8 + if ARM64_MPAM_DRIVER 9 + 10 + config ARM64_MPAM_DRIVER_DEBUG 11 + bool "Enable debug messages from the MPAM driver" 12 + help 13 + Say yes here to enable debug messages from the MPAM driver. 14 + 15 + endif
+4
drivers/resctrl/Makefile
··· 1 + obj-$(CONFIG_ARM64_MPAM_DRIVER) += mpam.o 2 + mpam-y += mpam_devices.o 3 + 4 + ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) += -DDEBUG
+190
drivers/resctrl/mpam_devices.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/acpi.h> 7 + #include <linux/arm_mpam.h> 8 + #include <linux/cacheinfo.h> 9 + #include <linux/cpumask.h> 10 + #include <linux/device.h> 11 + #include <linux/errno.h> 12 + #include <linux/gfp.h> 13 + #include <linux/list.h> 14 + #include <linux/lockdep.h> 15 + #include <linux/mutex.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/printk.h> 18 + #include <linux/srcu.h> 19 + #include <linux/types.h> 20 + 21 + #include "mpam_internal.h" 22 + 23 + /* 24 + * mpam_list_lock protects the SRCU lists when writing. Once the 25 + * mpam_enabled key is enabled these lists are read-only, 26 + * unless the error interrupt disables the driver. 27 + */ 28 + static DEFINE_MUTEX(mpam_list_lock); 29 + static LIST_HEAD(mpam_all_msc); 30 + 31 + struct srcu_struct mpam_srcu; 32 + 33 + /* 34 + * Number of MSCs that have been probed. Once all MSCs have been probed MPAM 35 + * can be enabled. 36 + */ 37 + static atomic_t mpam_num_msc; 38 + 39 + /* 40 + * An MSC can control traffic from a set of CPUs, but may only be accessible 41 + * from a (hopefully wider) set of CPUs. The common reason for this is power 42 + * management. If all the CPUs in a cluster are in PSCI:CPU_SUSPEND, the 43 + * corresponding cache may also be powered off. By making accesses from 44 + * one of those CPUs, we ensure we don't access a cache that's powered off. 45 + */ 46 + static void update_msc_accessibility(struct mpam_msc *msc) 47 + { 48 + u32 affinity_id; 49 + int err; 50 + 51 + err = device_property_read_u32(&msc->pdev->dev, "cpu_affinity", 52 + &affinity_id); 53 + if (err) 54 + cpumask_copy(&msc->accessibility, cpu_possible_mask); 55 + else 56 + acpi_pptt_get_cpus_from_container(affinity_id, &msc->accessibility); 57 + } 58 + 59 + static void mpam_msc_destroy(struct mpam_msc *msc) 60 + { 61 + struct platform_device *pdev = msc->pdev; 62 + 63 + lockdep_assert_held(&mpam_list_lock); 64 + 65 + list_del_rcu(&msc->all_msc_list); 66 + platform_set_drvdata(pdev, NULL); 67 + } 68 + 69 + static void mpam_msc_drv_remove(struct platform_device *pdev) 70 + { 71 + struct mpam_msc *msc = platform_get_drvdata(pdev); 72 + 73 + mutex_lock(&mpam_list_lock); 74 + mpam_msc_destroy(msc); 75 + mutex_unlock(&mpam_list_lock); 76 + 77 + synchronize_srcu(&mpam_srcu); 78 + } 79 + 80 + static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev) 81 + { 82 + int err; 83 + u32 tmp; 84 + struct mpam_msc *msc; 85 + struct resource *msc_res; 86 + struct device *dev = &pdev->dev; 87 + 88 + lockdep_assert_held(&mpam_list_lock); 89 + 90 + msc = devm_kzalloc(&pdev->dev, sizeof(*msc), GFP_KERNEL); 91 + if (!msc) 92 + return ERR_PTR(-ENOMEM); 93 + 94 + err = devm_mutex_init(dev, &msc->probe_lock); 95 + if (err) 96 + return ERR_PTR(err); 97 + 98 + err = devm_mutex_init(dev, &msc->part_sel_lock); 99 + if (err) 100 + return ERR_PTR(err); 101 + 102 + msc->id = pdev->id; 103 + msc->pdev = pdev; 104 + INIT_LIST_HEAD_RCU(&msc->all_msc_list); 105 + INIT_LIST_HEAD_RCU(&msc->ris); 106 + 107 + update_msc_accessibility(msc); 108 + if (cpumask_empty(&msc->accessibility)) { 109 + dev_err_once(dev, "MSC is not accessible from any CPU!"); 110 + return ERR_PTR(-EINVAL); 111 + } 112 + 113 + if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp)) 114 + msc->iface = MPAM_IFACE_MMIO; 115 + else 116 + msc->iface = MPAM_IFACE_PCC; 117 + 118 + if (msc->iface == MPAM_IFACE_MMIO) { 119 + void __iomem *io; 120 + 121 + io = devm_platform_get_and_ioremap_resource(pdev, 0, 122 + &msc_res); 123 + if (IS_ERR(io)) { 124 + dev_err_once(dev, "Failed to map MSC base address\n"); 125 + return ERR_CAST(io); 126 + } 127 + msc->mapped_hwpage_sz = msc_res->end - msc_res->start; 128 + msc->mapped_hwpage = io; 129 + } else { 130 + return ERR_PTR(-EINVAL); 131 + } 132 + 133 + list_add_rcu(&msc->all_msc_list, &mpam_all_msc); 134 + platform_set_drvdata(pdev, msc); 135 + 136 + return msc; 137 + } 138 + 139 + static int fw_num_msc; 140 + 141 + static int mpam_msc_drv_probe(struct platform_device *pdev) 142 + { 143 + int err; 144 + struct mpam_msc *msc = NULL; 145 + void *plat_data = pdev->dev.platform_data; 146 + 147 + mutex_lock(&mpam_list_lock); 148 + msc = do_mpam_msc_drv_probe(pdev); 149 + mutex_unlock(&mpam_list_lock); 150 + 151 + if (IS_ERR(msc)) 152 + return PTR_ERR(msc); 153 + 154 + /* Create RIS entries described by firmware */ 155 + err = acpi_mpam_parse_resources(msc, plat_data); 156 + if (err) { 157 + mpam_msc_drv_remove(pdev); 158 + return err; 159 + } 160 + 161 + if (atomic_add_return(1, &mpam_num_msc) == fw_num_msc) 162 + pr_info("Discovered all MSCs\n"); 163 + 164 + return 0; 165 + } 166 + 167 + static struct platform_driver mpam_msc_driver = { 168 + .driver = { 169 + .name = "mpam_msc", 170 + }, 171 + .probe = mpam_msc_drv_probe, 172 + .remove = mpam_msc_drv_remove, 173 + }; 174 + 175 + static int __init mpam_msc_driver_init(void) 176 + { 177 + if (!system_supports_mpam()) 178 + return -EOPNOTSUPP; 179 + 180 + init_srcu_struct(&mpam_srcu); 181 + 182 + fw_num_msc = acpi_mpam_count_msc(); 183 + if (fw_num_msc <= 0) { 184 + pr_err("No MSC devices found in firmware\n"); 185 + return -EINVAL; 186 + } 187 + 188 + return platform_driver_register(&mpam_msc_driver); 189 + } 190 + subsys_initcall(mpam_msc_driver_init);
+49
drivers/resctrl/mpam_internal.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + // Copyright (C) 2025 Arm Ltd. 3 + 4 + #ifndef MPAM_INTERNAL_H 5 + #define MPAM_INTERNAL_H 6 + 7 + #include <linux/arm_mpam.h> 8 + #include <linux/cpumask.h> 9 + #include <linux/io.h> 10 + #include <linux/mutex.h> 11 + #include <linux/types.h> 12 + 13 + struct platform_device; 14 + 15 + struct mpam_msc { 16 + /* member of mpam_all_msc */ 17 + struct list_head all_msc_list; 18 + 19 + int id; 20 + struct platform_device *pdev; 21 + 22 + /* Not modified after mpam_is_enabled() becomes true */ 23 + enum mpam_msc_iface iface; 24 + u32 nrdy_usec; 25 + cpumask_t accessibility; 26 + 27 + /* 28 + * probe_lock is only taken during discovery. After discovery these 29 + * properties become read-only and the lists are protected by SRCU. 30 + */ 31 + struct mutex probe_lock; 32 + unsigned long ris_idxs; 33 + u32 ris_max; 34 + 35 + /* mpam_msc_ris of this component */ 36 + struct list_head ris; 37 + 38 + /* 39 + * part_sel_lock protects access to the MSC hardware registers that are 40 + * affected by MPAMCFG_PART_SEL. (including the ID registers that vary 41 + * by RIS). 42 + * If needed, take msc->probe_lock first. 43 + */ 44 + struct mutex part_sel_lock; 45 + 46 + void __iomem *mapped_hwpage; 47 + size_t mapped_hwpage_sz; 48 + }; 49 + #endif /* MPAM_INTERNAL_H */