Update proc, remove old kernels

Signed-off-by: Alec Ari <neotheuser@ymail.com>
This commit is contained in:
Alec Ari 2014-08-18 13:25:18 -05:00
parent deacd9c122
commit 10a1b48d57
20 changed files with 3034 additions and 43401 deletions

View file

@ -12,7 +12,8 @@ librtdm_a_SOURCES = \
module.c \
proc.c \
rtai_taskq.c \
select.c
select.c \
vfile.c
include_HEADERS = \
rtdm.h \
@ -20,7 +21,8 @@ include_HEADERS = \
rtserial.h \
xn.h \
rtai_taskq.h \
select.h
select.h \
vfile.h
if CONFIG_KBUILD
rtai_rtdm$(modext): @RTAI_KBUILD_ENV@

View file

@ -2,7 +2,7 @@
* Copyright (C) 2005 Jan Kiszka <jan.kiszka@web.de>.
* Copyright (C) 2005 Joerg Langenberg <joerg.langenberg@gmx.net>.
*
* with adaptions for RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
* adapted to RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
*
* RTAI is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
@ -19,299 +19,427 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "rtdm/internal.h"
#include <rtdm/vfile.h>
/* Derived from Erwin Rol's rtai_proc_fs.h.
Assumes that output fits into the provided buffer. */
struct xnvfile_directory rtdm_vfroot; /* /proc/rtai/rtdm */
#define RTDM_PROC_PRINT_VARS(MAX_BLOCK_LEN) \
const int max_block_len = MAX_BLOCK_LEN; \
off_t __limit = count - MAX_BLOCK_LEN; \
int __len = 0; \
\
*eof = 1; \
if (count < MAX_BLOCK_LEN) \
return 0
struct vfile_device_data {
int h;
int hmax;
struct list_head *devmap;
struct list_head *curr;
};
#define RTDM_PROC_PRINT(fmt, args...) \
({ \
__len += snprintf(buf + __len, max_block_len, fmt, ##args); \
(__len <= __limit); \
})
#define RTDM_PROC_PRINT_DONE \
return __len
struct proc_dir_entry *rtdm_proc_root; /* /proc/rtai/rtdm */
static int proc_read_named_devs(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
static int get_nrt_lock(struct xnvfile *vfile)
{
int i;
struct list_head *entry;
struct rtdm_device *device;
RTDM_PROC_PRINT_VARS(80);
if (down_interruptible(&nrt_dev_lock))
return -ERESTARTSYS;
if (!RTDM_PROC_PRINT("Hash\tName\t\t\t\tDriver\t\t/proc\n"))
goto done;
for (i = 0; i < devname_hashtab_size; i++)
list_for_each(entry, &rtdm_named_devices[i]) {
device = list_entry(entry, struct rtdm_device,
reserved.entry);
if (!RTDM_PROC_PRINT("%02X\t%-31s\t%-15s\t%s\n",
i, device->device_name,
device->driver_name,
device->proc_name))
break;
}
done:
up(&nrt_dev_lock);
RTDM_PROC_PRINT_DONE;
return down_interruptible(&nrt_dev_lock) ? -ERESTARTSYS : 0;
}
static int proc_read_proto_devs(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
static void put_nrt_lock(struct xnvfile *vfile)
{
int i;
struct list_head *entry;
struct rtdm_device *device;
char txt[32];
RTDM_PROC_PRINT_VARS(80);
if (down_interruptible(&nrt_dev_lock))
return -ERESTARTSYS;
if (!RTDM_PROC_PRINT("Hash\tProtocolFamily:SocketType\tDriver\t\t"
"/proc\n"))
goto done;
for (i = 0; i < protocol_hashtab_size; i++)
list_for_each(entry, &rtdm_protocol_devices[i]) {
device = list_entry(entry, struct rtdm_device,
reserved.entry);
snprintf(txt, sizeof(txt), "%u:%u",
device->protocol_family, device->socket_type);
if (!RTDM_PROC_PRINT("%02X\t%-31s\t%-15s\t%s\n", i,
txt, device->driver_name,
device->proc_name))
break;
}
done:
up(&nrt_dev_lock);
RTDM_PROC_PRINT_DONE;
}
static int proc_read_open_fildes(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
static struct xnvfile_lock_ops lockops = {
.get = get_nrt_lock,
.put = put_nrt_lock,
};
static struct list_head *next_devlist(struct vfile_device_data *priv)
{
int i;
int close_lock_count;
struct rtdm_device *device;
struct rtdm_process owner;
spl_t s;
RTDM_PROC_PRINT_VARS(80);
struct list_head *head;
if (!RTDM_PROC_PRINT("Index\tLocked\tDevice\t\tOwner [PID]\n"))
goto done;
if (down_interruptible(&nrt_dev_lock))
return -ERESTARTSYS;
for (i = 0; i < RTDM_FD_MAX; i++) {
struct rtdm_dev_context *context;
xnlock_get_irqsave(&rt_fildes_lock, s);
context = fildes_table[i].context;
if (!context) {
xnlock_put_irqrestore(&rt_fildes_lock, s);
continue;
}
close_lock_count = atomic_read(&context->close_lock_count);
device = context->device;
if (context->reserved.owner)
memcpy(&owner, context->reserved.owner, sizeof(owner));
else {
strcpy(owner.name, "<kernel>");
owner.pid = -1;
}
xnlock_put_irqrestore(&rt_fildes_lock, s);
if (!RTDM_PROC_PRINT("%d\t%d\t%-15s %s [%d]\n", i,
close_lock_count,
(device->device_flags&RTDM_NAMED_DEVICE) ?
device->device_name : device->proc_name,
owner.name, owner.pid))
break;
while (priv->h < priv->hmax) {
head = priv->devmap + priv->h;
if (!list_empty(head))
return head;
priv->h++;
}
return NULL;
}
static void *next_dev(struct xnvfile_regular_iterator *it)
{
struct vfile_device_data *priv = xnvfile_iterator_priv(it);
struct list_head *next;
next = priv->curr->next;
seek:
if (next == priv->devmap + priv->h) {
/* Done with the current hash slot, let's progress. */
if (priv->h >= priv->hmax) {
next = NULL; /* all done. */
goto out;
}
priv->h++;
next = next_devlist(priv);
if (next) {
next = next->next; /* skip head. */
goto seek;
}
}
out:
priv->curr = next;
return next;
}
static void *named_begin(struct xnvfile_regular_iterator *it)
{
struct vfile_device_data *priv = xnvfile_iterator_priv(it);
struct list_head *devlist;
loff_t pos = 0;
priv->devmap = rtdm_named_devices;
priv->hmax = devname_hashtab_size;
priv->h = 0;
devlist = next_devlist(priv);
if (devlist == NULL)
return NULL; /* All devlists empty. */
priv->curr = devlist->next; /* Skip head. */
/*
* priv->curr now points to the first device; advance to the requested
* position from there.
*/
while (priv->curr && pos++ < it->pos)
priv->curr = next_dev(it);
if (pos == 1)
/* Output the header once, only if some device follows. */
xnvfile_puts(it, "Hash\tName\t\t\t\tDriver\t\t/proc\n");
return priv->curr;
}
static int named_show(struct xnvfile_regular_iterator *it, void *data)
{
struct vfile_device_data *priv = xnvfile_iterator_priv(it);
struct list_head *curr = data;
struct rtdm_device *device;
device = list_entry(curr, struct rtdm_device, reserved.entry);
xnvfile_printf(it, "%02X\t%-31s\t%-15s\t%s\n",
priv->h, device->device_name,
device->driver_name,
device->proc_name);
return 0;
}
static struct xnvfile_regular_ops named_vfile_ops = {
.begin = named_begin,
.next = next_dev,
.show = named_show,
};
static struct xnvfile_regular named_vfile = {
.privsz = sizeof(struct vfile_device_data),
.ops = &named_vfile_ops,
.entry = { .lockops = &lockops }
};
static void *proto_begin(struct xnvfile_regular_iterator *it)
{
struct vfile_device_data *priv = xnvfile_iterator_priv(it);
struct list_head *devlist;
loff_t pos = 0;
priv->devmap = rtdm_protocol_devices;
priv->hmax = protocol_hashtab_size;
priv->h = 0;
devlist = next_devlist(priv);
if (devlist == NULL)
return NULL; /* All devlists empty. */
priv->curr = devlist->next; /* Skip head. */
/*
* priv->curr now points to the first device; advance to the requested
* position from there.
*/
while (priv->curr && pos++ < it->pos)
priv->curr = next_dev(it);
if (pos == 1)
/* Output the header once, only if some device follows. */
xnvfile_puts(it, "Hash\tName\t\t\t\tDriver\t\t/proc\n");
return priv->curr;
}
static int proto_show(struct xnvfile_regular_iterator *it, void *data)
{
struct vfile_device_data *priv = xnvfile_iterator_priv(it);
struct list_head *curr = data;
struct rtdm_device *device;
char pnum[32];
device = list_entry(curr, struct rtdm_device, reserved.entry);
snprintf(pnum, sizeof(pnum), "%u:%u",
device->protocol_family, device->socket_type);
xnvfile_printf(it, "%02X\t%-31s\t%-15s\t%s\n",
priv->h,
pnum, device->driver_name,
device->proc_name);
return 0;
}
static struct xnvfile_regular_ops proto_vfile_ops = {
.begin = proto_begin,
.next = next_dev,
.show = proto_show,
};
static struct xnvfile_regular proto_vfile = {
.privsz = sizeof(struct vfile_device_data),
.ops = &proto_vfile_ops,
.entry = { .lockops = &lockops }
};
static void *openfd_begin(struct xnvfile_regular_iterator *it)
{
if (it->pos == 0)
return VFILE_SEQ_START;
return it->pos <= RTDM_FD_MAX ? it : NULL;
}
static void *openfd_next(struct xnvfile_regular_iterator *it)
{
if (it->pos > RTDM_FD_MAX)
return NULL;
return it;
}
static int openfd_show(struct xnvfile_regular_iterator *it, void *data)
{
struct rtdm_dev_context *context;
struct rtdm_device *device;
struct rtdm_process owner;
int close_lock_count, fd;
spl_t s;
if (data == NULL) {
xnvfile_puts(it, "Index\tLocked\tDevice\t\t\t\tOwner [PID]\n");
return 0;
}
fd = (int)it->pos - 1;
xnlock_get_irqsave(&rt_fildes_lock, s);
context = fildes_table[fd].context;
if (context == NULL) {
xnlock_put_irqrestore(&rt_fildes_lock, s);
return VFILE_SEQ_SKIP;
}
close_lock_count = atomic_read(&context->close_lock_count);
device = context->device;
if (context->reserved.owner)
memcpy(&owner, context->reserved.owner, sizeof(owner));
else {
strcpy(owner.name, "<kernel>");
owner.pid = -1;
}
xnlock_put_irqrestore(&rt_fildes_lock, s);
xnvfile_printf(it, "%d\t%d\t%-31s %s [%d]\n", fd,
close_lock_count,
(device->device_flags & RTDM_NAMED_DEVICE) ?
device->device_name : device->proc_name,
owner.name, owner.pid);
return 0;
}
static ssize_t openfd_store(struct xnvfile_input *input)
{
ssize_t ret, cret;
long val;
ret = xnvfile_get_integer(input, &val);
if (ret < 0)
return ret;
cret = __rt_dev_close(current, (int)val);
if (cret < 0)
return cret;
return ret;
}
static struct xnvfile_regular_ops openfd_vfile_ops = {
.begin = openfd_begin,
.next = openfd_next,
.show = openfd_show,
.store = openfd_store,
};
static struct xnvfile_regular openfd_vfile = {
.ops = &openfd_vfile_ops,
.entry = { .lockops = &lockops }
};
static int allfd_vfile_show(struct xnvfile_regular_iterator *it, void *data)
{
xnvfile_printf(it, "total=%d:open=%d:free=%d\n", RTDM_FD_MAX,
open_fildes, RTDM_FD_MAX - open_fildes);
return 0;
}
static struct xnvfile_regular_ops allfd_vfile_ops = {
.show = allfd_vfile_show,
};
static struct xnvfile_regular allfd_vfile = {
.ops = &allfd_vfile_ops,
};
static int devinfo_vfile_show(struct xnvfile_regular_iterator *it, void *data)
{
struct rtdm_device *device;
int i;
if (down_interruptible(&nrt_dev_lock))
return -ERESTARTSYS;
/*
* As the device may have disappeared while the handler was called,
* first match the pointer against registered devices.
*/
for (i = 0; i < devname_hashtab_size; i++)
list_for_each_entry(device, &rtdm_named_devices[i],
reserved.entry)
if (device == xnvfile_priv(it->vfile))
goto found;
for (i = 0; i < protocol_hashtab_size; i++)
list_for_each_entry(device, &rtdm_protocol_devices[i],
reserved.entry)
if (device == xnvfile_priv(it->vfile))
goto found;
up(&nrt_dev_lock);
return -ENODEV;
done:
RTDM_PROC_PRINT_DONE;
found:
xnvfile_printf(it, "driver:\t\t%s\nversion:\t%d.%d.%d\n",
device->driver_name,
RTDM_DRIVER_MAJOR_VER(device->driver_version),
RTDM_DRIVER_MINOR_VER(device->driver_version),
RTDM_DRIVER_PATCH_VER(device->driver_version));
xnvfile_printf(it, "peripheral:\t%s\nprovider:\t%s\n",
device->peripheral_name, device->provider_name);
xnvfile_printf(it, "class:\t\t%d\nsub-class:\t%d\n",
device->device_class, device->device_sub_class);
xnvfile_printf(it, "flags:\t\t%s%s%s\n",
(device->device_flags & RTDM_EXCLUSIVE) ?
"EXCLUSIVE " : "",
(device->device_flags & RTDM_NAMED_DEVICE) ?
"NAMED_DEVICE " : "",
(device->device_flags & RTDM_PROTOCOL_DEVICE) ?
"PROTOCOL_DEVICE " : "");
xnvfile_printf(it, "lock count:\t%d\n",
atomic_read(&device->reserved.refcount));
up(&nrt_dev_lock);
return 0;
}
static int proc_kill_open_fildes(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
char krnl_buf[32];
int fd;
int res;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (count >= sizeof(krnl_buf))
return -EINVAL;
if (copy_from_user(krnl_buf, buffer, count))
return -EFAULT;
krnl_buf[count] = '\0';
if (!sscanf(krnl_buf, "%d", &fd))
return -EINVAL;
res = __rt_dev_close(current, fd);
if (res < 0)
return res;
return count;
}
static int proc_read_fildes(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
RTDM_PROC_PRINT_VARS(80);
RTDM_PROC_PRINT("total=%d:open=%d:free=%d\n", RTDM_FD_MAX,
open_fildes, RTDM_FD_MAX - open_fildes);
RTDM_PROC_PRINT_DONE;
}
static int proc_read_dev_info(char *buf, char **start, off_t offset,
int count, int *eof, void *data)
{
/* accessing the device during unregister (remove_proc_entry) might be
racy, but no official workaround is known yet */
struct rtdm_device *device = data;
RTDM_PROC_PRINT_VARS(256);
if (!RTDM_PROC_PRINT("driver:\t\t%s\nversion:\t%d.%d.%d\n",
device->driver_name,
RTDM_DRIVER_MAJOR_VER(device->driver_version),
RTDM_DRIVER_MINOR_VER(device->driver_version),
RTDM_DRIVER_PATCH_VER(device->driver_version)))
goto done;
if (!RTDM_PROC_PRINT("peripheral:\t%s\nprovider:\t%s\n",
device->peripheral_name, device->provider_name))
goto done;
if (!RTDM_PROC_PRINT("class:\t\t%d\nsub-class:\t%d\n",
device->device_class, device->device_sub_class))
goto done;
if (!RTDM_PROC_PRINT("flags:\t\t%s%s%s\n",
(device->device_flags & RTDM_EXCLUSIVE) ?
"EXCLUSIVE " : "",
(device->device_flags & RTDM_NAMED_DEVICE) ?
"NAMED_DEVICE " : "",
(device->device_flags & RTDM_PROTOCOL_DEVICE) ?
"PROTOCOL_DEVICE " : ""))
goto done;
RTDM_PROC_PRINT("lock count:\t%d\n",
atomic_read(&device->reserved.refcount));
done:
RTDM_PROC_PRINT_DONE;
}
static struct xnvfile_regular_ops devinfo_vfile_ops = {
.show = devinfo_vfile_show,
};
int rtdm_proc_register_device(struct rtdm_device *device)
{
struct proc_dir_entry *dev_dir;
struct proc_dir_entry *proc_entry;
int ret;
dev_dir = create_proc_entry(device->proc_name, S_IFDIR, rtdm_proc_root);
if (!dev_dir)
ret = xnvfile_init_dir(device->proc_name,
&device->vfroot, &rtdm_vfroot);
if (ret)
goto err_out;
proc_entry = create_proc_entry("information", S_IFREG | S_IRUGO,
dev_dir);
if (!proc_entry) {
remove_proc_entry(device->proc_name, rtdm_proc_root);
memset(&device->info_vfile, 0, sizeof(device->info_vfile));
device->info_vfile.ops = &devinfo_vfile_ops;
ret = xnvfile_init_regular("information", &device->info_vfile,
&device->vfroot);
if (ret) {
xnvfile_destroy_dir(&device->vfroot);
goto err_out;
}
proc_entry->data = device;
proc_entry->read_proc = proc_read_dev_info;
device->proc_entry = dev_dir;
xnvfile_priv(&device->info_vfile) = device;
return 0;
err_out:
xnlogerr("RTDM: error while creating device proc entry\n");
return -EAGAIN;
xnlogerr("RTDM: error while creating device vfile\n");
return ret;
}
void rtdm_proc_unregister_device(struct rtdm_device *device)
{
remove_proc_entry("information", device->proc_entry);
remove_proc_entry(device->proc_name, rtdm_proc_root);
xnvfile_destroy_regular(&device->info_vfile);
xnvfile_destroy_dir(&device->vfroot);
}
int __init rtdm_proc_init(void)
{
struct proc_dir_entry *proc_entry;
int ret;
/* Initialise /proc entries */
rtdm_proc_root = create_proc_entry("rtai/rtdm", S_IFDIR, NULL);
if (!rtdm_proc_root)
return -EAGAIN;
/* Initialise vfiles */
// ret = xnvfile_init_root(); /proc/rtai is initted elsewhere
ret = xnvfile_init_dir("rtai/rtdm", &rtdm_vfroot, &nkvfroot);
if (ret)
goto error;
proc_entry = create_proc_entry("named_devices", S_IFREG | S_IRUGO,
rtdm_proc_root);
if (!proc_entry)
return -EAGAIN;
proc_entry->read_proc = proc_read_named_devs;
ret = xnvfile_init_regular("named_devices", &named_vfile, &rtdm_vfroot);
if (ret)
goto error;
proc_entry = create_proc_entry("protocol_devices", S_IFREG | S_IRUGO,
rtdm_proc_root);
if (!proc_entry)
return -EAGAIN;
proc_entry->read_proc = proc_read_proto_devs;
ret = xnvfile_init_regular("protocol_devices", &proto_vfile, &rtdm_vfroot);
if (ret)
goto error;
proc_entry =
create_proc_entry("open_fildes", S_IFREG | S_IRUGO, rtdm_proc_root);
if (!proc_entry)
return -EAGAIN;
proc_entry->read_proc = proc_read_open_fildes;
proc_entry->write_proc = proc_kill_open_fildes;
ret = xnvfile_init_regular("open_fildes", &openfd_vfile, &rtdm_vfroot);
if (ret)
goto error;
proc_entry =
create_proc_entry("fildes", S_IFREG | S_IRUGO, rtdm_proc_root);
if (!proc_entry)
return -EAGAIN;
proc_entry->read_proc = proc_read_fildes;
ret = xnvfile_init_regular("fildes", &allfd_vfile, &rtdm_vfroot);
if (ret)
goto error;
return 0;
error:
rtdm_proc_cleanup();
return ret;
}
void rtdm_proc_cleanup(void)
{
remove_proc_entry("fildes", rtdm_proc_root);
remove_proc_entry("open_fildes", rtdm_proc_root);
remove_proc_entry("protocol_devices", rtdm_proc_root);
remove_proc_entry("named_devices", rtdm_proc_root);
remove_proc_entry("rtai/rtdm", NULL);
xnvfile_destroy_regular(&allfd_vfile);
xnvfile_destroy_regular(&openfd_vfile);
xnvfile_destroy_regular(&proto_vfile);
xnvfile_destroy_regular(&named_vfile);
xnvfile_destroy_dir(&rtdm_vfroot);
// xnvfile_destroy_root(); /proc/rtai is destroyed elsewhere
}

View file

@ -40,6 +40,7 @@
#include "xn.h"
#include "select.h"
#include <rtdm/vfile.h>
#include <rtdm/rtdm.h>
@ -509,6 +510,12 @@ struct rtdm_device {
/** Name of /proc entry for the device, must not be NULL */
const char *proc_name;
#ifdef CONFIG_PROC_FS
/** Set to device's vfile data after registration, do not modify */
struct xnvfile_directory vfroot;
struct xnvfile_regular info_vfile;
#endif
/** Set to device's /proc root entry after registration, do not modify */
struct proc_dir_entry *proc_entry;

970
addons/rtdm/vfile.c Normal file
View file

@ -0,0 +1,970 @@
/**
* @file
* This file is part of the Xenomai project.
*
* @note Copyright (C) 2010 Philippe Gerum <rpm@xenomai.org>
*
* adapted to RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
*
* 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 RTAI 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 an RTAI 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 <rtdm/vfile.h>
/**
* @var struct xnvfile_directory nkvfroot
* @brief RTAI vfile root directory
*
* This vdir maps the /proc/rtai directory. It can be used to
* create a hierarchy of RTAI-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;
++*offp;
return it->databuf + pos * 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 xnvfile_snapshot *vfile = PDE_DATA(inode);
struct xnvfile_snapshot_ops *ops = vfile->ops;
struct xnvfile_snapshot_iterator *it;
int revtag, ret, nrdata;
struct seq_file *seq;
caddr_t data;
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;
}
if ((file->f_flags & O_EXCL) != 0 && xnvfile_nref(vfile) > 0)
return -EBUSY;
it = kzalloc(sizeof(*it) + vfile->privsz, GFP_KERNEL);
if (it == NULL)
return -ENOMEM;
it->vfile = vfile;
xnvfile_file(vfile) = file;
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().
*/
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) {
RTAI_BUGON(NUCLEUS, ops->end == NULL);
data = ops->begin(it);
if (data == NULL) {
kfree(it);
return -ENOMEM;
}
if (data != VFILE_SEQ_EMPTY) {
it->databuf = data;
it->endfn = ops->end;
}
} 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;
xnvfile_nref(vfile)++;
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) {
--xnvfile_nref(it->vfile);
RTAI_BUGON(NUCLEUS, it->vfile->entry.refcnt < 0);
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 xnvfile_snapshot *vfile = PDE_DATA(wrap_f_inode(file));
struct xnvfile_input input;
ssize_t ret;
if (vfile->entry.lockops) {
ret = vfile->entry.lockops->get(&vfile->entry);
if (ret)
return ret;
}
input.u_buf = buf;
input.size = size;
input.vfile = &vfile->entry;
ret = vfile->ops->store(&input);
if (vfile->entry.lockops)
vfile->entry.lockops->put(&vfile->entry);
return ret;
}
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/rtai 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;
RTAI_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 = proc_create_data(name, mode, ppde, &vfile_snapshot_fops, vfile);
if (pde == NULL)
return -ENOMEM;
wrap_proc_dir_entry_owner(pde);
vfile->entry.pde = pde;
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;
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;
void *data;
if (vfile->ops->next == NULL)
return NULL;
it->pos = *offp + 1;
data = vfile->ops->next(it);
if (data == NULL)
return NULL;
*offp = it->pos;
return data;
}
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 xnvfile_regular *vfile = PDE_DATA(inode);
struct xnvfile_regular_ops *ops = vfile->ops;
struct xnvfile_regular_iterator *it;
struct seq_file *seq;
int ret;
if ((file->f_flags & O_EXCL) != 0 && xnvfile_nref(vfile) > 0)
return -EBUSY;
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;
xnvfile_file(vfile) = file;
if (ops->rewind) {
ret = ops->rewind(it);
if (ret) {
fail:
kfree(it);
return ret;
}
}
ret = seq_open(file, &vfile_regular_ops);
if (ret)
goto fail;
seq = file->private_data;
it->seq = seq;
seq->private = it;
xnvfile_nref(vfile)++;
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) {
--xnvfile_nref(it->vfile);
RTAI_BUGON(NUCLEUS, xnvfile_nref(it->vfile) < 0);
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 xnvfile_regular *vfile = PDE_DATA(wrap_f_inode(file));
struct xnvfile_input input;
ssize_t ret;
if (vfile->entry.lockops) {
ret = vfile->entry.lockops->get(&vfile->entry);
if (ret)
return ret;
}
input.u_buf = buf;
input.size = size;
input.vfile = &vfile->entry;
ret = vfile->ops->store(&input);
if (vfile->entry.lockops)
vfile->entry.lockops->put(&vfile->entry);
return ret;
}
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/rtai 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 = proc_create_data(name, mode, ppde, &vfile_regular_fops, vfile);
if (pde == NULL)
return -ENOMEM;
wrap_proc_dir_entry_owner(pde);
vfile->entry.pde = pde;
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/rtai 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 = proc_mkdir(name, ppde);
if (pde == NULL)
return -ENOMEM;
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/rtai 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 (pde == NULL)
return -ENOMEM;
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)
{
proc_remove(vfile->pde);
}
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 && 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) - 1);
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 = proc_mkdir("rtai", NULL);
if (pde == NULL)
return -ENOMEM;
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("rtai", NULL);
}
/*@}*/

