From 107c42f138d21d80b49e6c6986cf0f6fd8e605b5 Mon Sep 17 00:00:00 2001
From: Alessandro Rubini <rubini@gnudd.com>
Date: Mon, 30 Jan 2012 01:29:07 +0100
Subject: [PATCH] core: offer generic_fops, add v_op

We will have buffers that use the default operations (the majority) and
buffers without file operations for in-kernel activities, like the
input subsystem. In the latter case we want the cdev to refuse being
openend, so NULL is reserved for that case.

Also, add v_op to the buffer, so it can support mmap.

Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
Acked-by: Federico Vaga <federico.vaga@gmail.com>
---
 buffers/zio-buf-kmalloc.c  | 15 +---------
 include/linux/zio-buffer.h | 25 +++++++---------
 include/linux/zio.h        |  3 ++
 zio-cdev.c                 | 60 +++++++++++++++++++++++++++++++++-----
 zio-sys.c                  |  9 +++++-
 5 files changed, 74 insertions(+), 38 deletions(-)

diff --git a/buffers/zio-buf-kmalloc.c b/buffers/zio-buf-kmalloc.c
index 0155e29..450ead8 100644
--- a/buffers/zio-buf-kmalloc.c
+++ b/buffers/zio-buf-kmalloc.c
@@ -243,19 +243,6 @@ static const struct zio_buffer_operations zbk_buffer_ops = {
 	.destroy =	zbk_destroy,
 };
 
