[PATCH] firmware: arm_scpi: publish scpi_ops with release semantics

From: Jaidev Shastri via B4 Relay

Date: Mon Sep 21 2026 - 21:08:26 EST


From: Jaidev Shastri <jaidevshastri@xxxxxx>

scpi_probe() stores scpi_info early because scpi_init_versions() sends
messages through it, and sets scpi_drvinfo->scpi_ops as its last step.
get_scpi_ops() returns scpi_info->scpi_ops, so the ops pointer is the
gate that scpi-cpufreq, scpi_pm_domain, clk-scpi and scpi-hwmon test
before they use the driver.

Both accesses to scpi_ops are plain, and a consumer that passes the gate
reads scpi_info->channels, ->commands, ->num_chans and
->protocol_version through a fresh load of scpi_info rather than through
the ops pointer. The version fields are written by scpi_init_versions()
after scpi_info was published, so a consumer on another CPU can see a
non-NULL scpi_ops together with stale versions.

Publish scpi_ops with smp_store_release() and read it with
smp_load_acquire() in get_scpi_ops().

Found with MBCheck, a static herd7-based memory consistency checker.

Signed-off-by: Jaidev Shastri <jaidevshastri@xxxxxx>
---
drivers/firmware/arm_scpi.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c
index 68a730d22..eaf8134d7 100644
--- a/drivers/firmware/arm_scpi.c
+++ b/drivers/firmware/arm_scpi.c
@@ -809,7 +809,10 @@ static struct scpi_ops scpi_ops = {

struct scpi_ops *get_scpi_ops(void)
{
- return scpi_info ? scpi_info->scpi_ops : NULL;
+ struct scpi_drvinfo *info = READ_ONCE(scpi_info);
+
+ /* Pairs with the smp_store_release() of scpi_ops in scpi_probe(). */
+ return info ? smp_load_acquire(&info->scpi_ops) : NULL;
}
EXPORT_SYMBOL_GPL(get_scpi_ops);

@@ -1029,7 +1032,13 @@ static int scpi_probe(struct platform_device *pdev)
FIELD_GET(FW_REV_PATCH_MASK,
scpi_drvinfo->firmware_version));

- scpi_drvinfo->scpi_ops = &scpi_ops;
+ /*
+ * scpi_info is already visible (scpi_init_versions() needs it), so the
+ * scpi_ops field is what get_scpi_ops() callers gate on. Publish it
+ * with release semantics so that a consumer that sees the ops also
+ * sees the channels, the command table and the version fields.
+ */
+ smp_store_release(&scpi_drvinfo->scpi_ops, &scpi_ops);

ret = devm_of_platform_populate(dev);
if (ret)

---
base-commit: 93f51579e7df248780214094418f205253383cc5
change-id: 20260921-mb-arm-scpi-904141530492

Best regards,
--
Jaidev Shastri <jaidevshastri@xxxxxx>