From bd5504b1edb5ee39d166b2f1cfcd3cd2c82fbb5c Mon Sep 17 00:00:00 2001
From: Federico Vaga <federico.vaga@gmail.com>
Date: Sun, 15 Jan 2012 19:49:52 +0100
Subject: [PATCH] core: enable/disable zio objects

A zio object can be enabled or disabled

Signed-off-by: Federico Vaga <federico.vaga@gmail.com>
Acked-by: Alessandro Rubini <rubini@gnudd.com>
---
 buffers/zio-buf-kmalloc.c   |   9 ++-
 include/linux/zio-trigger.h |   5 ++
 include/linux/zio.h         |   4 +-
 triggers/zio-trig-timer.c   |  30 ++++++--
 zio-sys.c                   | 148 +++++++++++++++++++++++++++++++++++-
 5 files changed, 185 insertions(+), 11 deletions(-)

diff --git a/buffers/zio-buf-kmalloc.c b/buffers/zio-buf-kmalloc.c
index 703385d..3c3bd9b 100644
--- a/buffers/zio-buf-kmalloc.c
+++ b/buffers/zio-buf-kmalloc.c
@@ -103,6 +103,9 @@ static void zbk_free_block(struct zio_bi *bi, struct zio_block *block)
 static inline int __try_push(struct zio_ti *ti, struct zio_channel *chan,
 			     struct zio_block *block)
 {
+	/* chek if trigger is disabled */
+	if (unlikely((ti->flags & ZIO_STATUS) == ZIO_DISABLED))
+		return 0;
 	if (ti->t_op->push_block(ti, chan, block) < 0)
 		return 0;
 	return 1;
@@ -183,8 +186,12 @@ out_unlock:
 	spin_unlock(&zbki->lock);
 	/* There is no data in buffer, and we may pull to have data soon */
 	ti = bi->cset->ti;
-	if ((bi->flags & ZIO_DIR) == ZIO_DIR_INPUT && ti->t_op->pull_block)
+	if ((bi->flags & ZIO_DIR) == ZIO_DIR_INPUT && ti->t_op->pull_block){
+		/* chek if trigger is disabled */
+		if (unlikely((ti->flags & ZIO_STATUS) == ZIO_DISABLED))
+			return NULL;
 		ti->t_op->pull_block(ti, bi->chan);
+	}
 	pr_debug("%s:%d (%p, %p)\n", __func__, __LINE__, bi, NULL);
 	return NULL;
 }
diff --git a/include/linux/zio-trigger.h b/include/linux/zio-trigger.h
index 5fb291d..0f48a8d 100644
--- a/include/linux/zio-trigger.h
+++ b/include/linux/zio-trigger.h
@@ -84,6 +84,8 @@ void zio_fire_trigger(struct zio_ti *ti);
  * the conf_set callback runs and the instance is notified) or by writing
  * a whole control to the control device. In this case the config method
  * is called by the write method.
