mirror of
https://github.com/torvalds/linux.git
synced 2026-09-29 12:24:02 +02:00
firmware: arm_scmi: Drop handle on protocol bind failures
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: 971fc0665f ("firmware: arm_scmi: Move handle get/set helpers")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-10-3afe499d46e3@kernel.org
Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
This commit is contained in:
parent
e6a0e7a49d
commit
e3a5c30d23
|
|
@ -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,
|
||||
|
|
@ -2927,6 +2937,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)
|
||||
|
|
@ -2936,18 +2947,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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user