[RFC 05/16] media: devnode: Refcount the media devnode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add reference count to the media devnode. The media_devnode is intended to
be embedded in another struct such as media_device.

Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
---
 drivers/media/media-device.c  |   7 +--
 drivers/media/media-devnode.c | 114 +++++++++++++++++++++++++++++++++---------
 include/media/media-devnode.h |  47 +++++++++++++----
 3 files changed, 133 insertions(+), 35 deletions(-)

diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 8bdc316..a11b3be 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -531,7 +531,8 @@ static const struct media_file_operations media_device_fops = {
 static ssize_t show_model(struct device *cd,
 			  struct device_attribute *attr, char *buf)
 {
-	struct media_device *mdev = to_media_device(to_media_devnode(cd));
+	struct media_device *mdev = to_media_device(
+		to_media_devnode_cdev(cd)->devnode);
 
 	return sprintf(buf, "%.*s\n", (int)sizeof(mdev->model), mdev->model);
 }
@@ -717,7 +718,7 @@ int __must_check __media_device_register(struct media_device *mdev,
 	if (ret < 0)
 		return ret;
 
-	ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
+	ret = device_create_file(&mdev->devnode.mdc->dev, &dev_attr_model);
 	if (ret < 0) {
 		media_devnode_unregister(&mdev->devnode);
 		return ret;
@@ -793,7 +794,7 @@ void media_device_unregister(struct media_device *mdev)
 
 	mutex_unlock(&mdev->graph_mutex);
 
-	device_remove_file(&mdev->devnode.dev, &dev_attr_model);
+	device_remove_file(&mdev->devnode.mdc->dev, &dev_attr_model);
 	dev_dbg(mdev->dev, "Media device unregistering\n");
 	media_devnode_unregister(&mdev->devnode);
 }
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index 7481c96..566ef08 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -57,25 +57,54 @@ static DEFINE_MUTEX(media_devnode_lock);
 static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
 
 /* Called when the last user of the media device exits. */
-static void media_devnode_release(struct device *cd)
+static void media_devnode_device_release(struct device *cd)
 {
-	struct media_devnode *devnode = to_media_devnode(cd);
+	struct media_devnode_cdev *mdc = to_media_devnode_cdev(cd);
 
-	mutex_lock(&media_devnode_lock);
+	dev_dbg(cd, "release media devnode device\n");
 
 	/* Delete the cdev on this minor as well */
-	cdev_del(&devnode->cdev);
+	cdev_del(&mdc->cdev);
 
 	/* Mark device node number as free */
-	clear_bit(devnode->minor, media_devnode_nums);
-
+	mutex_lock(&media_devnode_lock);
+	clear_bit(MINOR(mdc->cdev.dev), media_devnode_nums);
 	mutex_unlock(&media_devnode_lock);
+}
+
+static void media_devnode_release(struct kref *kref)
+{
+	struct media_devnode *devnode =
+		container_of(kref, struct media_devnode, kref);
+
+	dev_dbg(&devnode->mdc->dev, "release media devnode\n");
+
+	put_device(&devnode->mdc->dev);
 
 	/* Release media_devnode and perform other cleanups as needed. */
 	if (devnode->release)
 		devnode->release(devnode);
 }
 
+void media_devnode_init(struct media_devnode *devnode)
+{
+	kref_init(&devnode->kref);
+	devnode->use_kref = true;
+}
+EXPORT_SYMBOL_GPL(media_devnode_init);
+
+void media_devnode_get(struct media_devnode *devnode)
+{
+	kref_get(&devnode->kref);
+}
+EXPORT_SYMBOL_GPL(media_devnode_get);
+
+void media_devnode_put(struct media_devnode *devnode)
+{
+	kref_put(&devnode->kref, media_devnode_release);
+}
+EXPORT_SYMBOL_GPL(media_devnode_put);
+
 static struct bus_type media_bus_type = {
 	.name = MEDIA_NAME,
 };
@@ -164,7 +193,8 @@ static int media_open(struct inode *inode, struct file *filp)
 	 * a crash.
 	 */
 	mutex_lock(&media_devnode_lock);
-	devnode = container_of(inode->i_cdev, struct media_devnode, cdev);
+	devnode = container_of(inode->i_cdev,
+			       struct media_devnode_cdev, cdev)->devnode;
 	/* return ENXIO if the media device has been removed
 	   already or if it is not registered anymore. */
 	if (!media_devnode_is_registered(devnode)) {
@@ -172,7 +202,10 @@ static int media_open(struct inode *inode, struct file *filp)
 		return -ENXIO;
 	}
 	/* and increase the device refcount */
-	get_device(&devnode->dev);
+	if (devnode->use_kref)
+		media_devnode_get(devnode);
+	else
+		get_device(&devnode->mdc->dev);
 	mutex_unlock(&media_devnode_lock);
 
 	filp->private_data = devnode;
@@ -180,7 +213,10 @@ static int media_open(struct inode *inode, struct file *filp)
 	if (devnode->fops->open) {
 		ret = devnode->fops->open(filp);
 		if (ret) {
-			put_device(&devnode->dev);
+			if (devnode->use_kref)
+				media_devnode_put(devnode);
+			else
+				put_device(&devnode->mdc->dev);
 			filp->private_data = NULL;
 			return ret;
 		}
@@ -201,7 +237,11 @@ static int media_release(struct inode *inode, struct file *filp)
 
 	/* decrease the refcount unconditionally since the release()
 	   return value is ignored. */
-	put_device(&devnode->dev);
+	if (devnode->use_kref)
+		media_devnode_put(devnode);
+	else
+		put_device(&devnode->mdc->dev);
+
 	return 0;
 }
 
@@ -222,14 +262,20 @@ static const struct file_operations media_devnode_fops = {
 int __must_check media_devnode_register(struct media_devnode *devnode,
 					struct module *owner)
 {
+	struct media_devnode_cdev *mdc;
 	int minor;
 	int ret;
 
+	mdc = kzalloc(sizeof(*mdc), GFP_KERNEL);
+	if (!mdc)
+		return -ENOMEM;
+
 	/* Part 1: Find a free minor number */
 	mutex_lock(&media_devnode_lock);
 	minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0);
 	if (minor == MEDIA_NUM_DEVICES) {
 		mutex_unlock(&media_devnode_lock);
+		kfree(mdc);
 		pr_err("could not get a free minor\n");
 		return -ENFILE;
 	}
@@ -240,23 +286,26 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 	devnode->minor = minor;
 
 	/* Part 2: Initialize and register the character device */
-	cdev_init(&devnode->cdev, &media_devnode_fops);
-	devnode->cdev.owner = owner;
+	cdev_init(&mdc->cdev, &media_devnode_fops);
+	mdc->cdev.owner = owner;
+	mdc->devnode = devnode;
+	devnode->mdc = mdc;
 
-	ret = cdev_add(&devnode->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor), 1);
+	ret = cdev_add(&mdc->cdev, MKDEV(MAJOR(media_dev_t), devnode->minor),
+		       1);
 	if (ret < 0) {
 		pr_err("%s: cdev_add failed\n", __func__);
 		goto error;
 	}
 
 	/* Part 3: Register the media device */
-	devnode->dev.bus = &media_bus_type;
-	devnode->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
-	devnode->dev.release = media_devnode_release;
+	devnode->mdc->dev.bus = &media_bus_type;
+	devnode->mdc->dev.devt = MKDEV(MAJOR(media_dev_t), devnode->minor);
+	devnode->mdc->dev.release = media_devnode_device_release;
 	if (devnode->parent)
-		devnode->dev.parent = devnode->parent;
-	dev_set_name(&devnode->dev, "media%d", devnode->minor);
-	ret = device_register(&devnode->dev);
+		devnode->mdc->dev.parent = devnode->parent;
+	dev_set_name(&devnode->mdc->dev, "media%d", devnode->minor);
+	ret = device_register(&devnode->mdc->dev);
 	if (ret < 0) {
 		pr_err("%s: device_register failed\n", __func__);
 		goto error;
@@ -265,14 +314,19 @@ int __must_check media_devnode_register(struct media_devnode *devnode,
 	/* Part 4: Activate this minor. The char device can now be used. */
 	set_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 
+	/* For us, reference released in media_devnode_release(). */
+	get_device(&devnode->mdc->dev);
+
 	return 0;
 
 error:
 	mutex_lock(&media_devnode_lock);
-	cdev_del(&devnode->cdev);
+	cdev_del(&mdc->cdev);
 	clear_bit(devnode->minor, media_devnode_nums);
 	mutex_unlock(&media_devnode_lock);
 
+	kfree(mdc);
+
 	return ret;
 }
 
@@ -282,16 +336,30 @@ void media_devnode_unregister(struct media_devnode *devnode)
 	if (!media_devnode_is_registered(devnode))
 		return;
 
+	dev_dbg(&devnode->mdc->dev, "unregister media devnode\n");
+
+	device_unregister(&devnode->mdc->dev);
+
 	mutex_lock(&media_devnode_lock);
 	clear_bit(MEDIA_FLAG_REGISTERED, &devnode->flags);
 	mutex_unlock(&media_devnode_lock);
-	device_unregister(&devnode->dev);
+
+	if (!devnode->use_kref) {
+		media_devnode_device_release(&devnode->mdc->dev);
+
+		if (devnode->release)
+			devnode->release(devnode);
+
+		return;
+	}
+
+	media_devnode_put(devnode);
 }
 
 /*
  *	Initialise media for linux
  */
-static int __init media_devnode_init(void)
+static int __init __media_devnode_init(void)
 {
 	int ret;
 
@@ -319,7 +387,7 @@ static void __exit media_devnode_exit(void)
 	unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
 }
 
-subsys_initcall(media_devnode_init);
+subsys_initcall(__media_devnode_init);
 module_exit(media_devnode_exit)
 
 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@xxxxxxxxxxxxxxxx>");
diff --git a/include/media/media-devnode.h b/include/media/media-devnode.h
index a0f6823..4e9d066 100644
--- a/include/media/media-devnode.h
+++ b/include/media/media-devnode.h
@@ -28,10 +28,11 @@
 #ifndef _MEDIA_DEVNODE_H
 #define _MEDIA_DEVNODE_H
 
-#include <linux/poll.h>
-#include <linux/fs.h>
-#include <linux/device.h>
 #include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/poll.h>
 
 /*
  * Flag to mark the media_devnode struct as registered. Drivers must not touch
@@ -65,15 +66,24 @@ struct media_file_operations {
 	int (*release) (struct file *);
 };
 
+struct media_devnode_cdev {
+	struct device dev;		/* media device */
+	struct cdev cdev;		/* character device */
+	struct media_devnode *devnode;	/* pointer to media_devnode */
+};
+
+#define to_media_devnode_cdev(cd) \
+	container_of(cd, struct media_devnode_cdev, dev)
+
 /**
  * struct media_devnode - Media device node
  * @media_dev:	pointer to struct &media_device
  * @fops:	pointer to struct &media_file_operations with media device ops
- * @dev:	pointer to struct &device containing the media controller device
- * @cdev:	struct cdev pointer character device
  * @parent:	parent device
  * @minor:	device node minor number
  * @flags:	flags, combination of the MEDIA_FLAG_* constants
+ * @kref:	kref to this object
+ * @use_kref:	tell whether we're using kref or not (TEMPORARY)
  * @release:	release callback called at the end of media_devnode_release()
  *
  * This structure represents a media-related device node.
@@ -86,20 +96,39 @@ struct media_devnode {
 	const struct media_file_operations *fops;
 
 	/* sysfs */
-	struct device dev;		/* media device */
-	struct cdev cdev;		/* character device */
 	struct device *parent;		/* device parent */
 
 	/* device info */
 	int minor;
 	unsigned long flags;		/* Use bitops to access flags */
+	struct media_devnode_cdev *mdc;
+	struct kref kref;
+	bool use_kref;
 
 	/* callbacks */
 	void (*release)(struct media_devnode *devnode);
 };
 
-/* dev to media_devnode */
-#define to_media_devnode(cd) container_of(cd, struct media_devnode, dev)
+/**
+ * media_devnode_init - initialise a media device node (with kref)
+ *
+ * devnode: the device node to be initialised
+ */
+void media_devnode_init(struct media_devnode *devnode);
+
+/**
+ * media_devnode_get - get a reference to a media device node
+ *
+ * @devnode: the device node to get a reference to
+ */
+void media_devnode_get(struct media_devnode *devnode);
+
+/**
+ * media_devnode_put - put a reference to a media device node
+ *
+ * @devnode: the device node to put a reference from
+ */
+void media_devnode_put(struct media_devnode *devnode);
 
 /**
  * media_devnode_register - register a media device node
-- 
2.1.4

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



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux