All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sakari Ailus <sakari.ailus@linux.intel.com>
To: linux-media@vger.kernel.org
Cc: j.anaszewski@samsung.com, hverkuil@xs4all.nl
Subject: [PATCH 1/1] v4l: subdev: Serialise open and release internal ops
Date: Wed, 22 Jul 2015 19:14:10 +0300	[thread overview]
Message-ID: <1437581650-1422-1-git-send-email-sakari.ailus@linux.intel.com> (raw)

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.

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.ailus@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) \
-- 
2.1.0.231.g7484e3b


             reply	other threads:[~2015-07-22 16:15 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-22 16:14 Sakari Ailus [this message]
2015-07-24  9:08 ` [PATCH 1/1] v4l: subdev: Serialise open and release internal ops Hans Verkuil
2015-07-25 22:12   ` Sakari Ailus
2015-07-28 10:05 ` Hans Verkuil

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1437581650-1422-1-git-send-email-sakari.ailus@linux.intel.com \
    --to=sakari.ailus@linux.intel.com \
    --cc=hverkuil@xs4all.nl \
    --cc=j.anaszewski@samsung.com \
    --cc=linux-media@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.