[PATCH 6.18 159/752] media: v4l2-async: Fix error handling on steps after finding a match

Sasha Levin
2026-02-28T17:50:22Z

From: Sakari Ailus <sakari.ailus@linux.intel.com>

[ Upstream commit 7345d6d356336c448d6b9230ed8704f39679fd12 ]

Once an async connection is found to be matching with an fwnode, a
sub-device may be registered (in case it wasn't already), its bound
operation is called, ancillary links are created, the async connection
is added to the sub-device's list of connections and removed from the
global waiting connection list. Further on, the sub-device's possible own
notifier is searched for possible additional matches.

Fix these specific issues:

- If v4l2_async_match_notify() failed before the sub-notifier handling,
the async connection was unbound and its entry removed from the
sub-device's async connection list. The latter part was also done in
v4l2_async_match_notify().

- The async connection's sd field was only set after creating ancillary
links in v4l2_async_match_notify(). It was however dereferenced in
v4l2_async_unbind_subdev_one(), which was called on error path of
v4l2_async_match_notify() failure.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Tested-by: "Yew, Chang Ching" <chang.ching.yew@intel.com>
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/media/v4l2-core/v4l2-async.c | 45 +++++++++++++++++++---------
1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index ee884a8221fbd..1c08bba9ecb91 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -343,7 +343,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *sd,
struct v4l2_async_connection *asc)
{
- struct v4l2_async_notifier *subdev_notifier;
bool registered = false;
int ret;

@@ -389,6 +388,25 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n",
dev_name(sd->dev), ret);

+ return 0;
+
+err_call_unbind:
+ v4l2_async_nf_call_unbind(notifier, sd, asc);
+ list_del(&asc->asc_subdev_entry);
+
+err_unregister_subdev:
+ if (registered)
+ v4l2_device_unregister_subdev(sd);
+
+ return ret;
+}
+
+static int
+v4l2_async_nf_try_subdev_notifier(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_async_notifier *subdev_notifier;
+
/*
* See if the sub-device has a notifier. If not, return here.
*/
@@ -404,16 +422,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier,
subdev_notifier->parent = notifier;

return v4l2_async_nf_try_all_subdevs(subdev_notifier);
-
-err_call_unbind:
- v4l2_async_nf_call_unbind(notifier, sd, asc);
- list_del(&asc->asc_subdev_entry);
-
-err_unregister_subdev:
- if (registered)
- v4l2_device_unregister_subdev(sd);
-
- return ret;
}

/* Test all async sub-devices in a notifier for a match. */
@@ -445,6 +453,10 @@ v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier)
if (ret < 0)
return ret;

+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd);
+ if (ret < 0)
+ return ret;
+
/*
* v4l2_async_match_notify() may lead to registering a
* new notifier and thus changing the async subdevs
@@ -829,7 +841,11 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
ret = v4l2_async_match_notify(notifier, v4l2_dev, sd,
asc);
if (ret)
- goto err_unbind;
+ goto err_unlock;
+
+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd);
+ if (ret)
+ goto err_unbind_one;

ret = v4l2_async_nf_try_complete(notifier);
if (ret)
@@ -853,9 +869,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module)
if (subdev_notifier)
v4l2_async_nf_unbind_all_subdevs(subdev_notifier);

- if (asc)
- v4l2_async_unbind_subdev_one(notifier, asc);
+err_unbind_one:
+ v4l2_async_unbind_subdev_one(notifier, asc);

+err_unlock:
mutex_unlock(&list_lock);

sd->owner = NULL;
--
2.51.0