[PATCH v3 16/17] firmware: arm_scmi: Drop handle on protocol bind failures
Sudeep Holla
sudeep.holla at kernel.org
Fri Jul 3 13:22:52 PDT 2026
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 at kernel.org>
Signed-off-by: Sudeep Holla <sudeep.holla at 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
More information about the linux-arm-kernel
mailing list