* [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support
@ 2026-08-04 10:06 Andre Przywara
2026-08-04 10:06 ` [PATCH v8 01/11] arm_mpam: let low level MSC accessors return an error Andre Przywara
` (11 more replies)
0 siblings, 12 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Hi,
this is v8 of the MPAM-Fb code, for firmware based MSC accesses.
It fixes a potential use-after-free of the dev pointer on error
prints, and disables the IRQ on the irqchip side in case doing it on
the device side doesn't work. The rest are cosmetic fixes and adding
the tags from the diligent reviewers and testers, many thanks for that!
I dropped tags from the error IRQ patch (10/11) due to changes.
Find the changelog below. Based on v7.2-rc1.
=======================
The Arm MPAM specification defines Memory System Components (MSCs),
which are devices that are programmed through an MMIO register frame. In
some occasions this turned out to be too limiting: the MSC might be
located behind a separate bus system (for instance inside an on-board
controller), it might be mapped secure-only, or in a different processor
socket without direct MMIO mapping. Also the MMIO access might be too slow
or it would need to be filtered or otherwise access controlled. Finally
there might be bugs in the MSC integration, which require a mediating
firmware to be accessible.
To accommodate all those different use cases, the MPAM-Fb specification
[1] describes an alternative way to access MSCs. Accesses to an MSC
would be wrapped in a message and communicated to the system using a
shared-memory/mailbox system mostly mimicking the Arm SCMI spec.
For ACPI systems, this would be abstracted through an ACPI PCC channel,
which provides the shared-memory region and the mailbox trigger. We can
lean on existing ACPI parsing code to register with these two
subsystems, but cannot rely on the existing SCMI code in the kernel.
This means we somewhat need to open code a very simplified SCMI handler,
which just provides enough functionality for the very basic subset of
SCMI that the MPAM-Fb spec requires.
The first seven patches rework all MSC access wrappers to propagate error
information. Pure MMIO based MSC accesses would never fail, but the
MPAM-Fb access can go wrong in multiple ways. The patches have been split
up purely for reviewing reasons, if the number is a problem, we could as
well squash them. Please note that until the very last patch of this series
any MSC accesses would always only return 0, it's only the final enablement
of MPAM-Fb that could possibly introduce errors. Hence all former patches
can add error handling gradually, those code paths wouldn't be triggered
before patch 11/11.
Patch 8/11 solves a nasty problem: At the moment we protect stateful MSC
register accesses (mon_sel) through a spinlock. Unfortunately the mailbox
subsystem and the slow nature of the communication through this channel
forbid MPAM-Fb access in atomic context. So this patch keeps using a
spinlock for MMIO based accesses, but reverts to a mutex otherwise.
We just deny taking the lock for MPAM-Fb in atomic context, ideally we
wouldn't need that (no need to IPI another core when the MSC access does
not need to be local to one particular core), or we simply deny that part
of the functionality (access through perf).
Patch 9/11 adds the code to redirect MSC accesses through the
PCC shmem/mailbox system.
Patch 10/11 reworks the error interrupt handler to use a threaded IRQ for
MPAM-Fb, to be able to do MPAM-Fb MSC accesses inside (which might sleep).
The final patch 11/11 then adds the code to detect and store the PCC
channel information from the ACPI tables, and eventually enables
MPAM-Fb accesses.
This would enable systems where some MSCs are not accessible via MMIO to
use those components anyway.
Please have a look and test!
Cheers,
Andre
[1] https://developer.arm.com/documentation/den0144/latest
Changes in v8:
- disable error IRQ on irqchip level when device side fails
- use MSC dev pointer for dev_err() instead of mailbox one
- smaller white space and formatting fixes
- reorder MPAM-Fb definitions
- rename error translation function to mpam_fb_to_linux_errno()
- add review and test tags
Changes in v7:
- add tags
- add new patch to propagate errors in mpam_reprogram_ris_partid()
- prevent loop when mpam_diable() tries MSC accesses again
- register MMIO error IRQ handler without IRQF_ONESHOT
- re-use existing "err" variable instead of declaring "ret"
- initialise mon_sel_lock later, to wait for interface decision
- convert timeout units for nominal latency, between us and ms
Changes in v6:
- keep using hard IRQ handler for MMIO based accesses
- move shmem buffer size and MPAM-Fb version check into mpam_fb.c
- drop mutex guard in mpam_fb_send_request(), we use gotos anyway
- factor out MPAM-Fb protocol error translation
- add MPAM_FB_ERR_BUSY error code to translation
- explicitly report also MPAM-Fb protocol error number when disabling MPAM
- remove unneeded MPAM-Fb details from mpam_internal.h
- drop leftover mpam_fb_msc_id usage
Changes in v5:
- do not initialise _high variables early
- fix whitespace issues
- add Jonathan's Reviewed-by: tags
- init only either the spinlock OR the mutex for the mon_sel_lock
- drop warning about mapped_hwpage_sz for MPAM-Fb in mpam_msc_zero_mbwu_l()
- change IRQ handler to threaded handler, to allow MPAM-Fb MSC accesses
- disallow per-CPU IRQs (PPIs) for MPAM-Fb
- trigger MPAM disabling on MPAM-Fb protocol errors
- fix MPAM_PROTOCOL_VERSION_CMD name
- use PCC driver defined symbol for PCC_CHAN_FLAGS_IRQ
- add comment about usage of PCC's .signature field
- check for illegal MPAM-Fb command before sending request
- drop CPU accessibility check for MPAM-Fb based MSC accesses
- move MPAM-Fb protocol message structs inside build functions
- add comment about largest message size
- drop extra msc->mpam_fb_msc_id, in favour using pdev->id
- use already defined local dev variable instead of &pdev->dev
- use FIELD_GET() macro for MPAM-Fb protocol check
- drop devm_mutex_init() for pcc_chan_lock
Changes in v4:
- unify error check patterns
- use scoped_guard(mutex) and ACQUIRE to simplify error handling
- use struct kref for PCC channel refcount
- drop unused mailbox rx_callback function
- increase size of props[] array to hold new msc_id property
- set mailbox TX timeout based on ACPI MPAM table field
- use devm_mutex_init() to allow automatic cleanup
- avoid overwriting error value for arm,not-ready-us property read
- prune MPAM-Fb protocol token to fit into bitmask inside register
- check return value for AIDR read
- use two separate MPAM-Fb protocol payload structs
- prune header list for mpam_fb.c
- drop "nrdy handled as an error" change
- fix message size field in MPAM-Fb protocol shmem area
- drop message size return from MPAM-Fb message build functions
- bring back IRQ flag in MPAM-Fb protocol header
Changes in v3:
- drop inner/outer mon_sel lock patch, replace with simpler version
- harmonise code patterns in error propagation changes
- drop mon_sel lock before erroring out in mpam_ris_hw_probe_csu_nrdy()
- refactor mpam_msc_read_mbwu_l() to return an error
- drop special NRDY handling in __ris_msmon_read()
- add IRQ numbers in error interrupt handler to help pinpoint failure
- refactor MPAM-Fb message generation to accommodate more than read/write
- check MPAM-Fb protocol version at probe time
- drop mpam_fb.h header, merge into mpam_internal.h
- translate MPAM-Fb error code in Linux codes where needed
- clear IRQ flag bit in MPAM-Fb protocol header
- drop unneeded endianness conversions when crafting MPAM-Fb message
- use C struct to model MPAM-Fb message payload
Changes in v2:
- add patches to add error propagation to MSC access wrappers
- drop former patch 1/5 (not needed)
- drop lock in mpam_reprogram_msc(), to avoid double lock
- add support for multiple MSCs per PCC channel
- adjust SCMI protocol code to use a PCC subtype 3 channel
- let PCC code handle the PCC channel negotiation (due to subtype 3)
- drop SCMI names in shmem offsets, and use existing PCC type 3 struct
- adjust shmem field offsets to match MPAM-Fb spec, not pure SCMI
- prevent MPAM-Fb calls inside atomic smp_call_function_any() payload
- skip all MSC accesses inside the IRQ handler when using MPAM-Fb
Andre Przywara (11):
arm_mpam: let low level MSC accessors return an error
arm_mpam: propagate MSC access errors for hw_probe functions
arm_mpam: propagate MSC access errors for MBWU counters
arm_mpam: propagate MSC access errors for msmon helpers
arm_mpam: propagate MSC access errors for __ris_msmon_read()
arm_mpam: propagate MSC access errors for state saving function
arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid()
arm_mpam: prepare mon_sel locking for MPAM-Fb
arm_mpam: add MPAM-Fb MSC firmware access support
arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler
arm_mpam: detect and enable MPAM-Fb PCC support
drivers/resctrl/Makefile | 2 +-
drivers/resctrl/mpam_devices.c | 825 ++++++++++++++++++++++++--------
drivers/resctrl/mpam_fb.c | 252 ++++++++++
drivers/resctrl/mpam_internal.h | 62 ++-
include/linux/arm_mpam.h | 2 +-
5 files changed, 926 insertions(+), 217 deletions(-)
create mode 100644 drivers/resctrl/mpam_fb.c
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
--
2.43.0
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v8 01/11] arm_mpam: let low level MSC accessors return an error
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 02/11] arm_mpam: propagate MSC access errors for hw_probe functions Andre Przywara
` (10 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
The upcoming MPAM-Fb support does not use MMIO primitives to access an
MSC, but employs a shared-memory/doorbell based firmware protocol.
Its complexity means that it must be able to handle errors, whereas we
always assume an MMIO based MSC access succeeds today.
Change the __mpam_read_reg() low level accessor function to return the
requested data through a pointer, and return an error code instead.
Also change the __mpam_write_reg() accessor function to return an error
code. At the moment this is always 0, so all error handling paths are
not exercised at the moment. This will change later when alternative
MSC access methods like MPAM-Fb are activated.
Change all direct users of those MSC wrappers to comply with the new
prototypes, though the errors are not propagated all the way up yet.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 208 +++++++++++++++++++++------------
1 file changed, 134 insertions(+), 74 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index b69f99488111..04d504031752 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -177,59 +177,68 @@ static void mpam_assert_partid_sizes_fixed(void)
WARN_ON_ONCE(!partid_max_published);
}
-static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg)
+static int __mpam_read_reg(struct mpam_msc *msc, u16 reg, u32 *res)
{
WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
- return readl_relaxed(msc->mapped_hwpage + reg);
+ *res = readl_relaxed(msc->mapped_hwpage + reg);
+
+ return 0;
}
-static inline u32 _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg)
+static inline int _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg,
+ u32 *res)
{
lockdep_assert_held_once(&msc->part_sel_lock);
- return __mpam_read_reg(msc, reg);
+ return __mpam_read_reg(msc, reg, res);
}
-#define mpam_read_partsel_reg(msc, reg) _mpam_read_partsel_reg(msc, MPAMF_##reg)
+#define mpam_read_partsel_reg(msc, reg, res) _mpam_read_partsel_reg(msc, MPAMF_##reg, res)
-static void __mpam_write_reg(struct mpam_msc *msc, u16 reg, u32 val)
+static int __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);
+
+ return 0;
}
-static inline void _mpam_write_partsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
+static inline int _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);
+ return __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)
+static inline int _mpam_read_monsel_reg(struct mpam_msc *msc, u16 reg,
+ u32 *res)
{
mpam_mon_sel_lock_held(msc);
- return __mpam_read_reg(msc, reg);
+ return __mpam_read_reg(msc, reg, res);
}
-#define mpam_read_monsel_reg(msc, reg) _mpam_read_monsel_reg(msc, MSMON_##reg)
+#define mpam_read_monsel_reg(msc, reg, res) _mpam_read_monsel_reg(msc, MSMON_##reg, res)
-static inline void _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
+static inline int _mpam_write_monsel_reg(struct mpam_msc *msc, u16 reg, u32 val)
{
mpam_mon_sel_lock_held(msc);
- __mpam_write_reg(msc, reg, val);
+ return __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);
+ u32 aidr, major, minor;
+ if (__mpam_read_reg(msc, MPAMF_AIDR, &aidr))
+ return false;
+
+ major = FIELD_GET(MPAMF_AIDR_ARCH_MAJOR_REV, aidr);
+ 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.
@@ -242,25 +251,41 @@ static bool mpam_msc_check_aidr(struct mpam_msc *msc)
return true;
}
-static u64 mpam_msc_read_idr(struct mpam_msc *msc)
+static int mpam_msc_read_idr(struct mpam_msc *msc, u64 *res)
{
- u64 idr_high = 0, idr_low;
+ u32 idr_high, idr_low;
+ int ret;
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);
+ ret = mpam_read_partsel_reg(msc, IDR, &idr_low);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(MPAMF_IDR_EXT, idr_low)) {
+ ret = mpam_read_partsel_reg(msc, IDR + 4, &idr_high);
+ if (ret)
+ return ret;
+ } else {
+ idr_high = 0;
+ }
+
+ *res = ((u64)idr_high << 32) | idr_low;
- return (idr_high << 32) | idr_low;
+ return 0;
}
-static void mpam_msc_clear_esr(struct mpam_msc *msc)
+static int mpam_msc_clear_esr(struct mpam_msc *msc)
{
- u64 esr_low = __mpam_read_reg(msc, MPAMF_ESR);
+ u32 esr_low;
+ int ret;
+
+ ret = __mpam_read_reg(msc, MPAMF_ESR, &esr_low);
+ if (ret)
+ return ret;
if (!esr_low)
- return;
+ return 0;
/*
* Clearing the high/low bits of MPAMF_ESR can not be atomic.
@@ -268,44 +293,59 @@ static void mpam_msc_clear_esr(struct mpam_msc *msc)
* 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);
+ if (msc->has_extd_esr) {
+ ret = __mpam_write_reg(msc, MPAMF_ESR + 4, 0);
+ if (ret)
+ return ret;
+ }
+
+ return __mpam_write_reg(msc, MPAMF_ESR, 0);
}
-static u64 mpam_msc_read_esr(struct mpam_msc *msc)
+static int mpam_msc_read_esr(struct mpam_msc *msc, u64 *res)
{
- u64 esr_high = 0, esr_low;
+ u32 esr_high, esr_low;
+ int ret;
- esr_low = __mpam_read_reg(msc, MPAMF_ESR);
- if (msc->has_extd_esr)
- esr_high = __mpam_read_reg(msc, MPAMF_ESR + 4);
+ ret = __mpam_read_reg(msc, MPAMF_ESR, &esr_low);
+ if (ret)
+ return ret;
- return (esr_high << 32) | esr_low;
+ if (msc->has_extd_esr) {
+ ret = __mpam_read_reg(msc, MPAMF_ESR + 4, &esr_high);
+ if (ret)
+ return ret;
+ } else {
+ esr_high = 0;
+ }
+
+ *res = ((u64)esr_high << 32) | esr_low;
+
+ return 0;
}
-static void __mpam_part_sel_raw(u32 partsel, struct mpam_msc *msc)
+static int __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);
+ return mpam_write_partsel_reg(msc, PART_SEL, partsel);
}
-static void __mpam_part_sel(u8 ris_idx, u16 partid, struct mpam_msc *msc)
+static int __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);
+ return __mpam_part_sel_raw(partsel, msc);
}
-static void __mpam_intpart_sel(u8 ris_idx, u16 intpartid, struct mpam_msc *msc)
+static int __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);
+ return __mpam_part_sel_raw(partsel, msc);
}
int mpam_register_requestor(u16 partid_max, u8 pmg_max)
@@ -774,13 +814,13 @@ static bool mpam_ris_hw_probe_csu_nrdy(struct mpam_msc_ris *ris)
mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
_mpam_write_monsel_reg(msc, MSMON_CSU, MSMON___NRDY);
- now = _mpam_read_monsel_reg(msc, MSMON_CSU);
+ _mpam_read_monsel_reg(msc, MSMON_CSU, &now);
can_set = now & MSMON___NRDY;
_mpam_write_monsel_reg(msc, MSMON_CSU, 0);
/* Configuration change to try and coax hardware into setting nrdy */
mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0x1);
- now = _mpam_read_monsel_reg(msc, MSMON_CSU);
+ _mpam_read_monsel_reg(msc, MSMON_CSU, &now);
can_clear = !(now & MSMON___NRDY);
mpam_mon_sel_unlock(msc);
@@ -800,7 +840,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Cache Capacity Partitioning */
if (FIELD_GET(MPAMF_IDR_HAS_CCAP_PART, ris->idr)) {
- u32 ccap_features = mpam_read_partsel_reg(msc, CCAP_IDR);
+ u32 ccap_features;
+
+ mpam_read_partsel_reg(msc, CCAP_IDR, &ccap_features);
props->cmax_wd = FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features);
if (props->cmax_wd &&
@@ -823,7 +865,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Cache Portion partitioning */
if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) {
- u32 cpor_features = mpam_read_partsel_reg(msc, CPOR_IDR);
+ u32 cpor_features;
+
+ mpam_read_partsel_reg(msc, CPOR_IDR, &cpor_features);
props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features);
if (props->cpbm_wd)
@@ -832,7 +876,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Memory bandwidth partitioning */
if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) {
- u32 mbw_features = mpam_read_partsel_reg(msc, MBW_IDR);
+ u32 mbw_features;
+
+ mpam_read_partsel_reg(msc, MBW_IDR, &mbw_features);
/* portion bitmap resolution */
props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features);
@@ -860,7 +906,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Priority partitioning */
if (FIELD_GET(MPAMF_IDR_HAS_PRI_PART, ris->idr)) {
- u32 pri_features = mpam_read_partsel_reg(msc, PRI_IDR);
+ u32 pri_features;
+
+ mpam_read_partsel_reg(msc, PRI_IDR, &pri_features);
props->intpri_wd = FIELD_GET(MPAMF_PRI_IDR_INTPRI_WD, pri_features);
if (props->intpri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_INTPRI, pri_features)) {
@@ -879,7 +927,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Performance Monitoring */
if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) {
- u32 msmon_features = mpam_read_partsel_reg(msc, MSMON_IDR);
+ u32 msmon_features;
+
+ mpam_read_partsel_reg(msc, MSMON_IDR, &msmon_features);
/*
* If the firmware max-nrdy-us property is missing, the
@@ -892,7 +942,8 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) {
u32 csumonidr;
- csumonidr = mpam_read_partsel_reg(msc, CSUMON_IDR);
+ mpam_read_partsel_reg(msc, CSUMON_IDR, &csumonidr);
+
props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr);
if (props->num_csu_mon) {
bool hw_managed;
@@ -915,7 +966,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
}
if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_MBWU, msmon_features)) {
bool has_long;
- u32 mbwumon_idr = mpam_read_partsel_reg(msc, MBWUMON_IDR);
+ u32 mbwumon_idr;
+
+ mpam_read_partsel_reg(msc, MBWUMON_IDR, &mbwumon_idr);
props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr);
if (props->num_mbwu_mon) {
@@ -945,8 +998,11 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
*/
if (FIELD_GET(MPAMF_IDR_HAS_PARTID_NRW, ris->idr) &&
class->type != MPAM_CLASS_UNKNOWN) {
- u32 nrwidr = mpam_read_partsel_reg(msc, PARTID_NRW_IDR);
- u16 partid_max = FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr);
+ u16 partid_max;
+ u32 nrwidr;
+
+ mpam_read_partsel_reg(msc, PARTID_NRW_IDR, &nrwidr);
+ partid_max = FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr);
mpam_set_feature(mpam_feat_partid_nrw, props);
msc->partid_max = min(msc->partid_max, partid_max);
@@ -970,8 +1026,9 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
/* Grab an IDR value to find out how many RIS there are */
mutex_lock(&msc->part_sel_lock);
- idr = mpam_msc_read_idr(msc);
- msc->iidr = mpam_read_partsel_reg(msc, IIDR);
+ mpam_msc_read_idr(msc, &idr);
+ mpam_read_partsel_reg(msc, IIDR, &msc->iidr);
+
mutex_unlock(&msc->part_sel_lock);
mpam_enable_quirks(msc);
@@ -985,7 +1042,7 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) {
mutex_lock(&msc->part_sel_lock);
__mpam_part_sel(ris_idx, 0, msc);
- idr = mpam_msc_read_idr(msc);
+ mpam_msc_read_idr(msc, &idr);
mutex_unlock(&msc->part_sel_lock);
partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr);
@@ -1039,24 +1096,24 @@ static u64 mpam_msc_read_mbwu_l(struct mpam_msc *msc)
{
int retry = 3;
u32 mbwu_l_low;
- u64 mbwu_l_high1, mbwu_l_high2;
+ u32 mbwu_l_high1, mbwu_l_high2;
mpam_mon_sel_lock_held(msc);
WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
- mbwu_l_high2 = __mpam_read_reg(msc, MSMON_MBWU_L + 4);
+ __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
do {
mbwu_l_high1 = mbwu_l_high2;
- mbwu_l_low = __mpam_read_reg(msc, MSMON_MBWU_L);
- mbwu_l_high2 = __mpam_read_reg(msc, MSMON_MBWU_L + 4);
+ __mpam_read_reg(msc, MSMON_MBWU_L, &mbwu_l_low);
+ __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
retry--;
} while (mbwu_l_high1 != mbwu_l_high2 && retry > 0);
if (mbwu_l_high1 == mbwu_l_high2)
- return (mbwu_l_high1 << 32) | mbwu_l_low;
+ return ((u64)mbwu_l_high1 << 32) | mbwu_l_low;
pr_warn("Failed to read a stable value\n");
return MSMON___L_NRDY;
@@ -1120,14 +1177,14 @@ static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
switch (m->type) {
case mpam_feat_msmon_csu:
- *ctl_val = mpam_read_monsel_reg(msc, CFG_CSU_CTL);
- *flt_val = mpam_read_monsel_reg(msc, CFG_CSU_FLT);
+ mpam_read_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
+ mpam_read_monsel_reg(msc, CFG_CSU_FLT, flt_val);
break;
case mpam_feat_msmon_mbwu_31counter:
case mpam_feat_msmon_mbwu_44counter:
case mpam_feat_msmon_mbwu_63counter:
- *ctl_val = mpam_read_monsel_reg(msc, CFG_MBWU_CTL);
- *flt_val = mpam_read_monsel_reg(msc, CFG_MBWU_FLT);
+ mpam_read_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
+ mpam_read_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
break;
default:
pr_warn("Unexpected monitor type %d\n", m->type);
@@ -1206,6 +1263,7 @@ static u64 mpam_msmon_overflow_val(enum mpam_device_features type,
static void __ris_msmon_read(void *arg)
{
u64 now;
+ u32 now32;
bool nrdy = false;
bool config_mismatch;
bool overflow = false;
@@ -1268,9 +1326,9 @@ static void __ris_msmon_read(void *arg)
switch (m->type) {
case mpam_feat_msmon_csu:
- now = mpam_read_monsel_reg(msc, CSU);
- nrdy = now & MSMON___NRDY;
- now = FIELD_GET(MSMON___VALUE, now);
+ mpam_read_monsel_reg(msc, CSU, &now32);
+ nrdy = now32 & MSMON___NRDY;
+ now = FIELD_GET(MSMON___VALUE, now32);
if (mpam_has_quirk(IGNORE_CSU_NRDY, msc) && m->waited_timeout)
nrdy = false;
@@ -1288,9 +1346,9 @@ static void __ris_msmon_read(void *arg)
else
now = FIELD_GET(MSMON___L_VALUE, now);
} else {
- now = mpam_read_monsel_reg(msc, MBWU);
- nrdy = now & MSMON___NRDY;
- now = FIELD_GET(MSMON___VALUE, now);
+ mpam_read_monsel_reg(msc, MBWU, &now32);
+ nrdy = now32 & MSMON___NRDY;
+ now = FIELD_GET(MSMON___VALUE, now32);
}
if (mpam_has_quirk(T241_MBW_COUNTER_SCALE_64, msc) &&
@@ -1685,16 +1743,18 @@ static int mpam_save_mbwu_state(void *arg)
mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) |
FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
-
- cur_flt = mpam_read_monsel_reg(msc, CFG_MBWU_FLT);
- cur_ctl = mpam_read_monsel_reg(msc, CFG_MBWU_CTL);
+ mpam_read_monsel_reg(msc, CFG_MBWU_FLT, &cur_flt);
+ mpam_read_monsel_reg(msc, CFG_MBWU_CTL, &cur_ctl);
mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0);
if (mpam_ris_has_mbwu_long_counter(ris)) {
val = mpam_msc_read_mbwu_l(msc);
mpam_msc_zero_mbwu_l(msc);
} else {
- val = mpam_read_monsel_reg(msc, MBWU);
+ u32 val32;
+
+ mpam_read_monsel_reg(msc, MBWU, &val32);
+ val = val32;
mpam_write_monsel_reg(msc, MBWU, 0);
}
@@ -2465,7 +2525,7 @@ static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc)
&msc->accessibility)))
return IRQ_NONE;
- reg = mpam_msc_read_esr(msc);
+ mpam_msc_read_esr(msc, ®);
errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg);
if (!errcode)
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 02/11] arm_mpam: propagate MSC access errors for hw_probe functions
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
2026-08-04 10:06 ` [PATCH v8 01/11] arm_mpam: let low level MSC accessors return an error Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 03/11] arm_mpam: propagate MSC access errors for MBWU counters Andre Przywara
` (9 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Allow the functions probing for MSC hardware and features to return an
error, and propagate read and write errors from the lower level up.
This uses some "scoped cleanup" functions like scoped_guard() and
ACQUIRE() to avoid the complexity of error handling when a lock has been
taken. Since the mon_sel_lock is a bit special (even more so in an
upcoming patch), we define a new GUARD type for it.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 126 +++++++++++++++++++++-----------
drivers/resctrl/mpam_internal.h | 4 +
2 files changed, 87 insertions(+), 43 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 04d504031752..017e5bf067bc 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -798,38 +798,46 @@ static bool mpam_ris_hw_probe_csu_nrdy(struct mpam_msc_ris *ris)
bool can_set, can_clear;
struct mpam_msc *msc = ris->vmsc->msc;
- if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc)))
+ ACQUIRE(mon_sel_lock, guard)(msc);
+ if (ACQUIRE_ERR(mon_sel_lock, &guard))
return false;
mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, 0) |
FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
- mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
+ if (mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel))
+ return false;
/* Hardware might ignore nrdy if it's not enabled */
ctl_val = MSMON_CFG_CSU_CTL_TYPE_CSU;
ctl_val |= MSMON_CFG_x_CTL_MATCH_PARTID;
ctl_val |= MSMON_CFG_x_CTL_MATCH_PMG;
ctl_val |= MSMON_CFG_x_CTL_EN;
- mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0);
- mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
+ if (mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0))
+ return false;
+ if (mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val))
+ return false;
- _mpam_write_monsel_reg(msc, MSMON_CSU, MSMON___NRDY);
- _mpam_read_monsel_reg(msc, MSMON_CSU, &now);
+ if (_mpam_write_monsel_reg(msc, MSMON_CSU, MSMON___NRDY))
+ return false;
+ if (_mpam_read_monsel_reg(msc, MSMON_CSU, &now))
+ return false;
can_set = now & MSMON___NRDY;
- _mpam_write_monsel_reg(msc, MSMON_CSU, 0);
+ if (_mpam_write_monsel_reg(msc, MSMON_CSU, 0))
+ return false;
/* Configuration change to try and coax hardware into setting nrdy */
- mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0x1);
- _mpam_read_monsel_reg(msc, MSMON_CSU, &now);
+ if (mpam_write_monsel_reg(msc, CFG_CSU_FLT, 0x1))
+ return false;
+ if (_mpam_read_monsel_reg(msc, MSMON_CSU, &now))
+ return false;
can_clear = !(now & MSMON___NRDY);
- mpam_mon_sel_unlock(msc);
return (!can_set || !can_clear);
}
-static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
+static int mpam_ris_hw_probe(struct mpam_msc_ris *ris)
{
- int err;
+ int fw_has_nrdy_err, err;
struct mpam_msc *msc = ris->vmsc->msc;
struct device *dev = &msc->pdev->dev;
struct mpam_props *props = &ris->props;
@@ -842,7 +850,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
if (FIELD_GET(MPAMF_IDR_HAS_CCAP_PART, ris->idr)) {
u32 ccap_features;
- mpam_read_partsel_reg(msc, CCAP_IDR, &ccap_features);
+ err = mpam_read_partsel_reg(msc, CCAP_IDR, &ccap_features);
+ if (err)
+ return err;
props->cmax_wd = FIELD_GET(MPAMF_CCAP_IDR_CMAX_WD, ccap_features);
if (props->cmax_wd &&
@@ -867,7 +877,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
if (FIELD_GET(MPAMF_IDR_HAS_CPOR_PART, ris->idr)) {
u32 cpor_features;
- mpam_read_partsel_reg(msc, CPOR_IDR, &cpor_features);
+ err = mpam_read_partsel_reg(msc, CPOR_IDR, &cpor_features);
+ if (err)
+ return err;
props->cpbm_wd = FIELD_GET(MPAMF_CPOR_IDR_CPBM_WD, cpor_features);
if (props->cpbm_wd)
@@ -877,8 +889,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Memory bandwidth partitioning */
if (FIELD_GET(MPAMF_IDR_HAS_MBW_PART, ris->idr)) {
u32 mbw_features;
-
- mpam_read_partsel_reg(msc, MBW_IDR, &mbw_features);
+ err = mpam_read_partsel_reg(msc, MBW_IDR, &mbw_features);
+ if (err)
+ return err;
/* portion bitmap resolution */
props->mbw_pbm_bits = FIELD_GET(MPAMF_MBW_IDR_BWPBM_WD, mbw_features);
@@ -907,8 +920,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
/* Priority partitioning */
if (FIELD_GET(MPAMF_IDR_HAS_PRI_PART, ris->idr)) {
u32 pri_features;
-
- mpam_read_partsel_reg(msc, PRI_IDR, &pri_features);
+ err = mpam_read_partsel_reg(msc, PRI_IDR, &pri_features);
+ if (err)
+ return err;
props->intpri_wd = FIELD_GET(MPAMF_PRI_IDR_INTPRI_WD, pri_features);
if (props->intpri_wd && FIELD_GET(MPAMF_PRI_IDR_HAS_INTPRI, pri_features)) {
@@ -929,20 +943,24 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
if (FIELD_GET(MPAMF_IDR_HAS_MSMON, ris->idr)) {
u32 msmon_features;
- mpam_read_partsel_reg(msc, MSMON_IDR, &msmon_features);
+ err = mpam_read_partsel_reg(msc, MSMON_IDR, &msmon_features);
+ if (err)
+ return err;
/*
* If the firmware max-nrdy-us property is missing, the
* CSU counters can't be used. Should we wait forever?
*/
- err = device_property_read_u32(&msc->pdev->dev,
- "arm,not-ready-us",
- &msc->nrdy_usec);
+ fw_has_nrdy_err = device_property_read_u32(&msc->pdev->dev,
+ "arm,not-ready-us",
+ &msc->nrdy_usec);
if (FIELD_GET(MPAMF_MSMON_IDR_MSMON_CSU, msmon_features)) {
u32 csumonidr;
- mpam_read_partsel_reg(msc, CSUMON_IDR, &csumonidr);
+ err = mpam_read_partsel_reg(msc, CSUMON_IDR, &csumonidr);
+ if (err)
+ return err;
props->num_csu_mon = FIELD_GET(MPAMF_CSUMON_IDR_NUM_MON, csumonidr);
if (props->num_csu_mon) {
@@ -960,7 +978,7 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
* Accept the missing firmware property if NRDY appears
* un-implemented.
*/
- if (err && hw_managed)
+ if (fw_has_nrdy_err && hw_managed)
dev_err_once(dev, "Counters are not usable because not-ready timeout was not provided by firmware.");
}
}
@@ -968,7 +986,9 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
bool has_long;
u32 mbwumon_idr;
- mpam_read_partsel_reg(msc, MBWUMON_IDR, &mbwumon_idr);
+ err = mpam_read_partsel_reg(msc, MBWUMON_IDR, &mbwumon_idr);
+ if (err)
+ return err;
props->num_mbwu_mon = FIELD_GET(MPAMF_MBWUMON_IDR_NUM_MON, mbwumon_idr);
if (props->num_mbwu_mon) {
@@ -1001,16 +1021,22 @@ static void mpam_ris_hw_probe(struct mpam_msc_ris *ris)
u16 partid_max;
u32 nrwidr;
- mpam_read_partsel_reg(msc, PARTID_NRW_IDR, &nrwidr);
+ err = mpam_read_partsel_reg(msc, PARTID_NRW_IDR, &nrwidr);
+ if (err)
+ return err;
+
partid_max = FIELD_GET(MPAMF_PARTID_NRW_IDR_INTPARTID_MAX, nrwidr);
mpam_set_feature(mpam_feat_partid_nrw, props);
msc->partid_max = min(msc->partid_max, partid_max);
}
+
+ return 0;
}
static int mpam_msc_hw_probe(struct mpam_msc *msc)
{
+ int ret;
u64 idr;
u16 partid_max;
u8 ris_idx, pmg_max;
@@ -1025,11 +1051,15 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
}
/* Grab an IDR value to find out how many RIS there are */
- mutex_lock(&msc->part_sel_lock);
- mpam_msc_read_idr(msc, &idr);
- mpam_read_partsel_reg(msc, IIDR, &msc->iidr);
+ scoped_guard(mutex, &msc->part_sel_lock) {
+ ret = mpam_msc_read_idr(msc, &idr);
+ if (ret)
+ return ret;
- mutex_unlock(&msc->part_sel_lock);
+ ret = mpam_read_partsel_reg(msc, IIDR, &msc->iidr);
+ if (ret)
+ return ret;
+ }
mpam_enable_quirks(msc);
@@ -1040,10 +1070,15 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
msc->pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr);
for (ris_idx = 0; ris_idx <= msc->ris_max; ris_idx++) {
- mutex_lock(&msc->part_sel_lock);
- __mpam_part_sel(ris_idx, 0, msc);
- mpam_msc_read_idr(msc, &idr);
- mutex_unlock(&msc->part_sel_lock);
+ scoped_guard(mutex, &msc->part_sel_lock) {
+ ret = __mpam_part_sel(ris_idx, 0, msc);
+ if (ret)
+ return ret;
+
+ ret = mpam_msc_read_idr(msc, &idr);
+ if (ret)
+ return ret;
+ }
partid_max = FIELD_GET(MPAMF_IDR_PARTID_MAX, idr);
pmg_max = FIELD_GET(MPAMF_IDR_PMG_MAX, idr);
@@ -1051,17 +1086,22 @@ static int mpam_msc_hw_probe(struct mpam_msc *msc)
msc->pmg_max = min(msc->pmg_max, pmg_max);
msc->has_extd_esr = FIELD_GET(MPAMF_IDR_HAS_EXTD_ESR, idr);
- mutex_lock(&mpam_list_lock);
- ris = mpam_get_or_create_ris(msc, ris_idx);
- mutex_unlock(&mpam_list_lock);
- if (IS_ERR(ris))
- return PTR_ERR(ris);
+ scoped_guard(mutex, &mpam_list_lock) {
+ ris = mpam_get_or_create_ris(msc, ris_idx);
+ if (IS_ERR(ris))
+ return PTR_ERR(ris);
+ }
ris->idr = idr;
- mutex_lock(&msc->part_sel_lock);
- __mpam_part_sel(ris_idx, 0, msc);
- mpam_ris_hw_probe(ris);
- mutex_unlock(&msc->part_sel_lock);
+ scoped_guard(mutex, &msc->part_sel_lock) {
+ ret = __mpam_part_sel(ris_idx, 0, msc);
+ if (ret)
+ return ret;
+
+ ret = mpam_ris_hw_probe(ris);
+ if (ret)
+ return ret;
+ }
}
/* Clear any stale errors */
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 04d1a59f02af..57f2b3dbf7d0 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -162,6 +162,10 @@ static inline void mpam_mon_sel_lock_init(struct mpam_msc *msc)
raw_spin_lock_init(&msc->_mon_sel_lock);
}
+DEFINE_GUARD(mon_sel, struct mpam_msc *,
+ mpam_mon_sel_lock(_T), mpam_mon_sel_unlock(_T));
+DEFINE_GUARD_COND(mon_sel, _lock, mpam_mon_sel_lock(_T), _RET);
+
/* Bits for mpam features bitmaps */
enum mpam_device_features {
mpam_feat_cpor_part,
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 03/11] arm_mpam: propagate MSC access errors for MBWU counters
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
2026-08-04 10:06 ` [PATCH v8 01/11] arm_mpam: let low level MSC accessors return an error Andre Przywara
2026-08-04 10:06 ` [PATCH v8 02/11] arm_mpam: propagate MSC access errors for hw_probe functions Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 04/11] arm_mpam: propagate MSC access errors for msmon helpers Andre Przywara
` (8 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Allow the mpam_msc_read_mbwu_l() function to return an error, and
propagate errors from the lower level up. This also changes the users
of this function.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 45 +++++++++++++++++++++++-----------
1 file changed, 31 insertions(+), 14 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 017e5bf067bc..32ad1864f70f 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1132,8 +1132,9 @@ static bool mpam_ris_has_mbwu_long_counter(struct mpam_msc_ris *ris)
mpam_has_feature(mpam_feat_msmon_mbwu_44counter, &ris->props));
}
-static u64 mpam_msc_read_mbwu_l(struct mpam_msc *msc)
+static int mpam_msc_read_mbwu_l(struct mpam_msc *msc, u64 *res)
{
+ int ret;
int retry = 3;
u32 mbwu_l_low;
u32 mbwu_l_high1, mbwu_l_high2;
@@ -1143,20 +1144,30 @@ static u64 mpam_msc_read_mbwu_l(struct mpam_msc *msc)
WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
- __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
+ ret = __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
+ if (ret)
+ return ret;
+
do {
mbwu_l_high1 = mbwu_l_high2;
- __mpam_read_reg(msc, MSMON_MBWU_L, &mbwu_l_low);
- __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
+ ret = __mpam_read_reg(msc, MSMON_MBWU_L, &mbwu_l_low);
+ if (ret)
+ return ret;
+ ret = __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
+ if (ret)
+ return ret;
retry--;
} while (mbwu_l_high1 != mbwu_l_high2 && retry > 0);
- if (mbwu_l_high1 == mbwu_l_high2)
- return ((u64)mbwu_l_high1 << 32) | mbwu_l_low;
+ if (mbwu_l_high1 == mbwu_l_high2) {
+ *res = ((u64)mbwu_l_high1 << 32) | mbwu_l_low;
+ } else {
+ pr_warn("Failed to read a stable value\n");
+ *res = MSMON___L_NRDY;
+ }
- pr_warn("Failed to read a stable value\n");
- return MSMON___L_NRDY;
+ return 0;
}
static void mpam_msc_zero_mbwu_l(struct mpam_msc *msc)
@@ -1315,10 +1326,12 @@ static void __ris_msmon_read(void *arg)
struct mpam_msc *msc = m->ris->vmsc->msc;
u32 mon_sel, ctl_val, flt_val, cur_ctl, cur_flt;
- if (!mpam_mon_sel_lock(msc)) {
+ ACQUIRE(mon_sel_lock, guard)(msc);
+ if (ACQUIRE_ERR(mon_sel_lock, &guard)) {
m->err = -EIO;
return;
}
+
mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) |
FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
@@ -1378,7 +1391,9 @@ static void __ris_msmon_read(void *arg)
case mpam_feat_msmon_mbwu_44counter:
case mpam_feat_msmon_mbwu_63counter:
if (m->type != mpam_feat_msmon_mbwu_31counter) {
- now = mpam_msc_read_mbwu_l(msc);
+ m->err = mpam_msc_read_mbwu_l(msc, &now);
+ if (m->err)
+ return;
nrdy = now & MSMON___L_NRDY;
if (m->type == mpam_feat_msmon_mbwu_63counter)
@@ -1412,7 +1427,6 @@ static void __ris_msmon_read(void *arg)
default:
m->err = -EINVAL;
}
- mpam_mon_sel_unlock(msc);
if (nrdy)
m->err = -EBUSY;
@@ -1767,6 +1781,7 @@ static int mpam_save_mbwu_state(void *arg)
{
int i;
u64 val;
+ int ret;
struct mon_cfg *cfg;
u32 cur_flt, cur_ctl, mon_sel;
struct mpam_msc_ris *ris = arg;
@@ -1777,7 +1792,8 @@ static int mpam_save_mbwu_state(void *arg)
mbwu_state = &ris->mbwu_state[i];
cfg = &mbwu_state->cfg;
- if (WARN_ON_ONCE(!mpam_mon_sel_lock(msc)))
+ ACQUIRE(mon_sel_lock, guard)(msc);
+ if (ACQUIRE_ERR(mon_sel_lock, &guard))
return -EIO;
mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) |
@@ -1788,7 +1804,9 @@ static int mpam_save_mbwu_state(void *arg)
mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0);
if (mpam_ris_has_mbwu_long_counter(ris)) {
- val = mpam_msc_read_mbwu_l(msc);
+ ret = mpam_msc_read_mbwu_l(msc, &val);
+ if (ret)
+ return ret;
mpam_msc_zero_mbwu_l(msc);
} else {
u32 val32;
@@ -1804,7 +1822,6 @@ static int mpam_save_mbwu_state(void *arg)
cfg->partid = FIELD_GET(MSMON_CFG_x_FLT_PARTID, cur_flt);
mbwu_state->correction += val;
mbwu_state->enabled = FIELD_GET(MSMON_CFG_x_CTL_EN, cur_ctl);
- mpam_mon_sel_unlock(msc);
}
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 04/11] arm_mpam: propagate MSC access errors for msmon helpers
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (2 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 03/11] arm_mpam: propagate MSC access errors for MBWU counters Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 05/11] arm_mpam: propagate MSC access errors for __ris_msmon_read() Andre Przywara
` (7 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Allow the helper functions for msmon accesses to return an error, and
propagate read and write errors from the lower level up.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 72 ++++++++++++++++++++++------------
1 file changed, 47 insertions(+), 25 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 32ad1864f70f..9d9f0c635527 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1170,15 +1170,20 @@ static int mpam_msc_read_mbwu_l(struct mpam_msc *msc, u64 *res)
return 0;
}
-static void mpam_msc_zero_mbwu_l(struct mpam_msc *msc)
+static int mpam_msc_zero_mbwu_l(struct mpam_msc *msc)
{
+ int ret;
+
mpam_mon_sel_lock_held(msc);
WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
- __mpam_write_reg(msc, MSMON_MBWU_L, 0);
- __mpam_write_reg(msc, MSMON_MBWU_L + 4, 0);
+ ret = __mpam_write_reg(msc, MSMON_MBWU_L, 0);
+ if (ret)
+ return ret;
+
+ return __mpam_write_reg(msc, MSMON_MBWU_L + 4, 0);
}
static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
@@ -1221,24 +1226,28 @@ static void gen_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
}
}
-static void read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
- u32 *flt_val)
+static int read_msmon_ctl_flt_vals(struct mon_read *m, u32 *ctl_val,
+ u32 *flt_val)
{
struct mpam_msc *msc = m->ris->vmsc->msc;
+ int ret;
switch (m->type) {
case mpam_feat_msmon_csu:
- mpam_read_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
- mpam_read_monsel_reg(msc, CFG_CSU_FLT, flt_val);
- break;
+ ret = mpam_read_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
+ if (ret)
+ return ret;
+ return mpam_read_monsel_reg(msc, CFG_CSU_FLT, flt_val);
case mpam_feat_msmon_mbwu_31counter:
case mpam_feat_msmon_mbwu_44counter:
case mpam_feat_msmon_mbwu_63counter:
- mpam_read_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
- mpam_read_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
- break;
+ ret = mpam_read_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
+ if (ret)
+ return ret;
+ return mpam_read_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
default:
pr_warn("Unexpected monitor type %d\n", m->type);
+ return -EINVAL;
}
}
@@ -1251,10 +1260,11 @@ static inline void clean_msmon_ctl_val(u32 *cur_ctl)
*cur_ctl &= ~MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L;
}
-static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
- u32 flt_val)
+static int write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
+ u32 flt_val)
{
struct mpam_msc *msc = m->ris->vmsc->msc;
+ int ret;
/*
* Write the ctl_val with the enable bit cleared, reset the counter,
@@ -1262,25 +1272,37 @@ static void write_msmon_ctl_flt_vals(struct mon_read *m, u32 ctl_val,
*/
switch (m->type) {
case mpam_feat_msmon_csu:
- mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
- mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
- mpam_write_monsel_reg(msc, CSU, 0);
- mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
- break;
+ ret = mpam_write_monsel_reg(msc, CFG_CSU_FLT, flt_val);
+ if (ret)
+ return ret;
+ ret = mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val);
+ if (ret)
+ return ret;
+ ret = mpam_write_monsel_reg(msc, CSU, 0);
+ if (ret)
+ return ret;
+ return mpam_write_monsel_reg(msc, CFG_CSU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
case mpam_feat_msmon_mbwu_31counter:
case mpam_feat_msmon_mbwu_44counter:
case mpam_feat_msmon_mbwu_63counter:
- mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
- mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
- mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val | MSMON_CFG_x_CTL_EN);
+ ret = mpam_write_monsel_reg(msc, CFG_MBWU_FLT, flt_val);
+ if (ret)
+ return ret;
+ ret = mpam_write_monsel_reg(msc, CFG_MBWU_CTL, ctl_val);
+ if (ret)
+ return ret;
+ ret = mpam_write_monsel_reg(msc, CFG_MBWU_CTL,
+ ctl_val | MSMON_CFG_x_CTL_EN);
+ if (ret)
+ return ret;
/* Counting monitors require NRDY to be reset by software */
if (m->type == mpam_feat_msmon_mbwu_31counter)
- mpam_write_monsel_reg(msc, MBWU, 0);
- else
- mpam_msc_zero_mbwu_l(m->ris->vmsc->msc);
- break;
+ return mpam_write_monsel_reg(msc, MBWU, 0);
+
+ return mpam_msc_zero_mbwu_l(m->ris->vmsc->msc);
default:
pr_warn("Unexpected monitor type %d\n", m->type);
+ return -EINVAL;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 05/11] arm_mpam: propagate MSC access errors for __ris_msmon_read()
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (3 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 04/11] arm_mpam: propagate MSC access errors for msmon helpers Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 06/11] arm_mpam: propagate MSC access errors for state saving function Andre Przywara
` (6 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Allow the function for RIS accesses to return an error, and propagate
read and write errors from the lower level up.
This function is a bit special, since it can be called via an SMP
helper, so the prototype must stay as void. So propagate any errors
using the "err" field in the struct passed in as the sole argument to
that function.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 36 +++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 9d9f0c635527..fade30f0bcee 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1333,6 +1333,10 @@ static u64 mpam_msmon_overflow_val(enum mpam_device_features type,
return overflow_val;
}
+/*
+ * This function might be called via smp_call_function_any(), so propagate
+ * errors inside the arg struct.
+ */
static void __ris_msmon_read(void *arg)
{
u64 now;
@@ -1356,7 +1360,9 @@ static void __ris_msmon_read(void *arg)
mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, ctx->mon) |
FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
- mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
+ m->err = mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
+ if (m->err)
+ return;
switch (m->type) {
case mpam_feat_msmon_mbwu_31counter:
@@ -1376,7 +1382,9 @@ static void __ris_msmon_read(void *arg)
* Read the existing configuration to avoid re-writing the same values.
* This saves waiting for 'nrdy' on subsequent reads.
*/
- read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt);
+ m->err = read_msmon_ctl_flt_vals(m, &cur_ctl, &cur_flt);
+ if (m->err)
+ return;
if (mpam_feat_msmon_mbwu_31counter == m->type)
overflow = cur_ctl & MSMON_CFG_x_CTL_OFLOW_STATUS;
@@ -1390,18 +1398,24 @@ static void __ris_msmon_read(void *arg)
cur_ctl != (ctl_val | MSMON_CFG_x_CTL_EN);
if (config_mismatch || reset_on_next_read) {
- write_msmon_ctl_flt_vals(m, ctl_val, flt_val);
+ m->err = write_msmon_ctl_flt_vals(m, ctl_val, flt_val);
+ if (m->err)
+ return;
overflow = false;
} else if (overflow) {
- mpam_write_monsel_reg(msc, CFG_MBWU_CTL,
- cur_ctl &
- ~(MSMON_CFG_x_CTL_OFLOW_STATUS |
- MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L));
+ m->err = mpam_write_monsel_reg(msc, CFG_MBWU_CTL,
+ cur_ctl &
+ ~(MSMON_CFG_x_CTL_OFLOW_STATUS |
+ MSMON_CFG_MBWU_CTL_OFLOW_STATUS_L));
+ if (m->err)
+ return;
}
switch (m->type) {
case mpam_feat_msmon_csu:
- mpam_read_monsel_reg(msc, CSU, &now32);
+ m->err = mpam_read_monsel_reg(msc, CSU, &now32);
+ if (m->err)
+ return;
nrdy = now32 & MSMON___NRDY;
now = FIELD_GET(MSMON___VALUE, now32);
@@ -1423,7 +1437,9 @@ static void __ris_msmon_read(void *arg)
else
now = FIELD_GET(MSMON___L_VALUE, now);
} else {
- mpam_read_monsel_reg(msc, MBWU, &now32);
+ m->err = mpam_read_monsel_reg(msc, MBWU, &now32);
+ if (m->err)
+ return;
nrdy = now32 & MSMON___NRDY;
now = FIELD_GET(MSMON___VALUE, now32);
}
@@ -1792,6 +1808,8 @@ static int mpam_restore_mbwu_state(void *_ris)
mwbu_arg.val = &val;
__ris_msmon_read(&mwbu_arg);
+ if (mwbu_arg.err && mwbu_arg.err != -EBUSY)
+ return mwbu_arg.err;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 06/11] arm_mpam: propagate MSC access errors for state saving function
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (4 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 05/11] arm_mpam: propagate MSC access errors for __ris_msmon_read() Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 07/11] arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid() Andre Przywara
` (5 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Allow the mpam_save_mbwu_state() function to return an error, and
propagate read and write errors from the lower level up.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index fade30f0bcee..96579178f0c3 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1838,22 +1838,37 @@ static int mpam_save_mbwu_state(void *arg)
mon_sel = FIELD_PREP(MSMON_CFG_MON_SEL_MON_SEL, i) |
FIELD_PREP(MSMON_CFG_MON_SEL_RIS, ris->ris_idx);
- mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
- mpam_read_monsel_reg(msc, CFG_MBWU_FLT, &cur_flt);
- mpam_read_monsel_reg(msc, CFG_MBWU_CTL, &cur_ctl);
- mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0);
+ ret = mpam_write_monsel_reg(msc, CFG_MON_SEL, mon_sel);
+ if (ret)
+ return ret;
+ ret = mpam_read_monsel_reg(msc, CFG_MBWU_FLT, &cur_flt);
+ if (ret)
+ return ret;
+ ret = mpam_read_monsel_reg(msc, CFG_MBWU_CTL, &cur_ctl);
+ if (ret)
+ return ret;
+ ret = mpam_write_monsel_reg(msc, CFG_MBWU_CTL, 0);
+ if (ret)
+ return ret;
if (mpam_ris_has_mbwu_long_counter(ris)) {
ret = mpam_msc_read_mbwu_l(msc, &val);
if (ret)
return ret;
- mpam_msc_zero_mbwu_l(msc);
+ ret = mpam_msc_zero_mbwu_l(msc);
+ if (ret)
+ return ret;
} else {
u32 val32;
- mpam_read_monsel_reg(msc, MBWU, &val32);
+ ret = mpam_read_monsel_reg(msc, MBWU, &val32);
+ if (ret)
+ return ret;
val = val32;
- mpam_write_monsel_reg(msc, MBWU, 0);
+
+ ret = mpam_write_monsel_reg(msc, MBWU, 0);
+ if (ret)
+ return ret;
}
cfg->mon = i;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 07/11] arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid()
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (5 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 06/11] arm_mpam: propagate MSC access errors for state saving function Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 08/11] arm_mpam: prepare mon_sel locking for MPAM-Fb Andre Przywara
` (4 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Allow the mpam_reprogram_ris_partid() function to check for and return
errors, and propagate MSC read and write errors from the lower level up.
This also covers the callers of this function: mpam_reset_ris() and
apply_config().
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Review-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 137 ++++++++++++++++++++++-----------
1 file changed, 94 insertions(+), 43 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 96579178f0c3..49239b15a27f 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -1601,7 +1601,7 @@ void mpam_msmon_reset_mbwu(struct mpam_component *comp, struct mon_cfg *ctx)
}
}
-static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
+static int mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
{
u32 num_words, msb;
u32 bm = ~0;
@@ -1610,15 +1610,20 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
lockdep_assert_held(&msc->part_sel_lock);
if (wd == 0)
- return;
+ return 0;
/*
* Write all ~0 to all but the last 32bit-word, which may
* have fewer bits...
*/
num_words = DIV_ROUND_UP(wd, 32);
- for (i = 0; i < num_words - 1; i++, reg += sizeof(bm))
- __mpam_write_reg(msc, reg, bm);
+ for (i = 0; i < num_words - 1; i++, reg += sizeof(bm)) {
+ int ret;
+
+ ret = __mpam_write_reg(msc, reg, bm);
+ if (ret)
+ return ret;
+ }
/*
* ....and then the last (maybe) partial 32bit word. When wd is a
@@ -1626,7 +1631,7 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
*/
msb = (wd - 1) % 32;
bm = GENMASK(msb, 0);
- __mpam_write_reg(msc, reg, bm);
+ return __mpam_write_reg(msc, reg, bm);
}
static void mpam_apply_t241_erratum(struct mpam_msc_ris *ris, u16 partid)
@@ -1699,8 +1704,8 @@ static u16 mpam_wa_t241_calc_min_from_max(struct mpam_props *props,
}
/* Called via IPI. Call while holding an SRCU reference */
-static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
- struct mpam_config *cfg)
+static int mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
+ struct mpam_config *cfg)
{
u32 pri_val = 0;
u16 cmax = MPAMCFG_CMAX_CMAX;
@@ -1708,34 +1713,53 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
struct mpam_props *rprops = &ris->props;
u16 dspri = GENMASK(rprops->dspri_wd, 0);
u16 intpri = GENMASK(rprops->intpri_wd, 0);
+ int ret;
- mutex_lock(&msc->part_sel_lock);
- __mpam_part_sel(ris->ris_idx, partid, msc);
+ guard(mutex)(&msc->part_sel_lock);
+ ret = __mpam_part_sel(ris->ris_idx, partid, msc);
+ if (ret)
+ return ret;
if (mpam_has_feature(mpam_feat_partid_nrw, rprops)) {
/* Update the intpartid mapping */
- mpam_write_partsel_reg(msc, INTPARTID,
- MPAMCFG_INTPARTID_INTERNAL | partid);
+ ret = mpam_write_partsel_reg(msc, INTPARTID,
+ MPAMCFG_INTPARTID_INTERNAL | partid);
+ if (ret)
+ return ret;
/*
* Then switch to the 'internal' partid to update the
* configuration.
*/
- __mpam_intpart_sel(ris->ris_idx, partid, msc);
+ ret = __mpam_intpart_sel(ris->ris_idx, partid, msc);
+ if (ret)
+ return ret;
}
if (mpam_has_feature(mpam_feat_cpor_part, rprops)) {
- if (mpam_has_feature(mpam_feat_cpor_part, cfg))
- mpam_write_partsel_reg(msc, CPBM, cfg->cpbm);
- else
- mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM, rprops->cpbm_wd);
+ if (mpam_has_feature(mpam_feat_cpor_part, cfg)) {
+ ret = mpam_write_partsel_reg(msc, CPBM, cfg->cpbm);
+ if (ret)
+ return ret;
+ } else {
+ ret = mpam_reset_msc_bitmap(msc, MPAMCFG_CPBM,
+ rprops->cpbm_wd);
+ if (ret)
+ return ret;
+ }
}
if (mpam_has_feature(mpam_feat_mbw_part, rprops)) {
- if (mpam_has_feature(mpam_feat_mbw_part, cfg))
- mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
- else
- mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm);
+ if (mpam_has_feature(mpam_feat_mbw_part, cfg)) {
+ ret = mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM,
+ rprops->mbw_pbm_bits);
+ if (ret)
+ return ret;
+ } else {
+ ret = mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm);
+ if (ret)
+ return ret;
+ }
}
if (mpam_has_feature(mpam_feat_mbw_min, rprops)) {
@@ -1748,27 +1772,47 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
val = max(val, min);
}
- mpam_write_partsel_reg(msc, MBW_MIN, val);
+ ret = mpam_write_partsel_reg(msc, MBW_MIN, val);
+ if (ret)
+ return ret;
}
if (mpam_has_feature(mpam_feat_mbw_max, rprops)) {
- if (mpam_has_feature(mpam_feat_mbw_max, cfg))
- mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max);
- else
- mpam_write_partsel_reg(msc, MBW_MAX, MPAMCFG_MBW_MAX_MAX);
+ if (mpam_has_feature(mpam_feat_mbw_max, cfg)) {
+ ret = mpam_write_partsel_reg(msc, MBW_MAX, cfg->mbw_max);
+ if (ret)
+ return ret;
+ } else {
+ ret = mpam_write_partsel_reg(msc, MBW_MAX,
+ MPAMCFG_MBW_MAX_MAX);
+ if (ret)
+ return ret;
+ }
}
- if (mpam_has_feature(mpam_feat_mbw_prop, rprops))
- mpam_write_partsel_reg(msc, MBW_PROP, 0);
+ if (mpam_has_feature(mpam_feat_mbw_prop, rprops)) {
+ ret = mpam_write_partsel_reg(msc, MBW_PROP, 0);
+ if (ret)
+ return ret;
+ }
- if (mpam_has_feature(mpam_feat_cmax_cmax, rprops))
- mpam_write_partsel_reg(msc, CMAX, cmax);
+ if (mpam_has_feature(mpam_feat_cmax_cmax, rprops)) {
+ ret = mpam_write_partsel_reg(msc, CMAX, cmax);
+ if (ret)
+ return ret;
+ }
- if (mpam_has_feature(mpam_feat_cmax_cmin, rprops))
- mpam_write_partsel_reg(msc, CMIN, 0);
+ if (mpam_has_feature(mpam_feat_cmax_cmin, rprops)) {
+ ret = mpam_write_partsel_reg(msc, CMIN, 0);
+ if (ret)
+ return ret;
+ }
- if (mpam_has_feature(mpam_feat_cmax_cassoc, rprops))
- mpam_write_partsel_reg(msc, CASSOC, MPAMCFG_CASSOC_CASSOC);
+ if (mpam_has_feature(mpam_feat_cmax_cassoc, rprops)) {
+ ret = mpam_write_partsel_reg(msc, CASSOC, MPAMCFG_CASSOC_CASSOC);
+ if (ret)
+ return ret;
+ }
if (mpam_has_feature(mpam_feat_intpri_part, rprops) ||
mpam_has_feature(mpam_feat_dspri_part, rprops)) {
@@ -1783,12 +1827,14 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
if (mpam_has_feature(mpam_feat_dspri_part, rprops))
pri_val |= FIELD_PREP(MPAMCFG_PRI_DSPRI, dspri);
- mpam_write_partsel_reg(msc, PRI, pri_val);
+ ret = mpam_write_partsel_reg(msc, PRI, pri_val);
+ if (ret)
+ return ret;
}
mpam_quirk_post_config_change(ris, partid, cfg);
- mutex_unlock(&msc->part_sel_lock);
+ return 0;
}
/* Call with msc cfg_lock held */
@@ -1891,6 +1937,7 @@ static int mpam_reset_ris(void *arg)
u16 partid, partid_max;
struct mpam_config reset_cfg = {};
struct mpam_msc_ris *ris = arg;
+ int ret;
if (ris->in_reset_state)
return 0;
@@ -1898,8 +1945,11 @@ static int mpam_reset_ris(void *arg)
spin_lock(&partid_max_lock);
partid_max = mpam_partid_max;
spin_unlock(&partid_max_lock);
- for (partid = 0; partid <= partid_max; partid++)
- mpam_reprogram_ris_partid(ris, partid, &reset_cfg);
+ for (partid = 0; partid <= partid_max; partid++) {
+ ret = mpam_reprogram_ris_partid(ris, partid, &reset_cfg);
+ if (ret)
+ return ret;
+ }
return 0;
}
@@ -1938,9 +1988,8 @@ static int __write_config(void *arg)
{
struct mpam_write_config_arg *c = arg;
- mpam_reprogram_ris_partid(c->ris, c->partid, &c->comp->cfg[c->partid]);
-
- return 0;
+ return mpam_reprogram_ris_partid(c->ris, c->partid,
+ &c->comp->cfg[c->partid]);
}
static void mpam_reprogram_msc(struct mpam_msc *msc)
@@ -3088,6 +3137,7 @@ int mpam_apply_config(struct mpam_component *comp, u16 partid,
struct mpam_msc_ris *ris;
struct mpam_vmsc *vmsc;
struct mpam_msc *msc;
+ int ret;
lockdep_assert_cpus_held();
@@ -3105,14 +3155,15 @@ int mpam_apply_config(struct mpam_component *comp, u16 partid,
srcu_read_lock_held(&mpam_srcu)) {
msc = vmsc->msc;
- mutex_lock(&msc->cfg_lock);
+ guard(mutex)(&msc->cfg_lock);
list_for_each_entry_srcu(ris, &vmsc->ris, vmsc_list,
srcu_read_lock_held(&mpam_srcu)) {
arg.ris = ris;
- mpam_touch_msc(msc, __write_config, &arg);
+ ret = mpam_touch_msc(msc, __write_config, &arg);
+ if (ret)
+ return ret;
ris->in_reset_state = false;
}
- mutex_unlock(&msc->cfg_lock);
}
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 08/11] arm_mpam: prepare mon_sel locking for MPAM-Fb
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (6 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 07/11] arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid() Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 09/11] arm_mpam: add MPAM-Fb MSC firmware access support Andre Przywara
` (3 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
The MSC MON_SEL register needs to be accessed from hardirq for the overflow
interrupt, and when taking an IPI to access these registers on platforms
where MSCs are not accesible from every CPU. This makes an irqsave
spinlock the obvious lock to protect these registers. On systems with
MPAM-Fb mailbox MSC access it must be able to sleep, meaning a mutex must
be used. So MPAM-Fb platforms cannot support an overflow interrupt easily.
Clearly these two methods can't exist for one MSC at the same time.
Change the mon_sel locking wrapper function to only use a spinlock when
the MSC is accessed directly via MMIO. In case of MPAM-Fb, we use a
mutex, but only if we are in a sleepable context. If that's not the
case, we return an error. This should not happen, as MPAM-Fb by design
does not require an MSC access to happen from a specific CPU, so there
is no need for any IPIs or preemption disabling to satisfy CPU
constraints. And since overflow interrupts are not supported at the moment
anyway, we also wouldn't meet the other case.
Bailing out early is already happening in rare occasions today.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Reviewed-by: Gavin Shan <gshan@redhat.com>
Tested-by: Gavin Shan <gshan@redhat.com> # on NVIDIA Grace Hopper
---
drivers/resctrl/mpam_devices.c | 6 +++++-
drivers/resctrl/mpam_internal.h | 38 +++++++++++++++++++++++++++------
2 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 49239b15a27f..cda59864808d 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -2279,7 +2279,6 @@ static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
if (err)
return ERR_PTR(err);
- mpam_mon_sel_lock_init(msc);
msc->id = pdev->id;
msc->pdev = pdev;
INIT_LIST_HEAD_RCU(&msc->all_msc_list);
@@ -2300,6 +2299,11 @@ static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
else
msc->iface = MPAM_IFACE_PCC;
+ /* Lock type depends on MSC interface used */
+ err = mpam_mon_sel_lock_init(dev, msc);
+ if (err)
+ return ERR_PTR(err);
+
if (msc->iface == MPAM_IFACE_MMIO) {
void __iomem *io;
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 57f2b3dbf7d0..6121cd126510 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -126,6 +126,7 @@ struct mpam_msc {
*/
raw_spinlock_t _mon_sel_lock;
unsigned long _mon_sel_flags;
+ struct mutex mon_sel_mutex;
void __iomem *mapped_hwpage;
size_t mapped_hwpage_sz;
@@ -139,27 +140,50 @@ struct mpam_msc {
/* 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))
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ raw_spin_lock_irqsave(&msc->_mon_sel_lock, msc->_mon_sel_flags);
+
+ return true;
+ }
+
+ if (!preemptible())
return false;
- raw_spin_lock_irqsave(&msc->_mon_sel_lock, msc->_mon_sel_flags);
+ mutex_lock(&msc->mon_sel_mutex);
+
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);
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ raw_spin_unlock_irqrestore(&msc->_mon_sel_lock,
+ msc->_mon_sel_flags);
+
+ return;
+ }
+
+ mutex_unlock(&msc->mon_sel_mutex);
}
static inline void mpam_mon_sel_lock_held(struct mpam_msc *msc)
{
- lockdep_assert_held_once(&msc->_mon_sel_lock);
+ if (msc->iface == MPAM_IFACE_MMIO)
+ lockdep_assert_held_once(&msc->_mon_sel_lock);
+ else
+ lockdep_assert_held_once(&msc->mon_sel_mutex);
}
-static inline void mpam_mon_sel_lock_init(struct mpam_msc *msc)
+static inline int mpam_mon_sel_lock_init(struct device *dev,
+ struct mpam_msc *msc)
{
- raw_spin_lock_init(&msc->_mon_sel_lock);
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ raw_spin_lock_init(&msc->_mon_sel_lock);
+
+ return 0;
+ }
+
+ return devm_mutex_init(dev, &msc->mon_sel_mutex);
}
DEFINE_GUARD(mon_sel, struct mpam_msc *,
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 09/11] arm_mpam: add MPAM-Fb MSC firmware access support
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (7 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 08/11] arm_mpam: prepare mon_sel locking for MPAM-Fb Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 10/11] arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler Andre Przywara
` (2 subsequent siblings)
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
The Arm MPAM Firmware-backed (Fb) Profile document[1] describes an
alternative way of accessing the "Memory System Components" (MSC) in an
MPAM enabled system.
Normally the MSCs are MMIO mapped, but in some implementations this
might not be possible (MSC located outside of the local socket, MSC
mapped secure-only) or desirable (direct MMIO access too slow or needs
to be mediated through a control processor). MPAM-fb standardises a
protocol to abstract MSC accesses, building on the SCMI protocol.
Add functions that do an MSC read or write access by redirecting the
request through a firmware interface. For now this done via an ACPI
PCC shared memory and mailbox combination.
Since the protocol used is only a small subset of the full SCMI spec,
and the SCMI protocol has no full ACPI support anyway, open-code the
(simple) SCMI message generation, for just the fields we need.
[1] https://developer.arm.com/documentation/den0144/latest
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Tested-by: Ritwick Sharma <ritwick.sharma@arm.com> # on Arm AGI CPU
Reviewed-by: Gavin Shan <gshan@redhat.com>
---
drivers/resctrl/Makefile | 2 +-
drivers/resctrl/mpam_devices.c | 57 +++++++--
drivers/resctrl/mpam_fb.c | 211 ++++++++++++++++++++++++++++++++
drivers/resctrl/mpam_internal.h | 18 +++
include/linux/arm_mpam.h | 2 +-
5 files changed, 277 insertions(+), 13 deletions(-)
create mode 100644 drivers/resctrl/mpam_fb.c
diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile
index 4f6d0e81f9b8..097c036724e9 100644
--- a/drivers/resctrl/Makefile
+++ b/drivers/resctrl/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_ARM64_MPAM_DRIVER) += mpam.o
-mpam-y += mpam_devices.o
+mpam-y += mpam_devices.o mpam_fb.o
mpam-$(CONFIG_ARM64_MPAM_RESCTRL_FS) += mpam_resctrl.o
ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) += -DDEBUG
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index cda59864808d..e9818c75443d 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -83,6 +83,21 @@ static DECLARE_WORK(mpam_broken_work, &mpam_disable);
/* When mpam is disabled, the printed reason to aid debugging */
static char *mpam_disable_reason;
+void mpam_fb_disable_mpam(int err, int mpam_fb_err)
+{
+ static char mpam_fb_reason[64];
+
+ /* Prevent repeated calls when mpam_disable() does MSC accesses. */
+ if (!mpam_is_enabled())
+ return;
+
+ snprintf(mpam_fb_reason, sizeof(mpam_fb_reason),
+ "MPAM-Fb error %d, kernel error %d", mpam_fb_err, err);
+
+ mpam_disable_reason = mpam_fb_reason;
+ schedule_work(&mpam_broken_work);
+}
+
/*
* Whether resctrl has been setup. Used by cpuhp in preference to
* mpam_is_enabled(). The disable call after an error interrupt makes
@@ -179,8 +194,11 @@ static void mpam_assert_partid_sizes_fixed(void)
static int __mpam_read_reg(struct mpam_msc *msc, u16 reg, u32 *res)
{
- WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
+ if (msc->iface == MPAM_IFACE_PCC)
+ return mpam_fb_send_read_request(msc, reg, res);
+
+ WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
*res = readl_relaxed(msc->mapped_hwpage + reg);
return 0;
@@ -197,9 +215,12 @@ static inline int _mpam_read_partsel_reg(struct mpam_msc *msc, u16 reg,
static int __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));
+ if (msc->iface == MPAM_IFACE_PCC)
+ return mpam_fb_send_write_request(msc, reg, val);
+
+ WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
+ WARN_ON_ONCE(reg + sizeof(u32) > msc->mapped_hwpage_sz);
writel_relaxed(val, msc->mapped_hwpage + reg);
return 0;
@@ -1141,8 +1162,11 @@ static int mpam_msc_read_mbwu_l(struct mpam_msc *msc, u64 *res)
mpam_mon_sel_lock_held(msc);
- WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
- WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
+ WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(),
+ &msc->accessibility));
+ }
ret = __mpam_read_reg(msc, MSMON_MBWU_L + 4, &mbwu_l_high2);
if (ret)
@@ -1176,8 +1200,11 @@ static int mpam_msc_zero_mbwu_l(struct mpam_msc *msc)
mpam_mon_sel_lock_held(msc);
- WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
- WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility));
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ WARN_ON_ONCE((MSMON_MBWU_L + sizeof(u64)) > msc->mapped_hwpage_sz);
+ WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(),
+ &msc->accessibility));
+ }
ret = __mpam_write_reg(msc, MSMON_MBWU_L, 0);
if (ret)
@@ -1490,11 +1517,16 @@ static int _msmon_read(struct mpam_component *comp, struct mon_read *arg)
srcu_read_lock_held(&mpam_srcu)) {
arg->ris = ris;
- err = smp_call_function_any(&msc->accessibility,
- __ris_msmon_read, arg,
- true);
- if (!err && arg->err)
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ err = smp_call_function_any(&msc->accessibility,
+ __ris_msmon_read,
+ arg, true);
+ if (!err)
+ err = arg->err;
+ } else {
+ __ris_msmon_read(arg);
err = arg->err;
+ }
/*
* Save one error to be returned to the caller, but
@@ -1971,6 +2003,9 @@ static int mpam_get_msc_preferred_cpu(struct mpam_msc *msc)
static int mpam_touch_msc(struct mpam_msc *msc, int (*fn)(void *a), void *arg)
{
+ if (msc->iface != MPAM_IFACE_MMIO)
+ return fn(arg);
+
lockdep_assert_irqs_enabled();
lockdep_assert_cpus_held();
WARN_ON_ONCE(!srcu_read_lock_held((&mpam_srcu)));
diff --git a/drivers/resctrl/mpam_fb.c b/drivers/resctrl/mpam_fb.c
new file mode 100644
index 000000000000..bb743ee460a2
--- /dev/null
+++ b/drivers/resctrl/mpam_fb.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2024-2026 Arm Ltd.
+
+#include <linux/arm_mpam.h>
+#include <linux/cleanup.h>
+#include <linux/errno.h>
+#include <linux/mailbox_client.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <acpi/pcc.h>
+#include <asm/mpam.h>
+
+#include "mpam_internal.h"
+
+#define MPAM_MSC_TOKEN_MASK GENMASK_U32(27, 18)
+#define MPAM_MSC_PROT_ID_MASK GENMASK_U32(17, 10)
+
+#define MPAM_FB_PROTOCOL_ID 0x1a
+
+#define MPAM_PROTOCOL_VERSION_CMD 0x0
+#define MPAM_MSC_READ_CMD 0x4
+#define MPAM_MSC_WRITE_CMD 0x5
+
+#define MPAM_FB_PROT_HEADER_LEN sizeof(u32)
+
+#define MPAM_FB_SUCCESS 0
+#define MPAM_FB_ERR_NOT_SUPPORTED -1
+#define MPAM_FB_ERR_INVALID_PARAMETERS -2
+#define MPAM_FB_ERR_DENIED -3
+#define MPAM_FB_ERR_NOT_FOUND -4
+#define MPAM_FB_ERR_OUT_OF_RANGE -5
+#define MPAM_FB_ERR_BUSY -6
+#define MPAM_FB_ERR_COMMS_ERROR -7
+#define MPAM_FB_ERR_GENERIC_ERROR -8
+#define MPAM_FB_ERR_HW_ERROR -9
+#define MPAM_FB_ERR_PROTOCOL_ERROR -10
+#define MPAM_FB_ERR_IN_USE -11
+
+static atomic_t mpam_fb_token = ATOMIC_INIT(0);
+
+static int mpam_fb_to_linux_errno(int mpam_fb_code)
+{
+ switch (mpam_fb_code) {
+ case MPAM_FB_ERR_NOT_SUPPORTED:
+ return -EOPNOTSUPP;
+ case MPAM_FB_ERR_INVALID_PARAMETERS:
+ return -EINVAL;
+ case MPAM_FB_ERR_NOT_FOUND:
+ return -ENOENT;
+ case MPAM_FB_ERR_OUT_OF_RANGE:
+ return -ERANGE;
+ case MPAM_FB_ERR_BUSY:
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void mpam_fb_build_version_message(unsigned int token,
+ void __iomem *msg_buf)
+{
+ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
+
+ /* .signature is filled by the platform */
+ writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
+ writel_relaxed(MPAM_FB_PROT_HEADER_LEN, &pcc_shmem->length);
+ writel_relaxed(MPAM_PROTOCOL_VERSION_CMD |
+ FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
+ FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
+ &pcc_shmem->command);
+}
+
+static void mpam_fb_build_read_message(int msc_id, int reg, unsigned int token,
+ void __iomem *msg_buf)
+{
+ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
+ struct mpam_fb_read_payload {
+ u32 msc_id;
+ u32 flags;
+ u32 reg_offset;
+ } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
+ int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
+
+ /* .signature is filled by the platform */
+ writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
+ writel_relaxed(msg_size, &pcc_shmem->length);
+ writel_relaxed(MPAM_MSC_READ_CMD |
+ FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
+ FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
+ &pcc_shmem->command);
+
+ writel_relaxed(msc_id, &payload->msc_id);
+ writel_relaxed(0, &payload->flags);
+ writel_relaxed(reg, &payload->reg_offset);
+}
+
+static void mpam_fb_build_write_message(int msc_id, int reg, u32 val,
+ unsigned int token,
+ void __iomem *msg_buf)
+{
+ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem = msg_buf;
+ struct mpam_fb_write_payload {
+ u32 msc_id;
+ u32 flags;
+ u32 reg_offset;
+ u32 value;
+ } __packed __iomem *payload = msg_buf + sizeof(*pcc_shmem);
+ int msg_size = MPAM_FB_PROT_HEADER_LEN + sizeof(*payload);
+
+ /* .signature is filled by the platform */
+ writel_relaxed(PCC_CMD_COMPLETION_NOTIFY, &pcc_shmem->flags);
+ writel_relaxed(msg_size, &pcc_shmem->length);
+ writel_relaxed(MPAM_MSC_WRITE_CMD |
+ FIELD_PREP(MPAM_MSC_TOKEN_MASK, token) |
+ FIELD_PREP(MPAM_MSC_PROT_ID_MASK, MPAM_FB_PROTOCOL_ID),
+ &pcc_shmem->command);
+
+ writel_relaxed(msc_id, &payload->msc_id);
+ writel_relaxed(0, &payload->flags);
+ writel_relaxed(reg, &payload->reg_offset);
+ writel_relaxed(val, &payload->value);
+}
+
+static int mpam_fb_send_request(struct mpam_msc *msc, u32 msc_id,
+ u16 reg, u32 *result, int mpam_fb_command)
+{
+ unsigned int token = atomic_inc_return(&mpam_fb_token);
+ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_shmem;
+ struct mpam_pcc_chan *pcc_chan;
+ struct pcc_mbox_chan *chan;
+ void __iomem *payload_ofs;
+ int mpam_fb_err = 0;
+ u32 status;
+ int ret;
+
+ pcc_chan = msc->pcc_chan;
+ if (!pcc_chan)
+ return -ENODEV;
+
+ chan = pcc_chan->pcc_chan;
+
+ /* prune token to fit into the 10 bits inside the command register */
+ token = FIELD_GET(MPAM_MSC_TOKEN_MASK,
+ FIELD_PREP(MPAM_MSC_TOKEN_MASK, token));
+
+ mutex_lock(&pcc_chan->pcc_chan_lock);
+
+ switch (mpam_fb_command) {
+ case MPAM_PROTOCOL_VERSION_CMD:
+ mpam_fb_build_version_message(token, chan->shmem);
+ break;
+ case MPAM_MSC_READ_CMD:
+ mpam_fb_build_read_message(msc_id, reg, token, chan->shmem);
+ break;
+ case MPAM_MSC_WRITE_CMD:
+ mpam_fb_build_write_message(msc_id, reg, *result,
+ token, chan->shmem);
+ break;
+ default:
+ dev_err(&msc->pdev->dev, "unsupported MPAM-Fb command %d\n",
+ mpam_fb_command);
+ ret = -EINVAL;
+ goto out_err;
+ }
+
+ ret = mbox_send_message(chan->mchan, NULL);
+ if (ret < 0)
+ goto out_err;
+
+ pcc_shmem = chan->shmem;
+ payload_ofs = chan->shmem + sizeof(*pcc_shmem);
+ status = readl(&pcc_shmem->command);
+ if (FIELD_GET(MPAM_MSC_TOKEN_MASK, status) != token) {
+ ret = -ETIMEDOUT;
+ goto out_err;
+ }
+
+ mpam_fb_err = readl(payload_ofs + 0x0);
+ if (mpam_fb_err < 0) {
+ ret = mpam_fb_to_linux_errno(mpam_fb_err);
+ goto out_err;
+ }
+
+ if (mpam_fb_command != MPAM_MSC_WRITE_CMD)
+ *result = readl(payload_ofs + 0x4);
+
+ mutex_unlock(&pcc_chan->pcc_chan_lock);
+
+ return 0;
+
+out_err:
+ mutex_unlock(&pcc_chan->pcc_chan_lock);
+
+ mpam_fb_disable_mpam(ret, mpam_fb_err);
+
+ return ret;
+}
+
+int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result)
+{
+ return mpam_fb_send_request(msc, msc->id, reg, result,
+ MPAM_MSC_READ_CMD);
+}
+
+int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value)
+{
+ return mpam_fb_send_request(msc, msc->id, reg, &value,
+ MPAM_MSC_WRITE_CMD);
+}
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index 6121cd126510..f13a79a84c58 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -11,6 +11,7 @@
#include <linux/io.h>
#include <linux/jump_label.h>
#include <linux/llist.h>
+#include <linux/mailbox_client.h>
#include <linux/mutex.h>
#include <linux/resctrl.h>
#include <linux/spinlock.h>
@@ -57,6 +58,15 @@ struct mpam_garbage {
struct platform_device *pdev;
};
+struct mpam_pcc_chan {
+ struct list_head pcc_chans;
+ struct mbox_client pcc_cl;
+ struct pcc_mbox_chan *pcc_chan;
+ struct mutex pcc_chan_lock; /* only one message at a time */
+ struct kref refcount;
+ int subspace_id;
+};
+
struct mpam_msc {
/* member of mpam_all_msc */
struct list_head all_msc_list;
@@ -66,6 +76,7 @@ struct mpam_msc {
/* Not modified after mpam_is_enabled() becomes true */
enum mpam_msc_iface iface;
+ struct mpam_pcc_chan *pcc_chan;
u32 nrdy_usec;
cpumask_t accessibility;
bool has_extd_esr;
@@ -484,6 +495,9 @@ extern u8 mpam_pmg_max;
void mpam_enable(struct work_struct *work);
void mpam_disable(struct work_struct *work);
+/* helper function to call from outside mpam_devices.c */
+void mpam_fb_disable_mpam(int err, int mpam_fb_err);
+
/* Reset all the RIS in a class under cpus_read_lock() */
void mpam_reset_class_locked(struct mpam_class *class);
@@ -511,6 +525,10 @@ 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-Fb Firmware-backed protocol wrappers */
+int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result);
+int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value);
+
/*
* MPAM MSCs have the following register layout. See:
* Arm Memory System Resource Partitioning and Monitoring (MPAM) System
diff --git a/include/linux/arm_mpam.h b/include/linux/arm_mpam.h
index f92a36187a52..002f56e15362 100644
--- a/include/linux/arm_mpam.h
+++ b/include/linux/arm_mpam.h
@@ -12,7 +12,7 @@ struct mpam_msc;
enum mpam_msc_iface {
MPAM_IFACE_MMIO, /* a real MPAM MSC */
- MPAM_IFACE_PCC, /* a fake MPAM MSC */
+ MPAM_IFACE_PCC, /* using the MPAM-Fb firmware redirection */
};
enum mpam_class_types {
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 10/11] arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (8 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 09/11] arm_mpam: add MPAM-Fb MSC firmware access support Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 10:06 ` [PATCH v8 11/11] arm_mpam: detect and enable MPAM-Fb PCC support Andre Przywara
2026-08-04 12:28 ` [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
When an MPAM MSC gets into an error condition, it can trigger an error
IRQ. We cannot really do much about those errors, but we at least query
and log the error, then disable MPAM functionality.
This error report relies on reading the MSC's error status register
(ESR) in the current hard-IRQ handler, which is not possible for MPAM-Fb
based MSC accesses, since they involve mailbox routines that might sleep.
The same is true for clearing the interrupt at the source, which requires
an MSC access as well.
When an MSC is using MPAM-Fb, change the error IRQ to use a threaded IRQ
handler, with an empty hard IRQ routine, and doing all the MSC accesses
(to access the status and disable the IRQ line) in the threaded part.
Also forbid per-CPU interrupts (PPIs) for MPAM-Fb, as we cannot use a
threaded IRQ here.
The change in the actual IRQ handler is minimal, we just check for the
first MSC access error and bail out early. MMIO based MSCs keep using a
hard-IRQ handler, since they must be at least non-migrate-able when doing
MSC accesses, for the CPU affinity check to work.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/resctrl/mpam_devices.c | 56 ++++++++++++++++++++++++++++------
1 file changed, 47 insertions(+), 9 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index e9818c75443d..3c686903def3 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -2714,24 +2714,42 @@ static int mpam_disable_msc_ecr(void *_msc)
return 0;
}
+/*
+ * This will run as the threaded IRQ handler part when using MPAM-Fb, but
+ * as the sole hard-IRQ handler for MMIO based accesses.
+ */
static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc)
{
u64 reg;
+ int ret;
u16 partid;
u8 errcode, pmg, ris;
- if (WARN_ON_ONCE(!msc) ||
+ if (WARN_ON_ONCE(!msc))
+ return IRQ_NONE;
+
+ if (msc->iface == MPAM_IFACE_MMIO &&
WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(),
&msc->accessibility)))
return IRQ_NONE;
- mpam_msc_read_esr(msc, ®);
+ ret = mpam_msc_read_esr(msc, ®);
+ if (ret) {
+ pr_err_ratelimited("unknown error irq from msc:%u\n", msc->id);
+
+ /* Try out best here ... */
+ goto out_disable;
+ }
errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg);
if (!errcode)
return IRQ_NONE;
- /* Clear level triggered irq */
+ /*
+ * Clear the level triggered IRQ. If that fails, we cannot do anything
+ * about it, so ignore any errors. We will disable the IRQ either on
+ * the device side or on the irqchip level next anyway.
+ */
mpam_msc_clear_esr(msc);
partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg);
@@ -2742,16 +2760,24 @@ static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc)
msc->id, mpam_errcode_names[errcode], partid, pmg,
ris);
- /* Disable this interrupt. */
- mpam_disable_msc_ecr(msc);
+out_disable:
+ /*
+ * Disable this interrupt on the device side. If that fails, disable
+ * the IRQ on the irqchip level, as we must prevent further handler
+ * invocations. We only take the interrupt once anyway, as we
+ * are going to free the IRQ next, in mpam_disable().
+ */
+ ret = mpam_disable_msc_ecr(msc);
+ if (ret)
+ disable_irq_nosync(irq);
- /* Are we racing with the thread disabling MPAM? */
+ /* Check whether we are racing with the thread disabling MPAM. */
if (!mpam_is_enabled())
return IRQ_HANDLED;
/*
- * Schedule the teardown work. Don't use a threaded IRQ as we can't
- * unregister the interrupt from the threaded part of the handler.
+ * Schedule the teardown work. We have to defer it as we can't
+ * unregister the interrupt from the threaded part of a handler.
*/
mpam_disable_reason = "hardware error interrupt";
schedule_work(&mpam_broken_work);
@@ -2790,6 +2816,11 @@ static int mpam_register_irqs(void)
/* The MPAM spec says the interrupt can be SPI, PPI or LPI */
/* We anticipate sharing the interrupt with other MSCs */
if (irq_is_percpu(irq)) {
+ if (msc->iface != MPAM_IFACE_MMIO) {
+ dev_err(&msc->pdev->dev,
+ "Only MMIO MSCs can use per-CPU interrupts\n");
+ return -EINVAL;
+ }
err = request_percpu_irq(irq, &mpam_ppi_handler,
"mpam:msc:error",
msc->error_dev_id);
@@ -2800,12 +2831,19 @@ static int mpam_register_irqs(void)
smp_call_function_many(&msc->accessibility,
&_enable_percpu_irq, &irq,
true);
- } else {
+ } else if (msc->iface == MPAM_IFACE_MMIO) {
err = devm_request_irq(&msc->pdev->dev, irq,
&mpam_spi_handler, IRQF_SHARED,
"mpam:msc:error", msc);
if (err)
return err;
+ } else {
+ err = devm_request_threaded_irq(&msc->pdev->dev, irq,
+ NULL, &mpam_spi_handler,
+ IRQF_SHARED | IRQF_ONESHOT,
+ "mpam:msc:error", msc);
+ if (err)
+ return err;
}
mutex_lock(&msc->error_irq_lock);
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH v8 11/11] arm_mpam: detect and enable MPAM-Fb PCC support
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (9 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 10/11] arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler Andre Przywara
@ 2026-08-04 10:06 ` Andre Przywara
2026-08-04 12:28 ` [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
11 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 10:06 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
The Arm MPAM-Fb specification [1] describes a protocol to access MSC
registers through a firmware interface. This requires a shared memory
region to hold the message, and a mailbox to trigger the access.
For ACPI this is wrapped as a PCC channel, described using existing
ACPI abstractions.
Add code to parse those PCC table descriptions associated with an MSC,
and store the parsed information in the MSC struct.
There can be multiple PCC channels, and each channel can serve multiple
MSCs, so we need to keep track of the channel usage, using a list and
a refcount.
This will be used by the MPAM-Fb access wrapper code.
[1] https://developer.arm.com/documentation/den0144/latest
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Tested-by: Ritwick Sharma <ritwick.sharma@arm.com> # on Arm AGI CPU
Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
---
drivers/resctrl/mpam_devices.c | 113 +++++++++++++++++++++++++++++++-
drivers/resctrl/mpam_fb.c | 41 ++++++++++++
drivers/resctrl/mpam_internal.h | 2 +
3 files changed, 154 insertions(+), 2 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 3c686903def3..666ebf6234db 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -19,14 +19,19 @@
#include <linux/irqdesc.h>
#include <linux/list.h>
#include <linux/lockdep.h>
+#include <linux/mailbox_client.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <linux/property.h>
#include <linux/srcu.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <acpi/pcc.h>
+#include <acpi/acpi_io.h>
+
#include "mpam_internal.h"
/* Values for the T241 errata workaround */
@@ -49,6 +54,88 @@ static LIST_HEAD(mpam_all_msc);
struct srcu_struct mpam_srcu;
+/* PCC channels might be serving multiple MSCs, so keep a refcounted list. */
+static DEFINE_MUTEX(pcc_chan_list_lock);
+static LIST_HEAD(pcc_chan_list);
+
+static void mpam_pcc_chan_release(struct kref *ref)
+{
+ struct mpam_pcc_chan *cur = container_of(ref, struct mpam_pcc_chan,
+ refcount);
+
+ pcc_mbox_free_channel(cur->pcc_chan);
+ list_del(&cur->pcc_chans);
+ mutex_destroy(&cur->pcc_chan_lock);
+ kfree(cur);
+}
+
+static struct mpam_pcc_chan *mpam_pcc_chan_get(struct device *dev,
+ int subspace_id)
+{
+ struct mpam_pcc_chan *cur;
+
+ guard(mutex)(&pcc_chan_list_lock);
+
+ list_for_each_entry(cur, &pcc_chan_list, pcc_chans) {
+ if (cur->subspace_id == subspace_id) {
+ kref_get(&cur->refcount);
+
+ return cur;
+ }
+ }
+
+ cur = kzalloc_obj(*cur);
+ if (!cur)
+ return ERR_PTR(-ENOMEM);
+
+ cur->pcc_cl.dev = dev;
+ cur->pcc_cl.tx_block = true;
+
+ cur->pcc_chan = pcc_mbox_request_channel(&cur->pcc_cl, subspace_id);
+ if (IS_ERR(cur->pcc_chan)) {
+ long err = PTR_ERR(cur->pcc_chan);
+
+ kfree(cur);
+ return ERR_PTR(err);
+ }
+
+ /*
+ * Timeout based on the "nominal latency" in us, from the
+ * PCC ACPI table. tx_tout is in ms.
+ * Add some margin here to be on the safe side.
+ */
+ cur->pcc_cl.tx_tout = DIV_ROUND_UP(cur->pcc_chan->latency * 5, 1000);
+
+ mutex_init(&cur->pcc_chan_lock);
+
+ cur->subspace_id = subspace_id;
+ kref_init(&cur->refcount);
+
+ list_add_tail(&cur->pcc_chans, &pcc_chan_list);
+
+ return cur;
+}
+
+static int mpam_pcc_chan_put(struct mpam_pcc_chan *pcc_chan)
+{
+ struct mpam_pcc_chan *cur, *tmp;
+
+ if (!pcc_chan)
+ return 0;
+
+ guard(mutex)(&pcc_chan_list_lock);
+
+ list_for_each_entry_safe(cur, tmp, &pcc_chan_list, pcc_chans) {
+ if (cur == pcc_chan) {
+ kref_put(&cur->refcount, mpam_pcc_chan_release);
+
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
/*
* Number of MSCs that have been probed. Once all MSCs have been probed MPAM
* can be enabled.
@@ -2275,6 +2362,8 @@ static void mpam_msc_drv_remove(struct platform_device *pdev)
{
struct mpam_msc *msc = platform_get_drvdata(pdev);
+ mpam_pcc_chan_put(msc->pcc_chan);
+
mutex_lock(&mpam_list_lock);
mpam_msc_destroy(msc);
mutex_unlock(&mpam_list_lock);
@@ -2285,7 +2374,7 @@ static void mpam_msc_drv_remove(struct platform_device *pdev)
static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
{
int err;
- u32 tmp;
+ u32 pcc_subspace_id;
struct mpam_msc *msc;
struct resource *msc_res;
struct device *dev = &pdev->dev;
@@ -2329,7 +2418,7 @@ static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
if (err)
return ERR_PTR(err);
- if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp))
+ if (device_property_read_u32(dev, "pcc-channel", &pcc_subspace_id))
msc->iface = MPAM_IFACE_MMIO;
else
msc->iface = MPAM_IFACE_PCC;
@@ -2350,6 +2439,26 @@ static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
}
msc->mapped_hwpage_sz = msc_res->end - msc_res->start;
msc->mapped_hwpage = io;
+ } else if (msc->iface == MPAM_IFACE_PCC) {
+ msc->pcc_chan = mpam_pcc_chan_get(dev, pcc_subspace_id);
+ if (IS_ERR(msc->pcc_chan)) {
+ pr_err("Failed to request MSC PCC channel\n");
+ return ERR_CAST(msc->pcc_chan);
+ }
+
+ err = mpam_fb_check_shared_buffer_size(msc);
+ if (err) {
+ mpam_pcc_chan_put(msc->pcc_chan);
+
+ return ERR_PTR(err);
+ }
+
+ err = mpam_fb_check_protocol_version(msc);
+ if (err) {
+ mpam_pcc_chan_put(msc->pcc_chan);
+
+ return ERR_PTR(err);
+ }
} else {
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/resctrl/mpam_fb.c b/drivers/resctrl/mpam_fb.c
index bb743ee460a2..73cc1b09f676 100644
--- a/drivers/resctrl/mpam_fb.c
+++ b/drivers/resctrl/mpam_fb.c
@@ -20,10 +20,15 @@
#define MPAM_FB_PROTOCOL_ID 0x1a
#define MPAM_PROTOCOL_VERSION_CMD 0x0
+#define MPAM_FB_VERSION_MAJOR_MASK GENMASK_U32(31, 16)
+#define MPAM_FB_VERSION_MINOR_MASK GENMASK_U32(15, 0)
+
#define MPAM_MSC_READ_CMD 0x4
#define MPAM_MSC_WRITE_CMD 0x5
#define MPAM_FB_PROT_HEADER_LEN sizeof(u32)
+/* The longest message is MPAM_MSC_WRITE, with 4 parameters. */
+#define MPAM_FB_MAX_MSG_SIZE (4 * sizeof(u32))
#define MPAM_FB_SUCCESS 0
#define MPAM_FB_ERR_NOT_SUPPORTED -1
@@ -209,3 +214,39 @@ int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value)
return mpam_fb_send_request(msc, msc->id, reg, &value,
MPAM_MSC_WRITE_CMD);
}
+
+/* We only support MPAM-Fb protocol version 1.x */
+int mpam_fb_check_protocol_version(struct mpam_msc *msc)
+{
+ u32 version;
+ int ret;
+
+ ret = mpam_fb_send_request(msc, 0, 0, &version,
+ MPAM_PROTOCOL_VERSION_CMD);
+ if (ret)
+ return ret;
+
+ if (FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version) != 1) {
+ pr_err("Incompatible MPAM-Fb protocol version %d.%d\n",
+ FIELD_GET(MPAM_FB_VERSION_MAJOR_MASK, version),
+ FIELD_GET(MPAM_FB_VERSION_MINOR_MASK, version));
+
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc)
+{
+ int min_buffer_size = MPAM_FB_MAX_MSG_SIZE +
+ sizeof(struct acpi_pcct_ext_pcc_shared_memory);
+
+ if (msc->pcc_chan->pcc_chan->shmem_size < min_buffer_size) {
+ pr_err("MPAM-Fb PCC channel size too small.\n");
+
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
index f13a79a84c58..8859189088f3 100644
--- a/drivers/resctrl/mpam_internal.h
+++ b/drivers/resctrl/mpam_internal.h
@@ -528,6 +528,8 @@ static inline void mpam_resctrl_teardown_class(struct mpam_class *class) { }
/* MPAM-Fb Firmware-backed protocol wrappers */
int mpam_fb_send_read_request(struct mpam_msc *msc, u16 reg, u32 *result);
int mpam_fb_send_write_request(struct mpam_msc *msc, u16 reg, u32 value);
+int mpam_fb_check_protocol_version(struct mpam_msc *msc);
+int mpam_fb_check_shared_buffer_size(struct mpam_msc *msc);
/*
* MPAM MSCs have the following register layout. See:
--
2.43.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
` (10 preceding siblings ...)
2026-08-04 10:06 ` [PATCH v8 11/11] arm_mpam: detect and enable MPAM-Fb PCC support Andre Przywara
@ 2026-08-04 12:28 ` Andre Przywara
2026-08-04 12:50 ` Ben Horgan
2026-08-04 13:36 ` Srivathsa L Rao
11 siblings, 2 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 12:28 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Hi,
On 8/4/26 12:06, Andre Przywara wrote:
> this is v8 of the MPAM-Fb code, for firmware based MSC accesses.
> It fixes a potential use-after-free of the dev pointer on error
> prints, and disables the IRQ on the irqchip side in case doing it on
> the device side doesn't work. The rest are cosmetic fixes and adding
> the tags from the diligent reviewers and testers, many thanks for that!
> I dropped tags from the error IRQ patch (10/11) due to changes.
> Find the changelog below. Based on v7.2-rc1.
>
> =======================
> The Arm MPAM specification defines Memory System Components (MSCs),
> which are devices that are programmed through an MMIO register frame. In
> some occasions this turned out to be too limiting: the MSC might be
> located behind a separate bus system (for instance inside an on-board
> controller), it might be mapped secure-only, or in a different processor
> socket without direct MMIO mapping. Also the MMIO access might be too slow
> or it would need to be filtered or otherwise access controlled. Finally
> there might be bugs in the MSC integration, which require a mediating
> firmware to be accessible.
For the records, I looked at the sashiko complaints, by my analysis most
comments fall into one of those categories:
- Does this new early return / error return .... break MPAM logic?
It's concerned about just bailing out from the middle of a sequence of
MPAM read or writes, leaving the MSC in a misconfigured state. This is
true, but doesn't apply, because we tear down MPAM completely upon an
MPAM-Fb access returning an error. And if the communication between the
kernel and an MSC breaks down completely, there isn't much we can do
anyway: attempts to recover would fail as well.
- Since xxx() now returns an error code, do the callers need
to be updated to check it?
- Are there missing error checks in xxx() related to the new error
return values?
Yes, this is done in one of the following patches. The series is
constructed so that only the final patch enables MPAM-Fb, before that
there is no way MPAM-Fb could be instantiated, so only MMIO accesses are
happening. They always return 0, so no error could occur and error paths
are never exercised.
This is used to construct the functionality gradually, and allow
multiple, but smaller patches. Mentioned in the cover letter.
- This is a pre-existing issue, but ....
I haven't checked in detail, but from previous rounds those concerns
were addressed by Ben's fixes, which IIUC are on its way already. This
series is based on v7.2-rc1, so those fixes are not applied here.
Hope that helps,
Cheers,
Andre
> To accommodate all those different use cases, the MPAM-Fb specification
> [1] describes an alternative way to access MSCs. Accesses to an MSC
> would be wrapped in a message and communicated to the system using a
> shared-memory/mailbox system mostly mimicking the Arm SCMI spec.
> For ACPI systems, this would be abstracted through an ACPI PCC channel,
> which provides the shared-memory region and the mailbox trigger. We can
> lean on existing ACPI parsing code to register with these two
> subsystems, but cannot rely on the existing SCMI code in the kernel.
> This means we somewhat need to open code a very simplified SCMI handler,
> which just provides enough functionality for the very basic subset of
> SCMI that the MPAM-Fb spec requires.
>
> The first seven patches rework all MSC access wrappers to propagate error
> information. Pure MMIO based MSC accesses would never fail, but the
> MPAM-Fb access can go wrong in multiple ways. The patches have been split
> up purely for reviewing reasons, if the number is a problem, we could as
> well squash them. Please note that until the very last patch of this series
> any MSC accesses would always only return 0, it's only the final enablement
> of MPAM-Fb that could possibly introduce errors. Hence all former patches
> can add error handling gradually, those code paths wouldn't be triggered
> before patch 11/11.
> Patch 8/11 solves a nasty problem: At the moment we protect stateful MSC
> register accesses (mon_sel) through a spinlock. Unfortunately the mailbox
> subsystem and the slow nature of the communication through this channel
> forbid MPAM-Fb access in atomic context. So this patch keeps using a
> spinlock for MMIO based accesses, but reverts to a mutex otherwise.
> We just deny taking the lock for MPAM-Fb in atomic context, ideally we
> wouldn't need that (no need to IPI another core when the MSC access does
> not need to be local to one particular core), or we simply deny that part
> of the functionality (access through perf).
> Patch 9/11 adds the code to redirect MSC accesses through the
> PCC shmem/mailbox system.
> Patch 10/11 reworks the error interrupt handler to use a threaded IRQ for
> MPAM-Fb, to be able to do MPAM-Fb MSC accesses inside (which might sleep).
> The final patch 11/11 then adds the code to detect and store the PCC
> channel information from the ACPI tables, and eventually enables
> MPAM-Fb accesses.
>
> This would enable systems where some MSCs are not accessible via MMIO to
> use those components anyway.
>
> Please have a look and test!
>
> Cheers,
> Andre
>
> [1] https://developer.arm.com/documentation/den0144/latest
>
> Changes in v8:
> - disable error IRQ on irqchip level when device side fails
> - use MSC dev pointer for dev_err() instead of mailbox one
> - smaller white space and formatting fixes
> - reorder MPAM-Fb definitions
> - rename error translation function to mpam_fb_to_linux_errno()
> - add review and test tags
>
> Changes in v7:
> - add tags
> - add new patch to propagate errors in mpam_reprogram_ris_partid()
> - prevent loop when mpam_diable() tries MSC accesses again
> - register MMIO error IRQ handler without IRQF_ONESHOT
> - re-use existing "err" variable instead of declaring "ret"
> - initialise mon_sel_lock later, to wait for interface decision
> - convert timeout units for nominal latency, between us and ms
>
> Changes in v6:
> - keep using hard IRQ handler for MMIO based accesses
> - move shmem buffer size and MPAM-Fb version check into mpam_fb.c
> - drop mutex guard in mpam_fb_send_request(), we use gotos anyway
> - factor out MPAM-Fb protocol error translation
> - add MPAM_FB_ERR_BUSY error code to translation
> - explicitly report also MPAM-Fb protocol error number when disabling MPAM
> - remove unneeded MPAM-Fb details from mpam_internal.h
> - drop leftover mpam_fb_msc_id usage
>
> Changes in v5:
> - do not initialise _high variables early
> - fix whitespace issues
> - add Jonathan's Reviewed-by: tags
> - init only either the spinlock OR the mutex for the mon_sel_lock
> - drop warning about mapped_hwpage_sz for MPAM-Fb in mpam_msc_zero_mbwu_l()
> - change IRQ handler to threaded handler, to allow MPAM-Fb MSC accesses
> - disallow per-CPU IRQs (PPIs) for MPAM-Fb
> - trigger MPAM disabling on MPAM-Fb protocol errors
> - fix MPAM_PROTOCOL_VERSION_CMD name
> - use PCC driver defined symbol for PCC_CHAN_FLAGS_IRQ
> - add comment about usage of PCC's .signature field
> - check for illegal MPAM-Fb command before sending request
> - drop CPU accessibility check for MPAM-Fb based MSC accesses
> - move MPAM-Fb protocol message structs inside build functions
> - add comment about largest message size
> - drop extra msc->mpam_fb_msc_id, in favour using pdev->id
> - use already defined local dev variable instead of &pdev->dev
> - use FIELD_GET() macro for MPAM-Fb protocol check
> - drop devm_mutex_init() for pcc_chan_lock
>
> Changes in v4:
> - unify error check patterns
> - use scoped_guard(mutex) and ACQUIRE to simplify error handling
> - use struct kref for PCC channel refcount
> - drop unused mailbox rx_callback function
> - increase size of props[] array to hold new msc_id property
> - set mailbox TX timeout based on ACPI MPAM table field
> - use devm_mutex_init() to allow automatic cleanup
> - avoid overwriting error value for arm,not-ready-us property read
> - prune MPAM-Fb protocol token to fit into bitmask inside register
> - check return value for AIDR read
> - use two separate MPAM-Fb protocol payload structs
> - prune header list for mpam_fb.c
> - drop "nrdy handled as an error" change
> - fix message size field in MPAM-Fb protocol shmem area
> - drop message size return from MPAM-Fb message build functions
> - bring back IRQ flag in MPAM-Fb protocol header
>
> Changes in v3:
> - drop inner/outer mon_sel lock patch, replace with simpler version
> - harmonise code patterns in error propagation changes
> - drop mon_sel lock before erroring out in mpam_ris_hw_probe_csu_nrdy()
> - refactor mpam_msc_read_mbwu_l() to return an error
> - drop special NRDY handling in __ris_msmon_read()
> - add IRQ numbers in error interrupt handler to help pinpoint failure
> - refactor MPAM-Fb message generation to accommodate more than read/write
> - check MPAM-Fb protocol version at probe time
> - drop mpam_fb.h header, merge into mpam_internal.h
> - translate MPAM-Fb error code in Linux codes where needed
> - clear IRQ flag bit in MPAM-Fb protocol header
> - drop unneeded endianness conversions when crafting MPAM-Fb message
> - use C struct to model MPAM-Fb message payload
>
> Changes in v2:
> - add patches to add error propagation to MSC access wrappers
> - drop former patch 1/5 (not needed)
> - drop lock in mpam_reprogram_msc(), to avoid double lock
> - add support for multiple MSCs per PCC channel
> - adjust SCMI protocol code to use a PCC subtype 3 channel
> - let PCC code handle the PCC channel negotiation (due to subtype 3)
> - drop SCMI names in shmem offsets, and use existing PCC type 3 struct
> - adjust shmem field offsets to match MPAM-Fb spec, not pure SCMI
> - prevent MPAM-Fb calls inside atomic smp_call_function_any() payload
> - skip all MSC accesses inside the IRQ handler when using MPAM-Fb
>
> Andre Przywara (11):
> arm_mpam: let low level MSC accessors return an error
> arm_mpam: propagate MSC access errors for hw_probe functions
> arm_mpam: propagate MSC access errors for MBWU counters
> arm_mpam: propagate MSC access errors for msmon helpers
> arm_mpam: propagate MSC access errors for __ris_msmon_read()
> arm_mpam: propagate MSC access errors for state saving function
> arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid()
> arm_mpam: prepare mon_sel locking for MPAM-Fb
> arm_mpam: add MPAM-Fb MSC firmware access support
> arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler
> arm_mpam: detect and enable MPAM-Fb PCC support
>
> drivers/resctrl/Makefile | 2 +-
> drivers/resctrl/mpam_devices.c | 825 ++++++++++++++++++++++++--------
> drivers/resctrl/mpam_fb.c | 252 ++++++++++
> drivers/resctrl/mpam_internal.h | 62 ++-
> include/linux/arm_mpam.h | 2 +-
> 5 files changed, 926 insertions(+), 217 deletions(-)
> create mode 100644 drivers/resctrl/mpam_fb.c
>
>
> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support
2026-08-04 12:28 ` [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
@ 2026-08-04 12:50 ` Ben Horgan
2026-08-04 13:02 ` Andre Przywara
2026-08-04 13:36 ` Srivathsa L Rao
1 sibling, 1 reply; 16+ messages in thread
From: Ben Horgan @ 2026-08-04 12:50 UTC (permalink / raw)
To: Andre Przywara, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Catalin Marinas, Will Deacon, Rafael J . Wysocki, Len Brown,
James Morse, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Hi Andre,
On 8/4/26 13:28, Andre Przywara wrote:
> Hi,
>
> On 8/4/26 12:06, Andre Przywara wrote:
>> this is v8 of the MPAM-Fb code, for firmware based MSC accesses.
>> It fixes a potential use-after-free of the dev pointer on error
>> prints, and disables the IRQ on the irqchip side in case doing it on
>> the device side doesn't work. The rest are cosmetic fixes and adding
>> the tags from the diligent reviewers and testers, many thanks for that!
>> I dropped tags from the error IRQ patch (10/11) due to changes.
>> Find the changelog below. Based on v7.2-rc1.
>>
>> =======================
>> The Arm MPAM specification defines Memory System Components (MSCs),
>> which are devices that are programmed through an MMIO register frame. In
>> some occasions this turned out to be too limiting: the MSC might be
>> located behind a separate bus system (for instance inside an on-board
>> controller), it might be mapped secure-only, or in a different processor
>> socket without direct MMIO mapping. Also the MMIO access might be too slow
>> or it would need to be filtered or otherwise access controlled. Finally
>> there might be bugs in the MSC integration, which require a mediating
>> firmware to be accessible.
>
>
> For the records, I looked at the sashiko complaints, by my analysis most comments fall into one of
> those categories:
>
> - Does this new early return / error return .... break MPAM logic?
> It's concerned about just bailing out from the middle of a sequence of MPAM read or writes, leaving
> the MSC in a misconfigured state. This is true, but doesn't apply, because we tear down MPAM
> completely upon an MPAM-Fb access returning an error. And if the communication between the kernel
> and an MSC breaks down completely, there isn't much we can do anyway: attempts to recover would fail
> as well.
>
> - Since xxx() now returns an error code, do the callers need
> to be updated to check it?
> - Are there missing error checks in xxx() related to the new error
> return values?
> Yes, this is done in one of the following patches. The series is constructed so that only the final
> patch enables MPAM-Fb, before that there is no way MPAM-Fb could be instantiated, so only MMIO
> accesses are happening. They always return 0, so no error could occur and error paths are never
> exercised.
> This is used to construct the functionality gradually, and allow multiple, but smaller patches.
> Mentioned in the cover letter.
Just to add to this. The mpam_reset_component_locked() doesn't propagate the error, in
reset_arch_reset_all_ctrls() this is ok as this is only called when resctrl is being disabled. The
other place this is called from is the cpu hotplug handlers which aren't called by a user. So there
is no user to propagate the error up to.
The one valid concern as far as I can see is that mpam_disable_msc_ecr() always returns 0 and so the
disable_irq_nosync() can never be called.
Thanks,
Ben
>
> - This is a pre-existing issue, but ....
> I haven't checked in detail, but from previous rounds those concerns were addressed by Ben's fixes,
> which IIUC are on its way already. This series is based on v7.2-rc1, so those fixes are not applied
> here.
>
> Hope that helps,
> Cheers,
> Andre
>
>> To accommodate all those different use cases, the MPAM-Fb specification
>> [1] describes an alternative way to access MSCs. Accesses to an MSC
>> would be wrapped in a message and communicated to the system using a
>> shared-memory/mailbox system mostly mimicking the Arm SCMI spec.
>> For ACPI systems, this would be abstracted through an ACPI PCC channel,
>> which provides the shared-memory region and the mailbox trigger. We can
>> lean on existing ACPI parsing code to register with these two
>> subsystems, but cannot rely on the existing SCMI code in the kernel.
>> This means we somewhat need to open code a very simplified SCMI handler,
>> which just provides enough functionality for the very basic subset of
>> SCMI that the MPAM-Fb spec requires.
>>
>> The first seven patches rework all MSC access wrappers to propagate error
>> information. Pure MMIO based MSC accesses would never fail, but the
>> MPAM-Fb access can go wrong in multiple ways. The patches have been split
>> up purely for reviewing reasons, if the number is a problem, we could as
>> well squash them. Please note that until the very last patch of this series
>> any MSC accesses would always only return 0, it's only the final enablement
>> of MPAM-Fb that could possibly introduce errors. Hence all former patches
>> can add error handling gradually, those code paths wouldn't be triggered
>> before patch 11/11.
>> Patch 8/11 solves a nasty problem: At the moment we protect stateful MSC
>> register accesses (mon_sel) through a spinlock. Unfortunately the mailbox
>> subsystem and the slow nature of the communication through this channel
>> forbid MPAM-Fb access in atomic context. So this patch keeps using a
>> spinlock for MMIO based accesses, but reverts to a mutex otherwise.
>> We just deny taking the lock for MPAM-Fb in atomic context, ideally we
>> wouldn't need that (no need to IPI another core when the MSC access does
>> not need to be local to one particular core), or we simply deny that part
>> of the functionality (access through perf).
>> Patch 9/11 adds the code to redirect MSC accesses through the
>> PCC shmem/mailbox system.
>> Patch 10/11 reworks the error interrupt handler to use a threaded IRQ for
>> MPAM-Fb, to be able to do MPAM-Fb MSC accesses inside (which might sleep).
>> The final patch 11/11 then adds the code to detect and store the PCC
>> channel information from the ACPI tables, and eventually enables
>> MPAM-Fb accesses.
>>
>> This would enable systems where some MSCs are not accessible via MMIO to
>> use those components anyway.
>>
>> Please have a look and test!
>>
>> Cheers,
>> Andre
>>
>> [1] https://developer.arm.com/documentation/den0144/latest
>>
>> Changes in v8:
>> - disable error IRQ on irqchip level when device side fails
>> - use MSC dev pointer for dev_err() instead of mailbox one
>> - smaller white space and formatting fixes
>> - reorder MPAM-Fb definitions
>> - rename error translation function to mpam_fb_to_linux_errno()
>> - add review and test tags
>>
>> Changes in v7:
>> - add tags
>> - add new patch to propagate errors in mpam_reprogram_ris_partid()
>> - prevent loop when mpam_diable() tries MSC accesses again
>> - register MMIO error IRQ handler without IRQF_ONESHOT
>> - re-use existing "err" variable instead of declaring "ret"
>> - initialise mon_sel_lock later, to wait for interface decision
>> - convert timeout units for nominal latency, between us and ms
>> Changes in v6:
>> - keep using hard IRQ handler for MMIO based accesses
>> - move shmem buffer size and MPAM-Fb version check into mpam_fb.c
>> - drop mutex guard in mpam_fb_send_request(), we use gotos anyway
>> - factor out MPAM-Fb protocol error translation
>> - add MPAM_FB_ERR_BUSY error code to translation
>> - explicitly report also MPAM-Fb protocol error number when disabling MPAM
>> - remove unneeded MPAM-Fb details from mpam_internal.h
>> - drop leftover mpam_fb_msc_id usage
>>
>> Changes in v5:
>> - do not initialise _high variables early
>> - fix whitespace issues
>> - add Jonathan's Reviewed-by: tags
>> - init only either the spinlock OR the mutex for the mon_sel_lock
>> - drop warning about mapped_hwpage_sz for MPAM-Fb in mpam_msc_zero_mbwu_l()
>> - change IRQ handler to threaded handler, to allow MPAM-Fb MSC accesses
>> - disallow per-CPU IRQs (PPIs) for MPAM-Fb
>> - trigger MPAM disabling on MPAM-Fb protocol errors
>> - fix MPAM_PROTOCOL_VERSION_CMD name
>> - use PCC driver defined symbol for PCC_CHAN_FLAGS_IRQ
>> - add comment about usage of PCC's .signature field
>> - check for illegal MPAM-Fb command before sending request
>> - drop CPU accessibility check for MPAM-Fb based MSC accesses
>> - move MPAM-Fb protocol message structs inside build functions
>> - add comment about largest message size
>> - drop extra msc->mpam_fb_msc_id, in favour using pdev->id
>> - use already defined local dev variable instead of &pdev->dev
>> - use FIELD_GET() macro for MPAM-Fb protocol check
>> - drop devm_mutex_init() for pcc_chan_lock
>>
>> Changes in v4:
>> - unify error check patterns
>> - use scoped_guard(mutex) and ACQUIRE to simplify error handling
>> - use struct kref for PCC channel refcount
>> - drop unused mailbox rx_callback function
>> - increase size of props[] array to hold new msc_id property
>> - set mailbox TX timeout based on ACPI MPAM table field
>> - use devm_mutex_init() to allow automatic cleanup
>> - avoid overwriting error value for arm,not-ready-us property read
>> - prune MPAM-Fb protocol token to fit into bitmask inside register
>> - check return value for AIDR read
>> - use two separate MPAM-Fb protocol payload structs
>> - prune header list for mpam_fb.c
>> - drop "nrdy handled as an error" change
>> - fix message size field in MPAM-Fb protocol shmem area
>> - drop message size return from MPAM-Fb message build functions
>> - bring back IRQ flag in MPAM-Fb protocol header
>>
>> Changes in v3:
>> - drop inner/outer mon_sel lock patch, replace with simpler version
>> - harmonise code patterns in error propagation changes
>> - drop mon_sel lock before erroring out in mpam_ris_hw_probe_csu_nrdy()
>> - refactor mpam_msc_read_mbwu_l() to return an error
>> - drop special NRDY handling in __ris_msmon_read()
>> - add IRQ numbers in error interrupt handler to help pinpoint failure
>> - refactor MPAM-Fb message generation to accommodate more than read/write
>> - check MPAM-Fb protocol version at probe time
>> - drop mpam_fb.h header, merge into mpam_internal.h
>> - translate MPAM-Fb error code in Linux codes where needed
>> - clear IRQ flag bit in MPAM-Fb protocol header
>> - drop unneeded endianness conversions when crafting MPAM-Fb message
>> - use C struct to model MPAM-Fb message payload
>>
>> Changes in v2:
>> - add patches to add error propagation to MSC access wrappers
>> - drop former patch 1/5 (not needed)
>> - drop lock in mpam_reprogram_msc(), to avoid double lock
>> - add support for multiple MSCs per PCC channel
>> - adjust SCMI protocol code to use a PCC subtype 3 channel
>> - let PCC code handle the PCC channel negotiation (due to subtype 3)
>> - drop SCMI names in shmem offsets, and use existing PCC type 3 struct
>> - adjust shmem field offsets to match MPAM-Fb spec, not pure SCMI
>> - prevent MPAM-Fb calls inside atomic smp_call_function_any() payload
>> - skip all MSC accesses inside the IRQ handler when using MPAM-Fb
>>
>> Andre Przywara (11):
>> arm_mpam: let low level MSC accessors return an error
>> arm_mpam: propagate MSC access errors for hw_probe functions
>> arm_mpam: propagate MSC access errors for MBWU counters
>> arm_mpam: propagate MSC access errors for msmon helpers
>> arm_mpam: propagate MSC access errors for __ris_msmon_read()
>> arm_mpam: propagate MSC access errors for state saving function
>> arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid()
>> arm_mpam: prepare mon_sel locking for MPAM-Fb
>> arm_mpam: add MPAM-Fb MSC firmware access support
>> arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler
>> arm_mpam: detect and enable MPAM-Fb PCC support
>>
>> drivers/resctrl/Makefile | 2 +-
>> drivers/resctrl/mpam_devices.c | 825 ++++++++++++++++++++++++--------
>> drivers/resctrl/mpam_fb.c | 252 ++++++++++
>> drivers/resctrl/mpam_internal.h | 62 ++-
>> include/linux/arm_mpam.h | 2 +-
>> 5 files changed, 926 insertions(+), 217 deletions(-)
>> create mode 100644 drivers/resctrl/mpam_fb.c
>>
>>
>> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support
2026-08-04 12:50 ` Ben Horgan
@ 2026-08-04 13:02 ` Andre Przywara
0 siblings, 0 replies; 16+ messages in thread
From: Andre Przywara @ 2026-08-04 13:02 UTC (permalink / raw)
To: Ben Horgan, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Catalin Marinas, Will Deacon, Rafael J . Wysocki, Len Brown,
James Morse, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, Lee Trager,
Ritwick Sharma, Gavin Shan, linux-acpi, linux-arm-kernel,
linux-kernel
Hi,
On 8/4/26 14:50, Ben Horgan wrote:
> Hi Andre,
>
> On 8/4/26 13:28, Andre Przywara wrote:
>> Hi,
>>
>> On 8/4/26 12:06, Andre Przywara wrote:
>>> this is v8 of the MPAM-Fb code, for firmware based MSC accesses.
>>> It fixes a potential use-after-free of the dev pointer on error
>>> prints, and disables the IRQ on the irqchip side in case doing it on
>>> the device side doesn't work. The rest are cosmetic fixes and adding
>>> the tags from the diligent reviewers and testers, many thanks for that!
>>> I dropped tags from the error IRQ patch (10/11) due to changes.
>>> Find the changelog below. Based on v7.2-rc1.
>>>
>>> =======================
>>> The Arm MPAM specification defines Memory System Components (MSCs),
>>> which are devices that are programmed through an MMIO register frame. In
>>> some occasions this turned out to be too limiting: the MSC might be
>>> located behind a separate bus system (for instance inside an on-board
>>> controller), it might be mapped secure-only, or in a different processor
>>> socket without direct MMIO mapping. Also the MMIO access might be too slow
>>> or it would need to be filtered or otherwise access controlled. Finally
>>> there might be bugs in the MSC integration, which require a mediating
>>> firmware to be accessible.
>>
>>
>> For the records, I looked at the sashiko complaints, by my analysis most comments fall into one of
>> those categories:
>>
>> - Does this new early return / error return .... break MPAM logic?
>> It's concerned about just bailing out from the middle of a sequence of MPAM read or writes, leaving
>> the MSC in a misconfigured state. This is true, but doesn't apply, because we tear down MPAM
>> completely upon an MPAM-Fb access returning an error. And if the communication between the kernel
>> and an MSC breaks down completely, there isn't much we can do anyway: attempts to recover would fail
>> as well.
>>
>> - Since xxx() now returns an error code, do the callers need
>> to be updated to check it?
>> - Are there missing error checks in xxx() related to the new error
>> return values?
>> Yes, this is done in one of the following patches. The series is constructed so that only the final
>> patch enables MPAM-Fb, before that there is no way MPAM-Fb could be instantiated, so only MMIO
>> accesses are happening. They always return 0, so no error could occur and error paths are never
>> exercised.
>> This is used to construct the functionality gradually, and allow multiple, but smaller patches.
>> Mentioned in the cover letter.
>
> Just to add to this. The mpam_reset_component_locked() doesn't propagate the error, in
> reset_arch_reset_all_ctrls() this is ok as this is only called when resctrl is being disabled. The
> other place this is called from is the cpu hotplug handlers which aren't called by a user. So there
> is no user to propagate the error up to.
>
> The one valid concern as far as I can see is that mpam_disable_msc_ecr() always returns 0 and so the
> disable_irq_nosync() can never be called.
Yes, I just saw this as well, for some reason I missed that conversion.
It's a very simple fix, of course.
Cheers,
Andre
>
> Thanks,
>
> Ben
>
>>
>> - This is a pre-existing issue, but ....
>> I haven't checked in detail, but from previous rounds those concerns were addressed by Ben's fixes,
>> which IIUC are on its way already. This series is based on v7.2-rc1, so those fixes are not applied
>> here.
>>
>> Hope that helps,
>> Cheers,
>> Andre
>>
>>> To accommodate all those different use cases, the MPAM-Fb specification
>>> [1] describes an alternative way to access MSCs. Accesses to an MSC
>>> would be wrapped in a message and communicated to the system using a
>>> shared-memory/mailbox system mostly mimicking the Arm SCMI spec.
>>> For ACPI systems, this would be abstracted through an ACPI PCC channel,
>>> which provides the shared-memory region and the mailbox trigger. We can
>>> lean on existing ACPI parsing code to register with these two
>>> subsystems, but cannot rely on the existing SCMI code in the kernel.
>>> This means we somewhat need to open code a very simplified SCMI handler,
>>> which just provides enough functionality for the very basic subset of
>>> SCMI that the MPAM-Fb spec requires.
>>>
>>> The first seven patches rework all MSC access wrappers to propagate error
>>> information. Pure MMIO based MSC accesses would never fail, but the
>>> MPAM-Fb access can go wrong in multiple ways. The patches have been split
>>> up purely for reviewing reasons, if the number is a problem, we could as
>>> well squash them. Please note that until the very last patch of this series
>>> any MSC accesses would always only return 0, it's only the final enablement
>>> of MPAM-Fb that could possibly introduce errors. Hence all former patches
>>> can add error handling gradually, those code paths wouldn't be triggered
>>> before patch 11/11.
>>> Patch 8/11 solves a nasty problem: At the moment we protect stateful MSC
>>> register accesses (mon_sel) through a spinlock. Unfortunately the mailbox
>>> subsystem and the slow nature of the communication through this channel
>>> forbid MPAM-Fb access in atomic context. So this patch keeps using a
>>> spinlock for MMIO based accesses, but reverts to a mutex otherwise.
>>> We just deny taking the lock for MPAM-Fb in atomic context, ideally we
>>> wouldn't need that (no need to IPI another core when the MSC access does
>>> not need to be local to one particular core), or we simply deny that part
>>> of the functionality (access through perf).
>>> Patch 9/11 adds the code to redirect MSC accesses through the
>>> PCC shmem/mailbox system.
>>> Patch 10/11 reworks the error interrupt handler to use a threaded IRQ for
>>> MPAM-Fb, to be able to do MPAM-Fb MSC accesses inside (which might sleep).
>>> The final patch 11/11 then adds the code to detect and store the PCC
>>> channel information from the ACPI tables, and eventually enables
>>> MPAM-Fb accesses.
>>>
>>> This would enable systems where some MSCs are not accessible via MMIO to
>>> use those components anyway.
>>>
>>> Please have a look and test!
>>>
>>> Cheers,
>>> Andre
>>>
>>> [1] https://developer.arm.com/documentation/den0144/latest
>>>
>>> Changes in v8:
>>> - disable error IRQ on irqchip level when device side fails
>>> - use MSC dev pointer for dev_err() instead of mailbox one
>>> - smaller white space and formatting fixes
>>> - reorder MPAM-Fb definitions
>>> - rename error translation function to mpam_fb_to_linux_errno()
>>> - add review and test tags
>>>
>>> Changes in v7:
>>> - add tags
>>> - add new patch to propagate errors in mpam_reprogram_ris_partid()
>>> - prevent loop when mpam_diable() tries MSC accesses again
>>> - register MMIO error IRQ handler without IRQF_ONESHOT
>>> - re-use existing "err" variable instead of declaring "ret"
>>> - initialise mon_sel_lock later, to wait for interface decision
>>> - convert timeout units for nominal latency, between us and ms
>>> Changes in v6:
>>> - keep using hard IRQ handler for MMIO based accesses
>>> - move shmem buffer size and MPAM-Fb version check into mpam_fb.c
>>> - drop mutex guard in mpam_fb_send_request(), we use gotos anyway
>>> - factor out MPAM-Fb protocol error translation
>>> - add MPAM_FB_ERR_BUSY error code to translation
>>> - explicitly report also MPAM-Fb protocol error number when disabling MPAM
>>> - remove unneeded MPAM-Fb details from mpam_internal.h
>>> - drop leftover mpam_fb_msc_id usage
>>>
>>> Changes in v5:
>>> - do not initialise _high variables early
>>> - fix whitespace issues
>>> - add Jonathan's Reviewed-by: tags
>>> - init only either the spinlock OR the mutex for the mon_sel_lock
>>> - drop warning about mapped_hwpage_sz for MPAM-Fb in mpam_msc_zero_mbwu_l()
>>> - change IRQ handler to threaded handler, to allow MPAM-Fb MSC accesses
>>> - disallow per-CPU IRQs (PPIs) for MPAM-Fb
>>> - trigger MPAM disabling on MPAM-Fb protocol errors
>>> - fix MPAM_PROTOCOL_VERSION_CMD name
>>> - use PCC driver defined symbol for PCC_CHAN_FLAGS_IRQ
>>> - add comment about usage of PCC's .signature field
>>> - check for illegal MPAM-Fb command before sending request
>>> - drop CPU accessibility check for MPAM-Fb based MSC accesses
>>> - move MPAM-Fb protocol message structs inside build functions
>>> - add comment about largest message size
>>> - drop extra msc->mpam_fb_msc_id, in favour using pdev->id
>>> - use already defined local dev variable instead of &pdev->dev
>>> - use FIELD_GET() macro for MPAM-Fb protocol check
>>> - drop devm_mutex_init() for pcc_chan_lock
>>>
>>> Changes in v4:
>>> - unify error check patterns
>>> - use scoped_guard(mutex) and ACQUIRE to simplify error handling
>>> - use struct kref for PCC channel refcount
>>> - drop unused mailbox rx_callback function
>>> - increase size of props[] array to hold new msc_id property
>>> - set mailbox TX timeout based on ACPI MPAM table field
>>> - use devm_mutex_init() to allow automatic cleanup
>>> - avoid overwriting error value for arm,not-ready-us property read
>>> - prune MPAM-Fb protocol token to fit into bitmask inside register
>>> - check return value for AIDR read
>>> - use two separate MPAM-Fb protocol payload structs
>>> - prune header list for mpam_fb.c
>>> - drop "nrdy handled as an error" change
>>> - fix message size field in MPAM-Fb protocol shmem area
>>> - drop message size return from MPAM-Fb message build functions
>>> - bring back IRQ flag in MPAM-Fb protocol header
>>>
>>> Changes in v3:
>>> - drop inner/outer mon_sel lock patch, replace with simpler version
>>> - harmonise code patterns in error propagation changes
>>> - drop mon_sel lock before erroring out in mpam_ris_hw_probe_csu_nrdy()
>>> - refactor mpam_msc_read_mbwu_l() to return an error
>>> - drop special NRDY handling in __ris_msmon_read()
>>> - add IRQ numbers in error interrupt handler to help pinpoint failure
>>> - refactor MPAM-Fb message generation to accommodate more than read/write
>>> - check MPAM-Fb protocol version at probe time
>>> - drop mpam_fb.h header, merge into mpam_internal.h
>>> - translate MPAM-Fb error code in Linux codes where needed
>>> - clear IRQ flag bit in MPAM-Fb protocol header
>>> - drop unneeded endianness conversions when crafting MPAM-Fb message
>>> - use C struct to model MPAM-Fb message payload
>>>
>>> Changes in v2:
>>> - add patches to add error propagation to MSC access wrappers
>>> - drop former patch 1/5 (not needed)
>>> - drop lock in mpam_reprogram_msc(), to avoid double lock
>>> - add support for multiple MSCs per PCC channel
>>> - adjust SCMI protocol code to use a PCC subtype 3 channel
>>> - let PCC code handle the PCC channel negotiation (due to subtype 3)
>>> - drop SCMI names in shmem offsets, and use existing PCC type 3 struct
>>> - adjust shmem field offsets to match MPAM-Fb spec, not pure SCMI
>>> - prevent MPAM-Fb calls inside atomic smp_call_function_any() payload
>>> - skip all MSC accesses inside the IRQ handler when using MPAM-Fb
>>>
>>> Andre Przywara (11):
>>> arm_mpam: let low level MSC accessors return an error
>>> arm_mpam: propagate MSC access errors for hw_probe functions
>>> arm_mpam: propagate MSC access errors for MBWU counters
>>> arm_mpam: propagate MSC access errors for msmon helpers
>>> arm_mpam: propagate MSC access errors for __ris_msmon_read()
>>> arm_mpam: propagate MSC access errors for state saving function
>>> arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid()
>>> arm_mpam: prepare mon_sel locking for MPAM-Fb
>>> arm_mpam: add MPAM-Fb MSC firmware access support
>>> arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler
>>> arm_mpam: detect and enable MPAM-Fb PCC support
>>>
>>> drivers/resctrl/Makefile | 2 +-
>>> drivers/resctrl/mpam_devices.c | 825 ++++++++++++++++++++++++--------
>>> drivers/resctrl/mpam_fb.c | 252 ++++++++++
>>> drivers/resctrl/mpam_internal.h | 62 ++-
>>> include/linux/arm_mpam.h | 2 +-
>>> 5 files changed, 926 insertions(+), 217 deletions(-)
>>> create mode 100644 drivers/resctrl/mpam_fb.c
>>>
>>>
>>> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
>>
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support
2026-08-04 12:28 ` [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
2026-08-04 12:50 ` Ben Horgan
@ 2026-08-04 13:36 ` Srivathsa L Rao
1 sibling, 0 replies; 16+ messages in thread
From: Srivathsa L Rao @ 2026-08-04 13:36 UTC (permalink / raw)
To: Andre Przywara, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Catalin Marinas, Will Deacon, Rafael J . Wysocki, Len Brown,
James Morse, Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Ganapatrao Kulkarni, Trilok Soni,
Srinivas Ramana, Niyas Sait, Lee Trager, Ritwick Sharma,
Gavin Shan, linux-acpi, linux-arm-kernel, linux-kernel
Hi Andre,
On 8/4/2026 5:58 PM, Andre Przywara wrote:
> Hi,
>
> On 8/4/26 12:06, Andre Przywara wrote:
>> this is v8 of the MPAM-Fb code, for firmware based MSC accesses.
>> It fixes a potential use-after-free of the dev pointer on error
>> prints, and disables the IRQ on the irqchip side in case doing it on
>> the device side doesn't work. The rest are cosmetic fixes and adding
>> the tags from the diligent reviewers and testers, many thanks for that!
>> I dropped tags from the error IRQ patch (10/11) due to changes.
>> Find the changelog below. Based on v7.2-rc1.
>>
>> =======================
>> The Arm MPAM specification defines Memory System Components (MSCs),
>> which are devices that are programmed through an MMIO register frame. In
>> some occasions this turned out to be too limiting: the MSC might be
>> located behind a separate bus system (for instance inside an on-board
>> controller), it might be mapped secure-only, or in a different processor
>> socket without direct MMIO mapping. Also the MMIO access might be too
>> slow
>> or it would need to be filtered or otherwise access controlled. Finally
>> there might be bugs in the MSC integration, which require a mediating
>> firmware to be accessible.
>
>
> For the records, I looked at the sashiko complaints, by my analysis most
> comments fall into one of those categories:
>
> - Does this new early return / error return .... break MPAM logic?
> It's concerned about just bailing out from the middle of a sequence of
> MPAM read or writes, leaving the MSC in a misconfigured state. This is
> true, but doesn't apply, because we tear down MPAM completely upon an
> MPAM-Fb access returning an error. And if the communication between the
> kernel and an MSC breaks down completely, there isn't much we can do
> anyway: attempts to recover would fail as well.
>
> - Since xxx() now returns an error code, do the callers need
> to be updated to check it?
> - Are there missing error checks in xxx() related to the new error
> return values?
> Yes, this is done in one of the following patches. The series is
> constructed so that only the final patch enables MPAM-Fb, before that
> there is no way MPAM-Fb could be instantiated, so only MMIO accesses are
> happening. They always return 0, so no error could occur and error paths
> are never exercised.
> This is used to construct the functionality gradually, and allow
> multiple, but smaller patches. Mentioned in the cover letter.
>
> - This is a pre-existing issue, but ....
> I haven't checked in detail, but from previous rounds those concerns
> were addressed by Ben's fixes, which IIUC are on its way already. This
> series is based on v7.2-rc1, so those fixes are not applied here.
>
> Hope that helps,
> Cheers,
> Andre
>
>> To accommodate all those different use cases, the MPAM-Fb specification
>> [1] describes an alternative way to access MSCs. Accesses to an MSC
>> would be wrapped in a message and communicated to the system using a
>> shared-memory/mailbox system mostly mimicking the Arm SCMI spec.
>> For ACPI systems, this would be abstracted through an ACPI PCC channel,
>> which provides the shared-memory region and the mailbox trigger. We can
>> lean on existing ACPI parsing code to register with these two
>> subsystems, but cannot rely on the existing SCMI code in the kernel.
>> This means we somewhat need to open code a very simplified SCMI handler,
>> which just provides enough functionality for the very basic subset of
>> SCMI that the MPAM-Fb spec requires.
>>
>> The first seven patches rework all MSC access wrappers to propagate error
>> information. Pure MMIO based MSC accesses would never fail, but the
>> MPAM-Fb access can go wrong in multiple ways. The patches have been split
>> up purely for reviewing reasons, if the number is a problem, we could as
>> well squash them. Please note that until the very last patch of this
>> series
>> any MSC accesses would always only return 0, it's only the final
>> enablement
>> of MPAM-Fb that could possibly introduce errors. Hence all former patches
>> can add error handling gradually, those code paths wouldn't be triggered
>> before patch 11/11.
>> Patch 8/11 solves a nasty problem: At the moment we protect stateful MSC
>> register accesses (mon_sel) through a spinlock. Unfortunately the mailbox
>> subsystem and the slow nature of the communication through this channel
>> forbid MPAM-Fb access in atomic context. So this patch keeps using a
>> spinlock for MMIO based accesses, but reverts to a mutex otherwise.
>> We just deny taking the lock for MPAM-Fb in atomic context, ideally we
>> wouldn't need that (no need to IPI another core when the MSC access does
>> not need to be local to one particular core), or we simply deny that part
>> of the functionality (access through perf).
>> Patch 9/11 adds the code to redirect MSC accesses through the
>> PCC shmem/mailbox system.
>> Patch 10/11 reworks the error interrupt handler to use a threaded IRQ for
>> MPAM-Fb, to be able to do MPAM-Fb MSC accesses inside (which might
>> sleep).
>> The final patch 11/11 then adds the code to detect and store the PCC
>> channel information from the ACPI tables, and eventually enables
>> MPAM-Fb accesses.
>>
>> This would enable systems where some MSCs are not accessible via MMIO to
>> use those components anyway.
>>
>> Please have a look and test!
>>
>> Cheers,
>> Andre
>>
>> [1] https://developer.arm.com/documentation/den0144/latest
>>
>> Changes in v8:
>> - disable error IRQ on irqchip level when device side fails
>> - use MSC dev pointer for dev_err() instead of mailbox one
>> - smaller white space and formatting fixes
>> - reorder MPAM-Fb definitions
>> - rename error translation function to mpam_fb_to_linux_errno()
>> - add review and test tags
>>
>> Changes in v7:
>> - add tags
>> - add new patch to propagate errors in mpam_reprogram_ris_partid()
>> - prevent loop when mpam_diable() tries MSC accesses again
>> - register MMIO error IRQ handler without IRQF_ONESHOT
>> - re-use existing "err" variable instead of declaring "ret"
>> - initialise mon_sel_lock later, to wait for interface decision
>> - convert timeout units for nominal latency, between us and ms
>> Changes in v6:
>> - keep using hard IRQ handler for MMIO based accesses
>> - move shmem buffer size and MPAM-Fb version check into mpam_fb.c
>> - drop mutex guard in mpam_fb_send_request(), we use gotos anyway
>> - factor out MPAM-Fb protocol error translation
>> - add MPAM_FB_ERR_BUSY error code to translation
>> - explicitly report also MPAM-Fb protocol error number when disabling
>> MPAM
>> - remove unneeded MPAM-Fb details from mpam_internal.h
>> - drop leftover mpam_fb_msc_id usage
>>
>> Changes in v5:
>> - do not initialise _high variables early
>> - fix whitespace issues
>> - add Jonathan's Reviewed-by: tags
>> - init only either the spinlock OR the mutex for the mon_sel_lock
>> - drop warning about mapped_hwpage_sz for MPAM-Fb in
>> mpam_msc_zero_mbwu_l()
>> - change IRQ handler to threaded handler, to allow MPAM-Fb MSC accesses
>> - disallow per-CPU IRQs (PPIs) for MPAM-Fb
>> - trigger MPAM disabling on MPAM-Fb protocol errors
>> - fix MPAM_PROTOCOL_VERSION_CMD name
>> - use PCC driver defined symbol for PCC_CHAN_FLAGS_IRQ
>> - add comment about usage of PCC's .signature field
>> - check for illegal MPAM-Fb command before sending request
>> - drop CPU accessibility check for MPAM-Fb based MSC accesses
>> - move MPAM-Fb protocol message structs inside build functions
>> - add comment about largest message size
>> - drop extra msc->mpam_fb_msc_id, in favour using pdev->id
>> - use already defined local dev variable instead of &pdev->dev
>> - use FIELD_GET() macro for MPAM-Fb protocol check
>> - drop devm_mutex_init() for pcc_chan_lock
>>
>> Changes in v4:
>> - unify error check patterns
>> - use scoped_guard(mutex) and ACQUIRE to simplify error handling
>> - use struct kref for PCC channel refcount
>> - drop unused mailbox rx_callback function
>> - increase size of props[] array to hold new msc_id property
>> - set mailbox TX timeout based on ACPI MPAM table field
>> - use devm_mutex_init() to allow automatic cleanup
>> - avoid overwriting error value for arm,not-ready-us property read
>> - prune MPAM-Fb protocol token to fit into bitmask inside register
>> - check return value for AIDR read
>> - use two separate MPAM-Fb protocol payload structs
>> - prune header list for mpam_fb.c
>> - drop "nrdy handled as an error" change
>> - fix message size field in MPAM-Fb protocol shmem area
>> - drop message size return from MPAM-Fb message build functions
>> - bring back IRQ flag in MPAM-Fb protocol header
>>
>> Changes in v3:
>> - drop inner/outer mon_sel lock patch, replace with simpler version
>> - harmonise code patterns in error propagation changes
>> - drop mon_sel lock before erroring out in mpam_ris_hw_probe_csu_nrdy()
>> - refactor mpam_msc_read_mbwu_l() to return an error
>> - drop special NRDY handling in __ris_msmon_read()
>> - add IRQ numbers in error interrupt handler to help pinpoint failure
>> - refactor MPAM-Fb message generation to accommodate more than read/write
>> - check MPAM-Fb protocol version at probe time
>> - drop mpam_fb.h header, merge into mpam_internal.h
>> - translate MPAM-Fb error code in Linux codes where needed
>> - clear IRQ flag bit in MPAM-Fb protocol header
>> - drop unneeded endianness conversions when crafting MPAM-Fb message
>> - use C struct to model MPAM-Fb message payload
>>
>> Changes in v2:
>> - add patches to add error propagation to MSC access wrappers
>> - drop former patch 1/5 (not needed)
>> - drop lock in mpam_reprogram_msc(), to avoid double lock
>> - add support for multiple MSCs per PCC channel
>> - adjust SCMI protocol code to use a PCC subtype 3 channel
>> - let PCC code handle the PCC channel negotiation (due to subtype 3)
>> - drop SCMI names in shmem offsets, and use existing PCC type 3 struct
>> - adjust shmem field offsets to match MPAM-Fb spec, not pure SCMI
>> - prevent MPAM-Fb calls inside atomic smp_call_function_any() payload
>> - skip all MSC accesses inside the IRQ handler when using MPAM-Fb
>>
>> Andre Przywara (11):
>> arm_mpam: let low level MSC accessors return an error
>> arm_mpam: propagate MSC access errors for hw_probe functions
>> arm_mpam: propagate MSC access errors for MBWU counters
>> arm_mpam: propagate MSC access errors for msmon helpers
>> arm_mpam: propagate MSC access errors for __ris_msmon_read()
>> arm_mpam: propagate MSC access errors for state saving function
>> arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid()
>> arm_mpam: prepare mon_sel locking for MPAM-Fb
>> arm_mpam: add MPAM-Fb MSC firmware access support
>> arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler
>> arm_mpam: detect and enable MPAM-Fb PCC support
>>
>> drivers/resctrl/Makefile | 2 +-
>> drivers/resctrl/mpam_devices.c | 825 ++++++++++++++++++++++++--------
>> drivers/resctrl/mpam_fb.c | 252 ++++++++++
>> drivers/resctrl/mpam_internal.h | 62 ++-
>> include/linux/arm_mpam.h | 2 +-
>> 5 files changed, 926 insertions(+), 217 deletions(-)
>> create mode 100644 drivers/resctrl/mpam_fb.c
>>
>>
>> base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
>
Thoroughly tested on a QEMU-based emulation setup with a fake PCC-backed
MSC (MHU doorbell + SRAM shared memory + software-emulated MSC register
state). The setup exercises the full MPAM-Fb probe and runtime paths
including CPU hotplug save/restore of MBWU state, CSU monitoring via
SCMI, and kref lifecycle with multiple MSCs sharing a PCC channel.
Tested-by: Srivathsa L Rao <srivathsa.rao@oss.qualcomm.com>
Best Regards,
Srivathsa L Rao
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-08-04 13:36 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 10:06 [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
2026-08-04 10:06 ` [PATCH v8 01/11] arm_mpam: let low level MSC accessors return an error Andre Przywara
2026-08-04 10:06 ` [PATCH v8 02/11] arm_mpam: propagate MSC access errors for hw_probe functions Andre Przywara
2026-08-04 10:06 ` [PATCH v8 03/11] arm_mpam: propagate MSC access errors for MBWU counters Andre Przywara
2026-08-04 10:06 ` [PATCH v8 04/11] arm_mpam: propagate MSC access errors for msmon helpers Andre Przywara
2026-08-04 10:06 ` [PATCH v8 05/11] arm_mpam: propagate MSC access errors for __ris_msmon_read() Andre Przywara
2026-08-04 10:06 ` [PATCH v8 06/11] arm_mpam: propagate MSC access errors for state saving function Andre Przywara
2026-08-04 10:06 ` [PATCH v8 07/11] arm_mpam: propagate MSC access errors for mpam_reprogram_ris_partid() Andre Przywara
2026-08-04 10:06 ` [PATCH v8 08/11] arm_mpam: prepare mon_sel locking for MPAM-Fb Andre Przywara
2026-08-04 10:06 ` [PATCH v8 09/11] arm_mpam: add MPAM-Fb MSC firmware access support Andre Przywara
2026-08-04 10:06 ` [PATCH v8 10/11] arm_mpam: change MPAM-Fb error IRQ to use a threaded IRQ handler Andre Przywara
2026-08-04 10:06 ` [PATCH v8 11/11] arm_mpam: detect and enable MPAM-Fb PCC support Andre Przywara
2026-08-04 12:28 ` [PATCH v8 00/11] arm_mpam: Add MPAM-Fb firmware support Andre Przywara
2026-08-04 12:50 ` Ben Horgan
2026-08-04 13:02 ` Andre Przywara
2026-08-04 13:36 ` Srivathsa L Rao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox