media: v4l2-async: Fix error handling on steps after finding a match

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>
This commit is contained in:
Sakari Ailus 2025-11-21 13:48:40 +02:00 committed by Hans Verkuil
parent 679f0b7b6a
commit 7345d6d356

View File

@ -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;