Module: xenomai-rpm Branch: queue/vfile Commit: d807ba36ee72f3a1c3443d1f7ff8047f7fdbe6ec URL: http://git.xenomai.org/?p=xenomai-rpm.git;a=commit;h=d807ba36ee72f3a1c3443d1f7ff8047f7fdbe6ec
Author: Philippe Gerum <[email protected]> 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 <[email protected]> + * + * 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 <[email protected]> + * + * 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 [email protected] https://mail.gna.org/listinfo/xenomai-git
