Hi Jacopo,

On Wed, Dec 13, 2017 at 07:26:19PM +0100, Jacopo Mondi wrote:
> Currently, subdevice notifiers are tested against all available
> subdevices as soon as they get registered. It often happens anyway
> that the subdevice they are connected to is not yet initialized, as
> it usually gets registered later in drivers' code. This makes debug
> of v4l2_async particularly painful, as identifying a notifier with
> an unitialized subdevice is tricky as they don't have a valid
> 'struct device *' or 'struct fwnode_handle *' to be identified with.
> 
> In order to make sure that the notifier's subdevices is initialized
> when the notifier is tesed against available subdevices post-pone the
> actual notifier registration at subdevice registration time.
> 
> It is worth noting that post-poning registration of a subdevice notifier
> does not impact on the completion of the notifiers chain, as even if a
> subdev notifier completes as soon as it gets registered, the complete()
> call chain cannot be upscaled as long as the subdevice the notifiers
> belongs to is not registered.

Let me rephrase to make sure I understand the problem correctly ---

A sub-device notifier is registered but the notifier's sub-device is not
registered yet, and finding a match for this notifier leads, to, well
problems. Is that the reason for this patch?

I think there could be simpler solutions to address this.

I wonder if we could simply check for sub-device notifier's v4l2_dev field,
and fail in matching if it's not set. v4l2_device_register_subdev() would
still need to proceed with calling v4l2_async_notifier_try_all_subdevs()
and v4l2_async_notifier_try_complete() if that was the case.

What do you think?

> 
> Also, it is now safe to access a notifier 'struct device *' as we're now
> sure it is properly initialized when the notifier is actually
> registered.
> 
> Signed-off-by: Jacopo Mondi <jacopo+rene...@jmondi.org>
> ---
>  drivers/media/v4l2-core/v4l2-async.c | 65 
> +++++++++++++++++++++++-------------
>  include/media/v4l2-async.h           |  2 ++
>  2 files changed, 43 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-async.c 
> b/drivers/media/v4l2-core/v4l2-async.c
> index 0a1bf1d..c13a781 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -25,6 +25,13 @@
>  #include <media/v4l2-fwnode.h>
>  #include <media/v4l2-subdev.h>
> 
> +static struct device *v4l2_async_notifier_dev(
> +                                     struct v4l2_async_notifier *notifier)
> +{
> +     return notifier->v4l2_dev ? notifier->v4l2_dev->dev :
> +                                 notifier->sd->dev;
> +}
> +
>  static int v4l2_async_notifier_call_bound(struct v4l2_async_notifier *n,
>                                         struct v4l2_subdev *subdev,
>                                         struct v4l2_async_subdev *asd)
> @@ -124,19 +131,6 @@ static struct v4l2_async_subdev *v4l2_async_find_match(
>       return NULL;
>  }
> 
> -/* Find the sub-device notifier registered by a sub-device driver. */
> -static struct v4l2_async_notifier *v4l2_async_find_subdev_notifier(
> -     struct v4l2_subdev *sd)
> -{
> -     struct v4l2_async_notifier *n;
> -
> -     list_for_each_entry(n, &notifier_list, list)
> -             if (n->sd == sd)
> -                     return n;
> -
> -     return NULL;
> -}
> -
>  /* Get v4l2_device related to the notifier if one can be found. */
>  static struct v4l2_device *v4l2_async_notifier_find_v4l2_dev(
>       struct v4l2_async_notifier *notifier)
> @@ -160,7 +154,7 @@ static bool v4l2_async_notifier_can_complete(
> 
>       list_for_each_entry(sd, &notifier->done, async_list) {
>               struct v4l2_async_notifier *subdev_notifier =
> -                     v4l2_async_find_subdev_notifier(sd);
> +                                                     sd->subdev_notifier;
> 
>               if (subdev_notifier &&
>                   !v4l2_async_notifier_can_complete(subdev_notifier))
> @@ -228,7 +222,7 @@ static int v4l2_async_match_notify(struct 
> v4l2_async_notifier *notifier,
>       /*
>        * See if the sub-device has a notifier. If not, return here.
>        */
> -     subdev_notifier = v4l2_async_find_subdev_notifier(sd);
> +     subdev_notifier = sd->subdev_notifier;
>       if (!subdev_notifier || subdev_notifier->parent)
>               return 0;
> 
> @@ -294,7 +288,7 @@ static void v4l2_async_notifier_unbind_all_subdevs(
> 
>       list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
>               struct v4l2_async_notifier *subdev_notifier =
> -                     v4l2_async_find_subdev_notifier(sd);
> +                                                     sd->subdev_notifier;
> 
>               if (subdev_notifier)
>                       v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
> @@ -371,8 +365,7 @@ static bool v4l2_async_notifier_fwnode_has_async_subdev(
> 
>  static int __v4l2_async_notifier_register(struct v4l2_async_notifier 
> *notifier)
>  {
> -     struct device *dev =
> -             notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL;
> +     struct device *dev = v4l2_async_notifier_dev(notifier);
>       struct v4l2_async_subdev *asd;
>       int ret;
>       int i;
> @@ -383,6 +376,8 @@ static int __v4l2_async_notifier_register(struct 
> v4l2_async_notifier *notifier)
>       INIT_LIST_HEAD(&notifier->waiting);
>       INIT_LIST_HEAD(&notifier->done);
> 
> +     notifier->owner = dev_fwnode(dev);
> +
>       mutex_lock(&list_lock);
> 
>       for (i = 0; i < notifier->num_subdevs; i++) {
> @@ -421,6 +416,7 @@ static int __v4l2_async_notifier_register(struct 
> v4l2_async_notifier *notifier)
> 
>       /* Keep also completed notifiers on the list */
>       list_add(&notifier->list, &notifier_list);
> +     notifier->registered = true;
> 
>       mutex_unlock(&list_lock);
> 
> @@ -447,7 +443,7 @@ int v4l2_async_notifier_register(struct v4l2_device 
> *v4l2_dev,
>               return -EINVAL;
> 
>       notifier->v4l2_dev = v4l2_dev;
> -     notifier->owner = dev_fwnode(v4l2_dev->dev);
> +     notifier->registered = false;
> 
>       ret = __v4l2_async_notifier_register(notifier);
>       if (ret)
> @@ -466,7 +462,11 @@ int v4l2_async_subdev_notifier_register(struct 
> v4l2_subdev *sd,
>               return -EINVAL;
> 
>       notifier->sd = sd;
> -     notifier->owner = dev_fwnode(sd->dev);
> +     sd->subdev_notifier = notifier;
> +     notifier->registered = false;
> +
> +     if (!sd->dev || !sd->fwnode)
> +             return 0;
> 
>       ret = __v4l2_async_notifier_register(notifier);
>       if (ret)
> @@ -482,12 +482,15 @@ static void __v4l2_async_notifier_unregister(
>       if (!notifier || (!notifier->v4l2_dev && !notifier->sd))
>               return;
> 
> -     v4l2_async_notifier_unbind_all_subdevs(notifier);
> +     if (notifier->registered) {
> +             v4l2_async_notifier_unbind_all_subdevs(notifier);
> +             list_del(&notifier->list);
> +     }
> 
>       notifier->sd = NULL;
>       notifier->v4l2_dev = NULL;
> -
> -     list_del(&notifier->list);
> +     notifier->owner = NULL;
> +     notifier->registered = false;
>  }
> 
>  void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
> @@ -548,6 +551,20 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
>                       sd->fwnode = dev_fwnode(sd->dev);
>       }
> 
> +     /*
> +      * If the subdevice has an unregisterd notifier, it's now time
> +      * to register it.
> +      */
> +     subdev_notifier = sd->subdev_notifier;
> +     if (subdev_notifier && !subdev_notifier->registered) {
> +             ret = __v4l2_async_notifier_register(subdev_notifier);
> +             if (ret) {
> +                     sd->fwnode = NULL;
> +                     subdev_notifier->owner = NULL;
> +                     return ret;
> +             }
> +     }
> +
>       mutex_lock(&list_lock);
> 
>       INIT_LIST_HEAD(&sd->async_list);
> @@ -589,7 +606,7 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
>        * Complete failed. Unbind the sub-devices bound through registering
>        * this async sub-device.
>        */
> -     subdev_notifier = v4l2_async_find_subdev_notifier(sd);
> +     subdev_notifier = sd->subdev_notifier;
>       if (subdev_notifier)
>               v4l2_async_notifier_unbind_all_subdevs(subdev_notifier);
> 
> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> index a15c01d..6ab04ad 100644
> --- a/include/media/v4l2-async.h
> +++ b/include/media/v4l2-async.h
> @@ -110,6 +110,7 @@ struct v4l2_async_notifier_operations {
>   * @waiting: list of struct v4l2_async_subdev, waiting for their drivers
>   * @done:    list of struct v4l2_subdev, already probed
>   * @list:    member in a global list of notifiers
> + * @registered: notifier registered complete flag
>   */
>  struct v4l2_async_notifier {
>       const struct v4l2_async_notifier_operations *ops;
> @@ -123,6 +124,7 @@ struct v4l2_async_notifier {
>       struct list_head waiting;
>       struct list_head done;
>       struct list_head list;
> +     bool registered;
>  };
> 
>  /**
> --
> 2.7.4
> 

-- 
Sakari Ailus
sakari.ai...@linux.intel.com

Reply via email to