sysfs, kernfs: add kernfs_ops->seq_{start|next|stop}()
authorTejun Heo <tj@kernel.org>
Thu, 28 Nov 2013 19:54:26 +0000 (14:54 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 30 Nov 2013 01:48:14 +0000 (17:48 -0800)
kernfs_ops currently only supports single_open() behavior which is
pretty restrictive.  Add optional callbacks ->seq_{start|next|stop}()
which, when implemented, are invoked for seq_file traversal.  This
allows full seq_file functionality for kernfs users.  This currently
doesn't have any user and doesn't change any behavior.

v2: Refreshed on top of the updated "sysfs, kernfs: prepare read path
    for kernfs".

Signed-off-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/sysfs/file.c
include/linux/kernfs.h

index 9852450867cf336d66b8fcedcf58343afdb93117..74e3478d9cb4de530c668f155eb0a6c908e57d11 100644 (file)
@@ -146,6 +146,7 @@ static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf,
 static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos)
 {
        struct sysfs_open_file *of = sf->private;
+       const struct kernfs_ops *ops;
 
        /*
         * @of->mutex nests outside active ref and is just to ensure that
@@ -155,26 +156,42 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos)
        if (!sysfs_get_active(of->sd))
                return ERR_PTR(-ENODEV);
 
-       /*
-        * The same behavior and code as single_open().  Returns !NULL if
-        * pos is at the beginning; otherwise, NULL.
-        */
-       return NULL + !*ppos;
+       ops = kernfs_ops(of->sd);
+       if (ops->seq_start) {
+               return ops->seq_start(sf, ppos);
+       } else {
+               /*
+                * The same behavior and code as single_open().  Returns
+                * !NULL if pos is at the beginning; otherwise, NULL.
+                */
+               return NULL + !*ppos;
+       }
 }
 
 static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos)
 {
-       /*
-        * The same behavior and code as single_open(), always terminate
-        * after the initial read.
-        */
-       ++*ppos;
-       return NULL;
+       struct sysfs_open_file *of = sf->private;
+       const struct kernfs_ops *ops = kernfs_ops(of->sd);
+
+       if (ops->seq_next) {
+               return ops->seq_next(sf, v, ppos);
+       } else {
+               /*
+                * The same behavior and code as single_open(), always
+                * terminate after the initial read.
+                */
+               ++*ppos;
+               return NULL;
+       }
 }
 
 static void kernfs_seq_stop(struct seq_file *sf, void *v)
 {
        struct sysfs_open_file *of = sf->private;
+       const struct kernfs_ops *ops = kernfs_ops(of->sd);
+
+       if (ops->seq_stop)
+               ops->seq_stop(sf, v);
 
        sysfs_put_active(of->sd);
        mutex_unlock(&of->mutex);
index d0912cf02087e49e64dddf6f1254f8ed0886ae6c..ba993ebcd81ef019dda7a83bc2e7e1efb458cc53 100644 (file)
@@ -37,8 +37,9 @@ struct kernfs_ops {
        /*
         * Read is handled by either seq_file or raw_read().
         *
-        * If seq_show() is present, seq_file path is active.  The behavior
-        * is equivalent to single_open().  @sf->private points to the
+        * If seq_show() is present, seq_file path is active.  Other seq
+        * operations are optional and if not implemented, the behavior is
+        * equivalent to single_open().  @sf->private points to the
         * associated sysfs_open_file.
         *
         * read() is bounced through kernel buffer and a read larger than
@@ -46,6 +47,10 @@ struct kernfs_ops {
         */
        int (*seq_show)(struct seq_file *sf, void *v);
 
+       void *(*seq_start)(struct seq_file *sf, loff_t *ppos);
+       void *(*seq_next)(struct seq_file *sf, void *v, loff_t *ppos);
+       void (*seq_stop)(struct seq_file *sf, void *v);
+
        ssize_t (*read)(struct sysfs_open_file *of, char *buf, size_t bytes,
                        loff_t off);