Module: xenomai-rpm
Branch: queue/vfile
Commit: d807ba36ee72f3a1c3443d1f7ff8047f7fdbe6ec
URL:    
http://git.xenomai.org/?p=xenomai-rpm.git;a=commit;h=d807ba36ee72f3a1c3443d1f7ff8047f7fdbe6ec

Author: Philippe Gerum <r...@xenomai.org>
Date:   Wed Apr 28 17:00:31 2010 +0200

nucleus/vfile: introduce virtual file support

Virtual files provide a mean to export Xenomai object states to
user-space, based on common kernel interfaces.  This encapsulation is
aimed at:

- supporting consistent collection of very large record-based output,
without encurring latency peaks for undergoing real-time activities.

- in the future, hiding discrepancies between linux kernel releases,
regarding the proper way to export kernel object states to userland,
either via the /proc interface or by any other mean.

This virtual file implementation offers record-based read support
based on seq_files, single-buffer write support, directory and link
handling, all visible from the /proc namespace.

---

 include/nucleus/vfile.h |  165 +++++++++++++++++++++
 ksrc/nucleus/Makefile   |    2 +
 ksrc/nucleus/vfile.c    |  365 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 532 insertions(+), 0 deletions(-)

diff --git a/include/nucleus/vfile.h b/include/nucleus/vfile.h
new file mode 100644
index 0000000..a40bc10
--- /dev/null
+++ b/include/nucleus/vfile.h
@@ -0,0 +1,165 @@
+/**
+ * @file
+ * This file is part of the Xenomai project.
+ *
+ * @note Copyright (C) 2010 Philippe Gerum <r...@xenomai.org> 
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * \ingroup vfile
+ */
+
+#ifndef _XENO_NUCLEUS_VFILE_H
+#define _XENO_NUCLEUS_VFILE_H
+
+#ifdef CONFIG_PROC_FS
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <nucleus/types.h>
+
+struct xnvfile_entry {
+       struct proc_dir_entry *pde;
+       struct xnvfile_directory *parent;
+       void *private;
+};
+
+struct xnvfile_directory {
+       struct xnvfile_entry entry;
+};
+
+struct xnvfile_link {
+       struct xnvfile_entry entry;
+};
+
+struct xnvfile {
+       int revtag;
+       size_t privsz;
+       size_t datasz;
+       struct xnvfile_entry entry;
+       struct xnvfile_ops *ops;
+};
+
+struct xnvfile_iterator {
+       int nrdata;
+       caddr_t databuf;
+       struct seq_file *seq;
+       struct xnvfile *vfile;
+       char private[0];
+};
+
+struct xnvfile_input {
+       const char __user *u_buf;
+       size_t size;
+       struct xnvfile *vfile;
+};
+
+struct xnvfile_template {
+       size_t privsz;
+       size_t datasz;
+       struct xnvfile_ops *ops;
+};
+
+struct xnvfile_ops {
+       int (*rewind)(struct xnvfile_iterator *it);
+       void *(*begin)(struct xnvfile_iterator *it);
+       int (*next)(struct xnvfile_iterator *it, void *data);
+       void (*end)(struct xnvfile_iterator *it, void *buf);
+       int (*show)(struct xnvfile_iterator *it, void *data);
+       ssize_t (*store)(struct xnvfile_input *input);
+};
+
+#define VFILE_SEQ_EMPTY                        ((void *)-1)
+#define xnvfile_printf(it, args...)    seq_printf((it)->seq, ##args)
+#define xnvfile_write(it, data, len)   seq_write((it)->seq, (data),(len))
+#define xnvfile_puts(it, s)            seq_puts((it)->seq, (s))
+#define xnvfile_putc(it, c)            seq_putc((it)->seq, (c))
+
+static inline void xnvfile_touch(struct xnvfile *vfile)
+{
+       vfile->revtag++;
+}
+
+static inline void *xnvfile_iterator_priv(struct xnvfile_iterator *it)
+{
+       return &it->private;
+}
+
+static inline int xnvfile_reg_p(struct xnvfile_entry *entry)
+{
+       return S_ISREG(entry->pde->mode);
+}
+
+static inline int xnvfile_dir_p(struct xnvfile_entry *entry)
+{
+       return S_ISDIR(entry->pde->mode);
+}
+
+static inline int xnvfile_link_p(struct xnvfile_entry *entry)
+{
+       return S_ISLNK(entry->pde->mode);
+}
+
+#define xnvfile_noentry                        \
+       {                               \
+               .pde = NULL,            \
+               .parent = NULL,         \
+               .private = NULL,        \
+       }
+
+#define xnvfile_nodir  { .entry = xnvfile_noentry }
+#define xnvfile_nolink { .entry = xnvfile_noentry }
+#define xnvfile_nofile { .entry = xnvfile_noentry }
+
+#define xnvfile_parent(e)      ((e)->entry.parent)
+#define xnvfile_priv(e)                ((e)->entry.private)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int xnvfile_init(const char *name,
+                struct xnvfile *vfile,
+                struct xnvfile_directory *parent);
+
+void xnvfile_destroy(struct xnvfile *vfile);
+
+int xnvfile_init_dir(const char *name,
+                    struct xnvfile_directory *vdir,
+                    struct xnvfile_directory *parent);
+
+void xnvfile_destroy_dir(struct xnvfile_directory *vdir);
+
+int xnvfile_init_link(const char *from,
+                     const char *to,
+                     struct xnvfile_link *vlink,
+                     struct xnvfile_directory *parent);
+
+void xnvfile_destroy_link(struct xnvfile_link *vlink);
+
+ssize_t xnvfile_read_input(struct xnvfile_input *input,
+                          void *data, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#else /* !CONFIG_PROC_FS */
+
+#define xnvfile_touch(vfile)   do { } while (0)
+
+#endif /* !CONFIG_PROC_FS */
+
+#endif /* !_XENO_NUCLEUS_VFILE_H */
diff --git a/ksrc/nucleus/Makefile b/ksrc/nucleus/Makefile
index ee8512c..ab1042d 100644
--- a/ksrc/nucleus/Makefile
+++ b/ksrc/nucleus/Makefile
@@ -16,6 +16,7 @@ xeno_nucleus-$(CONFIG_XENO_OPT_PERVASIVE) += shadow.o
 xeno_nucleus-$(CONFIG_XENO_OPT_PIPE) += pipe.o
 xeno_nucleus-$(CONFIG_XENO_OPT_MAP) += map.o
 xeno_nucleus-$(CONFIG_XENO_OPT_SELECT) += select.o
+xeno_nucleus-$(CONFIG_PROC_FS) += vfile.o
 
 # CAUTION: this module shall appear last, so that dependencies may
 # exist on initcalls defined by other object files.
@@ -45,6 +46,7 @@ opt_objs-$(CONFIG_XENO_OPT_PERVASIVE) += shadow.o
 opt_objs-$(CONFIG_XENO_OPT_PIPE) += pipe.o
 opt_objs-$(CONFIG_XENO_OPT_MAP) += map.o
 opt_objs-$(CONFIG_XENO_OPT_SELECT) += select.o
+opt_objs-$(CONFIG_PROC_FS) += vfile.o
 
 xeno_nucleus-objs += $(opt_objs-y)
 
diff --git a/ksrc/nucleus/vfile.c b/ksrc/nucleus/vfile.c
new file mode 100644
index 0000000..1ed0842
--- /dev/null
+++ b/ksrc/nucleus/vfile.c
@@ -0,0 +1,365 @@
+/**
+ * @file
+ * This file is part of the Xenomai project.
+ *
+ * @note Copyright (C) 2010 Philippe Gerum <r...@xenomai.org> 
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * \ingroup vfile
+ */
+
+/*!
+ * \ingroup nucleus
+ * \defgroup vfile Virtual file services.
+ *
+ * Virtual files provide a mean to export Xenomai object states to
+ * user-space, based on common kernel interfaces.  This encapsulation
+ * is aimed at:
+ *
+ * - supporting consistent collection of very large record-based
+ * output, without encurring latency peaks for undergoing real-time
+ * activities.
+ *
+ * - in the future, hiding discrepancies between linux kernel
+ * releases, regarding the proper way to export kernel object states
+ * to userland, either via the /proc interface or by any other mean.
+ *
+ * This virtual file implementation offers record-based read support
+ * based on seq_files, single-buffer write support, directory and link
+ * handling, all visible from the /proc namespace.
+ *
+ *...@{*/
+
+#include <stdarg.h>
+#include <nucleus/pod.h>
+#include <nucleus/assert.h>
+#include <nucleus/vfile.h>
+
+static void *vfile_seq_start(struct seq_file *seq, loff_t *offp)
+{
+       struct xnvfile_iterator *it = seq->private;
+       loff_t pos = *offp;
+
+       if (pos > it->nrdata)
+               return NULL;
+
+       if (pos == 0)
+               return SEQ_START_TOKEN;
+
+       return it->databuf + (pos - 1) * it->vfile->datasz;
+}
+
+static void *vfile_seq_next(struct seq_file *seq, void *v, loff_t *offp)
+{
+       struct xnvfile_iterator *it = seq->private;
+       loff_t pos = ++*offp;
+
+       if (pos > it->nrdata)
+               return NULL;
+
+       return it->databuf + (pos - 1) * it->vfile->datasz;
+}
+
+static void vfile_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int vfile_seq_show(struct seq_file *seq, void *v)
+{
+       struct xnvfile_iterator *it = seq->private;
+       void *data = v == SEQ_START_TOKEN ? NULL : v;
+
+       return it->vfile->ops->show(it, data);
+}
+
+static struct seq_operations vfile_seq_ops = {
+       .start = vfile_seq_start,
+       .next = vfile_seq_next,
+       .stop = vfile_seq_stop,
+       .show = vfile_seq_show
+};
+
+static int vfile_open(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *pde = PDE(inode);
+       struct xnvfile *vfile = pde->data;
+       struct xnvfile_ops *ops = vfile->ops;
+       struct xnvfile_iterator *it;
+       struct seq_file *seq;
+       int revtag, ret;
+       caddr_t data;
+       spl_t s;
+
+       /*
+        * There is no point in reading/writing to v-files that must
+        * be connected to Xenomai resources if the system has not
+        * been initialized yet (i.e. xnpod_init() called).
+        */
+       if (!xnpod_active_p())
+               return -ESRCH;
+
+       if ((file->f_mode & FMODE_WRITE) != 0 &&
+           ops->store == NULL)
+               return -EACCES;
+
+       /*
+        * Make sure to create the seq_file backend only when reading
+        * from the v-file is possible.
+        */
+       if ((file->f_mode & FMODE_READ) == 0) {
+               file->private_data = NULL;
+               return 0;
+       }
+
+       it = kzalloc(sizeof(*it) + vfile->privsz, GFP_KERNEL);
+       if (it == NULL)
+               return -ENOMEM;
+
+       it->vfile = vfile;
+
+       xnlock_get_irqsave(&nklock, s);
+redo:
+       /*
+        * The ->rewind() method is optional; there may be cases where
+        * we don't have to take an atomic snapshot of the v-file
+        * contents before proceeding. In case ->rewind() detects a
+        * stale backend object, it can force us to bail out.
+        */
+       if (ops->rewind) {
+               ret = ops->rewind(it);
+               if (ret) {
+                       xnlock_put_irqrestore(&nklock, s);
+                       goto fail;
+               }
+       }
+       revtag = vfile->revtag;
+
+       xnlock_put_irqrestore(&nklock, s);
+
+       /* Release the data buffer, in case we had to restart. */
+       if (it->databuf)
+               ops->end(it, it->databuf);
+
+       /*
+        * Having no record to output is fine, in which case ->begin()
+        * shall return VFILE_SEQ_EMPTY if present. ->begin() may be
+        * absent, meaning that no allocation is even required to
+        * collect the records to output. NULL is kept for allocation
+        * errors in all other cases.
+        */
+       it->databuf = NULL;
+       if (ops->begin) {
+               data = ops->begin(it);
+               if (data == NULL) {
+                       kfree(it);
+                       return -ENOMEM;
+               }
+               if (data != VFILE_SEQ_EMPTY)
+                       it->databuf = data;
+       }
+
+       ret = seq_open(file, &vfile_seq_ops);
+       if (ret)
+               goto fail;
+
+       it->nrdata = 0;
+       data = it->databuf;
+
+       if (data == NULL)
+               goto finish;
+
+       /*
+        * Take a snapshot of the vfile contents, redo if the revision
+        * tag of the scanned data set changed concurrently.
+        */
+       for (;;) {
+               xnlock_get_irqsave(&nklock, s);
+               if (vfile->revtag != revtag)
+                       goto redo;
+               ret = ops->next(it, data);
+               xnlock_put_irqrestore(&nklock, s);
+               if (ret <= 0)
+                       break;
+               data += vfile->datasz;
+               it->nrdata++;
+       }
+
+       if (ret < 0) {
+               seq_release(inode, file);
+       fail:
+               if (it->databuf)
+                       ops->end(it, it->databuf);
+               kfree(it);
+               return ret;
+       }
+
+finish:
+       seq = file->private_data;
+       it->seq = seq;
+       seq->private = it;
+
+       return 0;
+}
+
+static int vfile_release(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *pde = PDE(inode);
+       struct xnvfile *vfile = pde->data;
+       struct xnvfile_iterator *it;
+       struct seq_file *seq;
+
+       seq = file->private_data;
+       if (seq == NULL)
+               /* Write-side only. */
+               return 0;
+
+       it = seq->private;
+       if (it) {
+               if (it->databuf)
+                       vfile->ops->end(it, it->databuf);
+               kfree(it);
+       }
+
+       return 0;
+}
+
+ssize_t vfile_write(struct file *file, const char __user *buf,
+                   size_t size, loff_t *ppos)
+{
+       struct proc_dir_entry *pde = PDE(wrap_f_inode(file));
+       struct xnvfile *vfile = pde->data;
+       struct xnvfile_input input;
+
+       input.u_buf = buf;
+       input.size = size;
+       input.vfile = vfile;
+
+       return vfile->ops->store(&input);
+}
+
+static struct file_operations vfile_ops = {
+       .owner = THIS_MODULE,
+       .open = vfile_open,
+       .read = seq_read,
+       .write = vfile_write,
+       .llseek = seq_lseek,
+       .release = vfile_release,
+};
+
+int xnvfile_init(const char *name,
+                struct xnvfile *vfile,
+                struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+
+       ppde = parent ? parent->entry.pde : rthal_proc_root;
+       pde = create_proc_entry(name, 0, ppde);
+       if (pde == NULL)
+               return -ENOMEM;
+
+       pde->proc_fops = &vfile_ops;
+       wrap_proc_dir_entry_owner(pde);
+
+       vfile->entry.parent = parent;
+       vfile->entry.pde = pde;
+       pde->data = vfile;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init);
+
+void xnvfile_destroy(struct xnvfile *vfile)
+{
+       struct proc_dir_entry *ppde;
+
+       ppde = vfile->entry.parent ?
+               vfile->entry.parent->entry.pde : rthal_proc_root;
+       remove_proc_entry(vfile->entry.pde->name, ppde);
+}
+EXPORT_SYMBOL_GPL(xnvfile_destroy);
+
+int xnvfile_init_dir(const char *name,
+                    struct xnvfile_directory *vdir,
+                    struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+
+       ppde = parent ? parent->entry.pde : rthal_proc_root;
+       pde = create_proc_entry(name, S_IFDIR, ppde);
+       if (pde == NULL)
+               return -ENOMEM;
+
+       vdir->entry.parent = parent;
+       vdir->entry.pde = pde;
+       wrap_proc_dir_entry_owner(pde);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init_dir);
+
+void xnvfile_destroy_dir(struct xnvfile_directory *vdir)
+{
+       struct proc_dir_entry *ppde;
+
+       ppde = vdir->entry.parent ?
+               vdir->entry.parent->entry.pde : rthal_proc_root;
+       remove_proc_entry(vdir->entry.pde->name, ppde);
+}
+EXPORT_SYMBOL_GPL(xnvfile_destroy_dir);
+
+int xnvfile_init_link(const char *from,
+                     const char *to,
+                     struct xnvfile_link *vlink,
+                     struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+
+       ppde = parent ? parent->entry.pde : rthal_proc_root;
+       pde = proc_symlink(from, ppde, to);
+       if (vlink->entry.pde == NULL)
+               return -ENOMEM;
+
+       vlink->entry.parent = parent;
+       vlink->entry.pde = pde;
+       wrap_proc_dir_entry_owner(pde);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init_link);
+
+void xnvfile_destroy_link(struct xnvfile_link *vlink)
+{
+       struct proc_dir_entry *ppde;
+
+       ppde = vlink->entry.parent ?
+               vlink->entry.parent->entry.pde : rthal_proc_root;
+       remove_proc_entry(vlink->entry.pde->name, ppde);
+}
+EXPORT_SYMBOL_GPL(xnvfile_destroy_link);
+
+ssize_t xnvfile_read_input(struct xnvfile_input *input,
+                          void *data, size_t size)
+{
+       if (input->size > size)
+               size = input->size;
+
+       if (size > 0 &&
+           __xn_safe_copy_from_user(data, input->u_buf, size))
+               return -EFAULT;
+
+       return size;
+}
+EXPORT_SYMBOL_GPL(xnvfile_read_input);


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to