From 4ccbb827bd20e3a7b3f4e114c49b651173bebd9a Mon Sep 17 00:00:00 2001
From: Federico Vaga <federico.vaga@gmail.com>
Date: Tue, 17 Apr 2012 15:42:31 +0200
Subject: [PATCH] core: improve abort and data_done mechanism

The helper zio_trigger_abort() is introduced; it can be used to abort a
data transfer.

The bit flag ZTI_COMPLETING is introduced: if high ZIO is completing the
transfert operation by invokng the data_done() function; if low the
transfer is running or the trigger is waiting for event. If abort() is
called when this glag is high, the functions does nothing.

The helper zio_trigger_data_done() is introduced; insted of call the
data_done() function with cset->ti->t_op->data_done() and manually set
the ZTI_COMPLETING flag, this hellper must be used. If the trigger
operation data_done is not set, then use the __zio_internal_data_done

Signed-off-by: Federico Vaga <federico.vaga@gmail.com>
Acked-by: Alessandro Rubini <rubini@gnudd.com>
---
 drivers/zio-ad788x.c        |  2 +-
 include/linux/zio-trigger.h |  1 +
 include/linux/zio.h         |  3 ++
 triggers/zio-trig-irq.c     |  1 -
 triggers/zio-trig-timer.c   |  1 -
 triggers/zio-trig-user.c    |  1 -
 zio-sys.c                   | 85 ++++++++++++++++++++++++++++++-------
 7 files changed, 74 insertions(+), 20 deletions(-)

diff --git a/drivers/zio-ad788x.c b/drivers/zio-ad788x.c
index a9f49d2..3128658 100644
--- a/drivers/zio-ad788x.c
+++ b/drivers/zio-ad788x.c
@@ -124,7 +124,7 @@ static void ad788x_complete(void *cont)
 				buf[i] = data[i * context->chan_enable + j];
 		++j;
 	}
-	cset->ti->t_op->data_done(cset);
+	zio_trigger_data_done(cset);
 	/* free context */
 	kfree(context->transfer.tx_buf);
 	kfree(context->transfer.rx_buf);
diff --git a/include/linux/zio-trigger.h b/include/linux/zio-trigger.h
index a968209..1876801 100644
--- a/include/linux/zio-trigger.h
+++ b/include/linux/zio-trigger.h
@@ -46,6 +46,7 @@ struct zio_ti {
 /* first 4bit are reserved for zio object universal flags */
 enum zti_flag_mask {
 	ZTI_BUSY = 0x10,	/* trigger fire and transfer occurs */
+	ZTI_COMPLETING = 0x20	/* trigger is clompleting transfert */
 };
 
 #define to_zio_ti(obj) container_of(obj, struct zio_ti, head.dev)
diff --git a/include/linux/zio.h b/include/linux/zio.h
index 24d9e3e..31b5938 100644
--- a/include/linux/zio.h
+++ b/include/linux/zio.h
@@ -277,6 +277,9 @@ static inline unsigned int __get_n_chan_enabled(struct zio_cset *cset) {
 	char *_name; \
 	module_param_named(buffer, _name, charp, 0444)
 
+void zio_trigger_data_done(struct zio_cset *cset);
+void zio_trigger_abort(struct zio_cset *cset);
+
 #ifdef __ZIO_INTERNAL__
 
 /* This list is used in the core to keep track of registered objects */
diff --git a/triggers/zio-trig-irq.c b/triggers/zio-trig-irq.c
index e7a59fd..6f3d69b 100644
--- a/triggers/zio-trig-irq.c
+++ b/triggers/zio-trig-irq.c
@@ -116,7 +116,6 @@ static void zti_destroy(struct zio_ti *ti)
 static const struct zio_trigger_operations zti_trigger_ops = {
 	.push_block = zti_push_block,
 	.pull_block = NULL,
-	.data_done = zio_generic_data_done,
 	.config = zti_config,
 	.create = zti_create,
 	.destroy = zti_destroy,
diff --git a/triggers/zio-trig-timer.c b/triggers/zio-trig-timer.c
index e848cca..a13e6ab 100644
--- a/triggers/zio-trig-timer.c
+++ b/triggers/zio-trig-timer.c
@@ -165,7 +165,6 @@ static void ztt_change_status(struct zio_ti *ti, unsigned int status)
 static const struct zio_trigger_operations ztt_trigger_ops = {
 	.push_block = ztt_push_block,
 	.pull_block = NULL,
-	.data_done = zio_generic_data_done,
 	.config = ztt_config,
 	.create = ztt_create,
 	.destroy = ztt_destroy,
diff --git a/triggers/zio-trig-user.c b/triggers/zio-trig-user.c
index d5d44f2..e91a955 100644
--- a/triggers/zio-trig-user.c
+++ b/triggers/zio-trig-user.c
@@ -88,7 +88,6 @@ static void ztu_destroy(struct zio_ti *ti)
 static const struct zio_trigger_operations ztu_trigger_ops = {
 	.push_block = ztu_push_block,
 	.pull_block = ztu_pull_block,
-	.data_done = zio_generic_data_done,
 	.config = ztu_config,
 	.create = ztu_create,
 	.destroy = ztu_destroy,
diff --git a/zio-sys.c b/zio-sys.c
index b2e7a72..bc31432 100644
--- a/zio-sys.c
+++ b/zio-sys.c
@@ -137,8 +137,7 @@ static inline void zio_trigger_put(struct zio_trigger_type *trig)
 	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)
+void __zio_internal_data_done(struct zio_cset *cset)
 {
 	struct zio_buffer_type *zbuf;
 	struct zio_device *zdev;
@@ -162,7 +161,7 @@ void zio_generic_data_done(struct zio_cset *cset)
 			/* We may have a new block ready, or not */
 			chan->active_block = zbuf->b_op->retr_block(chan->bi);
 		}
-		goto out;
+		return;
 	}
 	/* DIR_INPUT */
 	cset_for_each(cset, chan) {
@@ -173,12 +172,73 @@ void zio_generic_data_done(struct zio_cset *cset)
 		if (zbuf->b_op->store_block(bi, block)) /* may fail, no prob */
 			zbuf->b_op->free_block(bi, block);
 	}
-out:
+}
+
+/*
+ * zio_trigger_data_done
+ * This is a ZIO helper to invoke the data_done trigger operation when a data
+ * transfer is over and we need to complete the operation.
+ * It is useless check for pending ZTI_COMPLETING because only one fire at
+ * time is allowed so they cannot exist concurrent completation.
+ */
+void zio_trigger_data_done(struct zio_cset *cset)
+{
+	spin_lock(&cset->lock);
+	cset->ti->flags |= ZTI_COMPLETING; /* transfer is completing*/
+	spin_unlock(&cset->lock);
+
+	/* Call the data_done function */
+	if (cset->ti->t_op->data_done)
+		cset->ti->t_op->data_done(cset);
+	else
+		__zio_internal_data_done(cset);
+
+	/* transfer is over, resetting completing and busy flags */
+	spin_lock(&cset->lock);
+	cset->ti->flags &= (~(ZTI_COMPLETING | ZTI_BUSY));
+	spin_unlock(&cset->lock);
+}
+EXPORT_SYMBOL(zio_trigger_data_done);
+
+static void __zio_internal_abort_free(struct zio_cset *cset)
+{
+	struct zio_channel *chan;
+	struct zio_block *block;
+
+	cset_for_each(cset, chan) {
+		block = chan->active_block;
+		if (block)
+			cset->zbuf->b_op->free_block(chan->bi, block);
+		chan->active_block = NULL;
+	}
+}
+
+/*
+ * zio_trigger_abort
+ * This is a ZIO helper to invoke the abort function. This must be used when
+ * something is going wrong during the acquisition.
+ */
+void zio_trigger_abort(struct zio_cset *cset)
+{
+	struct zio_ti *ti = cset->ti;
+
+	/*
+	 * If trigger is running (ZTI_BUSY) but it is not
+	 * completing the transfer (ZTI_COMPLETING), then abort it.
+	 * If the trigger is completing its run, don't abort it because
+	 * it finished and the blocks are full of data.
+	 */
 	spin_lock(&cset->lock);
-	ti->flags &= (~ZTI_BUSY); /* Reset busy, now is idle */
+	if ((ti->flags & ZTI_BUSY) && !(ti->flags & ZTI_COMPLETING)) {
+		if(ti->t_op->abort)
+			ti->t_op->abort(cset);
+		else
+			__zio_internal_abort_free(cset);
+		ti->flags &= (~ZTI_BUSY); /* when disabled is not busy */
+	}
 	spin_unlock(&cset->lock);
 }
-EXPORT_SYMBOL(zio_generic_data_done);
+EXPORT_SYMBOL(zio_trigger_abort);
 
 static void __zio_fire_input_trigger(struct zio_ti *ti)
 {
@@ -231,7 +291,7 @@ static void __zio_fire_input_trigger(struct zio_ti *ti)
 	}
 	if (!cset->raw_io(cset)) {
 		/* It succeeded immediately */
-		ti->t_op->data_done(cset);
+		zio_trigger_data_done(cset);
 	}
 }
 
@@ -244,7 +304,7 @@ static void __zio_fire_output_trigger(struct zio_ti *ti)
 	/* We are expected to already have a block in active channels */
 	if (!cset->raw_io(cset)) {
 		/* It succeeded immediately */
-		ti->t_op->data_done(cset);
+		zio_trigger_data_done(cset);
 	}
 }
 
@@ -726,14 +786,7 @@ static void __zobj_enable(struct device *dev, unsigned int enable)
 		pr_debug("%s: zti\n", __func__);
 
 		ti = to_zio_ti(dev);
-		/* if trigger is running, abort it*/
-		spin_lock(&ti->cset->lock);
-		if (*flags & ZTI_BUSY) {
-			if(ti->t_op->abort)
-				ti->t_op->abort(ti->cset);
-			*flags &= ~ZTI_BUSY; /* when disabled is not busy */
-		}
-		spin_unlock(&ti->cset->lock);
+		zio_trigger_abort(ti->cset);
 		/* trigger instance callback */
 		if (ti->t_op->change_status)
 			ti->t_op->change_status(ti, status);
-- 
GitLab