Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
menuconfig ARM64_MPAM_DRIVER
bool "MPAM driver"
depends on ARM64 && ARM64_MPAM
select ACPI_MPAM if ACPI
help
Memory System Resource Partitioning and Monitoring (MPAM) driver for
System IP, e.g. caches and memory controllers.
if ARM64_MPAM_DRIVER
config ARM64_MPAM_DRIVER_DEBUG
bool "Enable debug messages from the MPAM driver"
help
Say yes here to enable debug messages from the MPAM driver.
config MPAM_KUNIT_TEST
bool "KUnit tests for MPAM driver " if !KUNIT_ALL_TESTS
depends on KUNIT=y
default KUNIT_ALL_TESTS
help
Enable this option to run tests in the MPAM driver.
If unsure, say N.
endif
config ARM64_MPAM_RESCTRL_FS
bool
default y if ARM64_MPAM_DRIVER && RESCTRL_FS
select RESCTRL_RMID_DEPENDS_ON_CLOSID
select RESCTRL_ASSIGN_FIXED
+5
View File
@@ -0,0 +1,5 @@
obj-$(CONFIG_ARM64_MPAM_DRIVER) += mpam.o
mpam-y += mpam_devices.o
mpam-$(CONFIG_ARM64_MPAM_RESCTRL_FS) += mpam_resctrl.o
ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) += -DDEBUG
File diff suppressed because it is too large Load Diff
+753
View File
@@ -0,0 +1,753 @@
/* SPDX-License-Identifier: GPL-2.0 */
// Copyright (C) 2025 Arm Ltd.
#ifndef MPAM_INTERNAL_H
#define MPAM_INTERNAL_H
#include <linux/arm_mpam.h>
#include <linux/atomic.h>
#include <linux/bitmap.h>
#include <linux/cpumask.h>
#include <linux/io.h>
#include <linux/jump_label.h>
#include <linux/llist.h>
#include <linux/mutex.h>
#include <linux/resctrl.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
#include <linux/types.h>
#include <asm/mpam.h>
#define MPAM_MSC_MAX_NUM_RIS 16
struct platform_device;
#ifdef CONFIG_MPAM_KUNIT_TEST
#define PACKED_FOR_KUNIT __packed
#else
#define PACKED_FOR_KUNIT
#endif
/*
* This 'mon' values must not alias an actual monitor, so must be larger than
* U16_MAX, but not be confused with an errno value, so smaller than
* (u32)-SZ_4K.
* USE_PRE_ALLOCATED is used to avoid confusion with an actual monitor.
*/
#define USE_PRE_ALLOCATED (U16_MAX + 1)
static inline bool mpam_is_enabled(void)
{
return static_branch_likely(&mpam_enabled);
}
/*
* Structures protected by SRCU may not be freed for a surprising amount of
* time (especially if perf is running). To ensure the MPAM error interrupt can
* tear down all the structures, build a list of objects that can be garbage
* collected once synchronize_srcu() has returned.
* If pdev is non-NULL, use devm_kfree().
*/
struct mpam_garbage {
/* member of mpam_garbage */
struct llist_node llist;
void *to_free;
struct platform_device *pdev;
};
struct mpam_msc {
/* member of mpam_all_msc */
struct list_head all_msc_list;
int id;
struct platform_device *pdev;
/* Not modified after mpam_is_enabled() becomes true */
enum mpam_msc_iface iface;
u32 nrdy_usec;
cpumask_t accessibility;
bool has_extd_esr;
int reenable_error_ppi;
struct mpam_msc * __percpu *error_dev_id;
atomic_t online_refs;
/*
* probe_lock is only taken during discovery. After discovery these
* properties become read-only and the lists are protected by SRCU.
*/
struct mutex probe_lock;
bool probed;
u16 partid_max;
u8 pmg_max;
unsigned long ris_idxs;
u32 ris_max;
u32 iidr;
u16 quirks;
/*
* error_irq_lock is taken when registering/unregistering the error
* interrupt and maniupulating the below flags.
*/
struct mutex error_irq_lock;
bool error_irq_req;
bool error_irq_hw_enabled;
/* mpam_msc_ris of this component */
struct list_head ris;
/*
* part_sel_lock protects access to the MSC hardware registers that are
* affected by MPAMCFG_PART_SEL. (including the ID registers that vary
* by RIS).
* If needed, take msc->probe_lock first.
*/
struct mutex part_sel_lock;
/*
* cfg_lock protects the msc configuration and guards against mbwu_state
* save and restore racing.
*/
struct mutex cfg_lock;
/*
* mon_sel_lock protects access to the MSC hardware registers that are
* affected by MPAMCFG_MON_SEL, and the mbwu_state.
* Access to mon_sel is needed from both process and interrupt contexts,
* but is complicated by firmware-backed platforms that can't make any
* access unless they can sleep.
* Always use the mpam_mon_sel_lock() helpers.
* Accesses to mon_sel need to be able to fail if they occur in the wrong
* context.
* If needed, take msc->probe_lock first.
*/
raw_spinlock_t _mon_sel_lock;
unsigned long _mon_sel_flags;
void __iomem *mapped_hwpage;
size_t mapped_hwpage_sz;
/* Values only used on some platforms for quirks */
u32 t241_id;
struct mpam_garbage garbage;
};
/* Returning false here means accesses to mon_sel must fail and report an error. */
static inline bool __must_check mpam_mon_sel_lock(struct mpam_msc *msc)
{
/* Locking will require updating to support a firmware backed interface */
if (WARN_ON_ONCE(msc->iface != MPAM_IFACE_MMIO))
return false;
raw_spin_lock_irqsave(&msc->_mon_sel_lock, msc->_mon_sel_flags);
return true;
}
static inline void mpam_mon_sel_unlock(struct mpam_msc *msc)
{
raw_spin_unlock_irqrestore(&msc->_mon_sel_lock, msc->_mon_sel_flags);
}
static inline void mpam_mon_sel_lock_held(struct mpam_msc *msc)
{
lockdep_assert_held_once(&msc->_mon_sel_lock);
}
static inline void mpam_mon_sel_lock_init(struct mpam_msc *msc)
{
raw_spin_lock_init(&msc->_mon_sel_lock);
}
/* Bits for mpam features bitmaps */
enum mpam_device_features {
mpam_feat_cpor_part,
mpam_feat_cmax_softlim,
mpam_feat_cmax_cmax,
mpam_feat_cmax_cmin,
mpam_feat_cmax_cassoc,
mpam_feat_mbw_part,
mpam_feat_mbw_min,
mpam_feat_mbw_max,
mpam_feat_mbw_prop,
mpam_feat_intpri_part,
mpam_feat_intpri_part_0_low,
mpam_feat_dspri_part,
mpam_feat_dspri_part_0_low,
mpam_feat_msmon,
mpam_feat_msmon_csu,
mpam_feat_msmon_csu_capture,
mpam_feat_msmon_csu_xcl,
mpam_feat_msmon_mbwu,
mpam_feat_msmon_mbwu_31counter,
mpam_feat_msmon_mbwu_44counter,
mpam_feat_msmon_mbwu_63counter,
mpam_feat_msmon_mbwu_capture,
mpam_feat_msmon_mbwu_rwbw,
mpam_feat_partid_nrw,
MPAM_FEATURE_LAST
};
struct mpam_props {
DECLARE_BITMAP(features, MPAM_FEATURE_LAST);
u16 cpbm_wd;
u16 mbw_pbm_bits;
u16 bwa_wd;
u16 cmax_wd;
u16 cassoc_wd;
u16 intpri_wd;
u16 dspri_wd;
u16 num_csu_mon;
u16 num_mbwu_mon;
/*
* Kunit tests use memset() to set up feature combinations that should be
* removed, and will false-positive if the compiler introduces padding that
* isn't cleared during sanitisation.
*/
} PACKED_FOR_KUNIT;
#define mpam_has_feature(_feat, x) test_bit(_feat, (x)->features)
/*
* The non-atomic get/set operations are used because if struct mpam_props is
* packed, the alignment requirements for atomics aren't met.
*/
#define mpam_set_feature(_feat, x) __set_bit(_feat, (x)->features)
#define mpam_clear_feature(_feat, x) __clear_bit(_feat, (x)->features)
/* Workaround bits for msc->quirks */
enum mpam_device_quirks {
T241_SCRUB_SHADOW_REGS,
T241_FORCE_MBW_MIN_TO_ONE,
T241_MBW_COUNTER_SCALE_64,
IGNORE_CSU_NRDY,
MPAM_QUIRK_LAST
};
#define mpam_has_quirk(_quirk, x) ((1 << (_quirk) & (x)->quirks))
#define mpam_set_quirk(_quirk, x) ((x)->quirks |= (1 << (_quirk)))
struct mpam_quirk {
int (*init)(struct mpam_msc *msc, const struct mpam_quirk *quirk);
u32 iidr;
u32 iidr_mask;
enum mpam_device_quirks workaround;
};
#define MPAM_IIDR_MATCH_ONE (FIELD_PREP_CONST(MPAMF_IIDR_PRODUCTID, 0xfff) | \
FIELD_PREP_CONST(MPAMF_IIDR_VARIANT, 0xf) | \
FIELD_PREP_CONST(MPAMF_IIDR_REVISION, 0xf) | \
FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0xfff))
#define MPAM_IIDR_NVIDIA_T241 (FIELD_PREP_CONST(MPAMF_IIDR_PRODUCTID, 0x241) | \
FIELD_PREP_CONST(MPAMF_IIDR_VARIANT, 0) | \
FIELD_PREP_CONST(MPAMF_IIDR_REVISION, 0) | \
FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0x36b))
#define MPAM_IIDR_ARM_CMN_650 (FIELD_PREP_CONST(MPAMF_IIDR_PRODUCTID, 0) | \
FIELD_PREP_CONST(MPAMF_IIDR_VARIANT, 0) | \
FIELD_PREP_CONST(MPAMF_IIDR_REVISION, 0) | \
FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0x43b))
/* The values for MSMON_CFG_MBWU_FLT.RWBW */
enum mon_filter_options {
COUNT_BOTH = 0,
COUNT_WRITE = 1,
COUNT_READ = 2,
};
struct mon_cfg {
/*
* mon must be large enough to hold out of range values like
* USE_PRE_ALLOCATED
*/
u32 mon;
u8 pmg;
bool match_pmg;
bool csu_exclude_clean;
u32 partid;
enum mon_filter_options opts;
};
/* Changes to msmon_mbwu_state are protected by the msc's mon_sel_lock. */
struct msmon_mbwu_state {
bool enabled;
bool reset_on_next_read;
struct mon_cfg cfg;
/*
* The value to add to the new reading to account for power management,
* and overflow.
*/
u64 correction;
struct mpam_garbage garbage;
};
struct mpam_class {
/* mpam_components in this class */
struct list_head components;
cpumask_t affinity;
struct mpam_props props;
u32 nrdy_usec;
u16 quirks;
u8 level;
enum mpam_class_types type;
/* member of mpam_classes */
struct list_head classes_list;
struct ida ida_csu_mon;
struct ida ida_mbwu_mon;
struct mpam_garbage garbage;
};
struct mpam_config {
/* Which configuration values are valid. */
DECLARE_BITMAP(features, MPAM_FEATURE_LAST);
u32 cpbm;
u32 mbw_pbm;
u16 mbw_max;
struct mpam_garbage garbage;
};
struct mpam_component {
u32 comp_id;
/* mpam_vmsc in this component */
struct list_head vmsc;
cpumask_t affinity;
/*
* Array of configuration values, indexed by partid.
* Read from cpuhp callbacks, hold the cpuhp lock when writing.
*/
struct mpam_config *cfg;
/* member of mpam_class:components */
struct list_head class_list;
/* parent: */
struct mpam_class *class;
struct mpam_garbage garbage;
};
struct mpam_vmsc {
/* member of mpam_component:vmsc_list */
struct list_head comp_list;
/* mpam_msc_ris in this vmsc */
struct list_head ris;
struct mpam_props props;
/* All RIS in this vMSC are members of this MSC */
struct mpam_msc *msc;
/* parent: */
struct mpam_component *comp;
struct mpam_garbage garbage;
};
struct mpam_msc_ris {
u8 ris_idx;
u64 idr;
struct mpam_props props;
bool in_reset_state;
cpumask_t affinity;
/* member of mpam_vmsc:ris */
struct list_head vmsc_list;
/* member of mpam_msc:ris */
struct list_head msc_list;
/* parent: */
struct mpam_vmsc *vmsc;
/* msmon mbwu configuration is preserved over reset */
struct msmon_mbwu_state *mbwu_state;
struct mpam_garbage garbage;
};
struct mpam_resctrl_dom {
struct mpam_component *ctrl_comp;
/*
* There is no single mon_comp because different events may be backed
* by different class/components. mon_comp is indexed by the event
* number.
*/
struct mpam_component *mon_comp[QOS_NUM_EVENTS];
struct rdt_ctrl_domain resctrl_ctrl_dom;
struct rdt_l3_mon_domain resctrl_mon_dom;
};
struct mpam_resctrl_res {
struct mpam_class *class;
struct rdt_resource resctrl_res;
bool cdp_enabled;
};
struct mpam_resctrl_mon {
struct mpam_class *class;
/* per-class data that resctrl needs will live here */
};
static inline int mpam_alloc_csu_mon(struct mpam_class *class)
{
struct mpam_props *cprops = &class->props;
if (!mpam_has_feature(mpam_feat_msmon_csu, cprops))
return -EOPNOTSUPP;
return ida_alloc_max(&class->ida_csu_mon, cprops->num_csu_mon - 1,
GFP_KERNEL);
}
static inline void mpam_free_csu_mon(struct mpam_class *class, int csu_mon)
{
ida_free(&class->ida_csu_mon, csu_mon);
}
static inline int mpam_alloc_mbwu_mon(struct mpam_class *class)
{
struct mpam_props *cprops = &class->props;
if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
return -EOPNOTSUPP;
return ida_alloc_max(&class->ida_mbwu_mon, cprops->num_mbwu_mon - 1,
GFP_KERNEL);
}
static inline void mpam_free_mbwu_mon(struct mpam_class *class, int mbwu_mon)
{
ida_free(&class->ida_mbwu_mon, mbwu_mon);
}
/* List of all classes - protected by srcu*/
extern struct srcu_struct mpam_srcu;
extern struct list_head mpam_classes;
/* System wide partid/pmg values */
extern u16 mpam_partid_max;
extern u8 mpam_pmg_max;
/* Scheduled work callback to enable mpam once all MSC have been probed */
void mpam_enable(struct work_struct *work);
void mpam_disable(struct work_struct *work);
/* Reset all the RIS in a class under cpus_read_lock() */
void mpam_reset_class_locked(struct mpam_class *class);
int mpam_apply_config(struct mpam_component *comp, u16 partid,
struct mpam_config *cfg);
int mpam_msmon_read(struct mpam_component *comp, struct mon_cfg *ctx,
enum mpam_device_features, u64 *val);
void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ctx);
int mpam_get_cpumask_from_cache_id(unsigned long cache_id, u32 cache_level,
cpumask_t *affinity);
#ifdef CONFIG_RESCTRL_FS
int mpam_resctrl_setup(void);
void mpam_resctrl_exit(void);
int mpam_resctrl_online_cpu(unsigned int cpu);
void mpam_resctrl_offline_cpu(unsigned int cpu);
void mpam_resctrl_teardown_class(struct mpam_class *class);
#else
static inline int mpam_resctrl_setup(void) { return 0; }
static inline void mpam_resctrl_exit(void) { }
static inline int mpam_resctrl_online_cpu(unsigned int cpu) { return 0; }
static inline void mpam_resctrl_offline_cpu(unsigned int cpu) { }
static inline void mpam_resctrl_teardown_class(struct mpam_class *class) { }
#endif /* CONFIG_RESCTRL_FS */
/*
* MPAM MSCs have the following register layout. See:
* Arm Memory System Resource Partitioning and Monitoring (MPAM) System
* Component Specification.
* https://developer.arm.com/documentation/ihi0099/aa/
*/
#define MPAM_ARCHITECTURE_V1 0x10
/* Memory mapped control pages */
/* ID Register offsets in the memory mapped page */
#define MPAMF_IDR 0x0000 /* features id register */
#define MPAMF_IIDR 0x0018 /* implementer id register */
#define MPAMF_AIDR 0x0020 /* architectural id register */
#define MPAMF_IMPL_IDR 0x0028 /* imp-def partitioning */
#define MPAMF_CPOR_IDR 0x0030 /* cache-portion partitioning */
#define MPAMF_CCAP_IDR 0x0038 /* cache-capacity partitioning */
#define MPAMF_MBW_IDR 0x0040 /* mem-bw partitioning */
#define MPAMF_PRI_IDR 0x0048 /* priority partitioning */
#define MPAMF_MSMON_IDR 0x0080 /* performance monitoring features */
#define MPAMF_CSUMON_IDR 0x0088 /* cache-usage monitor */
#define MPAMF_MBWUMON_IDR 0x0090 /* mem-bw usage monitor */
#define MPAMF_PARTID_NRW_IDR 0x0050 /* partid-narrowing */
/* Configuration and Status Register offsets in the memory mapped page */
#define MPAMCFG_PART_SEL 0x0100 /* partid to configure */
#define MPAMCFG_CPBM 0x1000 /* cache-portion config */
#define MPAMCFG_CMAX 0x0108 /* cache-capacity config */
#define MPAMCFG_CMIN 0x0110 /* cache-capacity config */
#define MPAMCFG_CASSOC 0x0118 /* cache-associativity config */
#define MPAMCFG_MBW_MIN 0x0200 /* min mem-bw config */
#define MPAMCFG_MBW_MAX 0x0208 /* max mem-bw config */
#define MPAMCFG_MBW_WINWD 0x0220 /* mem-bw accounting window config */
#define MPAMCFG_MBW_PBM 0x2000 /* mem-bw portion bitmap config */
#define MPAMCFG_PRI 0x0400 /* priority partitioning config */
#define MPAMCFG_MBW_PROP 0x0500 /* mem-bw stride config */
#define MPAMCFG_INTPARTID 0x0600 /* partid-narrowing config */
#define MSMON_CFG_MON_SEL 0x0800 /* monitor selector */
#define MSMON_CFG_CSU_FLT 0x0810 /* cache-usage monitor filter */
#define MSMON_CFG_CSU_CTL 0x0818 /* cache-usage monitor config */
#define MSMON_CFG_MBWU_FLT 0x0820 /* mem-bw monitor filter */
#define MSMON_CFG_MBWU_CTL 0x0828 /* mem-bw monitor config */
#define MSMON_CSU 0x0840 /* current cache-usage */
#define MSMON_CSU_CAPTURE 0x0848 /* last cache-usage value captured */
#define MSMON_MBWU 0x0860 /* current mem-bw usage value */
#define MSMON_MBWU_CAPTURE 0x0868 /* last mem-bw value captured */
#define MSMON_MBWU_L 0x0880 /* current long mem-bw usage value */
#define MSMON_MBWU_L_CAPTURE 0x0890 /* last long mem-bw value captured */
#define MSMON_CAPT_EVNT 0x0808 /* signal a capture event */
#define MPAMF_ESR 0x00F8 /* error status register */
#define MPAMF_ECR 0x00F0 /* error control register */
/* MPAMF_IDR - MPAM features ID register */
#define MPAMF_IDR_PARTID_MAX GENMASK(15, 0)
#define MPAMF_IDR_PMG_MAX GENMASK(23, 16)
#define MPAMF_IDR_HAS_CCAP_PART BIT(24)
#define MPAMF_IDR_HAS_CPOR_PART BIT(25)
#define MPAMF_IDR_HAS_MBW_PART BIT(26)
#define MPAMF_IDR_HAS_PRI_PART BIT(27)
#define MPAMF_IDR_EXT BIT(28)
#define MPAMF_IDR_HAS_IMPL_IDR BIT(29)
#define MPAMF_IDR_HAS_MSMON BIT(30)
#define MPAMF_IDR_HAS_PARTID_NRW BIT(31)
#define MPAMF_IDR_HAS_RIS BIT(32)
#define MPAMF_IDR_HAS_EXTD_ESR BIT(38)
#define MPAMF_IDR_HAS_ESR BIT(39)
#define MPAMF_IDR_RIS_MAX GENMASK(59, 56)
/* MPAMF_MSMON_IDR - MPAM performance monitoring ID register */
#define MPAMF_MSMON_IDR_MSMON_CSU BIT(16)
#define MPAMF_MSMON_IDR_MSMON_MBWU BIT(17)
#define MPAMF_MSMON_IDR_HAS_LOCAL_CAPT_EVNT BIT(31)
/* MPAMF_CPOR_IDR - MPAM features cache portion partitioning ID register */
#define MPAMF_CPOR_IDR_CPBM_WD GENMASK(15, 0)
/* MPAMF_CCAP_IDR - MPAM features cache capacity partitioning ID register */
#define MPAMF_CCAP_IDR_CMAX_WD GENMASK(5, 0)
#define MPAMF_CCAP_IDR_CASSOC_WD GENMASK(12, 8)
#define MPAMF_CCAP_IDR_HAS_CASSOC BIT(28)
#define MPAMF_CCAP_IDR_HAS_CMIN BIT(29)
#define MPAMF_CCAP_IDR_NO_CMAX BIT(30)
#define MPAMF_CCAP_IDR_HAS_CMAX_SOFTLIM BIT(31)
/* MPAMF_MBW_IDR - MPAM features memory bandwidth partitioning ID register */
#define MPAMF_MBW_IDR_BWA_WD GENMASK(5, 0)
#define MPAMF_MBW_IDR_HAS_MIN BIT(10)
#define MPAMF_MBW_IDR_HAS_MAX BIT(11)
#define MPAMF_MBW_IDR_HAS_PBM BIT(12)
#define MPAMF_MBW_IDR_HAS_PROP BIT(13)
#define MPAMF_MBW_IDR_WINDWR BIT(14)
#define MPAMF_MBW_IDR_BWPBM_WD GENMASK(28, 16)
/* MPAMF_PRI_IDR - MPAM features priority partitioning ID register */
#define MPAMF_PRI_IDR_HAS_INTPRI BIT(0)
#define MPAMF_PRI_IDR_INTPRI_0_IS_LOW BIT(1)
#define MPAMF_PRI_IDR_INTPRI_WD GENMASK(9, 4)
#define MPAMF_PRI_IDR_HAS_DSPRI BIT(16)
#define MPAMF_PRI_IDR_DSPRI_0_IS_LOW BIT(17)
#define MPAMF_PRI_IDR_DSPRI_WD GENMASK(25, 20)
/* MPAMF_CSUMON_IDR - MPAM cache storage usage monitor ID register */
#define MPAMF_CSUMON_IDR_NUM_MON GENMASK(15, 0)
#define MPAMF_CSUMON_IDR_HAS_OFLOW_CAPT BIT(24)
#define MPAMF_CSUMON_IDR_HAS_CEVNT_OFLW BIT(25)
#define MPAMF_CSUMON_IDR_HAS_OFSR BIT(26)
#define MPAMF_CSUMON_IDR_HAS_OFLOW_LNKG BIT(27)
#define MPAMF_CSUMON_IDR_HAS_XCL BIT(29)
#define MPAMF_CSUMON_IDR_CSU_RO BIT(30)
#define MPAMF_CSUMON_IDR_HAS_CAPTURE BIT(31)
/* MPAMF_MBWUMON_IDR - MPAM memory bandwidth usage monitor ID register */
#define MPAMF_MBWUMON_IDR_NUM_MON GENMASK(15, 0)
#define MPAMF_MBWUMON_IDR_HAS_RWBW BIT(28)
#define MPAMF_MBWUMON_IDR_LWD BIT(29)
#define MPAMF_MBWUMON_IDR_HAS_LONG BIT(30)
#define MPAMF_MBWUMON_IDR_HAS_CAPTURE BIT(31)
/* MPAMF_PARTID_NRW_IDR - MPAM PARTID narrowing ID register */
#define MPAMF_PARTID_NRW_IDR_INTPARTID_MAX GENMASK(15, 0)
/* MPAMF_IIDR - MPAM implementation ID register */
#define MPAMF_IIDR_IMPLEMENTER GENMASK(11, 0)
#define MPAMF_IIDR_REVISION GENMASK(15, 12)
#define MPAMF_IIDR_VARIANT GENMASK(19, 16)
#define MPAMF_IIDR_PRODUCTID GENMASK(31, 20)
/* MPAMF_AIDR - MPAM architecture ID register */
#define MPAMF_AIDR_ARCH_MINOR_REV GENMASK(3, 0)
#define MPAMF_AIDR_ARCH_MAJOR_REV GENMASK(7, 4)
/* MPAMCFG_PART_SEL - MPAM partition configuration selection register */
#define MPAMCFG_PART_SEL_PARTID_SEL GENMASK(15, 0)
#define MPAMCFG_PART_SEL_INTERNAL BIT(16)
#define MPAMCFG_PART_SEL_RIS GENMASK(27, 24)
/* MPAMCFG_CASSOC - MPAM cache maximum associativity partition configuration register */
#define MPAMCFG_CASSOC_CASSOC GENMASK(15, 0)
/* MPAMCFG_CMAX - MPAM cache capacity configuration register */
#define MPAMCFG_CMAX_SOFTLIM BIT(31)
#define MPAMCFG_CMAX_CMAX GENMASK(15, 0)
/* MPAMCFG_CMIN - MPAM cache capacity configuration register */
#define MPAMCFG_CMIN_CMIN GENMASK(15, 0)
/*
* MPAMCFG_MBW_MIN - MPAM memory minimum bandwidth partitioning configuration
* register
*/
#define MPAMCFG_MBW_MIN_MIN GENMASK(15, 0)
/*
* MPAMCFG_MBW_MAX - MPAM memory maximum bandwidth partitioning configuration
* register
*/
#define MPAMCFG_MBW_MAX_MAX GENMASK(15, 0)
#define MPAMCFG_MBW_MAX_HARDLIM BIT(31)
/*
* MPAMCFG_MBW_WINWD - MPAM memory bandwidth partitioning window width
* register
*/
#define MPAMCFG_MBW_WINWD_US_FRAC GENMASK(7, 0)
#define MPAMCFG_MBW_WINWD_US_INT GENMASK(23, 8)
/* MPAMCFG_PRI - MPAM priority partitioning configuration register */
#define MPAMCFG_PRI_INTPRI GENMASK(15, 0)
#define MPAMCFG_PRI_DSPRI GENMASK(31, 16)
/*
* MPAMCFG_MBW_PROP - Memory bandwidth proportional stride partitioning
* configuration register
*/
#define MPAMCFG_MBW_PROP_STRIDEM1 GENMASK(15, 0)
#define MPAMCFG_MBW_PROP_EN BIT(31)
/*
* MPAMCFG_INTPARTID - MPAM internal partition narrowing configuration register
*/
#define MPAMCFG_INTPARTID_INTPARTID GENMASK(15, 0)
#define MPAMCFG_INTPARTID_INTERNAL BIT(16)
/* MSMON_CFG_MON_SEL - Memory system performance monitor selection register */
#define MSMON_CFG_MON_SEL_MON_SEL GENMASK(15, 0)
#define MSMON_CFG_MON_SEL_RIS GENMASK(27, 24)
/* MPAMF_ESR - MPAM Error Status Register */
#define MPAMF_ESR_PARTID_MON GENMASK(15, 0)
#define MPAMF_ESR_PMG GENMASK(23, 16)
#define MPAMF_ESR_ERRCODE GENMASK(27, 24)
#define MPAMF_ESR_OVRWR BIT(31)
#define MPAMF_ESR_RIS GENMASK(35, 32)
/* MPAMF_ECR - MPAM Error Control Register */
#define MPAMF_ECR_INTEN BIT(0)
/* Error conditions in accessing memory mapped registers */
#define MPAM_ERRCODE_NONE 0
#define MPAM_ERRCODE_PARTID_SEL_RANGE 1
#define MPAM_ERRCODE_REQ_PARTID_RANGE 2
#define MPAM_ERRCODE_MSMONCFG_ID_RANGE 3
#define MPAM_ERRCODE_REQ_PMG_RANGE 4
#define MPAM_ERRCODE_MONITOR_RANGE 5
#define MPAM_ERRCODE_INTPARTID_RANGE 6
#define MPAM_ERRCODE_UNEXPECTED_INTERNAL 7
#define MPAM_ERRCODE_UNDEFINED_RIS_PART_SEL 8
#define MPAM_ERRCODE_RIS_NO_CONTROL 9
#define MPAM_ERRCODE_UNDEFINED_RIS_MON_SEL 10
#define MPAM_ERRCODE_RIS_NO_MONITOR 11
/*
* MSMON_CFG_CSU_CTL - Memory system performance monitor configure cache storage
* usage monitor control register
* MSMON_CFG_MBWU_CTL - Memory system performance monitor configure memory
* bandwidth usage monitor control register
*/
#define MSMON_CFG_x_CTL_TYPE GENMASK(7, 0)
#define MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L BIT(15)
#define MSMON_CFG_x_CTL_MATCH_PARTID BIT(16)
#define MSMON_CFG_x_CTL_MATCH_PMG BIT(17)
#define MSMON_CFG_MBWU_CTL_SCLEN BIT(19)
#define MSMON_CFG_x_CTL_SUBTYPE GENMASK(22, 20)
#define MSMON_CFG_x_CTL_OFLOW_FRZ BIT(24)
#define MSMON_CFG_x_CTL_OFLOW_INTR BIT(25)
#define MSMON_CFG_x_CTL_OFLOW_STATUS BIT(26)
#define MSMON_CFG_x_CTL_CAPT_RESET BIT(27)
#define MSMON_CFG_x_CTL_CAPT_EVNT GENMASK(30, 28)
#define MSMON_CFG_x_CTL_EN BIT(31)
#define MSMON_CFG_MBWU_CTL_TYPE_MBWU 0x42
#define MSMON_CFG_CSU_CTL_TYPE_CSU 0x43
/*
* MSMON_CFG_CSU_FLT - Memory system performance monitor configure cache storage
* usage monitor filter register
* MSMON_CFG_MBWU_FLT - Memory system performance monitor configure memory
* bandwidth usage monitor filter register
*/
#define MSMON_CFG_x_FLT_PARTID GENMASK(15, 0)
#define MSMON_CFG_x_FLT_PMG GENMASK(23, 16)
#define MSMON_CFG_MBWU_FLT_RWBW GENMASK(31, 30)
#define MSMON_CFG_CSU_FLT_XCL BIT(31)
/*
* MSMON_CSU - Memory system performance monitor cache storage usage monitor
* register
* MSMON_CSU_CAPTURE - Memory system performance monitor cache storage usage
* capture register
* MSMON_MBWU - Memory system performance monitor memory bandwidth usage
* monitor register
* MSMON_MBWU_CAPTURE - Memory system performance monitor memory bandwidth usage
* capture register
*/
#define MSMON___VALUE GENMASK(30, 0)
#define MSMON___NRDY BIT(31)
#define MSMON___L_NRDY BIT(63)
#define MSMON___L_VALUE GENMASK(43, 0)
#define MSMON___LWD_VALUE GENMASK(62, 0)
/*
* MSMON_CAPT_EVNT - Memory system performance monitoring capture event
* generation register
*/
#define MSMON_CAPT_EVNT_NOW BIT(0)
#endif /* MPAM_INTERNAL_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,397 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Arm Ltd.
/* This file is intended to be included into mpam_devices.c */
#include <kunit/test.h>
/*
* This test catches fields that aren't being sanitised - but can't tell you
* which one...
*/
static void test__props_mismatch(struct kunit *test)
{
struct mpam_props parent = { 0 };
struct mpam_props child;
memset(&child, 0xff, sizeof(child));
__props_mismatch(&parent, &child, false);
memset(&child, 0, sizeof(child));
KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0);
memset(&child, 0xff, sizeof(child));
__props_mismatch(&parent, &child, true);
KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0);
}
static struct list_head fake_classes_list;
static struct mpam_class fake_class = { 0 };
static struct mpam_component fake_comp1 = { 0 };
static struct mpam_component fake_comp2 = { 0 };
static struct mpam_vmsc fake_vmsc1 = { 0 };
static struct mpam_vmsc fake_vmsc2 = { 0 };
static struct mpam_msc fake_msc1 = { 0 };
static struct mpam_msc fake_msc2 = { 0 };
static struct mpam_msc_ris fake_ris1 = { 0 };
static struct mpam_msc_ris fake_ris2 = { 0 };
static struct platform_device fake_pdev = { 0 };
static inline void reset_fake_hierarchy(void)
{
INIT_LIST_HEAD(&fake_classes_list);
memset(&fake_class, 0, sizeof(fake_class));
fake_class.level = 3;
fake_class.type = MPAM_CLASS_CACHE;
INIT_LIST_HEAD_RCU(&fake_class.components);
INIT_LIST_HEAD(&fake_class.classes_list);
memset(&fake_comp1, 0, sizeof(fake_comp1));
memset(&fake_comp2, 0, sizeof(fake_comp2));
fake_comp1.comp_id = 1;
fake_comp2.comp_id = 2;
INIT_LIST_HEAD(&fake_comp1.vmsc);
INIT_LIST_HEAD(&fake_comp1.class_list);
INIT_LIST_HEAD(&fake_comp2.vmsc);
INIT_LIST_HEAD(&fake_comp2.class_list);
memset(&fake_vmsc1, 0, sizeof(fake_vmsc1));
memset(&fake_vmsc2, 0, sizeof(fake_vmsc2));
INIT_LIST_HEAD(&fake_vmsc1.ris);
INIT_LIST_HEAD(&fake_vmsc1.comp_list);
fake_vmsc1.msc = &fake_msc1;
INIT_LIST_HEAD(&fake_vmsc2.ris);
INIT_LIST_HEAD(&fake_vmsc2.comp_list);
fake_vmsc2.msc = &fake_msc2;
memset(&fake_ris1, 0, sizeof(fake_ris1));
memset(&fake_ris2, 0, sizeof(fake_ris2));
fake_ris1.ris_idx = 1;
INIT_LIST_HEAD(&fake_ris1.msc_list);
fake_ris2.ris_idx = 2;
INIT_LIST_HEAD(&fake_ris2.msc_list);
fake_msc1.pdev = &fake_pdev;
fake_msc2.pdev = &fake_pdev;
list_add(&fake_class.classes_list, &fake_classes_list);
}
static void test_mpam_enable_merge_features(struct kunit *test)
{
reset_fake_hierarchy();
mutex_lock(&mpam_list_lock);
/* One Class+Comp, two RIS in one vMSC with common features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = NULL;
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = NULL;
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc1;
list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 4;
fake_ris2.props.cpbm_wd = 4;
mpam_enable_merge_features(&fake_classes_list);
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
reset_fake_hierarchy();
/* One Class+Comp, two RIS in one vMSC with non-overlapping features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = NULL;
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = NULL;
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc1;
list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 4;
fake_ris2.props.cmax_wd = 4;
mpam_enable_merge_features(&fake_classes_list);
/* Multiple RIS within one MSC controlling the same resource can be mismatched */
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class.props));
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_vmsc1.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
KUNIT_EXPECT_EQ(test, fake_vmsc1.props.cmax_wd, 4);
KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 4);
reset_fake_hierarchy();
/* One Class+Comp, two MSC with overlapping features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = NULL;
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = &fake_comp1;
list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc2;
list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 4;
fake_ris2.props.cpbm_wd = 4;
mpam_enable_merge_features(&fake_classes_list);
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
reset_fake_hierarchy();
/* One Class+Comp, two MSC with non-overlapping features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = NULL;
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = &fake_comp1;
list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc2;
list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 4;
fake_ris2.props.cmax_wd = 4;
mpam_enable_merge_features(&fake_classes_list);
/*
* Multiple RIS in different MSC can't control the same resource,
* mismatched features can not be supported.
*/
KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0);
KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0);
reset_fake_hierarchy();
/* One Class+Comp, two MSC with incompatible overlapping features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = NULL;
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = &fake_comp1;
list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc2;
list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
mpam_set_feature(mpam_feat_mbw_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_mbw_part, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 5;
fake_ris2.props.cpbm_wd = 3;
fake_ris1.props.mbw_pbm_bits = 5;
fake_ris2.props.mbw_pbm_bits = 3;
mpam_enable_merge_features(&fake_classes_list);
/*
* Multiple RIS in different MSC can't control the same resource,
* mismatched features can not be supported.
*/
KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_mbw_part, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0);
KUNIT_EXPECT_EQ(test, fake_class.props.mbw_pbm_bits, 0);
reset_fake_hierarchy();
/* One Class+Comp, two MSC with overlapping features that need tweaking */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = NULL;
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = &fake_comp1;
list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc2;
list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
mpam_set_feature(mpam_feat_mbw_min, &fake_ris1.props);
mpam_set_feature(mpam_feat_mbw_min, &fake_ris2.props);
mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris1.props);
mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris2.props);
fake_ris1.props.bwa_wd = 5;
fake_ris2.props.bwa_wd = 3;
fake_ris1.props.cmax_wd = 5;
fake_ris2.props.cmax_wd = 3;
mpam_enable_merge_features(&fake_classes_list);
/*
* RIS with different control properties need to be sanitised so the
* class has the common set of properties.
*/
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_mbw_min, &fake_class.props));
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmax, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.bwa_wd, 3);
KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 3);
reset_fake_hierarchy();
/* One Class Two Comp with overlapping features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = &fake_class;
list_add(&fake_comp2.class_list, &fake_class.components);
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = &fake_comp2;
list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc);
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc2;
list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 4;
fake_ris2.props.cpbm_wd = 4;
mpam_enable_merge_features(&fake_classes_list);
KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
reset_fake_hierarchy();
/* One Class Two Comp with non-overlapping features */
fake_comp1.class = &fake_class;
list_add(&fake_comp1.class_list, &fake_class.components);
fake_comp2.class = &fake_class;
list_add(&fake_comp2.class_list, &fake_class.components);
fake_vmsc1.comp = &fake_comp1;
list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
fake_vmsc2.comp = &fake_comp2;
list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc);
fake_ris1.vmsc = &fake_vmsc1;
list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
fake_ris2.vmsc = &fake_vmsc2;
list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props);
fake_ris1.props.cpbm_wd = 4;
fake_ris2.props.cmax_wd = 4;
mpam_enable_merge_features(&fake_classes_list);
/*
* Multiple components can't control the same resource, mismatched features can
* not be supported.
*/
KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class.props));
KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0);
KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0);
mutex_unlock(&mpam_list_lock);
}
static void __test_mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
{
/* Avoid warnings when running with CONFIG_DEBUG_PREEMPT */
guard(preempt)();
mpam_reset_msc_bitmap(msc, reg, wd);
}
static void test_mpam_reset_msc_bitmap(struct kunit *test)
{
char __iomem *buf = (__force char __iomem *)kunit_kzalloc(test, SZ_16K, GFP_KERNEL);
struct mpam_msc fake_msc = {};
u32 *test_result;
if (!buf)
return;
fake_msc.mapped_hwpage = buf;
fake_msc.mapped_hwpage_sz = SZ_16K;
cpumask_copy(&fake_msc.accessibility, cpu_possible_mask);
/* Satisfy lockdep checks */
mutex_init(&fake_msc.part_sel_lock);
mutex_lock(&fake_msc.part_sel_lock);
test_result = (__force u32 *)(buf + MPAMCFG_CPBM);
__test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 0);
KUNIT_EXPECT_EQ(test, test_result[0], 0);
KUNIT_EXPECT_EQ(test, test_result[1], 0);
test_result[0] = 0;
test_result[1] = 0;
__test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 1);
KUNIT_EXPECT_EQ(test, test_result[0], 1);
KUNIT_EXPECT_EQ(test, test_result[1], 0);
test_result[0] = 0;
test_result[1] = 0;
__test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 16);
KUNIT_EXPECT_EQ(test, test_result[0], 0xffff);
KUNIT_EXPECT_EQ(test, test_result[1], 0);
test_result[0] = 0;
test_result[1] = 0;
__test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 32);
KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff);
KUNIT_EXPECT_EQ(test, test_result[1], 0);
test_result[0] = 0;
test_result[1] = 0;
__test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 33);
KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff);
KUNIT_EXPECT_EQ(test, test_result[1], 1);
test_result[0] = 0;
test_result[1] = 0;
mutex_unlock(&fake_msc.part_sel_lock);
}
static struct kunit_case mpam_devices_test_cases[] = {
KUNIT_CASE(test_mpam_reset_msc_bitmap),
KUNIT_CASE(test_mpam_enable_merge_features),
KUNIT_CASE(test__props_mismatch),
{}
};
static struct kunit_suite mpam_devices_test_suite = {
.name = "mpam_devices_test_suite",
.test_cases = mpam_devices_test_cases,
};
kunit_test_suites(&mpam_devices_test_suite);
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Arm Ltd.
/* This file is intended to be included into mpam_resctrl.c */
#include <kunit/test.h>
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/math.h>
#include <linux/sprintf.h>
struct percent_value_case {
u8 pc;
u8 width;
u16 value;
};
/*
* Mysterious inscriptions taken from the union of ARM DDI 0598D.b,
* "Arm Architecture Reference Manual Supplement - Memory System
* Resource Partitioning and Monitoring (MPAM), for A-profile
* architecture", Section 9.8, "About the fixed-point fractional
* format" (exact percentage entries only) and ARM IHI0099B.a
* "MPAM system component specification", Section 9.3,
* "The fixed-point fractional format":
*/
static const struct percent_value_case percent_value_cases[] = {
/* Architectural cases: */
{ 1, 8, 1 }, { 1, 12, 0x27 }, { 1, 16, 0x28e },
{ 25, 8, 0x3f }, { 25, 12, 0x3ff }, { 25, 16, 0x3fff },
{ 33, 8, 0x53 }, { 33, 12, 0x546 }, { 33, 16, 0x5479 },
{ 35, 8, 0x58 }, { 35, 12, 0x598 }, { 35, 16, 0x5998 },
{ 45, 8, 0x72 }, { 45, 12, 0x732 }, { 45, 16, 0x7332 },
{ 50, 8, 0x7f }, { 50, 12, 0x7ff }, { 50, 16, 0x7fff },
{ 52, 8, 0x84 }, { 52, 12, 0x850 }, { 52, 16, 0x851d },
{ 55, 8, 0x8b }, { 55, 12, 0x8cb }, { 55, 16, 0x8ccb },
{ 58, 8, 0x93 }, { 58, 12, 0x946 }, { 58, 16, 0x9479 },
{ 75, 8, 0xbf }, { 75, 12, 0xbff }, { 75, 16, 0xbfff },
{ 80, 8, 0xcb }, { 80, 12, 0xccb }, { 80, 16, 0xcccb },
{ 88, 8, 0xe0 }, { 88, 12, 0xe13 }, { 88, 16, 0xe146 },
{ 95, 8, 0xf2 }, { 95, 12, 0xf32 }, { 95, 16, 0xf332 },
{ 100, 8, 0xff }, { 100, 12, 0xfff }, { 100, 16, 0xffff },
};
static void test_percent_value_desc(const struct percent_value_case *param,
char *desc)
{
snprintf(desc, KUNIT_PARAM_DESC_SIZE,
"pc=%d, width=%d, value=0x%.*x\n",
param->pc, param->width,
DIV_ROUND_UP(param->width, 4), param->value);
}
KUNIT_ARRAY_PARAM(test_percent_value, percent_value_cases,
test_percent_value_desc);
struct percent_value_test_info {
u32 pc; /* result of value-to-percent conversion */
u32 value; /* result of percent-to-value conversion */
u32 max_value; /* maximum raw value allowed by test params */
unsigned int shift; /* promotes raw testcase value to 16 bits */
};
/*
* Convert a reference percentage to a fixed-point MAX value and
* vice-versa, based on param (not test->param_value!)
*/
static void __prepare_percent_value_test(struct kunit *test,
struct percent_value_test_info *res,
const struct percent_value_case *param)
{
struct mpam_props fake_props = { };
/* Reject bogus test parameters that would break the tests: */
KUNIT_ASSERT_GE(test, param->width, 1);
KUNIT_ASSERT_LE(test, param->width, 16);
KUNIT_ASSERT_LT(test, param->value, 1 << param->width);
mpam_set_feature(mpam_feat_mbw_max, &fake_props);
fake_props.bwa_wd = param->width;
res->shift = 16 - param->width;
res->max_value = GENMASK_U32(param->width - 1, 0);
res->value = percent_to_mbw_max(param->pc, &fake_props);
res->pc = mbw_max_to_percent(param->value << res->shift, &fake_props);
}
static void test_get_mba_granularity(struct kunit *test)
{
int ret;
struct mpam_props fake_props = { };
/* Use MBW_MAX */
mpam_set_feature(mpam_feat_mbw_max, &fake_props);
fake_props.bwa_wd = 0;
KUNIT_EXPECT_FALSE(test, mba_class_use_mbw_max(&fake_props));
fake_props.bwa_wd = 1;
KUNIT_EXPECT_TRUE(test, mba_class_use_mbw_max(&fake_props));
/* Architectural maximum: */
fake_props.bwa_wd = 16;
KUNIT_EXPECT_TRUE(test, mba_class_use_mbw_max(&fake_props));
/* No usable control... */
fake_props.bwa_wd = 0;
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 0);
fake_props.bwa_wd = 1;
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 50); /* DIV_ROUND_UP(100, 1 << 1)% = 50% */
fake_props.bwa_wd = 2;
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 25); /* DIV_ROUND_UP(100, 1 << 2)% = 25% */
fake_props.bwa_wd = 3;
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 13); /* DIV_ROUND_UP(100, 1 << 3)% = 13% */
fake_props.bwa_wd = 6;
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 2); /* DIV_ROUND_UP(100, 1 << 6)% = 2% */
fake_props.bwa_wd = 7;
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 1); /* DIV_ROUND_UP(100, 1 << 7)% = 1% */
/* Granularity saturates at 1% */
fake_props.bwa_wd = 16; /* architectural maximum */
ret = get_mba_granularity(&fake_props);
KUNIT_EXPECT_EQ(test, ret, 1); /* DIV_ROUND_UP(100, 1 << 16)% = 1% */
}
static void test_mbw_max_to_percent(struct kunit *test)
{
const struct percent_value_case *param = test->param_value;
struct percent_value_test_info res;
/*
* Since the reference values in percent_value_cases[] all
* correspond to exact percentages, round-to-nearest will
* always give the exact percentage back when the MPAM max
* value has precision of 0.5% or finer. (Always true for the
* reference data, since they all specify 8 bits or more of
* precision.
*
* So, keep it simple and demand an exact match:
*/
__prepare_percent_value_test(test, &res, param);
KUNIT_EXPECT_EQ(test, res.pc, param->pc);
}
static void test_percent_to_mbw_max(struct kunit *test)
{
const struct percent_value_case *param = test->param_value;
struct percent_value_test_info res;
__prepare_percent_value_test(test, &res, param);
KUNIT_EXPECT_GE(test, res.value, param->value << res.shift);
KUNIT_EXPECT_LE(test, res.value, (param->value + 1) << res.shift);
KUNIT_EXPECT_LE(test, res.value, res.max_value << res.shift);
/* No flexibility allowed for 0% and 100%! */
if (param->pc == 0)
KUNIT_EXPECT_EQ(test, res.value, 0);
if (param->pc == 100)
KUNIT_EXPECT_EQ(test, res.value, res.max_value << res.shift);
}
static const void *test_all_bwa_wd_gen_params(struct kunit *test, const void *prev,
char *desc)
{
uintptr_t param = (uintptr_t)prev;
if (param > 15)
return NULL;
param++;
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "wd=%u\n", (unsigned int)param);
return (void *)param;
}
static unsigned int test_get_bwa_wd(struct kunit *test)
{
uintptr_t param = (uintptr_t)test->param_value;
KUNIT_ASSERT_GE(test, param, 1);
KUNIT_ASSERT_LE(test, param, 16);
return param;
}
static void test_mbw_max_to_percent_limits(struct kunit *test)
{
struct mpam_props fake_props = {0};
u32 max_value;
mpam_set_feature(mpam_feat_mbw_max, &fake_props);
fake_props.bwa_wd = test_get_bwa_wd(test);
max_value = GENMASK(15, 16 - fake_props.bwa_wd);
KUNIT_EXPECT_EQ(test, mbw_max_to_percent(max_value, &fake_props),
MAX_MBA_BW);
KUNIT_EXPECT_EQ(test, mbw_max_to_percent(0, &fake_props),
get_mba_min(&fake_props));
/*
* Rounding policy dependent 0% sanity-check:
* With round-to-nearest, the minimum mbw_max value really
* should map to 0% if there are at least 200 steps.
* (100 steps may be enough for some other rounding policies.)
*/
if (fake_props.bwa_wd >= 8)
KUNIT_EXPECT_EQ(test, mbw_max_to_percent(0, &fake_props), 0);
if (fake_props.bwa_wd < 8 &&
mbw_max_to_percent(0, &fake_props) == 0)
kunit_warn(test, "wd=%d: Testsuite/driver Rounding policy mismatch?",
fake_props.bwa_wd);
}
/*
* Check that converting a percentage to mbw_max and back again (or, as
* appropriate, vice-versa) always restores the original value:
*/
static void test_percent_max_roundtrip_stability(struct kunit *test)
{
struct mpam_props fake_props = {0};
unsigned int shift;
u32 pc, max, pc2, max2;
mpam_set_feature(mpam_feat_mbw_max, &fake_props);
fake_props.bwa_wd = test_get_bwa_wd(test);
shift = 16 - fake_props.bwa_wd;
/*
* Converting a valid value from the coarser scale to the finer
* scale and back again must yield the original value:
*/
if (fake_props.bwa_wd >= 7) {
/* More than 100 steps: only test exact pc values: */
for (pc = get_mba_min(&fake_props); pc <= MAX_MBA_BW; pc++) {
max = percent_to_mbw_max(pc, &fake_props);
pc2 = mbw_max_to_percent(max, &fake_props);
KUNIT_EXPECT_EQ(test, pc2, pc);
}
} else {
/* Fewer than 100 steps: only test exact mbw_max values: */
for (max = 0; max < 1 << 16; max += 1 << shift) {
pc = mbw_max_to_percent(max, &fake_props);
max2 = percent_to_mbw_max(pc, &fake_props);
KUNIT_EXPECT_EQ(test, max2, max);
}
}
}
static void test_percent_to_max_rounding(struct kunit *test)
{
const struct percent_value_case *param = test->param_value;
unsigned int num_rounded_up = 0, total = 0;
struct percent_value_test_info res;
for (param = percent_value_cases, total = 0;
param < &percent_value_cases[ARRAY_SIZE(percent_value_cases)];
param++, total++) {
__prepare_percent_value_test(test, &res, param);
if (res.value > param->value << res.shift)
num_rounded_up++;
}
/*
* The MPAM driver applies a round-to-nearest policy, whereas a
* round-down policy seems to have been applied in the
* reference table from which the test vectors were selected.
*
* For a large and well-distributed suite of test vectors,
* about half should be rounded up and half down compared with
* the reference table. The actual test vectors are few in
* number and probably not very well distributed however, so
* tolerate a round-up rate of between 1/4 and 3/4 before
* crying foul:
*/
kunit_info(test, "Round-up rate: %u%% (%u/%u)\n",
DIV_ROUND_CLOSEST(num_rounded_up * 100, total),
num_rounded_up, total);
KUNIT_EXPECT_GE(test, 4 * num_rounded_up, 1 * total);
KUNIT_EXPECT_LE(test, 4 * num_rounded_up, 3 * total);
}
static struct kunit_case mpam_resctrl_test_cases[] = {
KUNIT_CASE(test_get_mba_granularity),
KUNIT_CASE_PARAM(test_mbw_max_to_percent, test_percent_value_gen_params),
KUNIT_CASE_PARAM(test_percent_to_mbw_max, test_percent_value_gen_params),
KUNIT_CASE_PARAM(test_mbw_max_to_percent_limits, test_all_bwa_wd_gen_params),
KUNIT_CASE(test_percent_to_max_rounding),
KUNIT_CASE_PARAM(test_percent_max_roundtrip_stability,
test_all_bwa_wd_gen_params),
{}
};
static struct kunit_suite mpam_resctrl_test_suite = {
.name = "mpam_resctrl_test_suite",
.test_cases = mpam_resctrl_test_cases,
};
kunit_test_suites(&mpam_resctrl_test_suite);