/** * @file * * @note Copyright (C) 2010 Philippe Gerum * * 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 #include struct xnvfile_directory; struct xnvfile_regular_iterator; struct xnvfile_snapshot_iterator; struct xnvfile_lock_ops; struct xnvfile { struct proc_dir_entry *pde; struct file *file; struct xnvfile_lock_ops *lockops; int refcnt; 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. */ 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_rewind This handler is called only once, * when the virtual file is opened, before the @ref * regular_begin "begin() handler" is invoked. * * @param it A pointer to the vfile iterator which will be * used to read the file contents. * * @return Zero should be returned upon success. Otherwise, a * negative error code aborts the operation, and is passed * back to the reader. * * @note This handler is optional. It should not be used to * allocate resources but rather to perform consistency * checks, since no closure call is issued in case the open * sequence eventually fails. */ int (*rewind)(struct xnvfile_regular_iterator *it); /** * @anchor regular_begin * This handler should prepare for iterating over the records * upon a read request, starting from the specified position. * * @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. This handler may be called multiple * times with different position requests. * * @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 * next 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. * * - 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 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; /** 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 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 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); } #define xnvfile_noentry \ { \ .pde = NULL, \ .private = NULL, \ .file = NULL, \ .refcnt = 0, \ } #define xnvfile_nodir { .entry = xnvfile_noentry } #define xnvfile_nolink { .entry = xnvfile_noentry } #define xnvfile_nofile { .entry = xnvfile_noentry } #define xnvfile_priv(e) ((e)->entry.private) #define xnvfile_nref(e) ((e)->entry.refcnt) #define xnvfile_file(e) ((e)->entry.file) #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_XENO_OPT_VFILE */ #define xnvfile_touch_tag(tag) do { } while (0) #define xnvfile_touch(vfile) do { } while (0) #endif /* !CONFIG_XENO_OPT_VFILE */ /*@}*/ #endif /* !_XENO_NUCLEUS_VFILE_H */