Module: xenomai-head Branch: master Commit: 21b1f8870c1669612cdc70fb58ed9fac529d70e0 URL: http://git.xenomai.org/?p=xenomai-head.git;a=commit;h=21b1f8870c1669612cdc70fb58ed9fac529d70e0
Author: Philippe Gerum <[email protected]> Date: Tue May 25 17:36:56 2010 +0200 nucleus: 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 | 682 ++++++++++++++++++++++++++++++++++ ksrc/nucleus/vfile.c | 944 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1626 insertions(+), 0 deletions(-) diff --git a/include/nucleus/vfile.h b/include/nucleus/vfile.h new file mode 100644 index 0000000..bda3542 --- /dev/null +++ b/include/nucleus/vfile.h @@ -0,0 +1,682 @@ +/** + * @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 + +#if defined(CONFIG_XENO_OPT_VFILE) || defined(DOXYGEN_CPP) + +/** @addtogroup vfile + *...@{*/ + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <nucleus/types.h> + +struct xnvfile_directory; +struct xnvfile_regular_iterator; +struct xnvfile_snapshot_iterator; +struct xnvfile_lock_ops; + +struct xnvfile { + struct proc_dir_entry *pde; + struct xnvfile_directory *parent; + struct xnvfile_lock_ops *lockops; + void *private; +}; + +/** + * @brief Vfile locking operations + * @anchor vfile_lockops + * + * This structure describes the operations to be provided for + * implementing locking support on vfiles. They apply to both + * snapshot-driven and regular vfiles. + */ +struct xnvfile_lock_ops { + /** + * @anchor lockops_get + * This handler should grab the desired lock. + * + * @param vfile A pointer to the virtual file which needs + * locking. + * + * @return zero should be returned if the call + * succeeds. Otherwise, a negative error code can be returned; + * upon error, the current vfile operation is aborted, and the + * user-space caller is passed back the error value. + */ + int (*get)(struct xnvfile *vfile); + /** + * @anchor lockops_put This handler should release the lock + * previously grabbed by the @ref lockops_get "get() handler". + * + * @param vfile A pointer to the virtual file which currently + * holds the lock to release. + */ + void (*put)(struct xnvfile *vfile); +}; + +/* + * XXX: struct semaphore is legacy for mutual exclusion, but supported + * on both 2.4 and 2.6 kernels. Will be changed to mutex when 2.4 + * support is dropped from Xenomai. + */ +struct xnvfile_hostlock_class { + struct xnvfile_lock_ops ops; + struct semaphore sem; +}; + +struct xnvfile_nklock_class { + struct xnvfile_lock_ops ops; + spl_t s; +}; + +struct xnvfile_input { + const char __user *u_buf; + size_t size; + struct xnvfile *vfile; +}; + +/** + * @brief Regular vfile operation descriptor + * @anchor regular_ops + * + * This structure describes the operations available with a regular + * vfile. It defines handlers for sending back formatted kernel data + * upon a user-space read request, and for obtaining user data upon a + * user-space write request. + */ +struct xnvfile_regular_ops { + /** + * @anchor regular_begin + * This handler should prepare for iterating over the records + * upon a read request, according to the iterator data. + * + * @param it A pointer to the current vfile iterator. On + * entry, it->pos is set to the (0-based) position of the + * first record to output. + * + * @return A pointer to the first record to format and output, + * to be passed to the @ref regular_show "show() handler" as + * its @a data parameter, if the call succeeds. Otherwise: + * + * - NULL in case no record is available, in which case the + * read operation will terminate immediately with no output. + * + * - VFILE_SEQ_START, a special value indicating that @ref + * regular_show "the show() handler" should receive a NULL + * data pointer first, in order to output a header. + * + * - ERR_PTR(errno), where errno is a negative error code; + * upon error, the current operation will be aborted + * immediately. + * + * @note This handler is optional; if none is given in the + * operation descriptor (i.e. NULL value), the @ref + * regular_show "show() handler()" will be called only once + * for a read operation, with a NULL @a data parameter. This + * particular setting is convenient for simple regular vfiles + * having a single, fixed record to output. + */ + void *(*begin)(struct xnvfile_regular_iterator *it); + /** + * @anchor regular_next + * This handler should return the address of the next record + * to format and output by the @ref regular_show "show() + * handler". + * + * @param it A pointer to the current vfile iterator. On + * entry, it->pos is set to the (0-based) position of the + * first record to output. + * + * @return A pointer to the next record to format and output, + * to be passed to the @ref regular_show "show() handler" as + * its @a data parameter, if the call succeeds. Otherwise: + * + * - NULL in case no record is available, in which case the + * read operation will terminate immediately with no output. + * + * - VFILE_SEQ_START, a special value indicating that @ref + * regular_show "the show() handler" should receive a NULL + * data pointer first, in order to output a header. + * + * - ERR_PTR(errno), where errno is a negative error code; + * upon error, the current operation will be aborted + * immediately. + * + * @note This handler is optional; if none is given in the + * operation descriptor (i.e. NULL value), the read operation + * will stop after the first invocation of the @ref regular_show + * "show() handler". + */ + void *(*next)(struct xnvfile_regular_iterator *it); + /** + * @anchor regular_end + * This handler is called after all records have been output. + * + * @param it A pointer to the current vfile iterator. + * + * @note This handler is optional and the pointer may be NULL. + */ + void (*end)(struct xnvfile_regular_iterator *it); + /** + * @anchor regular_show + * This handler should format and output a record. + * + * xnvfile_printf(), xnvfile_write(), xnvfile_puts() and + * xnvfile_putc() are available to format and/or emit the + * output. All routines take the iterator argument @a it as + * their first parameter. + * + * @param it A pointer to the current vfile iterator. + * + * @param data A pointer to the record to format then + * output. The first call to the handler may receive a NULL @a + * data pointer, depending on the presence and/or return of a + * @ref regular_begin "hander"; the show handler should test + * this special value to output any header that fits, prior to + * receiving more calls with actual records. + * + * @return zero if the call succeeds, also indicating that the + * handler should be called for the next record if + * any. Otherwise: + * + * - A negative error code. This will abort the output phase, + * and return this status to the reader. + * + * - VFILE_SEQ_SKIP, a special value indicating that the + * current record should be skipped and will not be output. + */ + int (*show)(struct xnvfile_regular_iterator *it, void *data); + /** + * @anchor regular_store + * This handler receives data written to the vfile, likely for + * updating some kernel setting, or triggering any other + * action which fits. This is the only handler which deals + * with the write-side of a vfile. It is called when writing + * to the /proc entry of the vfile from a user-space process. + * + * The input data is described by a descriptor passed to the + * handler, which may be subsequently passed to parsing helper + * routines. For instance, xnvfile_get_string() will accept + * the input descriptor for returning the written data as a + * null-terminated character string. On the other hand, + * xnvfile_get_integer() will attempt to return a long integer + * from the input data. + * + * @param input A pointer to an input descriptor. It refers to + * an opaque data from the handler's standpoint. + * + * @return the number of bytes read from the input descriptor + * if the call succeeds. Otherwise, a negative error code. + * Return values from parsing helper routines are commonly + * passed back to the caller by the @ref show_store + * "store() handler". + * + * @note This handler is optional, and may be omitted for + * read-only vfiles. + */ + ssize_t (*store)(struct xnvfile_input *input); +}; + +struct xnvfile_regular { + struct xnvfile entry; + size_t privsz; + struct xnvfile_regular_ops *ops; +}; + +struct xnvfile_regular_template { + size_t privsz; + struct xnvfile_regular_ops *ops; + struct xnvfile_lock_ops *lockops; +}; + +/** + * @brief Regular vfile iterator + * @anchor regular_iterator + * + * This structure defines an iterator over a regular vfile. + */ +struct xnvfile_regular_iterator { + /** Current record position while iterating. */ + loff_t pos; + /** Highest record position found. */ + loff_t maxpos; + /** Backlink to the host sequential file supporting the vfile. */ + struct seq_file *seq; + /** Backlink to the vfile being read. */ + struct xnvfile_regular *vfile; + /** + * Start of private area. Use xnvfile_iterator_priv() to + * address it. + */ + char private[0]; +}; + +/** + * @brief Snapshot vfile operation descriptor + * @anchor snapshot_ops + * + * This structure describes the operations available with a + * snapshot-driven vfile. It defines handlers for returning a + * printable snapshot of some Xenomai object contents upon a + * user-space read request, and for updating this object upon a + * user-space write request. + */ +struct xnvfile_snapshot_ops { + /** + * @anchor snapshot_rewind + * This handler (re-)initializes the data collection, moving + * the seek pointer at the first record. When the file + * revision tag is touched while collecting data, the current + * reading is aborted, all collected data dropped, and the + * vfile is eventually rewound. + * + * @param it A pointer to the current snapshot iterator. Two + * useful information can be retrieved from this iterator in + * this context: + * + * - it->vfile is a pointer to the descriptor of the virtual + * file being rewound. + * + * - xnvfile_iterator_priv(it) returns a pointer to the + * private data area, available from the descriptor, which + * size is vfile->privsz. If the latter size is zero, the + * returned pointer is meaningless and should not be used. + * + * @return A negative error code aborts the data collection, + * and is passed back to the reader. Otherwise: + * + * - a strictly positive value is interpreted as the total + * number of records which will be returned by the @ref + * snapshot_next "next() handler" during the data collection + * phase. If no @ref snapshot_begin "begin() handler" is + * provided in the @ref snapshot_ops "operation descriptor", + * this value is used to allocate the snapshot buffer + * internally. The size of this buffer would then be + * vfile->datasz * value. + * + * - zero leaves the allocation to the @ref snapshot_begin + * "begin() handler" if present, or indicates that no record + * is to be output in case such handler is not given. + * + * @note This handler is optional; a NULL value indicates that + * nothing needs to be done for rewinding the vfile. It is + * called with the vfile lock held. + */ + int (*rewind)(struct xnvfile_snapshot_iterator *it); + /** + * @anchor snapshot_begin + * This handler should allocate the snapshot buffer to hold + * records during the data collection phase. When specified, + * all records collected via the @ref snapshot_next "next() + * handler" will be written to a cell from the memory area + * returned by begin(). + * + * @param it A pointer to the current snapshot iterator. + * + * @return A pointer to the record buffer, if the call + * succeeds. Otherwise: + * + * - NULL in case of allocation error. This will abort the data + * collection, and return -ENOMEM to the reader. + * + * - VFILE_SEQ_EMPTY, a special value indicating that no + * record will be output. In such a case, the @ref + * snapshot_next "next() handler" will not be called, and the + * data collection will stop immediately. However, the @ref + * snapshot_show "show() handler" will still be called once, + * with a NULL data pointer (i.e. header display request). + * + * @note This handler is optional; if none is given, an + * internal allocation depending on the value returned by the + * @ref snapshot_rewind "rewind() handler" can be obtained. + */ + void *(*begin)(struct xnvfile_snapshot_iterator *it); + /** + * @anchor snapshot_end + * This handler releases the memory buffer previously obtained + * from begin(). It is usually called after the snapshot data + * has been output by show(), but it may also be called before + * rewinding the vfile after a revision change, to release the + * dropped buffer. + * + * @param it A pointer to the current snapshot iterator. + * + * @param buf A pointer to the buffer to release. + * + * @note This routine is optional and the pointer may be + * NULL. It is not needed upon internal buffer allocation; + * see the description of the @ref snapshot_rewind "rewind() + * handler". + */ + void (*end)(struct xnvfile_snapshot_iterator *it, void *buf); + /** + * @anchor snapshot_next + * This handler fetches the next record, as part of the + * snapshot data to be sent back to the reader via the + * show(). + * + * @param it A pointer to the current snapshot iterator. + * + * @param data A pointer to the record to fill in. + * + * @return a strictly positive value, if the call succeeds and + * leaves a valid record into @a data, which should be passed + * to the @ref snapshot_show "show() handler()" during the + * formatting and output phase. Otherwise: + * + * - A negative error code. This will abort the data + * collection, and return this status to the reader. + * + * - VFILE_SEQ_SKIP, a special value indicating that the + * current record should be skipped. In such a case, the @a + * data pointer is not advanced to the next position before + * the @ref snapshot_next "next() handler" is called anew. + * + * @note This handler is called with the vfile lock + * held. Before each invocation of this handler, the vfile + * core checks whether the revision tag has been touched, in + * which case the data collection is restarted from scratch. A + * data collection phase succeeds whenever all records can be + * fetched via the @ref snapshot_next "next() handler", while + * the revision tag remains unchanged, which indicates that a + * consistent snapshot of the object state was taken. + */ + int (*next)(struct xnvfile_snapshot_iterator *it, void *data); + /** + * @anchor snapshot_show + * This handler should format and output a record from the + * collected data. + * + * xnvfile_printf(), xnvfile_write(), xnvfile_puts() and + * xnvfile_putc() are available to format and/or emit the + * output. All routines take the iterator argument @a it as + * their first parameter. + * + * @param it A pointer to the current snapshot iterator. + * + * @param data A pointer to the record to format then + * output. The first call to the handler is always passed a + * NULL @a data pointer; the show handler should test this + * special value to output any header that fits, prior to + * receiving more calls with actual records. + * + * @return zero if the call succeeds, also indicating that the + * handler should be called for the next record if + * any. Otherwise: + * + * - A negative error code. This will abort the output phase, + * and return this status to the reader. + * + * - VFILE_SEQ_SKIP, a special value indicating that the + * current record should be skipped and will not be output. + */ + int (*show)(struct xnvfile_snapshot_iterator *it, void *data); + /** + * @anchor snapshot_store + * This handler receives data written to the vfile, likely for + * updating the associated Xenomai object's state, or + * triggering any other action which fits. This is the only + * handler which deals with the write-side of a vfile. It is + * called when writing to the /proc entry of the vfile + * from a user-space process. + * + * The input data is described by a descriptor passed to the + * handler, which may be subsequently passed to parsing helper + * routines. For instance, xnvfile_get_string() will accept + * the input descriptor for returning the written data as a + * null-terminated character string. On the other hand, + * xnvfile_get_integer() will attempt to return a long integer + * from the input data. + * + * @param input A pointer to an input descriptor. It refers to + * an opaque data from the handler's standpoint. + * + * @return the number of bytes read from the input descriptor + * if the call succeeds. Otherwise, a negative error code. + * Return values from parsing helper routines are commonly + * passed back to the caller by the @ref snapshot_store + * "store() handler". + * + * @note This handler is optional, and may be omitted for + * read-only vfiles. + */ + ssize_t (*store)(struct xnvfile_input *input); +}; + +/** + * @brief Snapshot revision tag + * @anchor revision_tag + * + * This structure defines a revision tag to be used with @ref + * snapshot_vfile "snapshot-driven vfiles". + */ +struct xnvfile_rev_tag { + /** Current revision number. */ + int rev; +}; + +struct xnvfile_snapshot_template { + size_t privsz; + size_t datasz; + struct xnvfile_rev_tag *tag; + struct xnvfile_snapshot_ops *ops; + struct xnvfile_lock_ops *lockops; +}; + +/** + * @brief Snapshot vfile descriptor + * @anchor snapshot_vfile + * + * This structure describes a snapshot-driven vfile. Reading from + * such a vfile involves a preliminary data collection phase under + * lock protection, and a subsequent formatting and output phase of + * the collected data records. Locking is done in a way that does not + * increase worst-case latency, regardless of the number of records to + * be collected for output. + */ +struct xnvfile_snapshot { + struct xnvfile entry; + size_t privsz; + size_t datasz; + struct xnvfile_rev_tag *tag; + struct xnvfile_snapshot_ops *ops; +}; + +/** + * @brief Snapshot-driven vfile iterator + * @anchor snapshot_iterator + * + * This structure defines an iterator over a snapshot-driven vfile. + */ +struct xnvfile_snapshot_iterator { + /** Number of collected records. */ + int nrdata; + /** Address of record buffer. */ + caddr_t databuf; + /** Backlink to the host sequential file supporting the vfile. */ + struct seq_file *seq; + /** Backlink to the vfile being read. */ + struct xnvfile_snapshot *vfile; + /** Buffer release handler. */ + void (*endfn)(struct xnvfile_snapshot_iterator *it, void *buf); + /** + * Start of private area. Use xnvfile_iterator_priv() to + * address it. + */ + char private[0]; +}; + +struct xnvfile_directory { + struct xnvfile entry; +}; + +struct xnvfile_link { + struct xnvfile entry; +}; + +/* vfile.begin()=> */ +#define VFILE_SEQ_EMPTY ((void *)-1) +/* =>vfile.show() */ +#define VFILE_SEQ_START SEQ_START_TOKEN +/* vfile.next/show()=> */ +#define VFILE_SEQ_SKIP 2 + +#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_tag(struct xnvfile_rev_tag *tag) +{ + tag->rev++; +} + +static inline void xnvfile_touch(struct xnvfile_snapshot *vfile) +{ + xnvfile_touch_tag(vfile->tag); +} + +static inline int xnvfile_reg_p(struct xnvfile *entry) +{ + return S_ISREG(entry->pde->mode); +} + +static inline int xnvfile_dir_p(struct xnvfile *entry) +{ + return S_ISDIR(entry->pde->mode); +} + +static inline int xnvfile_link_p(struct xnvfile *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) +#define xnvfile_iterator_priv(it) ((void *)(&(it)->private)) + +extern struct xnvfile_nklock_class xnvfile_nucleus_lock; + +extern struct xnvfile_directory nkvfroot; + +int xnvfile_init_root(void); + +void xnvfile_destroy_root(void); + +#ifdef __cplusplus +extern "C" { +#endif + +int xnvfile_init_snapshot(const char *name, + struct xnvfile_snapshot *vfile, + struct xnvfile_directory *parent); + +int xnvfile_init_regular(const char *name, + struct xnvfile_regular *vfile, + struct xnvfile_directory *parent); + +int xnvfile_init_dir(const char *name, + struct xnvfile_directory *vdir, + struct xnvfile_directory *parent); + +int xnvfile_init_link(const char *from, + const char *to, + struct xnvfile_link *vlink, + struct xnvfile_directory *parent); + +void xnvfile_destroy(struct xnvfile *vfile); + +ssize_t xnvfile_get_blob(struct xnvfile_input *input, + void *data, size_t size); + +ssize_t xnvfile_get_string(struct xnvfile_input *input, + char *s, size_t maxlen); + +ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp); + +int __vfile_hostlock_get(struct xnvfile *vfile); + +void __vfile_hostlock_put(struct xnvfile *vfile); + +#ifdef __cplusplus +} +#endif + +static inline +void xnvfile_destroy_snapshot(struct xnvfile_snapshot *vfile) +{ + xnvfile_destroy(&vfile->entry); +} + +static inline +void xnvfile_destroy_regular(struct xnvfile_regular *vfile) +{ + xnvfile_destroy(&vfile->entry); +} + +static inline +void xnvfile_destroy_dir(struct xnvfile_directory *vdir) +{ + xnvfile_destroy(&vdir->entry); +} + +static inline +void xnvfile_destroy_link(struct xnvfile_link *vlink) +{ + xnvfile_destroy(&vlink->entry); +} + +#define DEFINE_VFILE_HOSTLOCK(name) \ + struct xnvfile_hostlock_class name = { \ + .ops = { \ + .get = __vfile_hostlock_get, \ + .put = __vfile_hostlock_put, \ + }, \ + .sem = __SEMAPHORE_INITIALIZER(name.sem, 1), \ + } + +#else /* !CONFIG_PROC_FS */ + +#define xnvfile_touch_tag(tag) do { } while (0) + +#define xnvfile_touch(vfile) do { } while (0) + +#endif /* !CONFIG_PROC_FS */ + +/*...@}*/ + +#endif /* !_XENO_NUCLEUS_VFILE_H */ diff --git a/ksrc/nucleus/vfile.c b/ksrc/nucleus/vfile.c new file mode 100644 index 0000000..3e14633 --- /dev/null +++ b/ksrc/nucleus/vfile.c @@ -0,0 +1,944 @@ +/** + * @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 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. + * + * The vfile support exposes four filesystem object types: + * + * - snapshot-driven file (struct xnvfile_snapshot). This is commonly + * used to export real-time object states via the /proc filesystem. To + * minimize the latency involved in protecting the vfile routines from + * changes applied by real-time code on such objects, a snapshot of + * the data to output is first taken under proper locking, before the + * collected data is formatted and sent out in a lockless manner. + * + * Because a large number of records may have to be output, the data + * collection phase is not strictly atomic as a whole, but only + * protected at record level. The vfile implementation can be notified + * of updates to the underlying data set, and restart the collection + * from scratch until the snapshot is fully consistent. + * + * - regular sequential file (struct xnvfile_regular). This is + * basically an encapsulated sequential file object as available from + * the host kernel (i.e. seq_file), with a few additional features to + * make it more handy in a Xenomai environment, like implicit locking + * support and shortened declaration for simplest, single-record + * output. + * + * - virtual link (struct xnvfile_link). This is a symbolic link + * feature integrated with the vfile semantics. The link target is + * computed dynamically at creation time from a user-given helper + * routine. + * + * - virtual directory (struct xnvfile_directory). A directory object, + * which can be used to create a hierarchy for ordering a set of vfile + * objects. + * + *...@{*/ + +#include <stdarg.h> +#include <linux/ctype.h> +#include <nucleus/pod.h> +#include <nucleus/assert.h> +#include <nucleus/vfile.h> + +/** + * @var struct xnvfile_directory nkvfroot + * @brief Xenomai vfile root directory + * + * This vdir maps the /proc/xenomai directory. It can be used to + * create a hierarchy of Xenomai-related vfiles under this root. + */ +struct xnvfile_directory nkvfroot; +EXPORT_SYMBOL_GPL(nkvfroot); + +static struct xnvfile_directory sysroot; + +static void *vfile_snapshot_start(struct seq_file *seq, loff_t *offp) +{ + struct xnvfile_snapshot_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_snapshot_next(struct seq_file *seq, void *v, loff_t *offp) +{ + struct xnvfile_snapshot_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_snapshot_stop(struct seq_file *seq, void *v) +{ +} + +static int vfile_snapshot_show(struct seq_file *seq, void *v) +{ + struct xnvfile_snapshot_iterator *it = seq->private; + void *data = v == SEQ_START_TOKEN ? NULL : v; + int ret; + + ret = it->vfile->ops->show(it, data); + + return ret == VFILE_SEQ_SKIP ? SEQ_SKIP : ret; +} + +static struct seq_operations vfile_snapshot_ops = { + .start = vfile_snapshot_start, + .next = vfile_snapshot_next, + .stop = vfile_snapshot_stop, + .show = vfile_snapshot_show +}; + +static void vfile_snapshot_free(struct xnvfile_snapshot_iterator *it, void *buf) +{ + kfree(buf); +} + +static int vfile_snapshot_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + struct xnvfile_snapshot *vfile = pde->data; + struct xnvfile_snapshot_ops *ops = vfile->ops; + struct xnvfile_snapshot_iterator *it; + int revtag, ret, nrdata; + struct seq_file *seq; + caddr_t data; + + /* + * 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; + + ret = vfile->entry.lockops->get(&vfile->entry); + if (ret) + goto fail; +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 present, ->rewind() may return a strictly positive + * value, indicating how many records at most may be returned + * by ->next(). We use this hint to allocate the snapshot + * buffer, in case ->begin() is not provided. The size of this + * buffer would then be vfile->datasz * hint value. + * + * If ->begin() is given, we always expect the latter do the + * allocation for us regardless of the hint value. Otherwise, + * a NULL return from ->rewind() tells us that the vfile won't + * output any snapshot data via ->show(). + */ + it->endfn = ops->end; + nrdata = 0; + if (ops->rewind) { + nrdata = ops->rewind(it); + if (nrdata < 0) { + ret = nrdata; + vfile->entry.lockops->put(&vfile->entry); + goto fail; + } + } + revtag = vfile->tag->rev; + + vfile->entry.lockops->put(&vfile->entry); + + /* Release the data buffer, in case we had to restart. */ + if (it->databuf) { + it->endfn(it, it->databuf); + it->databuf = NULL; + } + + /* + * 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. + */ + if (ops->begin) { + XENO_BUGON(NUCLEUS, ops->end == NULL); + data = ops->begin(it); + if (data == NULL) { + kfree(it); + return -ENOMEM; + } + if (data != VFILE_SEQ_EMPTY) + it->databuf = data; + } else if (nrdata > 0 && vfile->datasz > 0) { + /* We have a hint for auto-allocation. */ + data = kmalloc(vfile->datasz * nrdata, GFP_KERNEL); + if (data == NULL) { + kfree(it); + return -ENOMEM; + } + it->databuf = data; + it->endfn = vfile_snapshot_free; + } + + ret = seq_open(file, &vfile_snapshot_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 (;;) { + ret = vfile->entry.lockops->get(&vfile->entry); + if (ret) + break; + if (vfile->tag->rev != revtag) + goto redo; + ret = ops->next(it, data); + vfile->entry.lockops->put(&vfile->entry); + if (ret <= 0) + break; + if (ret != VFILE_SEQ_SKIP) { + data += vfile->datasz; + it->nrdata++; + } + } + + if (ret < 0) { + seq_release(inode, file); + fail: + if (it->databuf) + it->endfn(it, it->databuf); + kfree(it); + return ret; + } + +finish: + seq = file->private_data; + it->seq = seq; + seq->private = it; + + return 0; +} + +static int vfile_snapshot_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct xnvfile_snapshot_iterator *it; + + if (seq) { + it = seq->private; + if (it) { + if (it->databuf) + it->endfn(it, it->databuf); + kfree(it); + } + + return seq_release(inode, file); + } + + return 0; +} + +ssize_t vfile_snapshot_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_snapshot *vfile = pde->data; + struct xnvfile_input input; + + input.u_buf = buf; + input.size = size; + input.vfile = &vfile->entry; + + return vfile->ops->store(&input); +} + +static struct file_operations vfile_snapshot_fops = { + .owner = THIS_MODULE, + .open = vfile_snapshot_open, + .read = seq_read, + .write = vfile_snapshot_write, + .llseek = seq_lseek, + .release = vfile_snapshot_release, +}; + +/** + * @fn int xnvfile_init_snapshot(const char *name, struct xnvfile_snapshot *vfile, struct xnvfile_directory *parent) + * @brief Initialize a snapshot-driven vfile. + * + * @param name The name which should appear in the pseudo-filesystem, + * identifying the vfile entry. + * + * @param vfile A pointer to a vfile descriptor to initialize + * from. The following fields in this structure should be filled in + * prior to call this routine: + * + * - .privsz is the size (in bytes) of the private data area to be + * reserved in the @ref snapshot_iterator "vfile iterator". A NULL + * value indicates that no private area should be reserved. + * + * - .datasz is the size (in bytes) of a single record to be collected + * by the @ref snapshot_next "next() handler" from the @ref + * snapshot_ops "operation descriptor". + * + * - .tag is a pointer to a mandatory vfile revision tag structure + * (struct xnvfile_rev_tag). This tag will be monitored for changes by + * the vfile core while collecting data to output, so that any update + * detected will cause the current snapshot data to be dropped, and + * the collection to restart from the beginning. To this end, any + * change to the data which may be part of the collected records, + * should also invoke xnvfile_touch() on the associated tag. + * + * - entry.lockops is a pointer to a @ref vfile_lockops "locking + * descriptor", defining the lock and unlock operations for the + * vfile. This pointer may be left to NULL, in which case the + * operations on the nucleus lock (i.e. nklock) will be used + * internally around calls to data collection handlers (see @ref + * snapshot_ops "operation descriptor"). + * + * - .ops is a pointer to an @ref snapshot_ops "operation descriptor". + * + * @param parent A pointer to a virtual directory descriptor; the + * vfile entry will be created into this directory. If NULL, the /proc + * root directory will be used. /proc/xenomai is mapped on the + * globally available @a nkvfroot vdir. + * + * @return 0 is returned on success. Otherwise: + * + * - -ENOMEM is returned if the virtual file entry cannot be created + * in the /proc hierarchy. + */ +int xnvfile_init_snapshot(const char *name, + struct xnvfile_snapshot *vfile, + struct xnvfile_directory *parent) +{ + struct proc_dir_entry *ppde, *pde; + int mode; + + XENO_BUGON(NUCLEUS, vfile->tag == NULL); + + if (vfile->entry.lockops == NULL) + /* Defaults to nucleus lock */ + vfile->entry.lockops = &xnvfile_nucleus_lock.ops; + + if (parent == NULL) + parent = &sysroot; + + mode = vfile->ops->store ? 0644 : 0444; + ppde = parent->entry.pde; + pde = create_proc_entry(name, mode, ppde); + if (pde == NULL) + return -ENOMEM; + + pde->proc_fops = &vfile_snapshot_fops; + wrap_proc_dir_entry_owner(pde); + + vfile->entry.parent = parent; + vfile->entry.pde = pde; + pde->data = vfile; + + return 0; +} +EXPORT_SYMBOL_GPL(xnvfile_init_snapshot); + +static void *vfile_regular_start(struct seq_file *seq, loff_t *offp) +{ + struct xnvfile_regular_iterator *it = seq->private; + struct xnvfile_regular *vfile = it->vfile; + int ret; + + if (it->pos >= it->maxpos) + return NULL; + + it->pos = *offp; + + if (vfile->entry.lockops) { + ret = vfile->entry.lockops->get(&vfile->entry); + if (ret) + return ERR_PTR(ret); + } + + /* + * If we have no begin() op, then we allow a single call only + * to ->show(), by returning the start token once. Otherwise, + * we are done. + */ + if (vfile->ops->begin == NULL) + return it->pos > 0 ? NULL : SEQ_START_TOKEN; + + return vfile->ops->begin(it); +} + +static void *vfile_regular_next(struct seq_file *seq, void *v, loff_t *offp) +{ + struct xnvfile_regular_iterator *it = seq->private; + struct xnvfile_regular *vfile = it->vfile; + loff_t pos = ++*offp; + + if (vfile->ops->next == NULL) + return NULL; + + it->pos = pos; + it->maxpos = pos; + + return vfile->ops->next(it); +} + +static void vfile_regular_stop(struct seq_file *seq, void *v) +{ + struct xnvfile_regular_iterator *it = seq->private; + struct xnvfile_regular *vfile = it->vfile; + + if (vfile->entry.lockops) + vfile->entry.lockops->put(&vfile->entry); + + if (vfile->ops->end) + vfile->ops->end(it); +} + +static int vfile_regular_show(struct seq_file *seq, void *v) +{ + struct xnvfile_regular_iterator *it = seq->private; + struct xnvfile_regular *vfile = it->vfile; + void *data = v == SEQ_START_TOKEN ? NULL : v; + int ret; + + ret = vfile->ops->show(it, data); + + return ret == VFILE_SEQ_SKIP ? SEQ_SKIP : ret; +} + +static struct seq_operations vfile_regular_ops = { + .start = vfile_regular_start, + .next = vfile_regular_next, + .stop = vfile_regular_stop, + .show = vfile_regular_show +}; + +static int vfile_regular_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + struct xnvfile_regular *vfile = pde->data; + struct xnvfile_regular_ops *ops = vfile->ops; + struct xnvfile_regular_iterator *it; + struct seq_file *seq; + int ret; + + if ((file->f_mode & FMODE_WRITE) != 0 && + ops->store == NULL) + return -EACCES; + + 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; + it->pos = -1; + + ret = seq_open(file, &vfile_regular_ops); + if (ret) { + kfree(it); + return ret; + } + + seq = file->private_data; + it->seq = seq; + seq->private = it; + + return 0; +} + +static int vfile_regular_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct xnvfile_regular_iterator *it; + + if (seq) { + it = seq->private; + if (it) + kfree(it); + + return seq_release(inode, file); + } + + return 0; +} + +ssize_t vfile_regular_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_regular *vfile = pde->data; + struct xnvfile_input input; + + input.u_buf = buf; + input.size = size; + input.vfile = &vfile->entry; + + return vfile->ops->store(&input); +} + +static struct file_operations vfile_regular_fops = { + .owner = THIS_MODULE, + .open = vfile_regular_open, + .read = seq_read, + .write = vfile_regular_write, + .llseek = seq_lseek, + .release = vfile_regular_release, +}; + +/** + * @fn int xnvfile_init_regular(const char *name, struct xnvfile_regular *vfile, struct xnvfile_directory *parent) + * @brief Initialize a regular vfile. + * + * @param name The name which should appear in the pseudo-filesystem, + * identifying the vfile entry. + * + * @param vfile A pointer to a vfile descriptor to initialize + * from. The following fields in this structure should be filled in + * prior to call this routine: + * + * - .privsz is the size (in bytes) of the private data area to be + * reserved in the @ref regular_iterator "vfile iterator". A NULL + * value indicates that no private area should be reserved. + * + * - entry.lockops is a pointer to a @ref vfile_lockops "locking + * descriptor", defining the lock and unlock operations for the + * vfile. This pointer may be left to NULL, in which case no + * locking will be applied. + * + * - .ops is a pointer to an @ref regular_ops "operation descriptor". + * + * @param parent A pointer to a virtual directory descriptor; the + * vfile entry will be created into this directory. If NULL, the /proc + * root directory will be used. /proc/xenomai is mapped on the + * globally available @a nkvfroot vdir. + * + * @return 0 is returned on success. Otherwise: + * + * - -ENOMEM is returned if the virtual file entry cannot be created + * in the /proc hierarchy. + */ +int xnvfile_init_regular(const char *name, + struct xnvfile_regular *vfile, + struct xnvfile_directory *parent) +{ + struct proc_dir_entry *ppde, *pde; + int mode; + + if (parent == NULL) + parent = &sysroot; + + mode = vfile->ops->store ? 0644 : 0444; + ppde = parent->entry.pde; + pde = create_proc_entry(name, mode, ppde); + if (pde == NULL) + return -ENOMEM; + + pde->proc_fops = &vfile_regular_fops; + wrap_proc_dir_entry_owner(pde); + + vfile->entry.parent = parent; + vfile->entry.pde = pde; + pde->data = vfile; + + return 0; +} +EXPORT_SYMBOL_GPL(xnvfile_init_regular); + +/** + * @fn int xnvfile_init_dir(const char *name, struct xnvfile_directory *vdir, struct xnvfile_directory *parent) + * @brief Initialize a virtual directory entry. + * + * @param name The name which should appear in the pseudo-filesystem, + * identifying the vdir entry. + * + * @param vdir A pointer to the virtual directory descriptor to + * initialize. + * + * @param parent A pointer to a virtual directory descriptor standing + * for the parent directory of the new vdir. If NULL, the /proc root + * directory will be used. /proc/xenomai is mapped on the globally + * available @a nkvfroot vdir. + * + * @return 0 is returned on success. Otherwise: + * + * - -ENOMEM is returned if the virtual directory entry cannot be + * created in the /proc hierarchy. + */ +int xnvfile_init_dir(const char *name, + struct xnvfile_directory *vdir, + struct xnvfile_directory *parent) +{ + struct proc_dir_entry *ppde, *pde; + + if (parent == NULL) + parent = &sysroot; + + ppde = parent->entry.pde; + pde = create_proc_entry(name, S_IFDIR, ppde); + if (pde == NULL) + return -ENOMEM; + + vdir->entry.parent = parent; + vdir->entry.pde = pde; + vdir->entry.lockops = NULL; + vdir->entry.private = NULL; + wrap_proc_dir_entry_owner(pde); + + return 0; +} +EXPORT_SYMBOL_GPL(xnvfile_init_dir); + +/** + * @fn int xnvfile_init_link(const char *from, const char *to, struct xnvfile_link *vlink, struct xnvfile_directory *parent) + * @brief Initialize a virtual link entry. + * + * @param from The name which should appear in the pseudo-filesystem, + * identifying the vlink entry. + * + * @param to The target file name which should be referred to + * symbolically by @a name. + * + * @param vlink A pointer to the virtual link descriptor to + * initialize. + * + * @param parent A pointer to a virtual directory descriptor standing + * for the parent directory of the new vlink. If NULL, the /proc root + * directory will be used. /proc/xenomai is mapped on the globally + * available @a nkvfroot vdir. + * + * @return 0 is returned on success. Otherwise: + * + * - -ENOMEM is returned if the virtual link entry cannot be created + * in the /proc hierarchy. + */ +int xnvfile_init_link(const char *from, + const char *to, + struct xnvfile_link *vlink, + struct xnvfile_directory *parent) +{ + struct proc_dir_entry *ppde, *pde; + + if (parent == NULL) + parent = &sysroot; + + ppde = parent->entry.pde; + pde = proc_symlink(from, ppde, to); + if (vlink->entry.pde == NULL) + return -ENOMEM; + + vlink->entry.parent = parent; + vlink->entry.pde = pde; + vlink->entry.lockops = NULL; + vlink->entry.private = NULL; + wrap_proc_dir_entry_owner(pde); + + return 0; +} +EXPORT_SYMBOL_GPL(xnvfile_init_link); + +/** + * @fn void xnvfile_destroy(struct xnvfile *vfile) + * @brief Removes a virtual file entry. + * + * @param vfile A pointer to the virtual file descriptor to + * remove. + */ +void xnvfile_destroy(struct xnvfile *vfile) +{ + struct proc_dir_entry *ppde; + + ppde = vfile->parent ? vfile->parent->entry.pde : nkvfroot.entry.pde; + remove_proc_entry(vfile->pde->name, ppde); +} +EXPORT_SYMBOL_GPL(xnvfile_destroy); + +/** + * @fn ssize_t xnvfile_get_blob(struct xnvfile_input *input, void *data, size_t size) + * @brief Read in a data bulk written to the vfile. + * + * When writing to a vfile, the associated store() handler from the + * @ref snapshot_store "snapshot-driven vfile" or @ref regular_store + * "regular vfile" is called, with a single argument describing the + * input data. xnvfile_get_blob() retrieves this data as an untyped + * binary blob, and copies it back to the caller's buffer. + * + * @param input A pointer to the input descriptor passed to the + * store() handler. + * + * @param data The address of the destination buffer to copy the input + * data to. + * + * @param size The maximum number of bytes to copy to the destination + * buffer. If @a size is larger than the actual data size, the input + * is truncated to @a size. + * + * @return The number of bytes read and copied to the destination + * buffer upon success. Otherwise, a negative error code is returned: + * + * - -EFAULT indicates an invalid source buffer address. + */ +ssize_t xnvfile_get_blob(struct xnvfile_input *input, + void *data, size_t size) +{ + ssize_t nbytes = input->size; + + if (nbytes < size) + nbytes = size; + + if (nbytes > 0 && + __xn_safe_copy_from_user(data, input->u_buf, nbytes)) + return -EFAULT; + + return nbytes; +} +EXPORT_SYMBOL_GPL(xnvfile_get_blob); + +/** + * @fn ssize_t xnvfile_get_string(struct xnvfile_input *input, char *s, size_t maxlen) + * @brief Read in a C-string written to the vfile. + * + * When writing to a vfile, the associated store() handler from the + * @ref snapshot_store "snapshot-driven vfile" or @ref regular_store + * "regular vfile" is called, with a single argument describing the + * input data. xnvfile_get_string() retrieves this data as a + * null-terminated character string, and copies it back to the + * caller's buffer. + * + * @param input A pointer to the input descriptor passed to the + * store() handler. + * + * @param s The address of the destination string buffer to copy the + * input data to. + * + * @param maxlen The maximum number of bytes to copy to the + * destination buffer, including the ending null character. If @a + * maxlen is larger than the actual string length, the input is + * truncated to @a maxlen. + * + * @return The number of characters read and copied to the destination + * buffer upon success. Otherwise, a negative error code is returned: + * + * - -EFAULT indicates an invalid source buffer address. + */ +ssize_t xnvfile_get_string(struct xnvfile_input *input, + char *s, size_t maxlen) +{ + ssize_t nbytes; + + if (maxlen < 1) + return -EINVAL; + + nbytes = xnvfile_get_blob(input, s, maxlen - 1); + if (nbytes < 0) + return nbytes; + + if (nbytes > 0 && s[nbytes - 1] == '\n') + nbytes--; + + s[nbytes] = '\0'; + + return nbytes; +} +EXPORT_SYMBOL_GPL(xnvfile_get_string); + +/** + * @fn ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp) + * @brief Evaluate the string written to the vfile as a long integer. + * + * When writing to a vfile, the associated store() handler from the + * @ref snapshot_store "snapshot-driven vfile" or @ref regular_store + * "regular vfile" is called, with a single argument describing the + * input data. xnvfile_get_integer() retrieves and interprets this + * data as a long integer, and copies the resulting value back to @a + * valp. + * + * The long integer can be expressed in decimal, octal or hexadecimal + * bases depending on the prefix found. + * + * @param input A pointer to the input descriptor passed to the + * store() handler. + * + * @param valp The address of a long integer variable to receive the + * value. + * + * @return The number of characters read while evaluating the input as + * a long integer upon success. Otherwise, a negative error code is + * returned: + * + * - -EINVAL indicates a parse error on the input stream; the written + * text cannot be evaluated as a long integer. + * + * - -EFAULT indicates an invalid source buffer address. + */ +ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp) +{ + char *end, buf[32]; + ssize_t nbytes; + long val; + + nbytes = xnvfile_get_blob(input, buf, sizeof(buf)); + if (nbytes < 0) + return nbytes; + + if (nbytes == 0) + return -EINVAL; + + buf[nbytes] = '\0'; + val = simple_strtol(buf, &end, 0); + + if (*end != '\0' && !isspace(*end)) + return -EINVAL; + + *valp = val; + + return nbytes; +} +EXPORT_SYMBOL_GPL(xnvfile_get_integer); + +int __vfile_hostlock_get(struct xnvfile *vfile) +{ + struct xnvfile_hostlock_class *lc; + + lc = container_of(vfile->lockops, struct xnvfile_hostlock_class, ops); + return down_interruptible(&lc->sem) ? -ERESTARTSYS : 0; +} +EXPORT_SYMBOL_GPL(__vfile_hostlock_get); + +void __vfile_hostlock_put(struct xnvfile *vfile) +{ + struct xnvfile_hostlock_class *lc; + + lc = container_of(vfile->lockops, struct xnvfile_hostlock_class, ops); + up(&lc->sem); +} +EXPORT_SYMBOL_GPL(__vfile_hostlock_put); + +static int __vfile_nklock_get(struct xnvfile *vfile) +{ + struct xnvfile_nklock_class *lc; + + lc = container_of(vfile->lockops, struct xnvfile_nklock_class, ops); + xnlock_get_irqsave(&nklock, lc->s); + + return 0; +} + +static void __vfile_nklock_put(struct xnvfile *vfile) +{ + struct xnvfile_nklock_class *lc; + + lc = container_of(vfile->lockops, struct xnvfile_nklock_class, ops); + xnlock_put_irqrestore(&nklock, lc->s); +} + +struct xnvfile_nklock_class xnvfile_nucleus_lock = { + .ops = { + .get = __vfile_nklock_get, + .put = __vfile_nklock_put, + }, +}; + +int __init xnvfile_init_root(void) +{ + struct xnvfile_directory *vdir = &nkvfroot; + struct proc_dir_entry *pde; + + pde = create_proc_entry("xenomai", S_IFDIR, NULL); + if (pde == NULL) + return -ENOMEM; + + vdir->entry.parent = NULL; + vdir->entry.pde = pde; + vdir->entry.lockops = NULL; + vdir->entry.private = NULL; + wrap_proc_dir_entry_owner(pde); + + return 0; +} + +void xnvfile_destroy_root(void) +{ + nkvfroot.entry.pde = NULL; + remove_proc_entry("xenomai", NULL); +} + +/*...@}*/ _______________________________________________ Xenomai-git mailing list [email protected] https://mail.gna.org/listinfo/xenomai-git