700
addons/rtdm/vfile.h Normal file
View file

@ -0,0 +1,700 @@
/**
* @file
* This file is part of the Xenomai project.
*
* @note Copyright (C) 2010 Philippe Gerum <rpm@xenomai.org>
*
* adapted to RTAI by Paolo Mantegazza <mantegazza@aero.polimi.it>
*
* 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 RTAI_RTDM_VFILE_H
#define RTAI_RTDM_VFILE_H
#ifdef CONFIG_PROC_FS
/** @addtogroup vfile
*@{*/
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <rtdm/xn.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
#define PDE_DATA(inode) PDE(inode)->data
static inline void proc_remove(struct proc_dir_entry *pde)
{
remove_proc_entry(pde->name, pde->parent);
}
#endif
#define wrap_f_inode(file) ((file)->f_path.dentry->d_inode)
#define wrap_proc_dir_entry_owner(entry) do { (void)entry; } while(0)
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, but supported
* on both 2.4 and 2.6 kernels. Will be changed to mutex when 2.4
* support is dropped from RTAI.
*/
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 RTAI 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 RTAI 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_PROC_FS */
#define xnvfile_touch_tag(tag) do { } while (0)
#define xnvfile_touch(vfile) do { } while (0)
#endif /* !CONFIG_PROC_FS */
/*@}*/
#endif /* !RTAI_RTDM_VFILE_H */

