* [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths
@ 2026-07-03 20:22 Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 01/17] firmware: arm_scmi: Fix OF node reference handling Sudeep Holla
` (16 more replies)
0 siblings, 17 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
This series fixes a set of SCMI core and transport lifetime issues found
around device creation, channel setup failure, notification teardown,
driver binding and malformed firmware descriptions.
Most of these issues were found by Sashiko[1][2] while reviewing the ACPI
SCMI PCC work[3]. They are posted separately because the problems are
independent SCMI core, mailbox and virtio transport cleanup bugs, and do
not depend on the ACPI PCC series.
Since I started fixing the initial set of issues pointed by sashiko, more
issues have been identified and hopefully with v3, it really calms down.
I can only wish 😄.
Fixes tags need to be checked again or even dropped if it is not a stable
material. Just some placeholders that I thought are appropriated are in place.
The fixes tighten ownership and teardown rules for generated SCMI devices,
transport devices and transport channels. They make internal transport
devices reachable by explicit teardown without exposing them to normal SCMI
driver binding, ensure partially initialized channels are unwound on setup
failures, and avoid IDR/device lifetime races during probe failure and
device unbind.
The series also fixes notifier and notification teardown ordering.
Requested device notifiers are unregistered before protocol IDRs are
destroyed, RCU is used around requested-device protocol lookups, transport
callbacks are stopped before notification state is released, and protocol
bind failures now drop any SCMI handle acquired before probe.
The mailbox transport fixes cover both setup failure unwinding and an early
interrupt window: mailbox callbacks can run as soon as mbox_request_channel()
binds the client and starts the controller, so the SCMI mailbox transport
now publishes the channel state before requesting channels.
The virtio transport fix clears virtio-owned channel state when SCMI
channels are freed, so devres-managed SCMI message pointers do not survive
across SCMI driver unbind and rebind.
Summary:
- Fix OF node reference ownership for generated SCMI devices.
- Allow explicit teardown lookup of internal transport devices.
- Clean up TX/RX channels when SCMI channel setup fails midway.
- Fix SCMI device lifetime handling around child lookup and bus ID reuse.
- Free transport resources when IDR insertion fails after channel setup.
- Unregister requested-device notifier before active protocol IDR teardown.
- Unwind mailbox channels on TX receiver and P2A receiver setup failures.
- Protect requested-device protocol lookup with RCU.
- Avoid modifying an IDR while iterating it during channel cleanup.
- Clear the SystemPower singleton flag on SCMI device creation failure.
- Reject out-of-range DT protocol IDs instead of allowing u8 truncation.
- Stop transport callbacks before releasing notification state.
- Publish mailbox channel state before requesting mailbox channels.
- Use the owning channel ID when tearing down shared transport devices.
- Drop SCMI handles on protocol bind and device-link failures.
- Clear virtio channel lists when freeing SCMI virtio channels.
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
--
Changes in v3:
- Added SCMI bus resource release helper so bus IDs are released from
destroy, register-failure and final-release paths, and cleared after
release to avoid later double-free.
- Moved SystemPower singleton cleanup into the same resource helper so
direct driver-core child release also clears the flag.
- Removed the unused mailbox P2A unwind label.
- Published cinfo->handle before transport chan_setup() so early mailbox
callbacks have a valid SCMI handle.
- Added fix to destroy shared transport devices using cinfo->id instead of
the IDR iterator key.
- Added SCMI handle cleanup on device_link_add() failure and
BUS_NOTIFY_DRIVER_NOT_BOUND.
- Added virtio channel cleanup to detach unused virtqueue buffers and clear
local channel lists across SCMI unbind/rebind.
- Link to v2: https://patch.msgid.link/20260701-scmi_core_fixes-v2-0-1f5e85553f73@kernel.org
Changes in v2:
(Mostly addressing the additional issues found by Sashiko)
- Added fixes for IDR mutation during channel cleanup.
- Added SystemPower singleton flag unwind on device creation failure.
- Added validation to skip out-of-range DT protocol IDs.
- Moved notification teardown after transport channel cleanup.
- Published mailbox cinfo before mbox_request_channel() can enable callbacks.
- Link to v1: https://patch.msgid.link/20260630-scmi_core_fixes-v1-0-f932c1e51992@kernel.org
[1] https://sashiko.dev/#/patchset/20260525-acpi_scmi_pcc-v2-0-4f38938d08d8@arm.com
[2] https://sashiko.dev/#/patchset/20260630-scmi_core_fixes-v1-0-f932c1e51992@kernel.org
[3] https://patch.msgid.link/20260525-acpi_scmi_pcc-v2-0-4f38938d08d8@arm.com
---
Sudeep Holla (17):
firmware: arm_scmi: Fix OF node reference handling
firmware: arm_scmi: Fix transport device teardown lookup
firmware: arm_scmi: Clean up channels on setup failure
firmware: arm_scmi: Fix SCMI device destroy lifetimes
firmware: arm_scmi: Free transport channel on IDR failure
firmware: arm_scmi: Unregister device notifier before IDR teardown
firmware: arm_scmi: Unwind TX receiver mailbox setup failure
firmware: arm_scmi: Unwind P2A receiver mailbox setup failure
firmware: arm_scmi: Protect device request lookup with RCU
firmware: arm_scmi: Avoid IDR updates while cleaning channels
firmware: arm_scmi: Clear SystemPower flag on create failure
firmware: arm_scmi: Reject out of range DT protocol IDs
firmware: arm_scmi: Stop channels before notification teardown
firmware: arm_scmi: Publish channel state before mailbox request
firmware: arm_scmi: Use channel ID for transport teardown
firmware: arm_scmi: Drop handle on protocol bind failures
firmware: arm_scmi: Clear virtio channel lists on free
drivers/firmware/arm_scmi/bus.c | 89 +++++++++++++++++---------
drivers/firmware/arm_scmi/common.h | 2 +
drivers/firmware/arm_scmi/driver.c | 79 ++++++++++++++---------
drivers/firmware/arm_scmi/transports/mailbox.c | 24 +++++--
drivers/firmware/arm_scmi/transports/virtio.c | 17 +++++
5 files changed, 143 insertions(+), 68 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260629-scmi_core_fixes-da3cd753b4ea
--
Regards,
Sudeep
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 01/17] firmware: arm_scmi: Fix OF node reference handling
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 02/17] firmware: arm_scmi: Fix transport device teardown lookup Sudeep Holla
` (15 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
SCMI devices store the DT node in dev.of_node through
device_set_node(), but that helper only assigns the fwnode and
of_node pointers without taking an OF node reference.
Take a reference when assigning the node and release it from the
SCMI device release path. With the device owning that reference,
remove the separate channel-side get/put pair from the core driver.
Fixes: 96da4a99ce50 ("firmware: arm_scmi: Set fwnode for the scmi_device")
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/bus.c | 3 ++-
drivers/firmware/arm_scmi/driver.c | 4 ----
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index 793be9eabaed..f643a1f0e282 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -395,6 +395,7 @@ static void scmi_device_release(struct device *dev)
{
struct scmi_device *scmi_dev = to_scmi_dev(dev);
+ of_node_put(dev->of_node);
kfree_const(scmi_dev->name);
kfree(scmi_dev);
}
@@ -465,7 +466,7 @@ __scmi_device_create(struct device_node *np, struct device *parent,
scmi_dev->id = id;
scmi_dev->protocol_id = protocol;
scmi_dev->dev.parent = parent;
- device_set_node(&scmi_dev->dev, of_fwnode_handle(np));
+ device_set_node(&scmi_dev->dev, of_fwnode_handle(of_node_get(np)));
scmi_dev->dev.bus = &scmi_bus_type;
scmi_dev->dev.release = scmi_device_release;
dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id);
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 3e0d975ec94c..b9245238e293 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2778,13 +2778,11 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
devm_kfree(info->dev, cinfo);
return -EINVAL;
}
- of_node_get(of_node);
cinfo->id = prot_id;
cinfo->dev = &tdev->dev;
ret = info->desc->ops->chan_setup(cinfo, info->dev, tx);
if (ret) {
- of_node_put(of_node);
scmi_device_destroy(info->dev, prot_id, name);
devm_kfree(info->dev, cinfo);
return ret;
@@ -2807,7 +2805,6 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
"unable to allocate SCMI idr slot err %d\n", ret);
/* Destroy channel and device only if created by this call. */
if (tdev) {
- of_node_put(of_node);
scmi_device_destroy(info->dev, prot_id, name);
devm_kfree(info->dev, cinfo);
}
@@ -2892,7 +2889,6 @@ static int scmi_chan_destroy(int id, void *p, void *idr)
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
struct scmi_device *sdev = to_scmi_dev(cinfo->dev);
- of_node_put(cinfo->dev->of_node);
scmi_device_destroy(info->dev, id, sdev->name);
cinfo->dev = NULL;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 02/17] firmware: arm_scmi: Fix transport device teardown lookup
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 01/17] firmware: arm_scmi: Fix OF node reference handling Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 03/17] firmware: arm_scmi: Clean up channels on setup failure Sudeep Holla
` (14 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
SCMI transport devices are deliberately excluded from normal SCMI bus
matching so protocol drivers cannot bind to the internal transport
children. However, scmi_device_destroy() uses the same protocol/name
lookup to find devices that must be unregistered during channel teardown.
Split the match helper so driver matching still skips transport devices,
while explicit child lookup can find them for teardown. Use a shared
transport-device name prefix macro for both matching and name generation.
Since transport-device names are derived from direction and protocol ID,
reject duplicate protocol channel setup before creating or finding a
transport device. This prevents malformed firmware with duplicate
protocol child nodes from reusing an existing transport device and then
destroying it when the duplicate IDR insertion fails.
Fixes: 9593804c44c2 ("firmware: arm_scmi: Exclude transport devices from bus matching")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/bus.c | 22 +++++++++++++++++-----
drivers/firmware/arm_scmi/common.h | 2 ++
drivers/firmware/arm_scmi/driver.c | 5 ++++-
3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index f643a1f0e282..d4beefa4234f 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -201,21 +201,33 @@ scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
scmi_protocol_device_unrequest(entry);
}
-static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
- const struct scmi_device_id *id_table)
+static bool scmi_device_is_transport(const struct scmi_device *scmi_dev)
+{
+ return !strncmp(scmi_dev->name, SCMI_TRANSPORT_DEVNAME_PREFIX,
+ strlen(SCMI_TRANSPORT_DEVNAME_PREFIX));
+}
+
+static int __scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
+ const struct scmi_device_id *id_table,
+ bool skip_transport)
{
if (!id_table || !id_table->name)
return 0;
- /* Always skip transport devices from matching */
for (; id_table->protocol_id && id_table->name; id_table++)
if (id_table->protocol_id == scmi_dev->protocol_id &&
- strncmp(scmi_dev->name, "__scmi_transport_device", 23) &&
+ !(skip_transport && scmi_device_is_transport(scmi_dev)) &&
!strcmp(id_table->name, scmi_dev->name))
return 1;
return 0;
}
+static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev,
+ const struct scmi_device_id *id_table)
+{
+ return __scmi_dev_match_by_id_table(scmi_dev, id_table, true);
+}
+
static int scmi_dev_match_id(struct scmi_device *scmi_dev,
const struct scmi_driver *scmi_drv)
{
@@ -235,7 +247,7 @@ static int scmi_match_by_id_table(struct device *dev, const void *data)
struct scmi_device *scmi_dev = to_scmi_dev(dev);
const struct scmi_device_id *id_table = data;
- return scmi_dev_match_by_id_table(scmi_dev, id_table);
+ return __scmi_dev_match_by_id_table(scmi_dev, id_table, false);
}
static struct scmi_device *scmi_child_dev_find(struct device *parent,
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index b9723c105fc1..fe8c22cfb9f7 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -34,6 +34,8 @@
#define SCMI_SHMEM_MAX_PAYLOAD_SIZE 104
+#define SCMI_TRANSPORT_DEVNAME_PREFIX "__scmi_transport_device"
+
enum scmi_error_codes {
SCMI_SUCCESS = 0, /* Success */
SCMI_ERR_SUPPORT = -1, /* Not supported */
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index b9245238e293..b9ba566fc759 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2751,6 +2751,9 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
idx = tx ? 0 : 1;
idr = tx ? &info->tx_idr : &info->rx_idr;
+ if (idr_find(idr, prot_id))
+ return -EEXIST;
+
if (!info->desc->ops->chan_available(of_node, idx)) {
cinfo = idr_find(idr, SCMI_PROTOCOL_BASE);
if (unlikely(!cinfo)) /* Possible only if platform has no Rx */
@@ -2768,7 +2771,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
cinfo->no_completion_irq = info->desc->no_completion_irq;
/* Create a unique name for this transport device */
- snprintf(name, 32, "__scmi_transport_device_%s_%02X",
+ snprintf(name, sizeof(name), SCMI_TRANSPORT_DEVNAME_PREFIX "_%s_%02X",
idx ? "rx" : "tx", prot_id);
/* Create a uniquely named, dedicated transport device for this chan */
tdev = scmi_device_create(of_node, info->dev, prot_id, name);
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 03/17] firmware: arm_scmi: Clean up channels on setup failure
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 01/17] firmware: arm_scmi: Fix OF node reference handling Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 02/17] firmware: arm_scmi: Fix transport device teardown lookup Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 04/17] firmware: arm_scmi: Fix SCMI device destroy lifetimes Sudeep Holla
` (13 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
scmi_channels_setup() can fail after the common BASE channel or earlier
protocol channels have already been registered in the TX/RX IDRs.
Route this failure through the existing channel cleanup label so the
transport channels, transport devices and IDR state created before the
failure are released before the probe error path frees the SCMI instance
ID.
Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index b9ba566fc759..861087b2920e 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -3262,7 +3262,7 @@ static int scmi_probe(struct platform_device *pdev)
ret = scmi_channels_setup(info);
if (ret) {
err_str = "failed to setup channels\n";
- goto clear_ida;
+ goto clear_txrx_setup;
}
ret = bus_register_notifier(&scmi_bus_type, &info->bus_nb);
@@ -3377,7 +3377,6 @@ static int scmi_probe(struct platform_device *pdev)
bus_unregister_notifier(&scmi_bus_type, &info->bus_nb);
clear_txrx_setup:
scmi_cleanup_txrx_channels(info);
-clear_ida:
ida_free(&scmi_id, info->id);
out_err:
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 04/17] firmware: arm_scmi: Fix SCMI device destroy lifetimes
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (2 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 03/17] firmware: arm_scmi: Clean up channels on setup failure Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 05/17] firmware: arm_scmi: Free transport channel on IDR failure Sudeep Holla
` (12 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
scmi_child_dev_find() drops the reference returned by
device_find_child() before returning the scmi_device pointer. A
concurrent unregister can then release the device while the destroy path
is still using the returned pointer.
Make the lookup helper return the device_find_child() reference and keep
it until scmi_device_destroy() has finished unregistering the child.
Also split device_unregister() in __scmi_device_destroy() so the SCMI bus
ID is not made reusable until after device_del() has removed the old
scmi_dev.N name from sysfs. This avoids a new SCMI device reusing the
same ID while the old device is still registered.
The final device release callback is also a possible cleanup path when
SCMI children are deleted by driver core recursion rather than
__scmi_device_destroy(). Release the SCMI bus ID from a common helper
used by destroy, register-failure and final-release paths, and clear
scmi_dev->id after freeing it so the final release cannot free the same
ID again.
Fixes: 46edb8d1322c ("firmware: arm_scmi: provide the mandatory device release callback")
Fixes: 9ca67840c0dd ("firmware: arm_scmi: Balance device refcount when destroying devices")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/bus.c | 36 ++++++++++++++++++++++++------------
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index d4beefa4234f..cbd2e6058210 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -250,8 +250,9 @@ static int scmi_match_by_id_table(struct device *dev, const void *data)
return __scmi_dev_match_by_id_table(scmi_dev, id_table, false);
}
-static struct scmi_device *scmi_child_dev_find(struct device *parent,
- int prot_id, const char *name)
+/* Returns a device_find_child() reference which must be dropped by caller. */
+static struct scmi_device *
+scmi_child_dev_find_get(struct device *parent, int prot_id, const char *name)
{
struct scmi_device_id id_table[2] = { 0 };
struct device *dev;
@@ -263,9 +264,6 @@ static struct scmi_device *scmi_child_dev_find(struct device *parent,
if (!dev)
return NULL;
- /* Drop the refcnt bumped implicitly by device_find_child */
- put_device(dev);
-
return to_scmi_dev(dev);
}
@@ -403,10 +401,19 @@ void scmi_driver_unregister(struct scmi_driver *driver)
}
EXPORT_SYMBOL_GPL(scmi_driver_unregister);
+static void scmi_device_release_resources(struct scmi_device *scmi_dev)
+{
+ if (scmi_dev->id) {
+ ida_free(&scmi_bus_id, scmi_dev->id);
+ scmi_dev->id = 0;
+ }
+}
+
static void scmi_device_release(struct device *dev)
{
struct scmi_device *scmi_dev = to_scmi_dev(dev);
+ scmi_device_release_resources(scmi_dev);
of_node_put(dev->of_node);
kfree_const(scmi_dev->name);
kfree(scmi_dev);
@@ -422,8 +429,9 @@ static void __scmi_device_destroy(struct scmi_device *scmi_dev)
if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM)
atomic_set(&scmi_syspower_registered, 0);
- ida_free(&scmi_bus_id, scmi_dev->id);
- device_unregister(&scmi_dev->dev);
+ device_del(&scmi_dev->dev);
+ scmi_device_release_resources(scmi_dev);
+ put_device(&scmi_dev->dev);
}
static struct scmi_device *
@@ -440,9 +448,11 @@ __scmi_device_create(struct device_node *np, struct device *parent,
* each DT defined protocol at probe time, and the concurrent
* registration of SCMI drivers.
*/
- scmi_dev = scmi_child_dev_find(parent, protocol, name);
- if (scmi_dev)
+ scmi_dev = scmi_child_dev_find_get(parent, protocol, name);
+ if (scmi_dev) {
+ put_device(&scmi_dev->dev);
return scmi_dev;
+ }
/*
* Ignore any possible subsequent failures while creating the device
@@ -492,8 +502,8 @@ __scmi_device_create(struct device_node *np, struct device *parent,
return scmi_dev;
put_dev:
+ scmi_device_release_resources(scmi_dev);
put_device(&scmi_dev->dev);
- ida_free(&scmi_bus_id, id);
return NULL;
}
@@ -574,9 +584,11 @@ void scmi_device_destroy(struct device *parent, int protocol, const char *name)
{
struct scmi_device *scmi_dev;
- scmi_dev = scmi_child_dev_find(parent, protocol, name);
- if (scmi_dev)
+ scmi_dev = scmi_child_dev_find_get(parent, protocol, name);
+ if (scmi_dev) {
__scmi_device_destroy(scmi_dev);
+ put_device(&scmi_dev->dev);
+ }
}
EXPORT_SYMBOL_GPL(scmi_device_destroy);
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 05/17] firmware: arm_scmi: Free transport channel on IDR failure
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (3 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 04/17] firmware: arm_scmi: Fix SCMI device destroy lifetimes Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 06/17] firmware: arm_scmi: Unregister device notifier before IDR teardown Sudeep Holla
` (11 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
If transport channel setup succeeds but the following IDR insertion fails,
the error path destroys the transport device and frees the channel info
without invoking the transport cleanup callback.
Call chan_free() before destroying the device so transport specific
resources such as IRQs, mailbox channels and mapped shared memory are
released consistently with the normal teardown path.
Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 861087b2920e..4b369b003003 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2808,6 +2808,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
"unable to allocate SCMI idr slot err %d\n", ret);
/* Destroy channel and device only if created by this call. */
if (tdev) {
+ info->desc->ops->chan_free(prot_id, cinfo, idr);
scmi_device_destroy(info->dev, prot_id, name);
devm_kfree(info->dev, cinfo);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 06/17] firmware: arm_scmi: Unregister device notifier before IDR teardown
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (4 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 05/17] firmware: arm_scmi: Free transport channel on IDR failure Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 07/17] firmware: arm_scmi: Unwind TX receiver mailbox setup failure Sudeep Holla
` (10 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
The requested-devices notifier looks up protocol fwnodes from the
active_protocols IDR. During remove, unregister the notifier before
releasing and destroying active_protocols so no notifier callback can race
with the IDR teardown.
Keep the bus notifier registered until after the protocol state is torn
down, matching the existing remove ordering for SCMI bus users.
Fixes: 53b8c25df708 ("firmware: arm_scmi: Add common notifier helpers")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 4b369b003003..6df0fe055d64 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -3402,6 +3402,9 @@ static void scmi_remove(struct platform_device *pdev)
scmi_notification_exit(&info->handle);
+ blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
+ &info->dev_req_nb);
+
mutex_lock(&info->protocols_mtx);
idr_destroy(&info->protocols);
mutex_unlock(&info->protocols_mtx);
@@ -3410,8 +3413,6 @@ static void scmi_remove(struct platform_device *pdev)
of_node_put(child);
idr_destroy(&info->active_protocols);
- blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
- &info->dev_req_nb);
bus_unregister_notifier(&scmi_bus_type, &info->bus_nb);
/* Safe to free channels since no more users */
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 07/17] firmware: arm_scmi: Unwind TX receiver mailbox setup failure
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (5 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 06/17] firmware: arm_scmi: Unregister device notifier before IDR teardown Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 08/17] firmware: arm_scmi: Unwind P2A " Sudeep Holla
` (9 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
mailbox_chan_setup() can request an additional unidirectional TX
receiver channel after successfully acquiring the primary channel. If
that second request fails, the function returns immediately and leaves
the primary channel allocated.
Unwind the primary mailbox channel before returning the error so probe
deferral or other setup failures do not leave the channel busy for later
probe attempts.
Fixes: 9f68ff79ec2c ("firmware: arm_scmi: Add support for unidirectional mailbox channels")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/transports/mailbox.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c
index ae0f67e6cc45..44d45ce838e5 100644
--- a/drivers/firmware/arm_scmi/transports/mailbox.c
+++ b/drivers/firmware/arm_scmi/transports/mailbox.c
@@ -225,9 +225,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
smbox->chan_receiver = mbox_request_channel(cl, a2p_rx_chan);
if (IS_ERR(smbox->chan_receiver)) {
ret = PTR_ERR(smbox->chan_receiver);
+ smbox->chan_receiver = NULL;
if (ret != -EPROBE_DEFER)
dev_err(cdev, "failed to request SCMI Tx Receiver mailbox\n");
- return ret;
+ goto err_free_chan;
}
}
@@ -246,6 +247,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
mutex_init(&smbox->chan_lock);
return 0;
+
+err_free_chan:
+ mbox_free_channel(smbox->chan);
+ return ret;
}
static int mailbox_chan_free(int id, void *p, void *data)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 08/17] firmware: arm_scmi: Unwind P2A receiver mailbox setup failure
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (6 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 07/17] firmware: arm_scmi: Unwind TX receiver mailbox setup failure Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 09/17] firmware: arm_scmi: Protect device request lookup with RCU Sudeep Holla
` (8 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
mailbox_chan_setup() can request an additional P2A receiver channel after
successfully acquiring the primary P2A channel. If that later request
fails, the function returns immediately and leaves the primary channel
allocated.
Unwind the primary mailbox channel before returning the error so probe
deferral or other setup failures do not leave the channel busy for later
probe attempts.
Fixes: fa8b28ba22d9 ("firmware: arm_scmi: Add support for platform to agent channel completion")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/transports/mailbox.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c
index 44d45ce838e5..d41b8451bd21 100644
--- a/drivers/firmware/arm_scmi/transports/mailbox.c
+++ b/drivers/firmware/arm_scmi/transports/mailbox.c
@@ -236,9 +236,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
smbox->chan_platform_receiver = mbox_request_channel(cl, p2a_rx_chan);
if (IS_ERR(smbox->chan_platform_receiver)) {
ret = PTR_ERR(smbox->chan_platform_receiver);
+ smbox->chan_platform_receiver = NULL;
if (ret != -EPROBE_DEFER)
dev_err(cdev, "failed to request SCMI P2A Receiver mailbox\n");
- return ret;
+ goto err_free_chan;
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 09/17] firmware: arm_scmi: Protect device request lookup with RCU
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (7 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 08/17] firmware: arm_scmi: Unwind P2A " Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 10/17] firmware: arm_scmi: Avoid IDR updates while cleaning channels Sudeep Holla
` (7 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
The SCMI device request notifier looks up protocol OF nodes from the
active_protocols IDR. The IDR lookup can run concurrently with protocol
activation while probe is still registering protocols and creating their
SCMI devices.
Wrap the lookup in an RCU read-side critical section as required by the
IDR API for lockless readers.
Fixes: 53b8c25df708 ("firmware: arm_scmi: Add common notifier helpers")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 6df0fe055d64..a575e661f1e2 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -33,6 +33,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/processor.h>
+#include <linux/rcupdate.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/xarray.h>
@@ -2957,7 +2958,9 @@ static int scmi_device_request_notifier(struct notifier_block *nb,
struct scmi_device_id *id_table = data;
struct scmi_info *info = req_nb_to_scmi_info(nb);
+ rcu_read_lock();
np = idr_find(&info->active_protocols, id_table->protocol_id);
+ rcu_read_unlock();
if (!np)
return NOTIFY_DONE;
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 10/17] firmware: arm_scmi: Avoid IDR updates while cleaning channels
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (8 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 09/17] firmware: arm_scmi: Protect device request lookup with RCU Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 11/17] firmware: arm_scmi: Clear SystemPower flag on create failure Sudeep Holla
` (6 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
scmi_cleanup_channels() walks the TX/RX channel IDRs with
idr_for_each() to free transport resources and destroy the dedicated
transport devices before calling idr_destroy().
The destroy callback removed each entry from the same IDR being walked.
That is not needed for this cleanup path, and it is unsafe because
idr_for_each() has not advanced its radix-tree iterator while the
callback is running. Removing the current entry from the callback can
invalidate the iterator state. The callback also cannot be protected by
rcu_read_lock(), because scmi_device_destroy() may sleep.
Leave IDR teardown to the following idr_destroy() call and keep the
callback limited to device destruction.
Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index a575e661f1e2..ae4b7128276b 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2886,7 +2886,7 @@ static int scmi_channels_setup(struct scmi_info *info)
return 0;
}
-static int scmi_chan_destroy(int id, void *p, void *idr)
+static int scmi_chan_destroy(int id, void *p, void *data)
{
struct scmi_chan_info *cinfo = p;
@@ -2898,8 +2898,6 @@ static int scmi_chan_destroy(int id, void *p, void *idr)
cinfo->dev = NULL;
}
- idr_remove(idr, id);
-
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 11/17] firmware: arm_scmi: Clear SystemPower flag on create failure
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (9 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 10/17] firmware: arm_scmi: Avoid IDR updates while cleaning channels Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 12/17] firmware: arm_scmi: Reject out of range DT protocol IDs Sudeep Holla
` (5 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
__scmi_device_create() reserves the singleton SystemPower protocol
device by setting scmi_syspower_registered before allocating and
registering the SCMI device.
If any later step fails, the function returns NULL but leaves the flag
set. A subsequent retry, for example after probe deferral, then observes
the stale reservation and rejects creation of the SystemPower protocol
device permanently.
Route all failures after the successful reservation through a common
unwind path which clears scmi_syspower_registered again. Keep the
duplicate-device rejection path unchanged because that path did not
acquire the reservation.
The same singleton reservation can also be dropped from the final release
callback if SCMI child devices are deleted directly by the driver core
rather than through __scmi_device_destroy(). Keep the SystemPower flag
clear in the common resource helper used by destroy, register-failure and
final-release paths so all SystemPower cleanup follows the same rule.
Fixes: 2c3e674465e7 ("firmware: arm_scmi: Refactor device create/destroy helpers")
Fixes: 46edb8d1322c ("firmware: arm_scmi: provide the mandatory device release callback")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/bus.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index cbd2e6058210..11ff0b8d2c05 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -403,6 +403,9 @@ EXPORT_SYMBOL_GPL(scmi_driver_unregister);
static void scmi_device_release_resources(struct scmi_device *scmi_dev)
{
+ if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM)
+ atomic_set(&scmi_syspower_registered, 0);
+
if (scmi_dev->id) {
ida_free(&scmi_bus_id, scmi_dev->id);
scmi_dev->id = 0;
@@ -426,9 +429,6 @@ static void __scmi_device_destroy(struct scmi_device *scmi_dev)
dev_name(&scmi_dev->dev), scmi_dev->protocol_id,
scmi_dev->name);
- if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM)
- atomic_set(&scmi_syspower_registered, 0);
-
device_del(&scmi_dev->dev);
scmi_device_release_resources(scmi_dev);
put_device(&scmi_dev->dev);
@@ -440,6 +440,7 @@ __scmi_device_create(struct device_node *np, struct device *parent,
{
int id, retval;
struct scmi_device *scmi_dev;
+ bool syspower = (protocol == SCMI_PROTOCOL_SYSTEM);
/*
* If the same protocol/name device already exist under the same parent
@@ -455,14 +456,12 @@ __scmi_device_create(struct device_node *np, struct device *parent,
}
/*
- * Ignore any possible subsequent failures while creating the device
- * since we are doomed anyway at that point; not using a mutex which
- * spans across this whole function to keep things simple and to avoid
- * to serialize all the __scmi_device_create calls across possibly
- * different SCMI server instances (parent)
+ * Reserve the singleton SystemPower protocol device before
+ * allocation and registration. This keeps creation simple without
+ * a mutex spanning the whole function; error paths after the
+ * reservation must drop it again.
*/
- if (protocol == SCMI_PROTOCOL_SYSTEM &&
- atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) {
+ if (syspower && atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) {
dev_warn(parent,
"SCMI SystemPower protocol device must be unique !\n");
return NULL;
@@ -470,19 +469,19 @@ __scmi_device_create(struct device_node *np, struct device *parent,
scmi_dev = kzalloc_obj(*scmi_dev);
if (!scmi_dev)
- return NULL;
+ goto clear_syspower;
scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL);
if (!scmi_dev->name) {
kfree(scmi_dev);
- return NULL;
+ goto clear_syspower;
}
id = ida_alloc_min(&scmi_bus_id, 1, GFP_KERNEL);
if (id < 0) {
kfree_const(scmi_dev->name);
kfree(scmi_dev);
- return NULL;
+ goto clear_syspower;
}
scmi_dev->id = id;
@@ -504,6 +503,9 @@ __scmi_device_create(struct device_node *np, struct device *parent,
put_dev:
scmi_device_release_resources(scmi_dev);
put_device(&scmi_dev->dev);
+clear_syspower:
+ if (syspower)
+ atomic_set(&scmi_syspower_registered, 0);
return NULL;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 12/17] firmware: arm_scmi: Reject out of range DT protocol IDs
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (10 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 11/17] firmware: arm_scmi: Clear SystemPower flag on create failure Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 13/17] firmware: arm_scmi: Stop channels before notification teardown Sudeep Holla
` (4 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
SCMI protocol IDs carried in message headers are limited by
MSG_PROTOCOL_ID_MASK. The DT parsing paths noticed protocol IDs
outside that range, but only logged an error and then kept processing
the invalid value.
That lets a malformed 32-bit DT reg value reach helpers which take a u8
protocol ID, where it can be truncated and/or treated as a different
protocol.
For channel setup, two different out-of-range values can also be used as
distinct IDR keys while aliasing the generated SCMI protocol identity.
Skip DT protocol nodes whose reg value does not fit the SCMI protocol ID
field before setting up channels or creating protocol devices.
Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index ae4b7128276b..f515b192c1bd 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2874,9 +2874,11 @@ static int scmi_channels_setup(struct scmi_info *info)
if (of_property_read_u32(child, "reg", &prot_id))
continue;
- if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) {
dev_err(info->dev,
"Out of range protocol %d\n", prot_id);
+ continue;
+ }
ret = scmi_txrx_setup(info, child, prot_id);
if (ret)
@@ -3341,8 +3343,10 @@ static int scmi_probe(struct platform_device *pdev)
if (of_property_read_u32(child, "reg", &prot_id))
continue;
- if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id))
+ if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) {
dev_err(dev, "Out of range protocol %d\n", prot_id);
+ continue;
+ }
if (!scmi_is_protocol_implemented(handle, prot_id)) {
dev_err(dev, "SCMI protocol %d not implemented\n",
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 13/17] firmware: arm_scmi: Stop channels before notification teardown
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (11 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 12/17] firmware: arm_scmi: Reject out of range DT protocol IDs Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 14/17] firmware: arm_scmi: Publish channel state before mailbox request Sudeep Holla
` (3 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
scmi_notification_exit() clears and releases the notification instance,
but transport callbacks can still deliver incoming notifications until
the TX/RX channels are freed.
During remove, an RX interrupt in that window can enter scmi_notify()
while the notification instance is being torn down and then dereference
freed notification state. The same ordering exists on the probe error
path after notification initialization.
Unregister the requested device notifier first, then stop transport
callbacks by cleaning up the TX/RX channels before releasing the
notification core. This keeps the notification data alive until no
transport callback can queue or process a new event.
Fixes: 1e7cbfaa66d3 ("firmware: arm_scmi: Free mailbox channels if probe fails")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index f515b192c1bd..ddd026b05300 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -3328,7 +3328,7 @@ static int scmi_probe(struct platform_device *pdev)
dev_err(dev, "%s", err_str);
return 0;
}
- goto notification_exit;
+ goto raw_mode_cleanup;
}
mutex_lock(&scmi_list_mutex);
@@ -3372,10 +3372,9 @@ static int scmi_probe(struct platform_device *pdev)
return 0;
-notification_exit:
+raw_mode_cleanup:
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
scmi_raw_mode_cleanup(info->raw);
- scmi_notification_exit(&info->handle);
clear_dev_req_notifier:
blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
&info->dev_req_nb);
@@ -3383,6 +3382,7 @@ static int scmi_probe(struct platform_device *pdev)
bus_unregister_notifier(&scmi_bus_type, &info->bus_nb);
clear_txrx_setup:
scmi_cleanup_txrx_channels(info);
+ scmi_notification_exit(&info->handle);
ida_free(&scmi_id, info->id);
out_err:
@@ -3405,11 +3405,13 @@ static void scmi_remove(struct platform_device *pdev)
list_del(&info->node);
mutex_unlock(&scmi_list_mutex);
- scmi_notification_exit(&info->handle);
-
blocking_notifier_chain_unregister(&scmi_requested_devices_nh,
&info->dev_req_nb);
+ /* Stop transport callbacks before tearing down notifications. */
+ scmi_cleanup_txrx_channels(info);
+ scmi_notification_exit(&info->handle);
+
mutex_lock(&info->protocols_mtx);
idr_destroy(&info->protocols);
mutex_unlock(&info->protocols_mtx);
@@ -3420,9 +3422,6 @@ static void scmi_remove(struct platform_device *pdev)
bus_unregister_notifier(&scmi_bus_type, &info->bus_nb);
- /* Safe to free channels since no more users */
- scmi_cleanup_txrx_channels(info);
-
ida_free(&scmi_id, info->id);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 14/17] firmware: arm_scmi: Publish channel state before mailbox request
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (12 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 13/17] firmware: arm_scmi: Stop channels before notification teardown Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 15/17] firmware: arm_scmi: Use channel ID for transport teardown Sudeep Holla
` (2 subsequent siblings)
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
mailbox_chan_setup() initializes smbox->cinfo only after all mailbox
channels have been requested successfully. That is too late because
mbox_request_channel() binds the client before invoking the controller
startup callback, and startup can enable interrupt delivery.
If a pending or spurious mailbox interrupt fires during that window,
mbox_chan_received_data() can call the SCMI mailbox rx_callback() before
smbox->cinfo is set. The callback dereferences smbox->cinfo on both the
spurious IRQ path and the normal RX path, so this can crash before
channel setup has completed.
Publishing only the mailbox transport pointers is not sufficient because
an early mailbox callback can enter the SCMI core before scmi_chan_setup()
has assigned cinfo->handle. The core derives scmi_info from cinfo->handle
in the RX path, so a NULL handle can still fault even though smbox->cinfo
is valid.
Publish cinfo->transport_info, smbox->cinfo, and cinfo->handle before
requesting any mailbox channel. Also initialize the chan_lock before the
request, and clear the early published transport pointers again on setup
failure so later cleanup does not see a half-initialized transport.
Fixes: 5c8a47a5a91d ("firmware: arm_scmi: Make scmi core independent of the transport type")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 2 +-
drivers/firmware/arm_scmi/transports/mailbox.c | 14 +++++++++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index ddd026b05300..df574961875c 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2785,6 +2785,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
cinfo->id = prot_id;
cinfo->dev = &tdev->dev;
+ cinfo->handle = &info->handle;
ret = info->desc->ops->chan_setup(cinfo, info->dev, tx);
if (ret) {
scmi_device_destroy(info->dev, prot_id, name);
@@ -2816,7 +2817,6 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node,
return ret;
}
- cinfo->handle = &info->handle;
return 0;
}
diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c
index d41b8451bd21..4c9d1e4abd85 100644
--- a/drivers/firmware/arm_scmi/transports/mailbox.c
+++ b/drivers/firmware/arm_scmi/transports/mailbox.c
@@ -211,13 +211,18 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
cl->tx_block = false;
cl->knows_txdone = tx;
+ cinfo->transport_info = smbox;
+ smbox->cinfo = cinfo;
+ mutex_init(&smbox->chan_lock);
+
smbox->chan = mbox_request_channel(cl, tx ? 0 : p2a_chan);
if (IS_ERR(smbox->chan)) {
ret = PTR_ERR(smbox->chan);
+ smbox->chan = NULL;
if (ret != -EPROBE_DEFER)
dev_err(cdev,
"failed to request SCMI %s mailbox\n", desc);
- return ret;
+ goto err_clear_cinfo;
}
/* Additional unidirectional channel for TX if needed */
@@ -243,14 +248,13 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
}
}
- cinfo->transport_info = smbox;
- smbox->cinfo = cinfo;
- mutex_init(&smbox->chan_lock);
-
return 0;
err_free_chan:
mbox_free_channel(smbox->chan);
+err_clear_cinfo:
+ cinfo->transport_info = NULL;
+ smbox->cinfo = NULL;
return ret;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 15/17] firmware: arm_scmi: Use channel ID for transport teardown
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (13 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 14/17] firmware: arm_scmi: Publish channel state before mailbox request Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 16/17] firmware: arm_scmi: Drop handle on protocol bind failures Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 17/17] firmware: arm_scmi: Clear virtio channel lists on free Sudeep Holla
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
SCMI protocols can share the BASE transport channel when firmware does
not describe a dedicated channel for the protocol. In that case multiple
IDR entries can point at the same scmi_chan_info, whose owning transport
device was created with cinfo->id.
scmi_chan_destroy() used the IDR iterator key when destroying the
transport device. If an alias entry is visited before the owning channel
entry, the lookup can miss the device because the iterator key does not
match the protocol ID used when the transport device was created. The
code then clears cinfo->dev, so the later owning entry skips teardown and
leaks the transport device.
Destroy the transport device using cinfo->id, which is the protocol ID
that owns the channel and was used when creating the transport device.
Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index df574961875c..12ebf32aa4da 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2896,7 +2896,7 @@ static int scmi_chan_destroy(int id, void *p, void *data)
struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
struct scmi_device *sdev = to_scmi_dev(cinfo->dev);
- scmi_device_destroy(info->dev, id, sdev->name);
+ scmi_device_destroy(info->dev, cinfo->id, sdev->name);
cinfo->dev = NULL;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 16/17] firmware: arm_scmi: Drop handle on protocol bind failures
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (14 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 15/17] firmware: arm_scmi: Use channel ID for transport teardown Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 17/17] firmware: arm_scmi: Clear virtio channel lists on free Sudeep Holla
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
The SCMI bus notifier acquires an SCMI handle when the driver core emits
BUS_NOTIFY_BIND_DRIVER, before invoking the protocol driver probe
callback. The protocol probe path only checks whether sdev->handle is
set.
If device_link_add() fails after the handle has been acquired, the
protocol device can still bind with a valid handle but without the
dependency link to the SCMI parent. A concurrent parent unbind can then
miss the child and tear down the SCMI instance while the child still
holds a handle into it.
If the protocol driver probe later fails, for example with
-EPROBE_DEFER, the driver core emits BUS_NOTIFY_DRIVER_NOT_BOUND rather
than BUS_NOTIFY_UNBOUND_DRIVER. The SCMI notifier only released the
handle on BUS_NOTIFY_UNBOUND_DRIVER, so each failed protocol-device bind
leaked the SCMI instance users refcount and left sdev->handle set after
the failed probe.
Make the link helper report failure and drop the acquired handle if the
link cannot be created. Also handle BUS_NOTIFY_DRIVER_NOT_BOUND in the
same cleanup path used for unbind so failed probes balance the earlier
BUS_NOTIFY_BIND_DRIVER acquisition.
Fixes: 971fc0665f13 ("firmware: arm_scmi: Move handle get/set helpers")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/driver.c | 31 +++++++++++++++++++++++--------
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 12ebf32aa4da..894c3427bc0b 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -2629,21 +2629,31 @@ static int scmi_handle_put(const struct scmi_handle *handle)
return 0;
}
-static void scmi_device_link_add(struct device *consumer,
+static bool scmi_device_link_add(struct device *consumer,
struct device *supplier)
{
struct device_link *link;
link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER);
- WARN_ON(!link);
+ return !WARN_ON(!link);
+}
+
+static void scmi_clear_handle(struct scmi_device *scmi_dev)
+{
+ if (!scmi_dev->handle)
+ return;
+
+ scmi_handle_put(scmi_dev->handle);
+ scmi_dev->handle = NULL;
}
static void scmi_set_handle(struct scmi_device *scmi_dev)
{
scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
- if (scmi_dev->handle)
- scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev);
+ if (scmi_dev->handle &&
+ !scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev))
+ scmi_clear_handle(scmi_dev);
}
static int __scmi_xfer_info_init(struct scmi_info *sinfo,
@@ -2926,6 +2936,7 @@ static int scmi_bus_notifier(struct notifier_block *nb,
{
struct scmi_info *info = bus_nb_to_scmi_info(nb);
struct scmi_device *sdev = to_scmi_dev(data);
+ const char *status;
/* Skip devices of different SCMI instances */
if (sdev->dev.parent != info->dev)
@@ -2935,18 +2946,22 @@ static int scmi_bus_notifier(struct notifier_block *nb,
case BUS_NOTIFY_BIND_DRIVER:
/* setup handle now as the transport is ready */
scmi_set_handle(sdev);
+ status = "about to be BOUND.";
+ break;
+ case BUS_NOTIFY_DRIVER_NOT_BOUND:
+ scmi_clear_handle(sdev);
+ status = "NOT BOUND.";
break;
case BUS_NOTIFY_UNBOUND_DRIVER:
- scmi_handle_put(sdev->handle);
- sdev->handle = NULL;
+ scmi_clear_handle(sdev);
+ status = "UNBOUND.";
break;
default:
return NOTIFY_DONE;
}
dev_dbg(info->dev, "Device %s (%s) is now %s\n", dev_name(&sdev->dev),
- sdev->name, action == BUS_NOTIFY_BIND_DRIVER ?
- "about to be BOUND." : "UNBOUND.");
+ sdev->name, status);
return NOTIFY_OK;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 17/17] firmware: arm_scmi: Clear virtio channel lists on free
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
` (15 preceding siblings ...)
2026-07-03 20:22 ` [PATCH v3 16/17] firmware: arm_scmi: Drop handle on protocol bind failures Sudeep Holla
@ 2026-07-03 20:22 ` Sudeep Holla
16 siblings, 0 replies; 18+ messages in thread
From: Sudeep Holla @ 2026-07-03 20:22 UTC (permalink / raw)
To: arm-scmi, linux-arm-kernel; +Cc: Cristian Marussi
SCMI virtio messages are allocated with devres against the SCMI
platform device, while the virtio channel structures are owned by the
virtio device and can survive SCMI driver unbind and rebind.
virtio_chan_free() only synchronizes channel shutdown. It leaves message
pointers on the channel free list, and possibly on the deferred pending
list or virtqueue, until SCMI devres later frees the messages. A
subsequent SCMI bind can then reuse stale list entries and dereference
freed memory.
After synchronized shutdown, detach any unused virtqueue buffers and
reinitialize both local message lists so no SCMI-devres message pointers
remain in virtio channel state.
Fixes: 5ffc1c4cb896 ("firmware: arm_scmi: Fix devres allocation device in virtio transport")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
drivers/firmware/arm_scmi/transports/virtio.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/firmware/arm_scmi/transports/virtio.c b/drivers/firmware/arm_scmi/transports/virtio.c
index 3282d8271839..c4738b866d62 100644
--- a/drivers/firmware/arm_scmi/transports/virtio.c
+++ b/drivers/firmware/arm_scmi/transports/virtio.c
@@ -178,6 +178,22 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
wait_for_completion(vioch->shutdown_done);
}
+static void scmi_vio_channel_cleanup(struct scmi_vio_channel *vioch)
+{
+ unsigned long flags;
+
+ while (virtqueue_detach_unused_buf(vioch->vqueue))
+ ;
+
+ spin_lock_irqsave(&vioch->free_lock, flags);
+ INIT_LIST_HEAD(&vioch->free_list);
+ spin_unlock_irqrestore(&vioch->free_lock, flags);
+
+ spin_lock_irqsave(&vioch->pending_lock, flags);
+ INIT_LIST_HEAD(&vioch->pending_cmds_list);
+ spin_unlock_irqrestore(&vioch->pending_lock, flags);
+}
+
/* Assumes to be called with vio channel acquired already */
static struct scmi_vio_msg *
scmi_virtio_get_free_msg(struct scmi_vio_channel *vioch)
@@ -484,6 +500,7 @@ static int virtio_chan_free(int id, void *p, void *data)
*/
virtio_break_device(vioch->vqueue->vdev);
scmi_vio_channel_cleanup_sync(vioch);
+ scmi_vio_channel_cleanup(vioch);
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-07-03 20:57 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-03 20:22 [PATCH v3 00/17] firmware: arm_scmi: Fix SCMI core cleanup paths Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 01/17] firmware: arm_scmi: Fix OF node reference handling Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 02/17] firmware: arm_scmi: Fix transport device teardown lookup Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 03/17] firmware: arm_scmi: Clean up channels on setup failure Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 04/17] firmware: arm_scmi: Fix SCMI device destroy lifetimes Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 05/17] firmware: arm_scmi: Free transport channel on IDR failure Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 06/17] firmware: arm_scmi: Unregister device notifier before IDR teardown Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 07/17] firmware: arm_scmi: Unwind TX receiver mailbox setup failure Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 08/17] firmware: arm_scmi: Unwind P2A " Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 09/17] firmware: arm_scmi: Protect device request lookup with RCU Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 10/17] firmware: arm_scmi: Avoid IDR updates while cleaning channels Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 11/17] firmware: arm_scmi: Clear SystemPower flag on create failure Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 12/17] firmware: arm_scmi: Reject out of range DT protocol IDs Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 13/17] firmware: arm_scmi: Stop channels before notification teardown Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 14/17] firmware: arm_scmi: Publish channel state before mailbox request Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 15/17] firmware: arm_scmi: Use channel ID for transport teardown Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 16/17] firmware: arm_scmi: Drop handle on protocol bind failures Sudeep Holla
2026-07-03 20:22 ` [PATCH v3 17/17] firmware: arm_scmi: Clear virtio channel lists on free Sudeep Holla
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox