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