View file

@ -1571,10 +1571,10 @@ extern void cleanup_tsc_sync(void);
extern volatile long rtai_tsc_ofst[];
#endif
static int rtai_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
static int PROC_READ_FUN(rtai_read_proc)
{
PROC_PRINT_VARS;
int i, none;
PROC_PRINT_VARS;
PROC_PRINT("\n** RTAI/x86:\n\n");
PROC_PRINT(" CPU Frequency: %lu (Hz)\n", rtai_tunables.cpu_freq);
@ -1629,11 +1629,13 @@ static int rtai_read_proc (char *page, char **start, off_t off, int count, int *
PROC_PRINT_DONE;
}
PROC_READ_OPEN_OPS(rtai_hal_proc_fops, rtai_read_proc);
static int rtai_proc_register (void)
{
struct proc_dir_entry *ent;
rtai_proc_root = create_proc_entry("rtai",S_IFDIR, 0);
rtai_proc_root = CREATE_PROC_ENTRY("rtai", S_IFDIR, NULL, &rtai_hal_proc_fops);
if (!rtai_proc_root) {
printk(KERN_ERR "Unable to initialize /proc/rtai.\n");
return -1;
@ -1641,20 +1643,21 @@ static int rtai_proc_register (void)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
rtai_proc_root->owner = THIS_MODULE;
#endif
ent = create_proc_entry("hal",S_IFREG|S_IRUGO|S_IWUSR,rtai_proc_root);
ent = CREATE_PROC_ENTRY("hal", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root,
&rtai_hal_proc_fops);
if (!ent) {
printk(KERN_ERR "Unable to initialize /proc/rtai/hal.\n");
return -1;
}
ent->read_proc = rtai_read_proc;
SET_PROC_READ_ENTRY(ent, rtai_read_proc);
return 0;
}
static void rtai_proc_unregister (void)
{
remove_proc_entry("hal",rtai_proc_root);
remove_proc_entry("rtai",0);
remove_proc_entry("hal", rtai_proc_root);
remove_proc_entry("rtai", 0);
}
#endif /* CONFIG_PROC_FS */

View file

@ -1469,10 +1469,10 @@ extern void cleanup_tsc_sync(void);
extern volatile long rtai_tsc_ofst[];
#endif
static int rtai_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
static int PROC_READ_FUN(rtai_read_proc)
{
PROC_PRINT_VARS;
int i, none;
PROC_PRINT_VARS;
PROC_PRINT("\n** RTAI/x86:\n\n");
PROC_PRINT(" CPU Frequency: %lu (Hz)\n", rtai_tunables.cpu_freq);
@ -1527,11 +1527,13 @@ static int rtai_read_proc (char *page, char **start, off_t off, int count, int *
PROC_PRINT_DONE;
}
PROC_READ_OPEN_OPS(rtai_hal_proc_fops, rtai_read_proc);
static int rtai_proc_register (void)
{
struct proc_dir_entry *ent;
rtai_proc_root = create_proc_entry("rtai",S_IFDIR, 0);
rtai_proc_root = CREATE_PROC_ENTRY("rtai", S_IFDIR, NULL, &rtai_hal_proc_fops);
if (!rtai_proc_root) {
printk(KERN_ERR "Unable to initialize /proc/rtai.\n");
return -1;
@ -1539,20 +1541,21 @@ static int rtai_proc_register (void)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
rtai_proc_root->owner = THIS_MODULE;
#endif
ent = create_proc_entry("hal",S_IFREG|S_IRUGO|S_IWUSR,rtai_proc_root);
ent = CREATE_PROC_ENTRY("hal", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root,
&rtai_hal_proc_fops);
if (!ent) {
printk(KERN_ERR "Unable to initialize /proc/rtai/hal.\n");
return -1;
}
ent->read_proc = rtai_read_proc;
SET_PROC_READ_ENTRY(ent, rtai_read_proc);
return 0;
}
static void rtai_proc_unregister (void)
{
remove_proc_entry("hal",rtai_proc_root);
remove_proc_entry("rtai",0);
remove_proc_entry("hal", rtai_proc_root);
remove_proc_entry("rtai", 0);
}
#endif /* CONFIG_PROC_FS */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -68,6 +68,7 @@
#include <rtai_sched.h>
#include <rtai_nam2num.h>
#include <linux/sched.h>
// scheduler
#define YIELD 0
@ -1481,6 +1482,25 @@ RTAI_PROTO(int, rt_task_masked_unblock, (RT_TASK *task, unsigned long mask))
#define rt_task_wakeup_sleeping(task) rt_task_masked_unblock(task, RT_SCHED_DELAYED)
/**
* Get execution time of task.
*
* If RTAI is configured to monitor task execution times, then this function
* can be used to retrieve such information.
*
* @param task is the task for which execution time is retrieved.
*
* @param exectime is an array of three RTIME variables.
*
* @a task can be NULL, in which case the current task is selected.
*
* @a exectime consists of three values; @a exectime[0] is the total time the
* task spent on CPUs, @a exectime[1] is the first time the task was scheduled
* and @a exectime[2] is the time of the call to this function.
*
* To track the execution time, @a exectime[0] is sufficient. To get the percentage
* of CPU usage, this value can be divided by @a exectime[2] - @a exectime[1].
*/
RTAI_PROTO(void, rt_get_exectime, (RT_TASK *task, RTIME *exectime))
{
RTIME lexectime[] = { 0LL, 0LL, 0LL };

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 1999-2003 Paolo Mantegazza <mantegazza@aero.polimi.it>
* Copyright (C) 1999-2014 Paolo Mantegazza <mantegazza@aero.polimi.it>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -19,10 +19,60 @@
#ifndef _RTAI_PROC_FS_H
#define _RTAI_PROC_FS_H
#define LIMIT (PAGE_SIZE - 80)
extern struct proc_dir_entry *rtai_proc_root;
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0)
#include <linux/seq_file.h>
#define PROC_READ_FUN(read_fun_name) \
read_fun_name(struct seq_file *pf, void *v)
#define PROC_READ_OPEN_OPS(rtai_proc_fops, read_fun_name) \
\
static int rtai_proc_open(struct inode *inode, struct file *file) { \
return single_open(file, read_fun_name, NULL); \
} \
\
static const struct file_operations rtai_proc_fops = { \
.owner = THIS_MODULE, \
.open = rtai_proc_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release \
};
static inline void *CREATE_PROC_ENTRY(const char *name, umode_t mode, void *parent, const struct file_operations *proc_fops)
{
return !parent ? proc_mkdir(name, NULL) : proc_create(name, mode, parent, proc_fops);
}
#define SET_PROC_READ_ENTRY(entry, read_fun) do { } while(0)
#define PROC_PRINT_VARS
#define PROC_PRINT(fmt, args...) \
do { seq_printf(pf, fmt, ##args); } while(0)
#define PROC_PRINT_RETURN do { goto done; } while(0)
#define PROC_PRINT_DONE do { return 0; } while(0)
#else /* LINUX_VERSION_CODE <= KERNEL_VERSION(3,10,0) */
#define PROC_READ_FUN \
static int rtai_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
static inline void *CREATE_PROC_ENTRY(const char *name, umode_t mode, void *parent, const struct file_operations *proc_fops)
{
return create_proc_entry(name, mode, parent);
}
#define SET_PROC_READ_ENTRY(entry, read_fun) \
do { entry->read_proc = read_fun; } while(0)
#define LIMIT (PAGE_SIZE - 80)
// proc print macros - Contributed by: Erwin Rol (erwin@muffin.org)
// macro that holds the local variables that
@ -77,6 +127,8 @@ do { \
return len; \
} while(0)
#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0) */
// End of proc print macros
#endif /* !_RTAI_PROC_FS_H */

View file

@ -213,6 +213,7 @@ typedef struct rt_task_struct {
unsigned long usp_flags_mask;
unsigned long force_soft;
volatile int is_hard;
int kerrno;
long busy_time_align;
void *linux_syscall_server;
@ -223,7 +224,7 @@ typedef struct rt_task_struct {
/* For use by exit handler functions. */
XHDL *ExitHook;
RTIME exectime[2];
RTIME exectime[2]; /* [0] = time spent in thread, [1] = start time of thread */
struct mcb_t mcb;
/* Real time heaps. */
@ -348,6 +349,8 @@ RTIME rt_get_real_time(void);
RTIME rt_get_real_time_ns(void);
void rt_get_exectime(struct rt_task_struct *task, RTIME *exectime);
int rt_get_prio(struct rt_task_struct *task);
int rt_get_inher_prio(struct rt_task_struct *task);

View file

@ -1818,66 +1818,46 @@ module_exit(__rtai_fifos_exit);
#ifdef CONFIG_PROC_FS
/* ----------------------< proc filesystem section >----------------------*/
static int rtai_read_fifos(char* buf, char** start, off_t offset,
int len, int *eof, void *data)
static int PROC_READ_FUN(rtai_read_fifos)
{
int i;
PROC_PRINT_VARS;
len = sprintf(buf, "RTAI Real Time fifos status.\n\n" );
if (len > LIMIT) {
return(len);
}
len += sprintf(buf + len, "Maximum number of FIFOS %d.\n\n", MaxFifos);
if (len > LIMIT) {
return(len);
}
len += sprintf(buf+len, "fifo No Open Cnt Buff Size handler malloc type");
if (len > LIMIT) {
return(len);
}
len += sprintf(buf+len, " Name\n----------------");
if (len > LIMIT) {
return(len);
}
len += sprintf(buf+len, "-----------------------------------------\n");
if (len > LIMIT) {
return(len);
}
PROC_PRINT("RTAI Real Time fifos status.\n\n");
PROC_PRINT("Maximum number of FIFOS %d.\n\n", MaxFifos);
PROC_PRINT("fifo No Open Cnt Buff Size handler malloc type");
PROC_PRINT(" Name\n----------------");
PROC_PRINT("-----------------------------------------\n");
/*
* Display the status of all open RT fifos.
*/
for (i = 0; i < MAX_FIFOS; i++) {
if (fifo[i].opncnt > 0) {
len += sprintf( buf+len, "%-8d %-9d %-10d %-10p %-12s", i,
PROC_PRINT("%-8d %-9d %-10d %-10p %-12s", i,
fifo[i].opncnt, fifo[i].mbx.size,
fifo[i].handler,
fifo[i].malloc_type == 'v'
? "vmalloc" : "kmalloc"
);
if (len > LIMIT) {
return(len);
}
len += sprintf(buf+len, "%s\n", fifo[i].name);
if (len > LIMIT) {
return(len);
}
PROC_PRINT("%s\n", fifo[i].name);
} /* End if - fifo is open. */
} /* End for loop - loop for all fifos. */
return len;
return 0;
} /* End function - rtai_read_fifos */
PROC_READ_OPEN_OPS(rtai_fifos_fops, rtai_read_fifos);
static int rtai_proc_fifo_register(void)
{
struct proc_dir_entry *proc_fifo_ent;
proc_fifo_ent = create_proc_entry("fifos", S_IFREG|S_IRUGO|S_IWUSR,
rtai_proc_root);
proc_fifo_ent = CREATE_PROC_ENTRY("fifos", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root, &rtai_fifos_fops);
if (!proc_fifo_ent) {
printk("Unable to initialize /proc/rtai/fifos\n");
return(-1);
}
proc_fifo_ent->read_proc = rtai_read_fifos;
return 0;
SET_PROC_READ_ENTRY(proc_fifo_ent, rtai_read_fifos);
PROC_PRINT_DONE;
}
static void rtai_proc_fifo_unregister(void)

View file

@ -914,11 +914,10 @@ EXPORT_SYMBOL(mq_unlink);
#ifdef CONFIG_PROC_FS
static int pqueue_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
static int PROC_READ_FUN(pqueue_read_proc)
{
PROC_PRINT_VARS;
int ind;
PROC_PRINT_VARS;
PROC_PRINT("\nRTAI Posix Queue Status\n");
PROC_PRINT("-----------------------\n\n");
@ -949,12 +948,14 @@ int ind;
PROC_PRINT_DONE;
}
PROC_READ_OPEN_OPS(rtai_pqueue_fops, pqueue_read_proc)
static struct proc_dir_entry *proc_rtai_pqueue;
static int pqueue_proc_register(void)
{
proc_rtai_pqueue = create_proc_entry("pqueue", 0, rtai_proc_root);
proc_rtai_pqueue->read_proc = pqueue_read_proc;
proc_rtai_pqueue = CREATE_PROC_ENTRY("pqueue", 0, rtai_proc_root, &rtai_pqueue_fops);
SET_PROC_READ_ENTRY(proc_rtai_pqueue, pqueue_read_proc);
return 0;
}

View file

@ -2066,13 +2066,13 @@ extern struct proc_dir_entry *rtai_proc_root;
/* ----------------------< proc filesystem section >----------------------*/
static int rtai_read_lxrt(char *page, char **start, off_t off, int count, int *eof, void *data)
static int PROC_READ_FUN(rtai_read_lxrt)
{
PROC_PRINT_VARS;
struct rt_registry_entry entry;
char *type_name[] = { "TASK", "SEM", "RWL", "SPL", "MBX", "PRX", "BITS", "TBX", "HPCK" };
unsigned int i = 1;
char name[8];
PROC_PRINT_VARS;
PROC_PRINT("\nRTAI LXRT Information.\n\n");
PROC_PRINT(" MAX_SLOTS = %d\n\n", MAX_SLOTS);
@ -2102,17 +2102,18 @@ static int rtai_read_lxrt(char *page, char **start, off_t off, int count, int *e
PROC_PRINT_DONE;
} /* End function - rtai_read_lxrt */
PROC_READ_OPEN_OPS(rtai_lxrt_fops, rtai_read_lxrt);
int rtai_proc_lxrt_register(void)
{
struct proc_dir_entry *proc_lxrt_ent;
proc_lxrt_ent = create_proc_entry("names", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root);
proc_lxrt_ent = CREATE_PROC_ENTRY("names", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root, &rtai_lxrt_fops);
if (!proc_lxrt_ent) {
printk("Unable to initialize /proc/rtai/lxrt\n");
return(-1);
}
proc_lxrt_ent->read_proc = rtai_read_lxrt;
SET_PROC_READ_ENTRY(proc_lxrt_ent, rtai_read_lxrt);
return(0);
} /* End function - rtai_proc_lxrt_register */
@ -2213,6 +2214,7 @@ EXPORT_SYMBOL(rt_get_time_ns_cpuid);
EXPORT_SYMBOL(rt_get_cpu_time_ns);
EXPORT_SYMBOL(rt_get_real_time);
EXPORT_SYMBOL(rt_get_real_time_ns);
EXPORT_SYMBOL(rt_get_exectime);
EXPORT_SYMBOL(rt_get_base_linux_task);
EXPORT_SYMBOL(rt_alloc_dynamic_task);
EXPORT_SYMBOL(rt_register_watchdog);

View file

@ -69,9 +69,6 @@ void rtai_proc_lxrt_unregister(void);
MODULE_LICENSE("GPL");
int ppp;
EXPORT_SYMBOL(ppp);
/* +++++++++++++++++ WHAT MUST BE AVAILABLE EVERYWHERE ++++++++++++++++++++++ */
RT_TASK rt_smp_linux_task[NR_RT_CPUS];
@ -1793,6 +1790,31 @@ RTIME rt_get_real_time_ns(void)
return boot_epoch.time[boot_epoch.touse][1] + llimd(rtai_rdtsc(), 1000000000, tuned.cpu_freq);
}
void rt_get_exectime(RT_TASK *task, RTIME *exectime)
{
#if CONFIG_RTAI_MONITOR_EXECTIME
/* FIXME: Any locking with rt_schedule needed? */
int cpuid = rtai_cpuid();
if (!task)
task = rt_smp_current[cpuid];
/* adopted from old GET_EXECTIME case of handle_lxrt_request: */
if (!task->exectime[0] || !task->exectime[1])
return;
exectime[0] = task->exectime[0];
exectime[1] = task->exectime[1];
exectime[2] = rtai_rdtsc();
/* if task is running, adjust exectime[0] by adding the runtime since its context switch */
if (task == rt_smp_current[cpuid])
exectime[0] += exectime[2] - switch_time[cpuid];
#else
exectime[0] = exectime[1] = exectime[2] = 0;
#endif
}
/* +++++++++++++++++++++++++++ SECRET BACK DOORS ++++++++++++++++++++++++++++ */
RT_TASK *rt_get_base_linux_task(RT_TASK **base_linux_tasks)
@ -2245,13 +2267,12 @@ extern unsigned long tlsf_get_used_size(rtheap_t *);
#define rt_get_heap_mem_used(heap) rtheap_used_mem(heap)
#endif
static int rtai_read_sched(char *page, char **start, off_t off, int count,
int *eof, void *data)
static int PROC_READ_FUN(rtai_read_sched)
{
PROC_PRINT_VARS;
int cpuid, i = 1;
unsigned long t;
RT_TASK *task;
PROC_PRINT_VARS;
PROC_PRINT("\nRTAI LXRT Real Time Task Scheduler.\n\n");
PROC_PRINT(" Calibrated Time Base Frequency: %lu Hz\n", tuned.cpu_freq);
@ -2323,18 +2344,19 @@ static int rtai_read_sched(char *page, char **start, off_t off, int count,
} /* End function - rtai_read_sched */
PROC_READ_OPEN_OPS(rtai_sched_proc_fops, rtai_read_sched);
static int rtai_proc_sched_register(void)
{
struct proc_dir_entry *proc_sched_ent;
proc_sched_ent = create_proc_entry("scheduler", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root);
proc_sched_ent = CREATE_PROC_ENTRY("scheduler", S_IFREG|S_IRUGO|S_IWUSR, rtai_proc_root, &rtai_sched_proc_fops);
if (!proc_sched_ent) {
printk("Unable to initialize /proc/rtai/scheduler\n");
return(-1);
}
proc_sched_ent->read_proc = rtai_read_sched;
SET_PROC_READ_ENTRY(proc_sched_ent, rtai_read_sched);
return(0);
} /* End function - rtai_proc_sched_register */
@ -2507,6 +2529,13 @@ static int __rtai_lxrt_init(void)
{
int cpuid, retval;
if (tuned.cpu_freq == 0)
{
retval = -EINVAL;
printk(KERN_INFO "RTAI[sched]: TimeBase freq \"0\" is invalid\n");
goto exit;
}
#ifdef IPIPE_NOSTACK_FLAG
// ipipe_set_foreign_stack(&rtai_domain);
#endif
@ -2701,6 +2730,12 @@ module_exit(__rtai_lxrt_exit);
#ifdef CONFIG_KBUILD
MODULE_ALIAS("rtai_up");
MODULE_ALIAS("rtai_mup");
MODULE_ALIAS("rtai_smp");
MODULE_ALIAS("rtai_ksched");
MODULE_ALIAS("rtai_lxrt");
EXPORT_SYMBOL(rt_fun_lxrt);
EXPORT_SYMBOL(clr_rtext);
EXPORT_SYMBOL(set_rtext);

View file

@ -624,11 +624,7 @@ static inline long long handle_lxrt_request (unsigned int lxsrq, long *arg, RT_T
}
case GET_EXECTIME: {
struct arg { RT_TASK *task; RTIME *exectime; };
if ((larg->task)->exectime[0] && (larg->task)->exectime[1]) {
larg->exectime[0] = (larg->task)->exectime[0];
larg->exectime[1] = (larg->task)->exectime[1];
larg->exectime[2] = rtai_rdtsc();
}
rt_get_exectime(larg->task, larg->exectime);
return 0;
}
case GET_TIMEORIG: {

View file

@ -1206,7 +1206,7 @@ fi
dnl extra arch-dependent opts to be passed to the compiler
RTAI_KMOD_CXXFLAGS="$RTAI_KMOD_CFLAGS -fno-rtti -fno-exceptions -fno-strength-reduce -pipe"
RTAI_REAL_USER_CFLAGS="$RTAI_REAL_USER_CFLAGS -Wall -Wstrict-prototypes -pipe"
RTAI_FP_CFLAGS="$RTAI_FP_CFLAGS -fno-fast-math -fno-unsafe-math-optimizations -mieee-fp -mfpmath=387 -mhard-float -msse -Wno-undef"
RTAI_FP_CFLAGS="$RTAI_FP_CFLAGS -ffreestanding -fno-builtin-sin -fno-builtin-cos -mieee-fp -mhard-float -mpreferred-stack-boundary=4 -msse -Wno-undef"
if test x$CONFIG_RTAI_KMOD_DEBUG = xy; then
RTAI_KMOD_CFLAGS="-g $RTAI_KMOD_CFLAGS"