lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Sat, 26 Nov 2011 18:30:42 +0100
From:	Alessandro Rubini <rubini@...dd.com>
To:	linux-iio@...r.kernel.org, greg@...ah.com,
	linux-kernel@...r.kernel.org
Cc:	federico.vaga@...il.com, dcobas@...n.ch, siglesia@...n.ch,
	manohar.vanga@...n.ch
Subject: [RFC PATCH 3/7] drivers/zio: core files for the ZIO input/output

From: Federico Vaga <federico.vaga@...il.com>

This adds the core files for ZIO.
The files match commit 7d37663 in git://ohwr.org/misc/zio.git .

The Makefile already includes material that is added by later
patches. This doesn't imply a compilation error as drivers/zio
is not yet being compiled by drivers/Makefile.

Signed-off-by: Federico Vaga <federico.vaga@...il.com>
Signed-off-by: Alessandro Rubini <rubini@...dd.com>
Acked-by: Juan David Gonzalez Cobas <dcobas@...n.ch>
Acked-by: Samuel Iglesias Gonsalvez <siglesia@...n.ch>
Acked-by: Manohar Vanga <manohar.vanga@...n.ch>
---
 drivers/zio/Makefile   |   10 +
 drivers/zio/zio-cdev.c |  498 +++++++++++++++++
 drivers/zio/zio-sys.c  | 1423 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1931 insertions(+), 0 deletions(-)
 create mode 100644 drivers/zio/Makefile
 create mode 100644 drivers/zio/zio-cdev.c
 create mode 100644 drivers/zio/zio-sys.c

diff --git a/drivers/zio/Makefile b/drivers/zio/Makefile
new file mode 100644
index 0000000..fb5dd14
--- /dev/null
+++ b/drivers/zio/Makefile
@@ -0,0 +1,10 @@
+
+zio-core-objs := zio-cdev.o zio-sys.o
+obj-$(CONFIG_ZIO) += zio-core.o
+
+obj-$(CONFIG_ZIO) += drivers/
+obj-$(CONFIG_ZIO) += buffers/
+obj-$(CONFIG_ZIO) += triggers/
+
+hostprogs-y := zio-dump
+
diff --git a/drivers/zio/zio-cdev.c b/drivers/zio/zio-cdev.c
new file mode 100644
index 0000000..fae5749
--- /dev/null
+++ b/drivers/zio/zio-cdev.c
@@ -0,0 +1,498 @@
+/* Federico Vaga and Alessandro Rubini for CERN, 2011, GNU GPLv2 or later */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+#define __ZIO_INTERNAL__
+#include <linux/zio.h>
+#include <linux/zio-buffer.h>
+#include <linux/zio-trigger.h>
+
+static DEFINE_MUTEX(zmutex);
+struct zio_status zio_global_status;
+static struct zio_status *zstat = &zio_global_status; /* Always use ptr */
+
+static ssize_t zio_show_version(struct class *class,
+			struct class_attribute *attr,
+			char *buf)
+{
+	return sprintf(buf, "%d.%d\n", ZIO_MAJOR_VERSION, ZIO_MINOR_VERSION);
+}
+
+static struct class_attribute zclass_attrs[] = {
+	__ATTR(version, S_IRUGO, zio_show_version, NULL),
+	__ATTR_NULL,
+};
+
+static char *zio_devnode(struct device *dev, mode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "zio/%s", dev_name(dev));
+}
+
+/*
+ * zio_class: don't use class_create to create class because it doesn't permit
+ * to insert a set of class attributes. This structure is the exact
+ * reproduction of what class_create does but with some additional settings.
+ */
+static struct class zio_class = {
+	.name		= "zio",
+	.owner		= THIS_MODULE,
+	.class_attrs	= zclass_attrs,
+	.devnode	= zio_devnode,
+};
+
+/* Retrieve a channel from one of its minors */
+static struct zio_channel *__zio_minor_to_chan(dev_t mm)
+{
+	struct zio_cset *zcset;
+	dev_t cset_base, chan_minor;
+	int found = 0;
+
+	/* Extract cset minor base */
+	chan_minor = mm & (ZIO_NMAX_CSET_MINORS-1);
+	cset_base = mm & (~(ZIO_NMAX_CSET_MINORS-1));
+
+	/* Look for this minor base*/
+	list_for_each_entry(zcset, &zstat->list_cset, list_cset) {
+		if (cset_base == zcset->basedev) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		return NULL;
+	return &zcset->chan[chan_minor/2];
+}
+
+static inline int zio_device_get(dev_t devt)
+{
+	struct zio_channel *chan;
+
+	/*
+	 * FIXME there is a little concurrency; to resolve this, get the owner
+	 * from device list by searching by minor
+	 */
+	chan = __zio_minor_to_chan(devt);
+	if (!chan) {
+		pr_err("ZIO: can't retrieve channel for minor %i\n",
+		       MINOR(devt));
+		return -EBUSY;
+	}
+	return try_module_get(chan->cset->zdev->owner);
+}
+static inline void zio_device_put(dev_t devt)
+{
+	struct zio_channel *chan;
+
+	/*
+	 * FIXME there is a little concurrency; to resolve this, get the owner
+	 * from device list by searching by minor
+	 */
+	chan = __zio_minor_to_chan(devt);
+	/* it is impossbile chan = NULL because __zio_device_get() found it */
+	module_put(chan->cset->zdev->owner);
+}
+
+static int zio_f_open(struct inode *ino, struct file *f)
+{
+	struct zio_f_priv *priv = NULL;
+	struct zio_channel *chan;
+	struct zio_buffer_type *zbuf;
+	const struct file_operations *old_fops, *new_fops;
+	int ret = -EINVAL, minor;
+
+	pr_debug("%s:%i\n", __func__, __LINE__);
+	if (f->f_flags & FMODE_WRITE)
+		goto out;
+
+	if (!zio_device_get(ino->i_rdev))
+		return -ENODEV;
+
+	minor = iminor(ino);
+	chan = __zio_minor_to_chan(ino->i_rdev);
+	if (!chan) {
+		pr_err("ZIO: can't retrieve channel for minor %i\n", minor);
+		return -EBUSY;
+	}
+	zbuf = chan->cset->zbuf;
+	f->private_data = NULL;
+	priv = kzalloc(sizeof(struct zio_f_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* if there is no instance, then create a new one */
+	if (!chan->bi)
+		chan->bi = zbuf->b_op->create(zbuf, chan, FMODE_READ);
+	priv->chan = chan;
+
+	/* even number is control, odd number is data */
+	if (minor & 0x1)
+		priv->type = ZIO_CDEV_DATA;
+	else
+		priv->type = ZIO_CDEV_CTRL;
+	f->private_data = priv;
+
+	/* replace zio fops with buffer fops (FIXME: make it a lib function */
+	mutex_lock(&zmutex);
+	old_fops = f->f_op;
+	new_fops = fops_get(zbuf->f_op);
+	ret = 0;
+	if (new_fops->open)
+		ret = new_fops->open(ino, f);
+	if (ret) {
+		fops_put(zbuf->f_op);
+		mutex_unlock(&zmutex);
+		goto out;
+	}
+	fops_put(old_fops);
+	f->f_op = new_fops;
+	mutex_unlock(&zmutex);
+	return 0;
+
+out:
+	kfree(priv);
+	return ret;
+}
+
+static const struct file_operations zfops = {
+	.owner = THIS_MODULE,
+	.open = zio_f_open,
+};
+
+int __zio_minorbase_get(struct zio_cset *zcset)
+{
+	int i;
+
+	i = find_first_zero_bit(zstat->cset_minors_mask, ZIO_CSET_MAXNUM);
+	if (i >= ZIO_CSET_MAXNUM)
+		return 1;
+	set_bit(i, zstat->cset_minors_mask);
+	/* set the base minor for a cset*/
+	zcset->basedev = zstat->basedev + (i * ZIO_NMAX_CSET_MINORS);
+	pr_debug("%s:%i BASEMINOR 0x%x\n", __func__, __LINE__, zcset->basedev);
+	return 0;
+}
+void __zio_minorbase_put(struct zio_cset *zcset)
+{
+	int i;
+
+	i = (zcset->basedev - zstat->basedev) / ZIO_NMAX_CSET_MINORS;
+	clear_bit(i, zstat->cset_minors_mask);
+}
+
+/*
+ * create control and data char devices for a channel. The even minor
+ * is for control, the odd one for data.
+ */
+int zio_create_chan_devices(struct zio_channel *chan)
+{
+	int err;
+	dev_t devt_c, devt_d;
+
+
+	devt_c = chan->cset->basedev + chan->index * 2;
+	pr_debug("%s:%d dev_t=0x%x\n", __func__, __LINE__, devt_c);
+	chan->ctrl_dev = device_create(&zio_class, NULL, devt_c, NULL,
+			"%s-%i-%i-ctrl",
+			chan->cset->zdev->head.name,
+			chan->cset->index,
+			chan->index);
+	if (IS_ERR(&chan->ctrl_dev)) {
+		err = PTR_ERR(&chan->ctrl_dev);
+		goto out;
+	}
+
+	devt_d = devt_c + 1;
+	pr_debug("%s:%d dev_t=0x%x\n", __func__, __LINE__, devt_d);
+	chan->data_dev = device_create(&zio_class, NULL, devt_d, NULL,
+			"%s-%i-%i-data",
+			chan->cset->zdev->head.name,
+			chan->cset->index,
+			chan->index);
+	if (IS_ERR(&chan->data_dev)) {
+		err = PTR_ERR(&chan->data_dev);
+		goto out_data;
+	}
+
+	return 0;
+
+out_data:
+	device_destroy(&zio_class, chan->ctrl_dev->devt);
+out:
+	return err;
+}
+
+void zio_destroy_chan_devices(struct zio_channel *chan)
+{
+	pr_debug("%s\n", __func__);
+	device_destroy(&zio_class, chan->data_dev->devt);
+	device_destroy(&zio_class, chan->ctrl_dev->devt);
+}
+
+int __zio_register_cdev()
+{
+	int err;
+
+	err = class_register(&zio_class);
+	if (err) {
+		pr_err("%s: unable to register class\n", __func__);
+		goto out;
+	}
+	/* alloc to zio the maximum number of minors usable in ZIO */
+	err = alloc_chrdev_region(&zstat->basedev, 0,
+			ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS, "zio");
+	if (err) {
+		pr_err("%s: unable to allocate region for %i minors\n",
+			__func__, ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS);
+		goto out;
+	}
+	/* all ZIO's devices, buffers and triggers has zfops as f_op */
+	cdev_init(&zstat->chrdev, &zfops);
+	zstat->chrdev.owner = THIS_MODULE;
+	err = cdev_add(&zstat->chrdev, zstat->basedev,
+			ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS);
+	if (err)
+		goto out_cdev;
+	INIT_LIST_HEAD(&zstat->list_cset);
+	return 0;
+out_cdev:
+	unregister_chrdev_region(zstat->basedev,
+			ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS);
+out:
+	class_unregister(&zio_class);
+	return err;
+}
+void __zio_unregister_cdev()
+{
+	cdev_del(&zstat->chrdev);
+	unregister_chrdev_region(zstat->basedev,
+				ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS);
+	class_unregister(&zio_class);
+}
+
+
+/*
+ * Helper functions to check whether read and write would block. The
+ * return value is a poll(2) mask, so the poll method just calls them.
+ */
+
+/* Read is quite straightforward, as blocks reack us already filled */
+static int __zio_read_allowed(struct zio_f_priv *priv)
+{
+	struct zio_channel *chan = priv->chan;
+	struct zio_bi *bi = chan->bi;
+	const int can_read =  POLLIN | POLLRDNORM;
+
+	if (!chan->user_block)
+		chan->user_block = bi->b_op->retr_block(bi);
+	if (!chan->user_block)
+		return 0;
+
+	/* We have a block. So there is data and possibly control too */
+	if (likely(priv->type == ZIO_CDEV_DATA))
+		return can_read;
+
+	if (!zio_is_cdone(chan->user_block))
+		return POLLIN | POLLRDNORM;
+
+	/* There's a block, but we want to re-read control. Get a new block */
+	bi->b_op->free_block(bi, chan->user_block);
+	chan->user_block = bi->b_op->retr_block(bi);
+	if (!chan->user_block)
+		return 0;
+	return POLLIN | POLLRDNORM;
+}
+
+/* Write is more tricky: we need control, so we may ask it to the trigger */
+static struct zio_block *__zio_write_allocblock(struct zio_bi *bi,
+						 struct zio_control *ctrl)
+{
+	struct zio_block *block;
+	size_t datalen;
+
+	if (!ctrl) {
+		ctrl = zio_alloc_control(GFP_KERNEL);
+		if (!ctrl)
+			return NULL;
+		memcpy(ctrl, bi->cset->ti->current_ctrl, ZIO_CONTROL_SIZE);
+	}
+	datalen = ctrl->ssize * ctrl->nsamples;
+	block = bi->b_op->alloc_block(bi, ctrl, datalen, GFP_KERNEL);
+	return block;
+}
+
+static int __zio_write_allowed(struct zio_f_priv *priv)
+{
+	struct zio_channel *chan = priv->chan;
+	struct zio_bi *bi = chan->bi;
+	struct zio_block *block;
+	const int can_write = POLLOUT | POLLWRNORM;
+
+	if (priv->type == ZIO_CDEV_CTRL) {
+		/* Control is always writeable */
+		return can_write;
+	}
+
+	/* We want to write data. If we have no control, retrieve one */
+	if (!chan->user_block)
+		chan->user_block = __zio_write_allocblock(bi, NULL);
+	block = chan->user_block;
+	if (!block)
+		return 0;
+
+	/* If the block is not full, user can write data */
+	if (block->uoff < block->datalen)
+		return can_write;
+
+	/* Block is full: try to push out to the buffer */
+	if (bi->b_op->store_block(bi, block) < 0)
+		return 0;
+
+	/* We sent it: get a new one for this new data */
+	chan->user_block = __zio_write_allocblock(bi, NULL);
+	return chan->user_block ? can_write : 0;
+}
+
+/*
+ * The following "generic" read and write (and poll and so on) should
+ * work for most buffer types, and are exported for use in their
+ * buffer operations.
+ */
+ssize_t zio_generic_read(struct file *f, char __user *ubuf,
+			 size_t count, loff_t *offp)
+{
+	struct zio_f_priv *priv = f->private_data;
+	struct zio_channel *chan = priv->chan;
+	struct zio_bi *bi = chan->bi;
+	struct zio_block *block;
+
+	pr_debug("%s:%d type %s\n", __func__, __LINE__,
+		priv->type == ZIO_CDEV_CTRL ? "ctrl" : "data");
+
+	if (priv->type == ZIO_CDEV_CTRL && count < ZIO_CONTROL_SIZE)
+		return -EINVAL;
+
+	if ((bi->flags & ZIO_DIR) == ZIO_DIR_OUTPUT) {
+		/* FIXME: read_control for output channels is missing */
+		return -EINVAL;
+	}
+
+	if (!__zio_read_allowed(priv)) {
+		if (f->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		wait_event_interruptible(bi->q, __zio_read_allowed(priv));
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+	block = chan->user_block;
+
+	/* So, it's readable */
+	if (unlikely(priv->type == ZIO_CDEV_CTRL)) {
+		zio_set_cdone(block);
+		if (copy_to_user(ubuf, zio_get_ctrl(block), ZIO_CONTROL_SIZE))
+			return -EFAULT;
+		*offp += ZIO_CONTROL_SIZE;
+		return ZIO_CONTROL_SIZE;
+	}
+
+	/* Data file, and we have data */
+	if (count > block->datalen - block->uoff)
+		count = block->datalen - block->uoff;
+	if (copy_to_user(ubuf, block->data + block->uoff, count))
+		return -EFAULT;
+	*offp += count;
+	block->uoff += count;
+	if (block->uoff == block->datalen) {
+		chan->user_block = NULL;
+		bi->b_op->free_block(bi, block);
+	}
+	return count;
+}
+EXPORT_SYMBOL(zio_generic_read);
+
+ssize_t zio_generic_write(struct file *f, const char __user *ubuf,
+			  size_t count, loff_t *offp)
+{
+	struct zio_f_priv *priv = f->private_data;
+	struct zio_channel *chan = priv->chan;
+	struct zio_bi *bi = chan->bi;
+	struct zio_block *block;
+	struct zio_control *ctrl;
+
+	pr_debug("%s:%d type %s\n", __func__, __LINE__,
+		priv->type == ZIO_CDEV_CTRL ? "ctrl" : "data");
+
+	if ((bi->flags & ZIO_DIR) == ZIO_DIR_INPUT) {
+		/* FIXME: write_control for input channels is missing */
+		return -EINVAL;
+	}
+
+	if (!__zio_write_allowed(priv)) {
+		if (f->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		wait_event_interruptible(bi->q, __zio_write_allowed(priv));
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+
+	if (likely(priv->type == ZIO_CDEV_DATA)) {
+		/* Data is writeable, so we have space in this block */
+		block = chan->user_block;
+		if (count > block->datalen - block->uoff)
+			count =  block->datalen - block->uoff;
+		if (copy_from_user(block->data + block->uoff, ubuf, count))
+			return -EFAULT;
+		block->uoff += count;
+		if (block->uoff == block->datalen)
+			if (bi->b_op->store_block(bi, block) == 0)
+				chan->user_block = NULL;
+		return count;
+	}
+
+	/* Control: drop the current block and create a new one */
+	if (priv->type == ZIO_CDEV_CTRL && count < ZIO_CONTROL_SIZE)
+		return -EINVAL;
+	count = ZIO_CONTROL_SIZE;
+
+	if (chan->user_block)
+		bi->b_op->free_block(bi, chan->user_block);
+	chan->user_block = NULL;
+	ctrl = zio_alloc_control(GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	if (copy_from_user(ctrl, ubuf, count))
+		return -EFAULT;
+	memcpy(bi->cset->ti->current_ctrl, ctrl, count);
+	*offp += count;
+	return count;
+}
+EXPORT_SYMBOL(zio_generic_write);
+
+unsigned int zio_generic_poll(struct file *f, struct poll_table_struct *w)
+{
+	struct zio_f_priv *priv = f->private_data;
+	struct zio_bi *bi = priv->chan->bi;
+
+	poll_wait(f, &bi->q, w);
+	return __zio_read_allowed(priv) | __zio_write_allowed(priv);
+}
+EXPORT_SYMBOL(zio_generic_poll);
+
+int zio_generic_release(struct inode *inode, struct file *f)
+{
+	struct zio_f_priv *priv = f->private_data;
+
+	/* priv is allocated by zio_f_open, must be freed */
+	kfree(priv);
+	zio_device_put(inode->i_rdev);
+	return 0;
+}
+EXPORT_SYMBOL(zio_generic_release);
+
diff --git a/drivers/zio/zio-sys.c b/drivers/zio/zio-sys.c
new file mode 100644
index 0000000..eef2491
--- /dev/null
+++ b/drivers/zio/zio-sys.c
@@ -0,0 +1,1423 @@
+/* Federico Vaga for CERN, 2011, GNU GPLv2 or later */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#define __ZIO_INTERNAL__
+#include <linux/zio.h>
+#include <linux/zio-sysfs.h>
+#include <linux/zio-buffer.h>
+#include <linux/zio-trigger.h>
+
+static struct zio_status *zstat = &zio_global_status; /* Always use ptr */
+
+const char zio_zdev_attr_names[ZATTR_STD_NUM_ZDEV][ZIO_NAME_LEN] = {
+	[ZATTR_GAIN]		= "gain_factor",
+	[ZATTR_OFFSET]		= "offset",
+	[ZATTR_NBIT]		= "resolution-bits",
+	[ZATTR_MAXRATE]		= "max-sample-rate",
+	[ZATTR_VREFTYPE]	= "vref-src",
+};
+EXPORT_SYMBOL(zio_zdev_attr_names);
+const char zio_trig_attr_names[ZATTR_STD_NUM_TRIG][ZIO_NAME_LEN] = {
+	[ZATTR_TRIG_REENABLE]	= "re-enable",
+	[ZATTR_TRIG_NSAMPLES]	= "nsamples",
+};
+EXPORT_SYMBOL(zio_trig_attr_names);
+const char zio_zbuf_attr_names[ZATTR_STD_NUM_ZBUF][ZIO_NAME_LEN] = {
+	[ZATTR_ZBUF_MAXLEN]	= "max-buffer-len",
+};
+EXPORT_SYMBOL(zio_zbuf_attr_names);
+
+static const char *__get_sysfs_name(enum zio_object_type type, int i)
+{
+	const char *name;
+
+	switch (type) {
+	case ZDEV:
+		name = zio_zdev_attr_names[i];
+		break;
+	case ZTRIG:
+	case ZTI:
+		name = zio_trig_attr_names[i];
+		break;
+	case ZBUF:
+	case ZBI:
+		name = zio_zbuf_attr_names[i];
+		break;
+	default:
+		name = NULL;
+		break;
+	}
+
+	return name;
+}
+
+/*
+ * Top-level ZIO objects has a unique name.
+ * You can find a particular object by searching its name.
+ */
+static inline struct zio_object_list_item *__find_by_name(
+			struct zio_object_list *zobj_list, char *name)
+{
+	struct zio_object_list_item *cur;
+
+	if (!name)
+		return NULL;
+	list_for_each_entry(cur, &zobj_list->list, list) {
+		pr_debug("%s:%d %s=%s\n", __func__, __LINE__, cur->name, name);
+		if (strcmp(cur->name, name) == 0)
+			return cur; /* object found */
+	}
+	return NULL;
+}
+
+static inline struct zio_object_list_item *__zio_object_get(
+	struct zio_object_list *zobj_list, char *name)
+{
+	struct zio_object_list_item *list_item;
+
+	/* search for default trigger */
+	list_item = __find_by_name(zobj_list, name);
+	if (!list_item)
+		return NULL;
+	/* increment trigger usage to prevent rmmod */
+	if (!try_module_get(list_item->owner))
+		return NULL;
+	return list_item;
+}
+static struct zio_buffer_type *zio_buffer_get(char *name)
+{
+	struct zio_object_list_item *list_item;
+
+	list_item = __zio_object_get(&zstat->all_buffer_types, name);
+	if (!list_item)
+		return ERR_PTR(-ENODEV);
+	return container_of(list_item->obj_head, struct zio_buffer_type, head);
+}
+static inline void zio_buffer_put(struct zio_buffer_type *zbuf)
+{
+	pr_debug("%s:%d %p\n", __func__, __LINE__, zbuf->owner);
+	module_put(zbuf->owner);
+}
+static struct zio_trigger_type *zio_trigger_get(char *name)
+{
+	struct zio_object_list_item *list_item;
+
+	list_item = __zio_object_get(&zstat->all_trigger_types, name);
+	if (!list_item)
+		return ERR_PTR(-ENODEV);
+	return container_of(list_item->obj_head, struct zio_trigger_type, head);
+}
+static inline void zio_trigger_put(struct zio_trigger_type *trig)
+{
+	pr_debug("%s:%d %p\n", __func__, __LINE__, trig->owner);
+	module_put(trig->owner);
+}
+
+/* data_done is called by the driver, after {in,out}put_cset */
+void zio_generic_data_done(struct zio_cset *cset)
+{
+	struct zio_buffer_type *zbuf;
+	struct zio_device *zdev;
+	struct zio_channel *chan;
+	struct zio_block *block;
+	struct zio_ti *ti;
+	struct zio_bi *bi;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+
+	ti = cset->ti;
+	zdev = cset->zdev;
+	zbuf = cset->zbuf;
+
+	if (unlikely((ti->flags & ZIO_DIR) == ZIO_DIR_OUTPUT)) {
+		cset_for_each(cset, chan) {
+			bi = chan->bi;
+			block = chan->active_block;
+			if (block)
+				zbuf->b_op->free_block(chan->bi, block);
+			/* We may have a new block ready, or not */
+			chan->active_block = zbuf->b_op->retr_block(chan->bi);
+		}
+		return;
+	}
+	/* DIR_INPUT */
+	cset_for_each(cset, chan) {
+		bi = chan->bi;
+		block = chan->active_block;
+		if (!block)
+			continue;
+		if (zbuf->b_op->store_block(bi, block)) /* may fail, no prob */
+			zbuf->b_op->free_block(bi, block);
+	}
+}
+EXPORT_SYMBOL(zio_generic_data_done);
+
+static void __zio_fire_input_trigger(struct zio_ti *ti)
+{
+	struct zio_buffer_type *zbuf;
+	struct zio_block *block;
+	struct zio_device *zdev;
+	struct zio_cset *cset;
+	struct zio_channel *chan;
+	struct zio_control *ctrl;
+	int errdone = 0;
+
+	cset = ti->cset;
+	zdev = cset->zdev;
+	zbuf = cset->zbuf;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+
+	/* FIXME: check if a trigger is already pending */
+
+	/* Allocate the buffer for the incoming sample, in active channels */
+	cset_for_each(cset, chan) {
+		ctrl = zio_alloc_control(GFP_ATOMIC);
+		if (!ctrl) {
+			if (!errdone++)
+				pr_err("%s: can't alloc control\n", __func__);
+			continue;
+		}
+		memcpy(ctrl, ti->current_ctrl, ZIO_CONTROL_SIZE);
+		ctrl->chan_i = chan->index;
+
+		block = zbuf->b_op->alloc_block(chan->bi, ctrl,
+						ctrl->ssize * ctrl->nsamples,
+						GFP_ATOMIC);
+		if (IS_ERR(block)) {
+			if (!errdone++)
+				pr_err("%s: can't alloc block\n", __func__);
+			zio_free_control(ctrl);
+			continue;
+		}
+		chan->active_block = block;
+	}
+	if (zdev->d_op->input_cset(cset)) {
+		/* It succeeded immediately */
+		ti->t_op->data_done(cset);
+	}
+}
+
+static void __zio_fire_output_trigger(struct zio_ti *ti)
+{
+	struct zio_cset *cset = ti->cset;
+	struct zio_device *zdev = cset->zdev;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+
+	/* We are expected to already have a block in active channels */
+	if (zdev->d_op->output_cset(cset)) {
+		/* It succeeded immediately */
+		ti->t_op->data_done(cset);
+	}
+}
+
+/*
+ * When a software trigger fires, it should call this function. Hw ones don't
+ */
+void zio_fire_trigger(struct zio_ti *ti)
+{
+	/* If the trigger runs too early, ti->cset is still NULL */
+	if (!ti->cset)
+		return;
+
+	/* Copy the stamp (we are software driven anyways) */
+	ti->current_ctrl->tstamp.secs = ti->tstamp.tv_sec;
+	ti->current_ctrl->tstamp.ticks = ti->tstamp.tv_nsec;
+	ti->current_ctrl->tstamp.bins = ti->tstamp_extra;
+	/*
+	 * And the sequence number too (first returned seq is 1).
+	 * Sequence number is always increased to identify un-stored
+	 * blocks or other errors in trigger activation.
+	 */
+	ti->current_ctrl->seq_num++;
+
+	if (likely((ti->flags & ZIO_DIR) == ZIO_DIR_INPUT))
+		__zio_fire_input_trigger(ti);
+	else
+		__zio_fire_output_trigger(ti);
+}
+EXPORT_SYMBOL(zio_fire_trigger);
+
+static int __has_auto_index(char *s)
+{
+	int i = 0;
+	for (i = 0; i < ZIO_NAME_LEN-1; i++) {
+		if (s[i] != '%')
+			continue;
+		i++;
+		if (s[i] == 'd')
+			return 1;
+	}
+	return 0;
+}
+static int __next_strlen(char *str)
+{
+	int increment = 0, i;
+
+	for (i = strlen(str)-1; i > 0; i--) {
+		/* if is an ascii number */
+		if (str[i] >= '0' && str[i] <= '9') {
+			if (str[i] == '9')
+				continue;
+			else
+				break;
+		} else {
+			increment++;
+			break;
+		}
+	}
+	return strlen(str) + increment;
+}
+
+/*
+ * The zio device name must be unique. If it is not unique, a busy error is
+ * returned.
+ */
+static int zobj_unique_name(struct zio_object_list *zobj_list, char *name)
+{
+	struct zio_object_list_item *cur;
+	struct zio_obj_head *tmp;
+	unsigned int counter = 0, again, len;
+	char name_to_check[ZIO_NAME_LEN];
+	int auto_index = __has_auto_index(name);
+
+	pr_debug("%s\n", __func__);
+
+	if (!name)
+		return -EINVAL;
+
+	len = strlen(name);
+	if (!len)
+		return -EINVAL;
+
+	strncpy(name_to_check, name, ZIO_NAME_LEN);
+	do {
+		again = 0;
+		if (auto_index) { /* TODO when zio become bus, it is useless */
+			sprintf(name_to_check, name, counter++);
+			len = strlen(name_to_check);
+		}
+
+		list_for_each_entry(cur, &zobj_list->list, list) {
+			tmp = cur->obj_head;
+			if (strcmp(tmp->name, name_to_check))
+				continue; /* no conflict */
+			/* conflict found */
+
+			/* if not auto-assigned, then error */
+			if (!auto_index) {
+				pr_err("ZIO: name \"%s\" is already taken\n",
+					name);
+				return -EBUSY;
+			}
+			/* build sequential name */
+			if (__next_strlen(name_to_check) > ZIO_NAME_LEN) {
+				pr_err("ZIO: invalid name \"%s\"\n", name);
+				return -EINVAL;
+			}
+			again = 1;
+			break;
+		}
+	} while (again);
+	strncpy(name, name_to_check, ZIO_NAME_LEN);
+	return 0;
+}
+
+static struct zio_attribute *__zattr_clone(const struct zio_attribute *src,
+		unsigned int n)
+{
+	struct zio_attribute *dest = NULL;
+	unsigned int size;
+
+	if (!src)
+		return NULL;
+	size = n * sizeof(struct zio_attribute);
+	dest = kmalloc(size, GFP_KERNEL);
+	if (!dest)
+		return NULL;
+
+	dest = memcpy(dest, src, size);
+
+	return dest;
+}
+
+static void __zattr_unclone(struct zio_attribute *zattr)
+{
+	kfree(zattr);
+}
+
+static int __zattr_set_copy(struct zio_attribute_set *dest,
+				     struct zio_attribute_set *src)
+{
+	if (!dest || !src)
+		return -EINVAL;
+	dest->n_std_attr = src->n_std_attr;
+	dest->n_ext_attr = src->n_ext_attr;
+	dest->std_zattr = __zattr_clone(src->std_zattr, dest->n_std_attr);
+	dest->ext_zattr = __zattr_clone(src->ext_zattr, dest->n_ext_attr);
+
+	return 0;
+}
+static void __zattr_set_free(struct zio_attribute_set *zattr_set)
+{
+	if (!zattr_set)
+		return;
+	__zattr_unclone(zattr_set->ext_zattr);
+	__zattr_unclone(zattr_set->std_zattr);
+}
+static int zattr_chan_pre_set(struct zio_channel *chan)
+{
+	struct zio_cset *cset = chan->cset;
+
+	if (!(cset->flags & ZCSET_CHAN_ALLOC))
+		return 0; /* nothing to do */
+
+	/*
+	 * If the channel has been allocated by ZIO, then attributes are
+	 * cloned from  the template channel description within parent cset
+	 */
+	chan->zattr_set.std_zattr =
+		__zattr_clone(
+			cset->zattr_set_chan.std_zattr,
+			ZATTR_STD_NUM_ZDEV);
+	if (IS_ERR(chan->zattr_set.std_zattr))
+		return PTR_ERR(chan->zattr_set.std_zattr);
+	chan->zattr_set.ext_zattr =
+		__zattr_clone(
+			cset->zattr_set.ext_zattr,
+			cset->zattr_set.n_ext_attr);
+	if (IS_ERR(chan->zattr_set.ext_zattr)) {
+		kfree(chan->zattr_set.std_zattr);
+		return PTR_ERR(chan->zattr_set.ext_zattr);
+	}
+	return 0;
+}
+
+static void zattr_chan_post_remove(struct zio_channel *chan)
+{
+	if (chan->cset->flags & ZCSET_CHAN_ALLOC) {
+		__zattr_unclone(chan->zattr_set.std_zattr);
+		__zattr_unclone(chan->zattr_set.ext_zattr);
+	}
+}
+
+/* When touching attributes, we always use the spinlock for the hosting dev */
+static spinlock_t *zdev_get_spinlock(struct zio_obj_head *head)
+{
+	spinlock_t *lock;
+
+	switch (head->zobj_type) {
+	case ZDEV:
+		lock = &to_zio_dev(&head->kobj)->lock;
+		break;
+	case ZCSET:
+		lock = &to_zio_cset(&head->kobj)->zdev->lock;
+		break;
+	case ZCHAN:
+		lock = &to_zio_chan(&head->kobj)->cset->zdev->lock;
+		break;
+	case ZTI: /* we might not want to take a lock but... */
+		lock = &to_zio_ti(&head->kobj)->cset->zdev->lock;
+		break;
+	case ZBI:
+		lock = &to_zio_bi(&head->kobj)->cset->zdev->lock;
+		break;
+	default:
+		WARN(1, "ZIO: unknown zio object %i\n", head->zobj_type);
+		return NULL;
+	}
+	return lock;
+}
+
+/* Retrieve an attribute set from an object head */
+static struct zio_attribute_set *__get_zattr_set(struct zio_obj_head *head)
+{
+	struct zio_attribute_set *zattr_set;
+
+	switch (head->zobj_type) {
+	case ZDEV:
+		zattr_set = &to_zio_dev(&head->kobj)->zattr_set;
+		break;
+	case ZCSET:
+		zattr_set = &to_zio_cset(&head->kobj)->zattr_set;
+		break;
+	case ZCHAN:
+		zattr_set = &to_zio_chan(&head->kobj)->zattr_set;
+		break;
+	case ZTRIG:
+		zattr_set = &to_zio_chan(&head->kobj)->zattr_set;
+		break;
+	case ZBUF:
+		zattr_set = &to_zio_chan(&head->kobj)->zattr_set;
+		break;
+	case ZTI:
+		zattr_set = &to_zio_ti(&head->kobj)->zattr_set;
+		break;
+	case ZBI:
+		zattr_set = &to_zio_bi(&head->kobj)->zattr_set;
+		break;
+	default:
+		WARN(1, "ZIO: unknown zio object %i\n", head->zobj_type);
+		return NULL;
+	}
+	return zattr_set;
+}
+
+ /*
+ * Zio objects all handle uint32_t values. So the show and store
+ * are centralized here, and each device has its own get_info and set_conf
+ * which handle binary 32-bit numbers. Both the function are locked to prevent
+ * concurrency issue when editing device register.
+ */
+static ssize_t zattr_show(struct kobject *kobj, struct attribute *attr,
+				char *buf)
+{
+	int err = 0;
+	ssize_t len = 0;
+	spinlock_t *lock;
+	struct zio_attribute *zattr = to_zio_zattr(attr);
+
+	pr_debug("%s\n", __func__);
+	if (unlikely(strcmp(attr->name, "name") == 0)) {
+		/* print device name*/
+		return sprintf(buf, "%s\n", to_zio_head(kobj)->name);
+	}
+
+	if (zattr->s_op->info_get) {
+		lock = zdev_get_spinlock(to_zio_head(kobj));
+		spin_lock(lock);
+		err = zattr->s_op->info_get(kobj, zattr, &zattr->value);
+		spin_unlock(lock);
+		if (err)
+			return err;
+	}
+	len = sprintf(buf, "%i\n", zattr->value);
+	return len;
+}
+static ssize_t zattr_store(struct kobject *kobj, struct attribute *attr,
+				const char *buf, size_t size)
+{
+	long val;
+	int err = 0;
+	struct zio_attribute *zattr = to_zio_zattr(attr);
+	spinlock_t *lock;
+
+	pr_debug("%s\n", __func__);
+	err = strict_strtol(buf, 0, &val);
+	if (err)
+		return -EINVAL;
+	if (zattr->s_op->conf_set) {
+		lock = zdev_get_spinlock(to_zio_head(kobj));
+		spin_lock(lock);
+		err = zattr->s_op->conf_set(kobj, zattr, val);
+		spin_unlock(lock);
+	}
+	return err == 0 ? size : err;
+}
+
+static const struct sysfs_ops zio_attribute_ktype_ops = {
+	.show  = zattr_show,
+	.store = zattr_store,
+};
+
+static struct attribute default_attrs[] = {
+		{
+			.name = "name", /* show the name */
+			.mode = 0444, /* read only */
+		},
+};
+static struct attribute *def_attr_ptr[] = {
+	&default_attrs[0],
+	NULL,
+};
+
+static struct kobj_type zdktype = { /* For standard and extended attribute */
+	.release   = NULL,
+	.sysfs_ops = &zio_attribute_ktype_ops,
+	.default_attrs = def_attr_ptr,
+};
+
+static mode_t zattr_is_visible(struct kobject *kobj, struct attribute *attr,
+				int n)
+{
+	unsigned int flag1, flag2, flag3;
+	mode_t mode = attr->mode;
+
+	/*
+	 * FIXME: if it's decided that activation
+	 * is always the first bit then is faster doing:
+	 * flag1 & flag2 & flag3 & 0x1
+	 * to verify content
+	 */
+	switch (__zio_get_object_type(kobj)) {
+	case ZDEV:
+		flag1 = to_zio_dev(kobj)->flags;
+		if (flag1 & ZIO_DISABLED)
+			mode = 0;
+		break;
+	case ZCSET:
+		flag1 = to_zio_cset(kobj)->flags;
+		flag2 = to_zio_cset(kobj)->zdev->flags;
+		if ((flag1 | flag2) & ZIO_DISABLED)
+			mode = 0;
+		break;
+	case ZCHAN:
+		flag1 = to_zio_chan(kobj)->flags;
+		flag2 = to_zio_chan(kobj)->cset->flags;
+		flag3 = to_zio_chan(kobj)->cset->zdev->flags;
+		if ((flag1 | flag2 | flag3) & ZIO_DISABLED)
+			mode = 0;
+		break;
+	case ZBI:
+		break;
+	case ZTI:
+		break;
+	default:
+		WARN(1, "ZIO: unknown zio object %i\n",
+		     __zio_get_object_type(kobj));
+	}
+
+	return mode;
+}
+
+/*
+ * Verify attributes within the group,
+ * If they are valid register the group
+ */
+static int zattr_create_group(struct kobject *kobj,
+			      struct zio_attribute *zattr,
+			      struct attribute_group *grp,
+			      unsigned int n_attr,
+			      const struct zio_sys_operations *s_op,
+			      int is_ext)
+{
+	int i;
+
+	pr_debug("%s\n", __func__);
+	if (!zattr || !n_attr) {
+		grp->attrs = NULL;
+		return 0;	/* no attributes */
+	}
+	/* extract the attributes */
+	grp->attrs = kzalloc(sizeof(struct attribute) * n_attr, GFP_KERNEL);
+	if (!grp->attrs)
+		return -ENOMEM;
+
+	grp->is_visible = zattr_is_visible;
+	for (i = 0; i < n_attr; i++) {
+		/* Add attribute and assign show and store functions */
+		grp->attrs[i] = &zattr[i].attr;
+		to_zio_zattr(grp->attrs[i])->s_op = s_op;
+		/* if not defined */
+		if (!grp->attrs[i]->name) {
+			if (is_ext) {
+				pr_warning("%s: can't create ext attributes. "
+				"%ith attribute has not a name", __func__, i);
+				return -EINVAL;
+			}
+			/*
+			 * Only standard attributes need these lines to fill
+			 * the empty hole in the array of attributes
+			 */
+			grp->attrs[i]->name = __get_sysfs_name(
+					to_zio_head(kobj)->zobj_type, i);
+			grp->attrs[i]->mode = 0;
+		}
+		/* if write permission but no write function */
+		if ((grp->attrs[i]->mode & S_IWUGO) == S_IWUGO &&
+		     !s_op->conf_set) {
+			pr_err("%s: %s has write permission but no write "
+				"function\n", __func__, grp->attrs[i]->name);
+			return -EINVAL;
+		}
+	}
+	return sysfs_create_group(kobj, grp);
+}
+
+
+/* Create a set of zio attributes: the standard one and the extended one */
+static void zattr_remove_group(struct kobject *kobj,
+			       struct attribute_group *grp)
+{
+	if (!grp->attrs)
+		return;
+	sysfs_remove_group(kobj, grp);
+	kfree(grp->attrs);
+}
+/* create a set of zio attributes: the standard one and the extended one */
+static int zattr_set_create(struct zio_obj_head *head,
+			    const struct zio_sys_operations *s_op)
+{
+	int err = 0;
+	struct zio_attribute_set *zattr_set;
+
+	zattr_set = __get_zattr_set(head);
+	if (!zattr_set)
+		return -EINVAL; /* message already printed */
+
+	/* Create the standard attributes from zio attributes */
+	err = zattr_create_group(&head->kobj, zattr_set->std_zattr,
+		&zattr_set->std_group, zattr_set->n_std_attr, s_op, 0);
+	if (err)
+		goto out;
+	/* Create the extended attributes from zio attributes */
+	err = zattr_create_group(&head->kobj, zattr_set->ext_zattr,
+		&zattr_set->ext_group, zattr_set->n_ext_attr, s_op, 1);
+	if (err && zattr_set->std_group.attrs)
+		sysfs_remove_group(&head->kobj, &zattr_set->std_group);
+out:
+	return err;
+}
+/* Remove an existent set of attributes */
+static void zattr_set_remove(struct zio_obj_head *head)
+{
+	struct zio_attribute_set *zattr_set;
+
+	zattr_set = __get_zattr_set(head);
+	if (!zattr_set)
+		return;
+
+	/* remove the standard attribute group */
+	zattr_remove_group(&head->kobj, &zattr_set->std_group);
+	/* remove the extended attribute group */
+	zattr_remove_group(&head->kobj, &zattr_set->ext_group);
+}
+
+/* Create a buffer instance according to the buffer type defined in cset */
+static int __buffer_create_instance(struct zio_channel *chan)
+{
+	struct zio_buffer_type *zbuf = chan->cset->zbuf;
+	struct zio_bi *bi;
+	int err;
+
+	/* create buffer */
+	bi = zbuf->b_op->create(zbuf, chan, FMODE_READ);
+	if (IS_ERR(bi))
+		return PTR_ERR(bi);
+	/* Now fill the trigger instance, ops, head, then the rest */
+	bi->b_op = zbuf->b_op;
+	bi->f_op = zbuf->f_op;
+	bi->flags |= (chan->flags & ZIO_DIR);
+	bi->head.zobj_type = ZBI;
+	err = kobject_init_and_add(&bi->head.kobj, &zdktype,
+			&chan->head.kobj, "buffer");
+	if (err)
+		goto out_kobj;
+	snprintf(bi->head.name, ZIO_NAME_LEN, "%s-%s-%d-%d",
+			zbuf->head.name,
+			chan->cset->zdev->head.name,
+			chan->cset->index,
+			chan->index);
+
+	err = __zattr_set_copy(&bi->zattr_set, &zbuf->zattr_set);
+	if (err)
+		goto out_clone;
+	err = zattr_set_create(&bi->head, zbuf->s_op);
+	if (err)
+		goto out_sysfs;
+	init_waitqueue_head(&bi->q);
+
+	/* Add to buffer instance list */
+	spin_lock(&zbuf->lock);
+	list_add(&bi->list, &zbuf->list);
+	spin_unlock(&zbuf->lock);
+	bi->cset = chan->cset;
+	chan->bi = bi;
+
+	/* Done. This cset->ti marks everything is running (FIXME?) */
+	mb();
+	bi->chan = chan;
+
+	return 0;
+
+out_sysfs:
+	__zattr_set_free(&bi->zattr_set);
+out_clone:
+	kobject_del(&bi->head.kobj);
+out_kobj:
+	kobject_put(&bi->head.kobj);
+	zbuf->b_op->destroy(bi);
+	return err;
+}
+
+/* Destroy a buffer instance */
+static void __buffer_destroy_instance(struct zio_channel *chan)
+{
+	struct zio_buffer_type *zbuf = chan->cset->zbuf;
+	struct zio_bi *bi = chan->bi;
+
+	chan->bi = NULL;
+
+	/* Remove from buffer instance list */
+	spin_lock(&zbuf->lock);
+	list_del(&bi->list);
+	spin_unlock(&zbuf->lock);
+	/* Remove from sysfs */
+	zattr_set_remove(&bi->head);
+	__zattr_set_free(&bi->zattr_set);
+	kobject_del(&bi->head.kobj);
+	kobject_put(&bi->head.kobj);
+	/* Finally destroy the instance */
+	zbuf->b_op->destroy(bi);
+}
+
+/* Create a trigger instance according to the trigger type defined in cset */
+static int __trigger_create_instance(struct zio_cset *cset)
+{
+	int err;
+	struct zio_control *ctrl;
+	struct zio_ti *ti;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	/* Allocate and fill current control as much as possible*/
+	ctrl = zio_alloc_control(GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+	ctrl->cset_i = cset->index;
+	strncpy(ctrl->devname, cset->zdev->head.name, ZIO_NAME_LEN);
+	strncpy(ctrl->triggername, cset->trig->head.name, ZIO_NAME_LEN);
+	ctrl->sbits = 8; /* FIXME: retrieve from attribute */
+	ctrl->ssize = cset->ssize;
+
+	ti = cset->trig->t_op->create(cset->trig, cset, ctrl, 0/*FIXME*/);
+	if (IS_ERR(ti)) {
+		err = PTR_ERR(ti);
+		pr_err("%s: can't create trigger error %i\n", __func__, err);
+		goto out;
+	}
+	/* Now fill the trigger instance, ops, head, then the rest */
+	ti->t_op = cset->trig->t_op;
+	ti->f_op = cset->trig->f_op;
+	ti->flags |= cset->flags & ZIO_DIR;
+	ti->head.zobj_type = ZTI;
+	err = kobject_init_and_add(&ti->head.kobj, &zdktype,
+		&cset->head.kobj, "trigger");
+	if (err)
+		goto out_kobj;
+	snprintf(ti->head.name, ZIO_NAME_LEN, "%s-%s-%d",
+			cset->trig->head.name,
+			cset->zdev->head.name,
+			cset->index);
+
+	err = __zattr_set_copy(&ti->zattr_set, &cset->trig->zattr_set);
+	if (err)
+		goto out_clone;
+	err = zattr_set_create(&ti->head, cset->trig->s_op);
+	if (err)
+		goto out_sysfs;
+
+	/* Add to trigger instance list */
+	spin_lock(&cset->trig->lock);
+	list_add(&ti->list, &cset->trig->list);
+	spin_unlock(&cset->trig->lock);
+	cset->ti = ti;
+
+	/* Done. This cset->ti marks everything is running (FIXME?) */
+	mb();
+	ti->cset = cset;
+
+	return 0;
+
+out_sysfs:
+	__zattr_set_free(&ti->zattr_set);
+out_clone:
+	kobject_del(&ti->head.kobj);
+out_kobj:
+	kobject_put(&ti->head.kobj);
+	ti->t_op->destroy(ti);
+out:
+	zio_free_control(ctrl);
+	return err;
+}
+
+/* Destroy a buffer instance */
+static void __trigger_destroy_instance(struct zio_cset *cset)
+{
+	struct zio_ti *ti = cset->ti;
+	struct zio_control *ctrl = ti->current_ctrl;
+
+	cset->ti = NULL;
+
+	/* Remove from trigger instance list */
+	spin_lock(&cset->trig->lock);
+	list_del(&ti->list);
+	spin_unlock(&cset->trig->lock);
+	/* Remove from sysfs */
+	zattr_set_remove(&ti->head);
+	__zattr_set_free(&ti->zattr_set);
+	kobject_del(&ti->head.kobj);
+	kobject_put(&ti->head.kobj);
+	/* Finally destroy the instance and free the default control*/
+	cset->trig->t_op->destroy(ti);
+	zio_free_control(ctrl);
+}
+
+/*
+ * chan_register registers one channel.  It is important to register
+ * or unregister all the channels of a cset at the same time to prevent
+ * overlaps in the minors.
+ */
+static int chan_register(struct zio_channel *chan)
+{
+	int err;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!chan)
+		return -EINVAL;
+
+	chan->head.zobj_type = ZCHAN;
+	err = kobject_init_and_add(&chan->head.kobj, &zdktype,
+			&chan->cset->head.kobj, "chan%i", chan->index);
+	if (err)
+		goto out_add;
+
+	err = zattr_chan_pre_set(chan);
+	if (err)
+		goto out_pre;
+
+	/* Create sysfs channel attributes */
+	err = zattr_set_create(&chan->head, chan->cset->zdev->s_op);
+	if (err)
+		goto out_sysfs;
+
+	/* Create buffer */
+	err = __buffer_create_instance(chan);
+	if (err)
+		goto out_buf;
+
+	/* Create channel char devices*/
+	err = zio_create_chan_devices(chan);
+	if (err)
+		goto out_create;
+	/*
+	 * If no name was assigned, ZIO assigns it.  channel name is
+	 * set to the kobject name. kobject name has no length limit,
+	 * so the channel name is the first ZIO_NAME_LEN characters of
+	 * kobject name. A duplicate channel name is not a problem
+	 * anyways.
+	 */
+	if (!strlen(chan->head.name))
+		strncpy(chan->head.name, chan->head.kobj.name, ZIO_NAME_LEN);
+	return 0;
+
+out_create:
+	__buffer_destroy_instance(chan);
+out_buf:
+	zattr_set_remove(&chan->head);
+out_sysfs:
+	zattr_chan_post_remove(chan);
+out_pre:
+	kobject_del(&chan->head.kobj);
+out_add:
+	/* we must _put even if it returned error */
+	kobject_put(&chan->head.kobj);
+	return err;
+}
+
+static void chan_unregister(struct zio_channel *chan)
+{
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!chan)
+		return;
+	zio_destroy_chan_devices(chan);
+	/* destroy buffer instance */
+	__buffer_destroy_instance(chan);
+	/* remove sysfs cset attributes */
+	zattr_set_remove(&chan->head);
+	zattr_chan_post_remove(chan);
+	kobject_del(&chan->head.kobj);
+	kobject_put(&chan->head.kobj);
+}
+
+/*
+ * @cset_alloc_chan: low-level drivers can avoid allocating their channels,
+ * they say how many are there and ZIO allocates them.
+ * @cset_free_chan: if ZIO allocated channels, then it frees them; otherwise
+ * it does nothing.
+ */
+static struct zio_channel *cset_alloc_chan(struct zio_cset *cset)
+{
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	/*if no static channels, then ZIO must alloc them */
+	if (cset->chan)
+		return cset->chan;
+	/* initialize memory to zero to have correct flags and attrs */
+	cset->chan = kzalloc(sizeof(struct zio_channel) *
+					cset->n_chan, GFP_KERNEL);
+	if (!cset->chan)
+		return ERR_PTR(-ENOMEM);
+	cset->flags |= ZCSET_CHAN_ALLOC;
+
+	return cset->chan;
+}
+static inline void cset_free_chan(struct zio_cset *cset)
+{
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	/* Only allocated channels need to be freed */
+	if (cset->flags & ZCSET_CHAN_ALLOC)
+		kfree(cset->chan);
+}
+
+static int cset_register(struct zio_cset *cset)
+{
+	int i, j, err = 0;
+	struct zio_buffer_type *zbuf;
+	struct zio_trigger_type *trig;
+	char *name;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!cset)
+		return -EINVAL;
+
+	if (!cset->n_chan) {
+		pr_err("ZIO: no channels in cset%i\n", cset->index);
+		return -EINVAL;
+	}
+
+	if (!cset->ssize) {
+		pr_err("ZIO: ssize can not be 0 in cset%i\n", cset->index);
+		return -EINVAL;
+	}
+
+	/* Get an available minor base */
+	err = __zio_minorbase_get(cset);
+	if (err) {
+		pr_err("ZIO: no minors available\n");
+		return -EBUSY;
+	}
+
+	cset->head.zobj_type = ZCSET;
+	err = kobject_init_and_add(&cset->head.kobj, &zdktype,
+			&cset->zdev->head.kobj, "cset%i", cset->index);
+	if (err)
+		goto out_add;
+	/* Create sysfs cset attributes */
+	err = zattr_set_create(&cset->head, cset->zdev->s_op);
+	if (err)
+		goto out_sysfs;
+
+	cset->chan = cset_alloc_chan(cset);
+	if (IS_ERR(cset->chan)) {
+		err = PTR_ERR(cset->chan);
+		goto out_alloc;
+	}
+
+	/*
+	 * The cset must have a buffer type. If none is associated
+	 * to the cset, ZIO selects the preferred or default one.
+	 */
+	if (!cset->zbuf) {
+		name = cset->zdev->preferred_buffer;
+		zbuf = zio_buffer_get(name);
+		if (name && IS_ERR(zbuf))
+			pr_warning("%s: no buffer \"%s\" (error %li), using "
+				   "default\n", __func__, name, PTR_ERR(zbuf));
+		if (IS_ERR(zbuf))
+			zbuf = zio_buffer_get(ZIO_DEFAULT_BUFFER);
+		if (IS_ERR(zbuf)) {
+			err = PTR_ERR(zbuf);
+			goto out_buf;
+		}
+		cset->zbuf = zbuf;
+	}
+
+	/* Register all child channels */
+	for (i = 0; i < cset->n_chan; i++) {
+		cset->chan[i].index = i;
+		cset->chan[i].cset = cset;
+		cset->chan[i].flags |= cset->flags & ZIO_DIR;
+		err = chan_register(&cset->chan[i]);
+		if (err)
+			goto out_reg;
+	}
+
+	/*
+	 * If no name was assigned, ZIO assigns it.  cset name is
+	 * set to the kobject name. kobject name has no length limit,
+	 * so the cset name is the first ZIO_NAME_LEN characters of
+	 * kobject name. A duplicate cset name is not a problem
+	 * anyways.
+	 */
+	if (!strlen(cset->head.name))
+		strncpy(cset->head.name, cset->head.kobj.name, ZIO_NAME_LEN);
+
+	/*
+	 * The cset must have a trigger type. If none  is associated
+	 * to the cset, ZIO selects the default or preferred one.
+	 * This is done late because each channel must be ready when
+	 * the trigger fires.
+	 */
+	if (!cset->trig) {
+		name = cset->zdev->preferred_trigger;
+		trig = zio_trigger_get(name);
+		if (name && IS_ERR(trig))
+			pr_warning("%s: no trigger \"%s\" (error %li), using "
+				   "default\n", __func__, name, PTR_ERR(trig));
+		if (IS_ERR(trig))
+			trig = zio_trigger_get(ZIO_DEFAULT_TRIGGER);
+		if (IS_ERR(trig)) {
+			err = PTR_ERR(trig);
+			goto out_trig;
+		}
+		cset->trig = trig;
+		err = __trigger_create_instance(cset);
+		if (err)
+			goto out_trig;
+	}
+
+	list_add(&cset->list_cset, &zstat->list_cset);
+
+	/* Private initialization function */
+	if (cset->init) {
+		err = cset->init(cset);
+		if (err)
+			goto out_init;
+	}
+	return 0;
+
+out_init:
+	__trigger_destroy_instance(cset);
+out_trig:
+	zio_trigger_put(cset->trig);
+	cset->trig = NULL;
+out_reg:
+	for (j = i-1; j >= 0; j--)
+		chan_unregister(&cset->chan[j]);
+	zio_buffer_put(cset->zbuf);
+out_buf:
+	cset_free_chan(cset);
+out_alloc:
+	zattr_set_remove(&cset->head);
+out_sysfs:
+	kobject_del(&cset->head.kobj);
+out_add:
+	/* we must _put even if it returned error */
+	kobject_put(&cset->head.kobj);
+	return err;
+}
+
+static void cset_unregister(struct zio_cset *cset)
+{
+	int i;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!cset)
+		return;
+	/* Private exit function */
+	if (cset->exit)
+		cset->exit(cset);
+
+	/* Remove from csets list*/
+	list_del(&cset->list_cset);
+	/* destroy instance and decrement trigger usage */
+	__trigger_destroy_instance(cset);
+	zio_trigger_put(cset->trig);
+	cset->trig = NULL;
+	/* Unregister all child channels */
+	for (i = 0; i < cset->n_chan; i++)
+		chan_unregister(&cset->chan[i]);
+	/* decrement buffer usage */
+	zio_buffer_put(cset->zbuf);
+	cset->zbuf = NULL;
+	cset_free_chan(cset);
+	/* Remove from sysfs */
+	zattr_set_remove(&cset->head);
+	kobject_del(&cset->head.kobj);
+	kobject_put(&cset->head.kobj);
+	/* Release a group of minors */
+	__zio_minorbase_put(cset);
+}
+
+/*
+ * Register a generic zio object. It can be a device, a buffer type or
+ * a trigger type.
+ */
+static int zobj_register(struct zio_object_list *zlist,
+			 struct zio_obj_head *head,
+			 enum zio_object_type type,
+			 struct module *owner,
+			 const char *name)
+{
+	int err;
+	struct zio_object_list_item *item;
+
+	head->zobj_type = type;
+	if (strlen(name) > ZIO_NAME_OBJ)
+		pr_warning("ZIO: name too long, cut to %d characters\n",
+			   ZIO_NAME_OBJ);
+	strncpy(head->name, name, ZIO_NAME_OBJ);
+
+	/* Name must be unique */
+	err = zobj_unique_name(zlist, head->name);
+	if (err)
+		goto out;
+	err = kobject_init_and_add(&head->kobj, &zdktype, zlist->kobj,
+					head->name);
+	if (err)
+		goto out_kobj;
+
+	/* Add to object list */
+	item = kmalloc(sizeof(struct zio_object_list_item), GFP_KERNEL);
+	if (!item) {
+		err = -ENOMEM;
+		goto out_km;
+	}
+	item->obj_head = head;
+	item->owner = owner;
+	strncpy(item->name, head->name, ZIO_NAME_OBJ);
+	/* add to the object list*/
+	spin_lock(&zstat->lock);
+	list_add(&item->list, &zlist->list);
+	spin_unlock(&zstat->lock);
+	return 0;
+
+out_km:
+	kobject_del(&head->kobj);
+out_kobj:
+	kobject_put(&head->kobj); /* we must _put even if it returned error */
+out:
+	return err;
+}
+static void zobj_unregister(struct zio_object_list *zlist,
+		struct zio_obj_head *zobj)
+{
+	struct zio_object_list_item *item;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!zobj)
+		return;
+	list_for_each_entry(item, &zlist->list, list) {
+		if (item->obj_head == zobj) {
+			/* Remove from object list */
+			spin_lock(&zstat->lock);
+			list_del(&item->list);
+			spin_unlock(&zstat->lock);
+			kfree(item);
+			break;
+		}
+	}
+
+	kobject_del(&zobj->kobj);
+	kobject_put(&zobj->kobj);
+}
+
+/* Register a zio device */
+int zio_register_dev(struct zio_device *zdev, const char *name)
+{
+	int err = 0, i, j;
+
+	if (!zdev->d_op) {
+		pr_err("%s: new devices has no operations\n", __func__);
+		return -EINVAL;
+	}
+	if (!zdev->owner) {
+		pr_err("%s: new device has no owner\n", __func__);
+		return -EINVAL;
+	}
+	/* Register the device */
+	err = zobj_register(&zstat->all_devices, &zdev->head,
+			    ZDEV, zdev->owner, name);
+	if (err)
+		goto out;
+	zdev->zattr_set.n_std_attr = ZATTR_STD_NUM_ZDEV;
+	spin_lock_init(&zdev->lock);
+	/* Create standard and extended sysfs attribute for device */
+	err = zattr_set_create(&zdev->head, zdev->s_op);
+	if (err)
+		goto out_sysfs;
+
+	/* Register all child channel sets */
+	for (i = 0; i < zdev->n_cset; i++) {
+		zdev->cset[i].index = i;
+		zdev->cset[i].zdev = zdev;
+		err = cset_register(&zdev->cset[i]);
+		if (err)
+			goto out_cset;
+	}
+
+	return 0;
+
+out_cset:
+	for (j = i-1; j >= 0; j--)
+		cset_unregister(zdev->cset + j);
+out_sysfs:
+	zobj_unregister(&zstat->all_devices, &zdev->head);
+out:
+	return err;
+}
+EXPORT_SYMBOL(zio_register_dev);
+
+void zio_unregister_dev(struct zio_device *zdev)
+{
+	int i;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!zdev)
+		return;
+
+	/* Unregister all child channel sets */
+	for (i = 0; i < zdev->n_cset; i++)
+		cset_unregister(&zdev->cset[i]);
+	/* Remove from sysfs */
+	zattr_set_remove(&zdev->head);
+	/* Unregister the device */
+	zobj_unregister(&zstat->all_devices, &zdev->head);
+}
+EXPORT_SYMBOL(zio_unregister_dev);
+
+/* Register a buffer into the available buffer list */
+int zio_register_buf(struct zio_buffer_type *zbuf, const char *name)
+{
+	int err;
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!zbuf || !name)
+		return -EINVAL;
+
+	err = zobj_register(&zstat->all_buffer_types, &zbuf->head,
+			    ZBUF, zbuf->owner, name);
+	if (err)
+		return err;
+	zbuf->zattr_set.n_std_attr = ZATTR_STD_NUM_ZBUF;
+	INIT_LIST_HEAD(&zbuf->list);
+	spin_lock_init(&zbuf->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(zio_register_buf);
+
+void zio_unregister_buf(struct zio_buffer_type *zbuf)
+{
+	if (!zbuf)
+		return;
+	zobj_unregister(&zstat->all_buffer_types, &zbuf->head);
+}
+EXPORT_SYMBOL(zio_unregister_buf);
+
+/* Register a trigger into the available trigger list */
+int zio_register_trig(struct zio_trigger_type *trig, const char *name)
+{
+	int err;
+
+	if (!trig || !name)
+		return -EINVAL;
+	err = zobj_register(&zstat->all_trigger_types, &trig->head,
+			    ZTRIG, trig->owner, name);
+	if (err)
+		return err;
+	trig->zattr_set.n_std_attr = ZATTR_STD_NUM_TRIG;
+	INIT_LIST_HEAD(&trig->list);
+	spin_lock_init(&trig->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(zio_register_trig);
+
+void zio_unregister_trig(struct zio_trigger_type *trig)
+{
+	if (!trig)
+		return;
+	zobj_unregister(&zstat->all_trigger_types, &trig->head);
+}
+EXPORT_SYMBOL(zio_unregister_trig);
+
+/* Initialize a list of objects */
+static int zlist_register(struct zio_object_list *zlist,
+			  struct kobject *parent,
+			  enum zio_object_type type,
+			  const char *name)
+{
+	int err = 0;
+
+	/* Create a defaul kobject for the list and add it to sysfs */
+	zlist->kobj = kobject_create_and_add(name, parent);
+	if (!zlist->kobj)
+		goto out_kobj;
+	pr_debug("%s:%d\n", __func__, __LINE__);
+
+	/* Initialize the specific list */
+	INIT_LIST_HEAD(&zlist->list);
+	zlist->zobj_type = type;
+	return 0;
+
+out_kobj:
+	kobject_put(zlist->kobj); /* we must _put even if it returned error */
+	return err;
+}
+/* Remove a list of objects */
+static void zlist_unregister(struct zio_object_list *zlist)
+{
+	kobject_del(zlist->kobj);
+	kobject_put(zlist->kobj);
+}
+
+static int __init zio_init(void)
+{
+	int err;
+
+	/* Some compile-time checks, so developers are free to hack around */
+	BUILD_BUG_ON_NOT_POWER_OF_2(ZIO_CHAN_MAXNUM);
+	BUILD_BUG_ON_NOT_POWER_OF_2(ZIO_CSET_MAXNUM);
+	BUILD_BUG_ON(ZIO_CSET_MAXNUM * ZIO_CHAN_MAXNUM * 2 > MINORMASK);
+	BUILD_BUG_ON(ZATTR_STD_NUM_ZDEV != ARRAY_SIZE(zio_zdev_attr_names));
+	BUILD_BUG_ON(ZATTR_STD_NUM_ZBUF != ARRAY_SIZE(zio_zbuf_attr_names));
+	BUILD_BUG_ON(ZATTR_STD_NUM_TRIG != ARRAY_SIZE(zio_trig_attr_names));
+
+	/* Initialize char device */
+	err = __zio_register_cdev();
+	if (err)
+		goto out_cdev;
+
+	/* Create the zio container */
+	zstat->kobj = kobject_create_and_add("zio", NULL);
+	if (!zstat->kobj)
+		goto out_kobj;
+
+	/* Register the three object lists (device, buffer and trigger) */
+	zlist_register(&zstat->all_devices, zstat->kobj, ZDEV,
+			"devices");
+	zlist_register(&zstat->all_trigger_types, zstat->kobj, ZTRIG,
+			"triggers");
+	zlist_register(&zstat->all_buffer_types, zstat->kobj, ZBUF,
+			"buffers");
+	pr_info("zio-core had been loaded\n");
+	return 0;
+
+out_kobj:
+	__zio_unregister_cdev();
+out_cdev:
+	return err;
+}
+
+static void __exit zio_exit(void)
+{
+	/* Remove the three object lists*/
+	zlist_unregister(&zstat->all_devices);
+	zlist_unregister(&zstat->all_buffer_types);
+	zlist_unregister(&zstat->all_trigger_types);
+
+	/* Remove from sysfs */
+	kobject_del(zstat->kobj);
+	kobject_put(zstat->kobj);
+
+	/* Remove char device */
+	__zio_unregister_cdev();
+
+	pr_info("zio-core had been unloaded\n");
+	return;
+}
+
+subsys_initcall(zio_init);
+module_exit(zio_exit);
+
+MODULE_AUTHOR("Federico Vaga <federico.vaga@...il.com>");
+MODULE_DESCRIPTION("ZIO - ZIO Input Output");
+MODULE_LICENSE("GPL");
-- 
1.7.7.2
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