Commit 87ce2046 authored by Alessandro Rubini's avatar Alessandro Rubini Committed by Federico Vaga

new feature: a "sniff" device receiving all control structures

/dev/zio-sniff.ctrl is a misc device, selected by CONFIG_ZIO_SNIFF_DEV
which currently is on by default.  Every time a block is released,
a copy of the control is delivered to every reader of the device.

This is meant to be useful for monitoring, and to retrieve the
perfomance figures that will be added by later commits.  We need
a sniff device to monitor output channels, because even the application
driving I/O cannot recover the control structures that leave the
ZIO pipeline.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
Acked-by: 's avatarFederico Vaga <federico.vaga@gmail.com>
parent aa617f17
......@@ -4,6 +4,11 @@ zio-y := core.o chardev.o sysfs.o misc.o
zio-y += bus.o objects.o helpers.o
zio-y += buffers/zio-buf-kmalloc.o triggers/zio-trig-user.o
# Waiting for Kconfig...
CONFIG_ZIO_SNIFF_DEV:=y
zio-$(CONFIG_ZIO_SNIFF_DEV) += sniff-dev.o
obj-m = zio.o
obj-m += drivers/
obj-m += buffers/
......
......@@ -41,8 +41,14 @@ struct zio_control *zio_alloc_control(gfp_t gfp)
}
EXPORT_SYMBOL(zio_alloc_control);
/* At control release time, we can copy it to sniffers, if configured so */
void __weak zio_sniffdev_add(struct zio_control *ctrl)
{
}
void zio_free_control(struct zio_control *ctrl)
{
zio_sniffdev_add(ctrl);
kmem_cache_free(zio_ctrl_slab, ctrl);
}
EXPORT_SYMBOL(zio_free_control);
......@@ -78,6 +84,17 @@ struct zio_device *zio_find_device(char *name, uint32_t dev_id)
}
EXPORT_SYMBOL(zio_find_device);
/* if CONFIG_ZIO_SNIFF_DEV code in sniff-dev.c overrides the following two */
int __weak zio_sniffdev_init(void)
{
return 0;
}
void __weak zio_sniffdev_exit(void)
{
return;
}
/* Oerall init and exit */
static int __init zio_init(void)
{
int err;
......@@ -124,6 +141,10 @@ static int __init zio_init(void)
err = zio_default_trigger_init();
if (err)
pr_warning("%s: cannot register default trigger\n", __func__);
if (zio_sniffdev_init())
pr_warning("%s: cannot initialize /dev/zio-sniff.ctrl\n",
__func__);
pr_info("zio-core had been loaded\n");
return 0;
......@@ -136,6 +157,7 @@ out:
static void __exit zio_exit(void)
{
zio_sniffdev_exit();
zio_default_trigger_exit();
zio_default_buffer_exit();
......
......@@ -328,6 +328,11 @@ static inline unsigned int zio_control_size(struct zio_channel *chan)
return __ZIO_CONTROL_SIZE;
}
/* We have an optional misc device that returns all control blocks */
int zio_sniffdev_init(void);
void zio_sniffdev_exit(void);
void zio_sniffdev_add(struct zio_control *ctrl);
/*
* Misc library-like code, from zio-misc.c
*/
......
/*
* Copyright 2011 CERN
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* GNU GPLv2 or later
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/spinlock.h>
#include <linux/zio.h>
#include <linux/zio-user.h>
#define ZSD_MAX_LEN 1024 /* Half a meg at most. Static by now */
/* There is one such things for each reader. This hosts a list of controls */
struct zio_sniffdev_file {
struct list_head list;
spinlock_t lock;
wait_queue_head_t q;
struct list_head controls;
int len;
uint8_t zio_alarms; /* Either 0 or ZIO_ALARM_LOST_SNIFF */
};
static LIST_HEAD(zio_sniffdev_files);
static DEFINE_MUTEX(zio_sniffdev_mtx);
/* Each control in the list "controls" above is one of these */
struct zio_sniffdev_ctrl {
struct list_head list;
struct zio_control *ctrl;
int size;
};
/* Add a new control to all files. Can be called in atomic context */
void zio_sniffdev_add(struct zio_control *ctrl)
{
int size = zio_control_size(NULL); /* oops when ch->ctrlsize */
struct zio_sniffdev_file *f;
struct zio_sniffdev_ctrl *c;
struct zio_control *newctrl;
unsigned long flags;
list_for_each_entry(f, &zio_sniffdev_files, list) {
if (f->len >= ZSD_MAX_LEN) {
f->zio_alarms |= ZIO_ALARM_LOST_SNIFF;
continue;
}
newctrl = kmemdup(ctrl, size, GFP_ATOMIC);
c = kmalloc(sizeof(*c), GFP_ATOMIC);
if (!newctrl || !c) {
f->zio_alarms |= ZIO_ALARM_LOST_SNIFF;
kfree(c);
kfree(newctrl);
continue;
}
newctrl->zio_alarms |= f->zio_alarms;
c->ctrl = newctrl;
c->size = size;
spin_lock_irqsave(&f->lock, flags);
list_add_tail(&c->list, &f->controls);
f->len++;
spin_unlock_irqrestore(&f->lock, flags);
wake_up_interruptible(&f->q);
}
}
int zio_sniffdev_open (struct inode *ino, struct file *file)
{
struct zio_sniffdev_file *f;
f = kzalloc(sizeof(*f), GFP_USER);
if (!f)
return -ENOMEM;
spin_lock_init(&f->lock);
init_waitqueue_head(&f->q);
INIT_LIST_HEAD(&f->controls);
mutex_lock(&zio_sniffdev_mtx);
list_add(&f->list, &zio_sniffdev_files);
mutex_unlock(&zio_sniffdev_mtx);
file->private_data = f;
return 0;
}
int zio_sniffdev_release(struct inode *ino, struct file *file)
{
struct zio_sniffdev_file *f = file->private_data;
struct zio_sniffdev_ctrl *c;
struct list_head *l, *n;
mutex_lock(&zio_sniffdev_mtx);
list_del(&f->list);
mutex_unlock(&zio_sniffdev_mtx);
list_for_each_safe(l, n, &f->controls) {
c = list_entry(l, struct zio_sniffdev_ctrl, list);
list_del(&c->list);
kfree(c);
}
return 0;
}
static inline int __zio_sniffdev_empty(struct zio_sniffdev_file *f )
{
unsigned long flags;
int ret;
spin_lock_irqsave(&f->lock, flags);
ret = f->len == 0;
spin_unlock_irqrestore(&f->lock, flags);
return ret;
}
ssize_t zio_sniffdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offp)
{
struct zio_sniffdev_file *f = file->private_data;
struct zio_sniffdev_ctrl *c = NULL;
unsigned long flags;
again:
while (__zio_sniffdev_empty(f)) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
wait_event_interruptible(f->q, !__zio_sniffdev_empty(f));
if (signal_pending(current))
return -ERESTARTSYS;
}
/* Lock again: child and parent may contend */
spin_lock_irqsave(&f->lock, flags);
if (f->len) {
f->len--;
c = list_entry(f->controls.next, struct zio_sniffdev_ctrl,
list);
list_del(&c->list);
}
spin_unlock_irqrestore(&f->lock, flags);
if (!c)
goto again;
if (count < c->size)
return -EINVAL;
count = c->size;
if (copy_to_user(buf, c->ctrl, count))
return -EFAULT;
*offp += count;
kfree(c->ctrl);
kfree(c);
return count;
}
unsigned int zio_sniffdev_poll(struct file *file, struct poll_table_struct *w)
{
struct zio_sniffdev_file *f = file->private_data;
poll_wait(file, &f->q, w);
if (__zio_sniffdev_empty(f))
return 0;
return POLLIN | POLLRDNORM;
}
struct file_operations zio_sniffdev_fops = {
.owner= THIS_MODULE,
.open = zio_sniffdev_open,
.release = zio_sniffdev_release,
.read = zio_sniffdev_read,
.poll = zio_sniffdev_poll,
.llseek = no_llseek,
};
struct miscdevice zio_sniffdev_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &zio_sniffdev_fops,
.name = "zio-sniff.ctrl",
};
int zio_sniffdev_init(void)
{
/* This will fail when TLV is there, as the code above needs fixing */
BUILD_BUG_ON(zio_control_size(NULL) != __ZIO_CONTROL_SIZE);
return misc_register(&zio_sniffdev_misc);
}
void zio_sniffdev_exit(void)
{
misc_deregister(&zio_sniffdev_misc);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment