// SPDX-License-Identifier: GPL-2.0// Copyright (C) 2025 Arm Ltd. #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ #include <linux/acpi.h>#include <linux/atomic.h>#include <linux/arm_mpam.h>#include <linux/bitfield.h>#include <linux/bitmap.h>#include <linux/cacheinfo.h>#include <linux/cpu.h>#include <linux/cpumask.h>#include <linux/device.h>#include <linux/errno.h>#include <linux/gfp.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/irqdesc.h>#include <linux/list.h>#include <linux/lockdep.h>#include <linux/mutex.h>#include <linux/platform_device.h>#include <linux/printk.h>#include <linux/srcu.h>#include <linux/spinlock.h>#include <linux/types.h>#include <linux/workqueue.h> #include "mpam_internal.h" /* Values for the T241 errata workaround */#define T241_CHIPS_MAX 4#define T241_CHIP_NSLICES 12#define T241_SPARE_REG0_OFF 0x1b0000#define T241_SPARE_REG1_OFF 0x1c0000#define T241_CHIP_ID(phys) FIELD_GET(GENMASK_ULL(44, 43), phys)#define T241_SHADOW_REG_OFF(sidx, pid) (0x360048 + (sidx) * 0x10000 + (pid) * 8)#define SMCCC_SOC_ID_T241 0x036b0241static void __iomem *t241_scratch_regs[T241_CHIPS_MAX]; /* * mpam_list_lock protects the SRCU lists when writing. Once the * mpam_enabled key is enabled these lists are read-only, * unless the error interrupt disables the driver. */static DEFINE_MUTEX(mpam_list_lock);static LIST_HEAD(mpam_all_msc); struct srcu_struct mpam_srcu; /* * Number of MSCs that have been probed. Once all MSCs have been probed MPAM * can be enabled. */static atomic_t mpam_num_msc; static int mpam_cpuhp_state;static DEFINE_MUTEX(mpam_cpuhp_state_lock); /* * The smallest common values for any CPU or MSC in the system. * Generating traffic outside this range will result in screaming interrupts. */u16 mpam_partid_max;u8 mpam_pmg_max;static bool partid_max_init, partid_max_published;static DEFINE_SPINLOCK(partid_max_lock); /* * mpam is enabled once all devices have been probed from CPU online callbacks, * scheduled via this work_struct. If access to an MSC depends on a CPU that * was not brought online at boot, this can happen surprisingly late. */static DECLARE_WORK(mpam_enable_work, &mpam_enable); /* * All mpam error interrupts indicate a software bug. On receipt, disable the * driver. */static DECLARE_WORK(mpam_broken_work, &mpam_disable); /* When mpam is disabled, the printed reason to aid debugging */static char *mpam_disable_reason; /* * Whether resctrl has been setup. Used by cpuhp in preference to * mpam_is_enabled(). The disable call after an error interrupt makes * mpam_is_enabled() false before the cpuhp callbacks are made. * Reads/writes should hold mpam_cpuhp_state_lock, (or be cpuhp callbacks). */static bool mpam_resctrl_enabled; /* * An MSC is a physical container for controls and monitors, each identified by * their RIS index. These share a base-address, interrupts and some MMIO * registers. A vMSC is a virtual container for RIS in an MSC that control or * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but * not all RIS in an MSC share a vMSC. * * Components are a group of vMSC that control or monitor the same thing but * are from different MSC, so have different base-address, interrupts etc. * Classes are the set components of the same type. * * The features of a vMSC is the union of the RIS it contains. * The features of a Class and Component are the common subset of the vMSC * they contain. * * e.g. The system cache may have bandwidth controls on multiple interfaces, * for regulating traffic from devices independently of traffic from CPUs. * If these are two RIS in one MSC, they will be treated as controlling * different things, and will not share a vMSC/component/class. * * e.g. The L2 may have one MSC and two RIS, one for cache-controls another * for bandwidth. These two RIS are members of the same vMSC. * * e.g. The set of RIS that make up the L2 are grouped as a component. These * are sometimes termed slices. They should be configured the same, as if there * were only one. * * e.g. The SoC probably has more than one L2, each attached to a distinct set * of CPUs. All the L2 components are grouped as a class. * * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, * then linked via struct mpam_ris to a vmsc, component and class. * The same MSC may exist under different class->component->vmsc paths, but the * RIS index will be unique. */LIST_HEAD(mpam_classes); /* List of all objects that can be free()d after synchronise_srcu() */static LLIST_HEAD(mpam_garbage); static inline void init_garbage(struct mpam_garbage *garbage){ init_llist_node(&garbage->llist);} #define add_to_garbage(x) \do { \ __typeof__(x) _x = (x); \ _x->garbage.to_free = _x; \ llist_add(&_x->garbage.llist, &mpam_garbage); \} while (0) static void mpam_free_garbage(void){ struct mpam_garbage *iter, *tmp; struct llist_node *to_free = llist_del_all(&mpam_garbage); if (!to_free) return; synchronize_srcu(&mpam_srcu); llist_for_each_entry_safe(iter, tmp, to_free, llist) { if (iter->pdev) devm_kfree(&iter->pdev->dev, iter->to_free); else kfree(iter->to_free); }} /* * Once mpam is enabled, new requestors cannot further reduce the available * partid. Assert that the size is fixed, and new requestors will be turned * away. This is needed when walking over structures sized by PARTID. * * During mpam_disable() these structures are not fixed, but the MSC state * is still reset using whatever sizes have been discovered so far. As only * PARTID 0 will be used after mpam_disable(), any race would be benign. * Skip the check if a mpam_disable_reason has been set. */static void mpam_assert_partid_sizes_fixed(void){ if (!mpam_disable_reason) WARN_ON_ONCE(!partid_max_published);} static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg){ WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); return readl_relaxed(msc->mapped_hwpage + reg);} static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg){ lockdep_assert_held_once(&msc->part_sel_lock); return __mpam_read_reg(msc, reg);} #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg) static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val){ WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); writel_relaxed(val, msc->mapped_hwpage + reg);} static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val){ lockdep_assert_held_once(&msc->part_sel_lock); __mpam_write_reg(msc, reg, val);} #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val) static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg){ mpam_mon_sel_lock_held(msc); return __mpam_read_reg(msc, reg);} #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg) static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val){ mpam_mon_sel_lock_held(msc); __mpam_write_reg(msc, reg, val);} #define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc, MSMON_##reg, val) static bool mpam_msc_check_aidr(struct mpam_msc *msc){ u32 aidr = __mpam_read_reg(msc, MPAMF_AIDR); u32 major = FIELD_GET(MPAMF_AIDR_ARCH_MAJOR_REV, aidr); u32 minor = FIELD_GET(MPAMF_AIDR_ARCH_MINOR_REV, aidr); /* * v0.0 and >v2.x aren't supported, but anything else should be backward * compatible to v0.1 or v1.0. */ if (!major && !minor) return false; if (major > 1) return false; return true;} static u64 mpam_msc_read_idr(struct mpam_msc *msc){ u64 idr_high = 0, idr_low; lockdep_assert_held(&msc->part_sel_lock); idr_low = mpam_read_partsel_reg(msc, IDR); if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) idr_high = mpam_read_partsel_reg(msc, IDR + 4); return (idr_high << 32) | idr_low;} static void mpam_msc_clear_esr(struct mpam_msc *msc){ u64 esr_low = __mpam_read_reg(msc, MPAMF_ESR); if (!esr_low) return; /* * Clearing the high/low bits of MPAMF_ESR can not be atomic. * Clear the top half first, so that the pending error bits in the * lower half prevent hardware from updating either half of the * register. */ if (msc->has_extd_esr) __mpam_write_reg(msc, MPAMF_ESR + 4, 0); __mpam_write_reg(msc, MPAMF_ESR, 0);} static u64 mpam_msc_read_esr(struct mpam_msc *msc){ u64 esr_high = 0, esr_low; esr_low = __mpam_read_reg(msc, MPAMF_ESR); if (msc->has_extd_esr) esr_high = __mpam_read_reg(msc, MPAMF_ESR + 4); return (esr_high << 32) | esr_low;} static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc){ lockdep_assert_held(&msc->part_sel_lock); mpam_write_partsel_reg(msc, PART_SEL, partsel);} static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc){ u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); __mpam_part_sel_raw(partsel, msc);} static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc *msc){ u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, intpartid) | MPAMCFG_PART_SEL_INTERNAL; __mpam_part_sel_raw(partsel, msc);} int mpam_register_requestor(u16 partid_max, u8 pmg_max){ guard(spinlock)(&partid_max_lock); if (!partid_max_init) { mpam_partid_max = partid_max; mpam_pmg_max = pmg_max; partid_max_init = true; } else if (!partid_max_published) { mpam_partid_max = min(mpam_partid_max, partid_max); mpam_pmg_max = min(mpam_pmg_max, pmg_max); } else { /* New requestors can't lower the values */ if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) return -EBUSY; } return 0;}EXPORT_SYMBOL(mpam_register_requestor); static struct mpam_class *mpam_class_alloc(u8 level_idx, enum mpam_class_types type){ struct mpam_class *class; lockdep_assert_held(&mpam_list_lock); class = kzalloc_obj(*class); if (!class) return ERR_PTR(-ENOMEM); init_garbage(&class->garbage); INIT_LIST_HEAD_RCU(&class->components); /* Affinity is updated when ris are added */ class->level = level_idx; class->type = type; INIT_LIST_HEAD_RCU(&class->classes_list); ida_init(&class->ida_csu_mon); ida_init(&class->ida_mbwu_mon); list_add_rcu(&class->classes_list, &mpam_classes); return class;} static void mpam_class_destroy(struct mpam_class *class){ lockdep_assert_held(&mpam_list_lock); list_del_rcu(&class->classes_list); add_to_garbage(class);} static struct mpam_class *mpam_class_find(u8 level_idx, enum mpam_class_types type){ struct mpam_class *class; lockdep_assert_held(&mpam_list_lock); list_for_each_entry(class, &mpam_classes, classes_list) { if (class->type == type && class->level == level_idx) return class; } return mpam_class_alloc(level_idx, type);} static struct mpam_component *mpam_component_alloc(struct mpam_class *class, int id){ struct mpam_component *comp; lockdep_assert_held(&mpam_list_lock); comp = kzalloc_obj(*comp); if (!comp) return ERR_PTR(-ENOMEM); init_garbage(&comp->garbage); comp->comp_id = id; INIT_LIST_HEAD_RCU(&comp->vmsc); /* Affinity is updated when RIS are added */ INIT_LIST_HEAD_RCU(&comp->class_list); comp->class = class; list_add_rcu(&comp->class_list, &class->components); return comp;} static void __destroy_component_cfg(struct mpam_component *comp); static void mpam_component_destroy(struct mpam_component *comp){ struct mpam_class *class = comp->class; lockdep_assert_held(&mpam_list_lock); __destroy_component_cfg(comp); list_del_rcu(&comp->class_list); add_to_garbage(comp); if (list_empty(&class->components)) mpam_class_destroy(class);} static struct mpam_component *mpam_component_find(struct mpam_class *class, int id){ struct mpam_component *comp; lockdep_assert_held(&mpam_list_lock); list_for_each_entry(comp, &class->components, class_list) { if (comp->comp_id == id) return comp; } return mpam_component_alloc(class, id);} static struct mpam_vmsc *mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc){ struct mpam_vmsc *vmsc; lockdep_assert_held(&mpam_list_lock); vmsc = kzalloc_obj(*vmsc); if (!vmsc) return ERR_PTR(-ENOMEM); init_garbage(&vmsc->garbage); INIT_LIST_HEAD_RCU(&vmsc->ris); INIT_LIST_HEAD_RCU(&vmsc->comp_list); vmsc->comp = comp; vmsc->msc = msc; list_add_rcu(&vmsc->comp_list, &comp->vmsc); return vmsc;} static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc){ struct mpam_component *comp = vmsc->comp; lockdep_assert_held(&mpam_list_lock); list_del_rcu(&vmsc->comp_list); add_to_garbage(vmsc); if (list_empty(&comp->vmsc)) mpam_component_destroy(comp);} static struct mpam_vmsc *mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc){ struct mpam_vmsc *vmsc; lockdep_assert_held(&mpam_list_lock); list_for_each_entry(vmsc, &comp->vmsc, comp_list) { if (vmsc->msc->id == msc->id) return vmsc; } return mpam_vmsc_alloc(comp, msc);} /* * The cacheinfo structures are only populated when CPUs are online. * This helper walks the acpi tables to include offline CPUs too. */int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity){ return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity);} /* * cpumask_of_node() only knows about online CPUs. This can't tell us whether * a class is represented on all possible CPUs. */static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity){ int cpu;Lines 1–500 of 2980. Load more
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Arm Ltd. #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ #include <linux/acpi.h> #include <linux/atomic.h> #include <linux/arm_mpam.h> #include <linux/bitfield.h> #include <linux/bitmap.h> #include <linux/cacheinfo.h> #include <linux/cpu.h> #include <linux/cpumask.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/gfp.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdesc.h> #include <linux/list.h> #include <linux/lockdep.h> #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/printk.h> #include <linux/srcu.h> #include <linux/spinlock.h> #include <linux/types.h> #include <linux/workqueue.h> #include "mpam_internal.h" /* Values for the T241 errata workaround */ #define T241_CHIPS_MAX 4 #define T241_CHIP_NSLICES 12 #define T241_SPARE_REG0_OFF 0x1b0000 #define T241_SPARE_REG1_OFF 0x1c0000 #define T241_CHIP_ID(phys) FIELD_GET(GENMASK_ULL(44, 43), phys) #define T241_SHADOW_REG_OFF(sidx, pid) (0x360048 + (sidx) * 0x10000 + (pid) * 8) #define SMCCC_SOC_ID_T241 0x036b0241 static void __iomem *t241_scratch_regs[T241_CHIPS_MAX]; /* * mpam_list_lock protects the SRCU lists when writing. Once the * mpam_enabled key is enabled these lists are read-only, * unless the error interrupt disables the driver. */ static DEFINE_MUTEX(mpam_list_lock); static LIST_HEAD(mpam_all_msc); struct srcu_struct mpam_srcu; /* * Number of MSCs that have been probed. Once all MSCs have been probed MPAM * can be enabled. */ static atomic_t mpam_num_msc; static int mpam_cpuhp_state; static DEFINE_MUTEX(mpam_cpuhp_state_lock); /* * The smallest common values for any CPU or MSC in the system. * Generating traffic outside this range will result in screaming interrupts. */ u16 mpam_partid_max; u8 mpam_pmg_max; static bool partid_max_init, partid_max_published; static DEFINE_SPINLOCK(partid_max_lock); /* * mpam is enabled once all devices have been probed from CPU online callbacks, * scheduled via this work_struct. If access to an MSC depends on a CPU that * was not brought online at boot, this can happen surprisingly late. */ static DECLARE_WORK(mpam_enable_work, &mpam_enable); /* * All mpam error interrupts indicate a software bug. On receipt, disable the * driver. */ static DECLARE_WORK(mpam_broken_work, &mpam_disable); /* When mpam is disabled, the printed reason to aid debugging */ static char *mpam_disable_reason; /* * Whether resctrl has been setup. Used by cpuhp in preference to * mpam_is_enabled(). The disable call after an error interrupt makes * mpam_is_enabled() false before the cpuhp callbacks are made. * Reads/writes should hold mpam_cpuhp_state_lock, (or be cpuhp callbacks). */ static bool mpam_resctrl_enabled; /* * An MSC is a physical container for controls and monitors, each identified by * their RIS index. These share a base-address, interrupts and some MMIO * registers. A vMSC is a virtual container for RIS in an MSC that control or * monitor the same thing. Members of a vMSC are all RIS in the same MSC, but * not all RIS in an MSC share a vMSC. * * Components are a group of vMSC that control or monitor the same thing but * are from different MSC, so have different base-address, interrupts etc. * Classes are the set components of the same type. * * The features of a vMSC is the union of the RIS it contains. * The features of a Class and Component are the common subset of the vMSC * they contain. * * e.g. The system cache may have bandwidth controls on multiple interfaces, * for regulating traffic from devices independently of traffic from CPUs. * If these are two RIS in one MSC, they will be treated as controlling * different things, and will not share a vMSC/component/class. * * e.g. The L2 may have one MSC and two RIS, one for cache-controls another * for bandwidth. These two RIS are members of the same vMSC. * * e.g. The set of RIS that make up the L2 are grouped as a component. These * are sometimes termed slices. They should be configured the same, as if there * were only one. * * e.g. The SoC probably has more than one L2, each attached to a distinct set * of CPUs. All the L2 components are grouped as a class. * * When creating an MSC, struct mpam_msc is added to the all mpam_all_msc list, * then linked via struct mpam_ris to a vmsc, component and class. * The same MSC may exist under different class->component->vmsc paths, but the * RIS index will be unique. */ LIST_HEAD(mpam_classes); /* List of all objects that can be free()d after synchronise_srcu() */ static LLIST_HEAD(mpam_garbage); static inline void init_garbage(struct mpam_garbage *garbage) { init_llist_node(&garbage->llist); } #define add_to_garbage(x) \ do { \ __typeof__(x) _x = (x); \ _x->garbage.to_free = _x; \ llist_add(&_x->garbage.llist, &mpam_garbage); \ } while (0) static void mpam_free_garbage(void) { struct mpam_garbage *iter, *tmp; struct llist_node *to_free = llist_del_all(&mpam_garbage); if (!to_free) return; synchronize_srcu(&mpam_srcu); llist_for_each_entry_safe(iter, tmp, to_free, llist) { if (iter->pdev) devm_kfree(&iter->pdev->dev, iter->to_free); else kfree(iter->to_free); } } /* * Once mpam is enabled, new requestors cannot further reduce the available * partid. Assert that the size is fixed, and new requestors will be turned * away. This is needed when walking over structures sized by PARTID. * * During mpam_disable() these structures are not fixed, but the MSC state * is still reset using whatever sizes have been discovered so far. As only * PARTID 0 will be used after mpam_disable(), any race would be benign. * Skip the check if a mpam_disable_reason has been set. */ static void mpam_assert_partid_sizes_fixed(void) { if (!mpam_disable_reason) WARN_ON_ONCE(!partid_max_published); } static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg) { WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); return readl_relaxed(msc->mapped_hwpage + reg); } static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg) { lockdep_assert_held_once(&msc->part_sel_lock); return __mpam_read_reg(msc, reg); } #define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg) static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val) { WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz); WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility)); writel_relaxed(val, msc->mapped_hwpage + reg); } static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val) { lockdep_assert_held_once(&msc->part_sel_lock); __mpam_write_reg(msc, reg, val); } #define mpam_write_partsel_reg(msc, reg, val) _mpam_write_partsel_reg(msc, MPAMCFG_##reg, val) static inline u32 _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg) { mpam_mon_sel_lock_held(msc); return __mpam_read_reg(msc, reg); } #define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg) static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val) { mpam_mon_sel_lock_held(msc); __mpam_write_reg(msc, reg, val); } #define mpam_write_monsel_reg(msc, reg, val) _mpam_write_monsel_reg(msc, MSMON_##reg, val) static bool mpam_msc_check_aidr(struct mpam_msc *msc) { u32 aidr = __mpam_read_reg(msc, MPAMF_AIDR); u32 major = FIELD_GET(MPAMF_AIDR_ARCH_MAJOR_REV, aidr); u32 minor = FIELD_GET(MPAMF_AIDR_ARCH_MINOR_REV, aidr); /* * v0.0 and >v2.x aren't supported, but anything else should be backward * compatible to v0.1 or v1.0. */ if (!major && !minor) return false; if (major > 1) return false; return true; } static u64 mpam_msc_read_idr(struct mpam_msc *msc) { u64 idr_high = 0, idr_low; lockdep_assert_held(&msc->part_sel_lock); idr_low = mpam_read_partsel_reg(msc, IDR); if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) idr_high = mpam_read_partsel_reg(msc, IDR + 4); return (idr_high << 32) | idr_low; } static void mpam_msc_clear_esr(struct mpam_msc *msc) { u64 esr_low = __mpam_read_reg(msc, MPAMF_ESR); if (!esr_low) return; /* * Clearing the high/low bits of MPAMF_ESR can not be atomic. * Clear the top half first, so that the pending error bits in the * lower half prevent hardware from updating either half of the * register. */ if (msc->has_extd_esr) __mpam_write_reg(msc, MPAMF_ESR + 4, 0); __mpam_write_reg(msc, MPAMF_ESR, 0); } static u64 mpam_msc_read_esr(struct mpam_msc *msc) { u64 esr_high = 0, esr_low; esr_low = __mpam_read_reg(msc, MPAMF_ESR); if (msc->has_extd_esr) esr_high = __mpam_read_reg(msc, MPAMF_ESR + 4); return (esr_high << 32) | esr_low; } static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc) { lockdep_assert_held(&msc->part_sel_lock); mpam_write_partsel_reg(msc, PART_SEL, partsel); } static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc) { u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, partid); __mpam_part_sel_raw(partsel, msc); } static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc *msc) { u32 partsel = FIELD_PREP(MPAMCFG_PART_SEL_RIS, ris_idx) | FIELD_PREP(MPAMCFG_PART_SEL_PARTID_SEL, intpartid) | MPAMCFG_PART_SEL_INTERNAL; __mpam_part_sel_raw(partsel, msc); } int mpam_register_requestor(u16 partid_max, u8 pmg_max) { guard(spinlock)(&partid_max_lock); if (!partid_max_init) { mpam_partid_max = partid_max; mpam_pmg_max = pmg_max; partid_max_init = true; } else if (!partid_max_published) { mpam_partid_max = min(mpam_partid_max, partid_max); mpam_pmg_max = min(mpam_pmg_max, pmg_max); } else { /* New requestors can't lower the values */ if (partid_max < mpam_partid_max || pmg_max < mpam_pmg_max) return -EBUSY; } return 0; } EXPORT_SYMBOL(mpam_register_requestor); static struct mpam_class * mpam_class_alloc(u8 level_idx, enum mpam_class_types type) { struct mpam_class *class; lockdep_assert_held(&mpam_list_lock); class = kzalloc_obj(*class); if (!class) return ERR_PTR(-ENOMEM); init_garbage(&class->garbage); INIT_LIST_HEAD_RCU(&class->components); /* Affinity is updated when ris are added */ class->level = level_idx; class->type = type; INIT_LIST_HEAD_RCU(&class->classes_list); ida_init(&class->ida_csu_mon); ida_init(&class->ida_mbwu_mon); list_add_rcu(&class->classes_list, &mpam_classes); return class; } static void mpam_class_destroy(struct mpam_class *class) { lockdep_assert_held(&mpam_list_lock); list_del_rcu(&class->classes_list); add_to_garbage(class); } static struct mpam_class * mpam_class_find(u8 level_idx, enum mpam_class_types type) { struct mpam_class *class; lockdep_assert_held(&mpam_list_lock); list_for_each_entry(class, &mpam_classes, classes_list) { if (class->type == type && class->level == level_idx) return class; } return mpam_class_alloc(level_idx, type); } static struct mpam_component * mpam_component_alloc(struct mpam_class *class, int id) { struct mpam_component *comp; lockdep_assert_held(&mpam_list_lock); comp = kzalloc_obj(*comp); if (!comp) return ERR_PTR(-ENOMEM); init_garbage(&comp->garbage); comp->comp_id = id; INIT_LIST_HEAD_RCU(&comp->vmsc); /* Affinity is updated when RIS are added */ INIT_LIST_HEAD_RCU(&comp->class_list); comp->class = class; list_add_rcu(&comp->class_list, &class->components); return comp; } static void __destroy_component_cfg(struct mpam_component *comp); static void mpam_component_destroy(struct mpam_component *comp) { struct mpam_class *class = comp->class; lockdep_assert_held(&mpam_list_lock); __destroy_component_cfg(comp); list_del_rcu(&comp->class_list); add_to_garbage(comp); if (list_empty(&class->components)) mpam_class_destroy(class); } static struct mpam_component * mpam_component_find(struct mpam_class *class, int id) { struct mpam_component *comp; lockdep_assert_held(&mpam_list_lock); list_for_each_entry(comp, &class->components, class_list) { if (comp->comp_id == id) return comp; } return mpam_component_alloc(class, id); } static struct mpam_vmsc * mpam_vmsc_alloc(struct mpam_component *comp, struct mpam_msc *msc) { struct mpam_vmsc *vmsc; lockdep_assert_held(&mpam_list_lock); vmsc = kzalloc_obj(*vmsc); if (!vmsc) return ERR_PTR(-ENOMEM); init_garbage(&vmsc->garbage); INIT_LIST_HEAD_RCU(&vmsc->ris); INIT_LIST_HEAD_RCU(&vmsc->comp_list); vmsc->comp = comp; vmsc->msc = msc; list_add_rcu(&vmsc->comp_list, &comp->vmsc); return vmsc; } static void mpam_vmsc_destroy(struct mpam_vmsc *vmsc) { struct mpam_component *comp = vmsc->comp; lockdep_assert_held(&mpam_list_lock); list_del_rcu(&vmsc->comp_list); add_to_garbage(vmsc); if (list_empty(&comp->vmsc)) mpam_component_destroy(comp); } static struct mpam_vmsc * mpam_vmsc_find(struct mpam_component *comp, struct mpam_msc *msc) { struct mpam_vmsc *vmsc; lockdep_assert_held(&mpam_list_lock); list_for_each_entry(vmsc, &comp->vmsc, comp_list) { if (vmsc->msc->id == msc->id) return vmsc; } return mpam_vmsc_alloc(comp, msc); } /* * The cacheinfo structures are only populated when CPUs are online. * This helper walks the acpi tables to include offline CPUs too. */ int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level, cpumask_t *affinity) { return acpi_pptt_get_cpumask_from_cache_id(cache_id, affinity); } /* * cpumask_of_node() only knows about online CPUs. This can't tell us whether * a class is represented on all possible CPUs. */ static void get_cpumask_from_node_id(u32 node_id, cpumask_t *affinity) { int cpu;