+ *
+ * FIXME describe well abort and change_status
  */
 struct zio_trigger_operations {
 	int			(*push_block)(struct zio_ti *ti,
@@ -102,6 +104,9 @@ struct zio_trigger_operations {
 					  struct zio_control *ctrl,
 					  fmode_t flags);
 	void			(*destroy)(struct zio_ti *ti);
+	void			(*change_status)(struct zio_ti *ti,
+						 unsigned int status);
+	void			(*abort)(struct zio_cset *cset);
 };
 
 void zio_generic_data_done(struct zio_cset *cest);
diff --git a/include/linux/zio.h b/include/linux/zio.h
index 47f5313..0e0ae81 100644
--- a/include/linux/zio.h
+++ b/include/linux/zio.h
@@ -68,7 +68,9 @@ static inline enum zio_object_type __zio_get_object_type(struct kobject *kobj)
 
 /* Bits 0..3 are reserved for use in all objects. By now only bit 1 is used */
 enum zobj_flags {
-	ZIO_DISABLED		= 0x1,	/* 0 (default) is enabled */
+	ZIO_STATUS		= 0x1,	/* 0 (default) is enabled */
+	ZIO_ENABLED		= 0x0,
+	ZIO_DISABLED		= 0x1,
 	ZIO_DIR			= 0x2,	/* 0 is input  - 1 is output*/
 	ZIO_DIR_INPUT		= 0x0,
 	ZIO_DIR_OUTPUT		= 0x2,
diff --git a/triggers/zio-trig-timer.c b/triggers/zio-trig-timer.c
index f1345d6..0155a73 100644
--- a/triggers/zio-trig-timer.c
+++ b/triggers/zio-trig-timer.c
@@ -112,14 +112,18 @@ static int ztt_config(struct zio_ti *ti, struct zio_control *ctrl)
 	pr_debug("%s:%d\n", __func__, __LINE__);
 	return 0;
 }
-
+static void ztt_start_timer(struct ztt_instance *ztt_instance, uint32_t ms)
+{
+	ztt_instance->next_run = jiffies + HZ;
+	ztt_instance->period = msecs_to_jiffies(ms);
+	mod_timer(&ztt_instance->timer, ztt_instance->next_run);
+}
 static struct zio_ti *ztt_create(struct zio_trigger_type *trig,
 				 struct zio_cset *cset,
 				 struct zio_control *ctrl, fmode_t flags)
 {
 	struct ztt_instance *ztt_instance;
 	struct zio_ti *ti;
-	uint32_t ms;
 
 	pr_debug("%s:%d\n", __func__, __LINE__);
 
@@ -135,12 +139,7 @@ static struct zio_ti *ztt_create(struct zio_trigger_type *trig,
 	/* Fill own fields */
 	setup_timer(&ztt_instance->timer, ztt_fn,
 		    (unsigned long)(&ztt_instance->ti));
-	ztt_instance->next_run = jiffies + HZ;
-	ms = ztt_ext_attr[0].value;
-	ztt_instance->period = msecs_to_jiffies(ms); /* module param */
-
-	/* Start the timer (dangerous: ti is not filled) */
-	mod_timer(&ztt_instance->timer, ztt_instance->next_run);
+	ztt_start_timer(ztt_instance, ztt_ext_attr[0].value);
 
 	return ti;
 }
@@ -155,6 +154,20 @@ static void ztt_destroy(struct zio_ti *ti)
 	kfree(ti);
 }
 
+static void ztt_change_status(struct zio_ti *ti, unsigned int status)
+{
+	struct ztt_instance *ztt_instance;
+
+	pr_debug("%s:%d status=%d\n", __func__, __LINE__, status);
+	ztt_instance = to_ztt_instance(ti);
+
+	if (!status) {	/* enable */
+		ztt_start_timer(ztt_instance, ztt_instance->period);
+	} else {	/* disable */
+		/* FIXME kernel/timer.c don't use this is lock*/
+		del_timer_sync(&ztt_instance->timer);
+	}
+}
 static const struct zio_trigger_operations ztt_trigger_ops = {
 	.push_block = ztt_push_block,
 	.pull_block = NULL,
@@ -162,6 +175,7 @@ static const struct zio_trigger_operations ztt_trigger_ops = {
 	.config = ztt_config,
 	.create = ztt_create,
 	.destroy = ztt_destroy,
+	.change_status = ztt_change_status,
 };
 
 static struct zio_trigger_type ztt_trigger = {
diff --git a/zio-sys.c b/zio-sys.c
index ea7db9d..ec1a91c 100644
--- a/zio-sys.c
+++ b/zio-sys.c
@@ -17,6 +17,7 @@
 #include <linux/zio-trigger.h>
 
 #define ZOBJ_SYSFS_NAME "name"
+#define ZOBJ_SYSFS_ENABLE "enable"
 #define CSET_SYSFS_BUFFER "current_buffer"
 #define CSET_SYSFS_TRIGGER "current_trigger"
 
@@ -224,6 +225,9 @@ void zio_fire_trigger(struct zio_ti *ti)
 	/* If the trigger runs too early, ti->cset is still NULL */
 	if (!ti->cset)
 		return;
+	/* chek if trigger is disabled */
+	if (unlikely((ti->flags & ZIO_STATUS) == ZIO_DISABLED))
+		return;
 	/* check if previouvs fire is still running*/
 	if ((ti->flags & ZTI_STATUS) == ZTI_STATUS_ON)
 		return;
@@ -435,6 +439,39 @@ static struct zio_attribute_set *__get_zattr_set(struct zio_obj_head *head)
 	}
 	return zattr_set;
 }
+/* Retrieve flag from an object head */
+static unsigned long *__get_flag(struct zio_obj_head *head)
+{
+	unsigned long *flags;
+
+	switch (head->zobj_type) {
+	case ZDEV:
+		flags = &to_zio_dev(&head->kobj)->flags;
+		break;
+	case ZCSET:
+		flags = &to_zio_cset(&head->kobj)->flags;
+		break;
+	case ZCHAN:
+		flags = &to_zio_chan(&head->kobj)->flags;
+		break;
+	case ZTRIG:
+		flags = &to_zio_chan(&head->kobj)->flags;
+		break;
+	case ZBUF:
+		flags = &to_zio_chan(&head->kobj)->flags;
+		break;
+	case ZTI:
+		flags = &to_zio_ti(&head->kobj)->flags;
+		break;
+	case ZBI:
+		flags = &to_zio_bi(&head->kobj)->flags;
+		break;
+	default:
+		WARN(1, "ZIO: unknown zio object %i\n", head->zobj_type);
+		return NULL;
+	}
+	return flags;
+}
 
 static int zio_change_current_trigger(struct zio_cset *cset, char *name)
 {
@@ -630,7 +667,10 @@ static ssize_t zattr_show(struct kobject *kobj, struct attribute *attr,
 	/* print current buffer name */
 	if (unlikely(strcmp(attr->name, CSET_SYSFS_BUFFER) == 0))
 		return sprintf(buf, "%s\n", to_zio_cset(kobj)->zbuf->head.name);
-
+	/* print current enable status */
+	if (unlikely(strcmp(attr->name, ZOBJ_SYSFS_ENABLE) == 0))
+		return sprintf(buf, "%d\n",
+			  !((*__get_flag(to_zio_head(kobj))) & ZIO_DISABLED));
 
 	if (zattr->s_op->info_get) {
 		lock = __get_spinlock(to_zio_head(kobj));
@@ -643,6 +683,91 @@ static ssize_t zattr_show(struct kobject *kobj, struct attribute *attr,
 	len = sprintf(buf, "%i\n", zattr->value);
 	return len;
 }
+
+/* enable/disable a zio object*/
+static void __zobj_enable(struct kobject *kobj, unsigned int enable,
+			  unsigned int need_lock)
+{
+	unsigned long *flags;
+	int i, status;
+	struct zio_obj_head *head;
+	struct zio_device *zdev;
+	struct zio_cset *cset;
+	struct zio_ti *ti;
+	spinlock_t *lock;
+
+	pr_debug("%s\n", __func__);
+	head = to_zio_head(kobj);
+
+	/* lock object if needed */
+	lock = __get_spinlock(head);
+	if (need_lock)
+		spin_lock(lock);
+
+	flags = __get_flag(to_zio_head(kobj));
+	status = !((*flags) & ZIO_STATUS);
+	/* if the status is not changing */
+	if (!(enable ^ status)) {
+		goto out;
+	}
+	/* change status */
+	*flags = (*flags | ZIO_STATUS) & status;
+	switch (head->zobj_type) {
+	case ZDEV:
+		pr_debug("%s: zdev\n", __func__);
+
+		zdev = to_zio_dev(kobj);
+		/* enable/disable all cset */
+		for (i = 0; i < zdev->n_cset; ++i) {
+			__zobj_enable(&zdev->cset[i].head.kobj, enable, 0);
+		}
+		/* device callback */
+		break;
+	case ZCSET:
+		pr_debug("%s: zcset\n", __func__);
+
+		cset = to_zio_cset(kobj);
+		/* enable/disable trigger instance */
+		__zobj_enable(&cset->ti->head.kobj, enable, 1);
+		/* enable/disable all channel*/
+		for (i = 0; i < cset->n_chan; ++i) {
+			__zobj_enable(&cset->chan[i].head.kobj, enable, 0);
+		}
+		/* cset callback */
+		break;
+	case ZCHAN:
+		pr_debug("%s: zchan\n", __func__);
+		/* channel callback */
+		break;
+	case ZTI:
+		pr_debug("%s: zti\n", __func__);
+
+		ti = to_zio_ti(kobj);
+		/* if trigger is running, abort it*/
+		if ((*flags & ZTI_STATUS) == ZTI_STATUS_ON)
+			if(ti->t_op->abort)
+				ti->t_op->abort(ti->cset);
+		/* trigger instance callback */
+		if (ti->t_op->change_status) {
+			pr_debug("%s:%d\n", __func__, __LINE__);
+			ti->t_op->change_status(ti, status);
+		}
+		break;
+	/* following objects can't be enabled/disabled */
+	case ZBUF:
+	case ZTRIG:
+	case ZBI:
+		pr_debug("%s: others\n", __func__);
+		/* buffer instance callback */
+		break;
+	default:
+		WARN(1, "ZIO: unknown zio object %i\n", head->zobj_type);
+	}
+out:
+	if (need_lock)
+		spin_unlock(lock);
+}
+
 static ssize_t zattr_store(struct kobject *kobj, struct attribute *attr,
 				const char *buf, size_t size)
 {
@@ -668,9 +793,19 @@ static ssize_t zattr_store(struct kobject *kobj, struct attribute *attr,
 		err = zio_change_current_buffer(to_zio_cset(kobj), buf_tmp);
 		return err == 0 ? size : err;
 	}
+
 	err = strict_strtol(buf, 0, &val);
 	if (err)
 		return -EINVAL;
+
+	/* change enable status */
+	if (unlikely(strcmp(attr->name, ZOBJ_SYSFS_ENABLE) == 0 &&
+	    (val == 0 || val == 1))) {
+		__zobj_enable(kobj, val, 1);
+		return size;
+	}
+
+	/* device attributes */
 	if (zattr->s_op->conf_set) {
 		lock = __get_spinlock(to_zio_head(kobj));
 		spin_lock(lock);
@@ -695,6 +830,11 @@ static struct attribute default_cset_attrs[] = {
 			.name = ZOBJ_SYSFS_NAME,
 			.mode = 0444, /* read only */
 		},
+		{
+			/* enable/disable object */
+			.name = ZOBJ_SYSFS_ENABLE,
+			.mode = 0666, /* read write */
+		},
 		{	/* get/set trigger */
 			.name = CSET_SYSFS_TRIGGER,
 			.mode = 0666, /* read write */
@@ -708,6 +848,7 @@ static struct attribute *def_cset_attr_ptr[] = {
 	&default_cset_attrs[0],
 	&default_cset_attrs[1],
 	&default_cset_attrs[2],
+	&default_cset_attrs[3],
 	NULL,
 };
 static struct kobj_type zdkctype = { /* only for cset */
@@ -720,9 +861,14 @@ static struct attribute default_attrs[] = {
 			.name = ZOBJ_SYSFS_NAME,
 			.mode = 0444, /* read only */
 		},
+		{	/* enable/disable object */
+			.name = ZOBJ_SYSFS_ENABLE,
+			.mode = 0666, /* read write */
+		},
 };
 static struct attribute *def_attr_ptr[] = {
 	&default_attrs[0],
+	&default_attrs[1],
 	NULL,
 };
 
-- 
GitLab