-/*
- * File operations. We only have read and write: mmap is definitely
- * not suitable here, and open/release are not needed.
- */
-
-static const struct file_operations zbk_file_ops = {
-	.owner =	THIS_MODULE,
-	.read =		zio_generic_read,
-	.write =	zio_generic_write,
-	.poll =		zio_generic_poll,
-	.release =	zio_generic_release,
-};
-
 static struct zio_buffer_type zbk_buffer = {
 	.owner =	THIS_MODULE,
 	.zattr_set = {
@@ -263,7 +250,7 @@ static struct zio_buffer_type zbk_buffer = {
 	},
 	.s_op = &zbk_sysfs_ops,
 	.b_op = &zbk_buffer_ops,
-	.f_op = &zbk_file_ops,
+	.f_op = &zio_generic_file_operations,
 };
 
 static int __init zbk_init(void)
diff --git a/include/linux/zio-buffer.h b/include/linux/zio-buffer.h
index df987d1..7cda57b 100644
--- a/include/linux/zio-buffer.h
+++ b/include/linux/zio-buffer.h
@@ -26,26 +26,27 @@ struct zio_buffer_type {
 	struct module		*owner;
 	struct list_head	list; /* instances, and list lock */
 	struct spinlock		lock;
-	unsigned long		flags; /* to be defined */
+	unsigned long		flags;
 
-	/* file operations (read/write etc) are buffer-specific too */
 	const struct zio_sysfs_operations	*s_op;
 	const struct zio_buffer_operations	*b_op;
+	/*
+	 * file operations (read/write etc) are buffer-specific too, but
+	 * you are strongly suggested to use zio_generic_file_operations. 
+	 * If the field is NULL, you'll get ENODEV when opening the cdev.
+	 */
 	const struct file_operations		*f_op;
+	const struct vm_operations		*v_op;
 
 	/* default attributes for instance */
 	struct zio_attribute_set		zattr_set;
 };
 #define to_zio_buf(ptr) container_of(ptr, struct zio_buffer_type, head.kobj)
 
-/* read and write may often be the generic ones */
-ssize_t zio_generic_read(struct file *, char __user *,
-			 size_t, loff_t *);
-ssize_t zio_generic_write(struct file *, const char __user *,
-			  size_t, loff_t *);
-unsigned int zio_generic_poll(struct file *, struct poll_table_struct *);
-int zio_generic_release(struct inode *inode, struct file *f);
+/* buffer_type->flags */
+#define ZIO_BFLAG_ALLOC_FOPS	0x00000001 /* set by zio-core */
 
+extern const struct file_operations zio_generic_file_operations;
 
 int __must_check zio_register_buf(struct zio_buffer_type *zbuf,
 				  const char *name);
@@ -141,10 +142,4 @@ struct zio_f_priv {
 	enum zio_cdev_type type;
 };
 
-ssize_t zio_generic_read(struct file *f, char __user *ubuf,
-			 size_t count, loff_t *offp);
-ssize_t zio_generic_write(struct file *f, const char __user *ubuf,
-			  size_t count, loff_t *offp);
-unsigned int zio_generic_poll(struct file *f, struct poll_table_struct *w);
-
 #endif /* __ZIO_BUFFER_H__ */
diff --git a/include/linux/zio.h b/include/linux/zio.h
index 588548f..eaf1627 100644
--- a/include/linux/zio.h
+++ b/include/linux/zio.h
@@ -266,6 +266,9 @@ void zio_default_buffer_exit(void);
 int zio_default_trigger_init(void);
 void zio_default_trigger_exit(void);
 
+int zio_init_buffer_fops(struct zio_buffer_type *zbuf);
+int zio_fini_buffer_fops(struct zio_buffer_type *zbuf);
+
 #endif /* __ZIO_INTERNAL__ */
 
 #endif /* __KERNEL__ */
diff --git a/zio-cdev.c b/zio-cdev.c
index 54488be..9bfbd32 100644
--- a/zio-cdev.c
+++ b/zio-cdev.c
@@ -125,9 +125,15 @@ static int zio_f_open(struct inode *ino, struct file *f)
 	chan = __zio_minor_to_chan(ino->i_rdev);
 	if (!chan) {
 		pr_err("ZIO: can't retrieve channel for minor %i\n", minor);
+		zio_device_put(ino->i_rdev);
 		return -EBUSY;
 	}
 	zbuf = chan->cset->zbuf;
+	if (!zbuf->f_op) {
+		zio_device_put(ino->i_rdev);
+		return -ENODEV;
+	}
+
 	f->private_data = NULL;
 	priv = kzalloc(sizeof(struct zio_f_priv), GFP_KERNEL);
 	if (!priv)
@@ -370,7 +376,7 @@ static int __zio_write_allowed(struct zio_f_priv *priv)
  * work for most buffer types, and are exported for use in their
  * buffer operations.
  */
-ssize_t zio_generic_read(struct file *f, char __user *ubuf,
+static ssize_t zio_generic_read(struct file *f, char __user *ubuf,
 			 size_t count, loff_t *offp)
 {
 	struct zio_f_priv *priv = f->private_data;
@@ -418,9 +424,8 @@ ssize_t zio_generic_read(struct file *f, char __user *ubuf,
 	}
 	return count;
 }
-EXPORT_SYMBOL(zio_generic_read);
 
-ssize_t zio_generic_write(struct file *f, const char __user *ubuf,
+static ssize_t zio_generic_write(struct file *f, const char __user *ubuf,
 			  size_t count, loff_t *offp)
 {
 	struct zio_f_priv *priv = f->private_data;
@@ -475,9 +480,9 @@ ssize_t zio_generic_write(struct file *f, const char __user *ubuf,
 	*offp += count;
 	return count;
 }
-EXPORT_SYMBOL(zio_generic_write);
 
-unsigned int zio_generic_poll(struct file *f, struct poll_table_struct *w)
+static unsigned int zio_generic_poll(struct file *f,
+				     struct poll_table_struct *w)
 {
 	struct zio_f_priv *priv = f->private_data;
 	struct zio_bi *bi = priv->chan->bi;
@@ -485,9 +490,8 @@ unsigned int zio_generic_poll(struct file *f, struct poll_table_struct *w)
 	poll_wait(f, &bi->q, w);
 	return __zio_read_allowed(priv) | __zio_write_allowed(priv);
 }
-EXPORT_SYMBOL(zio_generic_poll);
 
-int zio_generic_release(struct inode *inode, struct file *f)
+static int zio_generic_release(struct inode *inode, struct file *f)
 {
 	struct zio_f_priv *priv = f->private_data;
 
@@ -496,5 +500,45 @@ int zio_generic_release(struct inode *inode, struct file *f)
 	zio_device_put(inode->i_rdev);
 	return 0;
 }
-EXPORT_SYMBOL(zio_generic_release);
 
+const struct file_operations zio_generic_file_operations = {
+	/* no owner: this template is copied over */
+	.read =		zio_generic_read,
+	.write =	zio_generic_write,
+	.poll =		zio_generic_poll,
+	.release =	zio_generic_release,
+};
+/* Export, so buffers can use it or internal function */
+EXPORT_SYMBOL(zio_generic_file_operations);
+
+/* Currently, this is a "all or nothing" choice */
+int zio_init_buffer_fops(struct zio_buffer_type *zbuf)
+{
+	struct file_operations *ops;
+
+	/* Current fops may be NULL (buffer for in-kernel data handling */
+	if (!zbuf->f_op)
+		return 0;
+	if (zbuf->f_op != &zio_generic_file_operations)
+		return 0;
+
+	/* If it's the generic ones, we must clone to fit the owner field */
+	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		return -ENOMEM;
+	zbuf->flags |= ZIO_BFLAG_ALLOC_FOPS;
+	*ops = zio_generic_file_operations;
+	ops->owner = zbuf->owner;
+	zbuf->f_op = ops;
+	return 0;
+}
+
+int zio_fini_buffer_fops(struct zio_buffer_type *zbuf)
+{
+	if (!(zbuf->flags & ZIO_BFLAG_ALLOC_FOPS))
+		return 0;
+	zbuf->flags &= ~ZIO_BFLAG_ALLOC_FOPS;
+	kfree(zbuf->f_op);
+	zbuf->f_op = &zio_generic_file_operations;
+	return 0;
+}
diff --git a/zio-sys.c b/zio-sys.c
index 5cd661c..107da39 100644
--- a/zio-sys.c
+++ b/zio-sys.c
@@ -1671,10 +1671,16 @@ int zio_register_buf(struct zio_buffer_type *zbuf, const char *name)
 	if (!zbuf || !name)
 		return -EINVAL;
 
+	err = zio_init_buffer_fops(zbuf);
+	if (err < 0)
+		return err;
+
 	err = zobj_register(&zstat->all_buffer_types, &zbuf->head,
 			    ZBUF, zbuf->owner, name);
-	if (err)
+	if (err) {
+		zio_fini_buffer_fops(zbuf);
 		return err;
+	}
 	zbuf->zattr_set.n_std_attr = ZATTR_STD_NUM_ZBUF;
 	INIT_LIST_HEAD(&zbuf->list);
 	spin_lock_init(&zbuf->lock);
@@ -1687,6 +1693,7 @@ void zio_unregister_buf(struct zio_buffer_type *zbuf)
 {
 	if (!zbuf)
 		return;
+	zio_fini_buffer_fops(zbuf);
 	zobj_unregister(&zstat->all_buffer_types, &zbuf->head);
 }
 EXPORT_SYMBOL(zio_unregister_buf);
-- 
GitLab