Hi Sakari,

On 07/22/2015 06:14 PM, Sakari Ailus wrote:
> By default, serialise open and release internal ops using a mutex.
> 
> The underlying problem is that a large proportion of the drivers do use
> v4l2_fh_is_singular() in their open() handlers (struct
> v4l2_subdev_internal_ops). v4l2_subdev_open(), .open file operation handler
> of the V4L2 sub-device framework, calls v4l2_fh_add() which adds the file
> handle to the list of open file handles of the device. Later on in the
> open() handler of the sub-device driver uses v4l2_fh_is_singular() to
> determine whether it was the file handle which was first opened. The check
> may go wrong if the device was opened by multiple process closely enough in
> time.

I don't like this patch for a few reasons: first of all it makes open/close
different from the open/close handling for normal v4l2 drivers. The decision
was made to use a core lock to serialize ioctls, but not the file operations.

The reason was that not all file operations need to take a lock, and that
drivers often need to do special things in the open/close anyway and using
the core lock for this caused more headaches than it solved.

I think we need to stick to the same scheme here. Note that I wouldn't mind
introducing a serialization lock for subdev ioctls. There isn't one at the
moment, and I think that that will simplify locking for subdevs, just as it
did for non-subdev drivers.

The second problem is that this depends on a new flag which is fairly ugly.

Drivers should just take a lock before calling fh_add and fh_singular.

Things can be simplified a bit with the v4l2-fh functions: I think it makes
sense if v4l2_fh_add and v4l2_fh_del both return a bool which is true if it
was the first or last filehandle.

That way you can just do:

        mutex_lock(&lock);
        if (v4l2_fh_add()) {
                ...
        }
        mutex_unlock(&lock);

Same with v4l2_fh_del().

While we're at it: v4l2_fh_is_singular(_file) should return a bool, not an int.

Hmm, let me make a patch series for these fh changes, shouldn't be too 
difficult.

Regards,

        Hans

> 
> Why this is especially important is because many drivers perform power
> management and other hardware initialisation based on open file handles.
> 
> Example one: Two processes open the device, but both end up determining
> neither was the first file handle opened on the device.
> 
>       process A: v4l2_fh_add()
>       process B: v4l2_fh_add()
> 
>       ...
> 
>       process A: v4l2_fh_is_singular() # false
>       process B: v4l2_fh_is_singular() # false
> 
> Example two:
> 
>       process A: v4l2_fh_add()
> 
>       ...
> 
>       process A: v4l2_fh_is_singular() # true
>                                        # at this point the driver does
>                                        # time-consuming hardware
>                                        # initialisation in the context of
>                                        # process A
> 
>       process B: v4l2_fh_add()
>       process B: v4l2_fh_is_singular() # false
>                                        # open system call finishes
> 
>       ...
> 
>       process B proceeds to access the sub-device
> 
>       ...
> 
>       process A finishes hardware initialisation
> 
> If the sub-device's open() handler in struct v4l2_subdev_internal_ops is not
> defined, then the information on whether the file handle was the first to be
> opened is not needed to complete the open system call on the device, and
> serialisation of the open and release system calls thus is not needed, with
> the possible exception for the driver's own reasons, but that is entirely
> the job of the driver.
> 
> Signed-off-by: Sakari Ailus <sakari.ai...@linux.intel.com>
> ---
>  drivers/media/v4l2-core/v4l2-device.c |  4 ++++
>  drivers/media/v4l2-core/v4l2-subdev.c | 41 
> +++++++++++++++++++++++++++++------
>  include/media/v4l2-subdev.h           |  4 ++++
>  3 files changed, 42 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-device.c 
> b/drivers/media/v4l2-core/v4l2-device.c
> index 5b0a30b..a13e0be 100644
> --- a/drivers/media/v4l2-core/v4l2-device.c
> +++ b/drivers/media/v4l2-core/v4l2-device.c
> @@ -191,6 +191,8 @@ int v4l2_device_register_subdev(struct v4l2_device 
> *v4l2_dev,
>       }
>  #endif
>  
> +     mutex_init(&sd->open_lock);
> +
>       spin_lock(&v4l2_dev->lock);
>       list_add_tail(&sd->list, &v4l2_dev->subdevs);
>       spin_unlock(&v4l2_dev->lock);
> @@ -292,5 +294,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
>       video_unregister_device(sd->devnode);
>       if (!sd->owner_v4l2_dev)
>               module_put(sd->owner);
> +
> +     mutex_destroy(&sd->open_lock);
>  }
>  EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c 
> b/drivers/media/v4l2-core/v4l2-subdev.c
> index b3f7da9..d8a98c4 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -50,6 +50,18 @@ static void subdev_fh_free(struct v4l2_subdev_fh *fh)
>  #endif
>  }
>  
> +static void subdev_open_lock(struct v4l2_subdev *sd)
> +{
> +     if (sd->flags & V4L2_SUBDEV_FL_SERIALISE_OPEN)
> +             mutex_lock(&sd->open_lock);
> +}
> +
> +static void subdev_open_unlock(struct v4l2_subdev *sd)
> +{
> +     if (sd->flags & V4L2_SUBDEV_FL_SERIALISE_OPEN)
> +             mutex_unlock(&sd->open_lock);
> +}
> +
>  static int subdev_open(struct file *file)
>  {
>       struct video_device *vdev = video_devdata(file);
> @@ -64,11 +76,11 @@ static int subdev_open(struct file *file)
>       if (subdev_fh == NULL)
>               return -ENOMEM;
>  
> +     subdev_open_lock(sd);
> +
>       ret = subdev_fh_init(subdev_fh, sd);
> -     if (ret) {
> -             kfree(subdev_fh);
> -             return ret;
> -     }
> +     if (ret)
> +             goto err_free_subdev_fh;
>  
>       v4l2_fh_init(&subdev_fh->vfh, vdev);
>       v4l2_fh_add(&subdev_fh->vfh);
> @@ -78,7 +90,7 @@ static int subdev_open(struct file *file)
>               entity = media_entity_get(&sd->entity);
>               if (!entity) {
>                       ret = -EBUSY;
> -                     goto err;
> +                     goto err_media_entity_put;
>               }
>       }
>  #endif
> @@ -86,20 +98,26 @@ static int subdev_open(struct file *file)
>       if (sd->internal_ops && sd->internal_ops->open) {
>               ret = sd->internal_ops->open(sd, subdev_fh);
>               if (ret < 0)
> -                     goto err;
> +                     goto err_media_entity_put;
>       }
>  
> +     subdev_open_unlock(sd);
> +
>       return 0;
>  
> -err:
> +err_media_entity_put:
>  #if defined(CONFIG_MEDIA_CONTROLLER)
>       media_entity_put(entity);
>  #endif
>       v4l2_fh_del(&subdev_fh->vfh);
>       v4l2_fh_exit(&subdev_fh->vfh);
>       subdev_fh_free(subdev_fh);
> +
> +err_free_subdev_fh:
>       kfree(subdev_fh);
>  
> +     subdev_open_unlock(sd);
> +
>       return ret;
>  }
>  
> @@ -110,6 +128,8 @@ static int subdev_close(struct file *file)
>       struct v4l2_fh *vfh = file->private_data;
>       struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
>  
> +     subdev_open_lock(sd);
> +
>       if (sd->internal_ops && sd->internal_ops->close)
>               sd->internal_ops->close(sd, subdev_fh);
>  #if defined(CONFIG_MEDIA_CONTROLLER)
> @@ -117,7 +137,11 @@ static int subdev_close(struct file *file)
>               media_entity_put(&sd->entity);
>  #endif
>       v4l2_fh_del(vfh);
> +
> +     subdev_open_unlock(sd);
> +
>       v4l2_fh_exit(vfh);
> +
>       subdev_fh_free(subdev_fh);
>       kfree(subdev_fh);
>       file->private_data = NULL;
> @@ -586,6 +610,9 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const 
> struct v4l2_subdev_ops *ops)
>       sd->ops = ops;
>       sd->v4l2_dev = NULL;
>       sd->flags = 0;
> +     /* Default to serialised open and release. */
> +     if (sd->internal_ops && sd->internal_ops->open)
> +             sd->flags |= V4L2_SUBDEV_FL_SERIALISE_OPEN;
>       sd->name[0] = '\0';
>       sd->grp_id = 0;
>       sd->dev_priv = NULL;
> diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
> index 5622699..11ffd50 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -624,6 +624,8 @@ struct v4l2_subdev_internal_ops {
>  #define V4L2_SUBDEV_FL_HAS_DEVNODE           (1U << 2)
>  /* Set this flag if this subdev generates events. */
>  #define V4L2_SUBDEV_FL_HAS_EVENTS            (1U << 3)
> +/* Set this flag if the sub-device open/close do NOT need to be serialised. 
> */
> +#define V4L2_SUBDEV_FL_SERIALISE_OPEN                (1U << 4)
>  
>  struct regulator_bulk_data;
>  
> @@ -672,6 +674,8 @@ struct v4l2_subdev {
>       struct v4l2_async_notifier *notifier;
>       /* common part of subdevice platform data */
>       struct v4l2_subdev_platform_data *pdata;
> +     /* serialise open and release based on the flags field */
> +     struct mutex open_lock;
>  };
>  
>  #define media_entity_to_v4l2_subdev(ent) \
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to