2014-02-28 07:35:42

by Chase Southwood

[permalink] [raw]
Subject: [PATCH] Staging: comedi: add timeouts to while loops in s626.c

Smatch located a handful of while loops testing readl calls in s626.c.
Since these while loops depend on readl succeeding, it's safer to make
sure they time out eventually.

Signed-off-by: Chase Southwood <[email protected]>
---
Ian and/or Hartley, I'd love your comments on this. It seems to me that
we want these kinds of while loops properly timed out, but I want to make
sure I'm doing everything properly. First off, s626_debi_transfer() says
directly that it is called from within critical sections, so I assume
that means that the new comedi_timeout() function is no good here, and
s626_send_dac() looked equally suspicious, so I opted for iterative
timeouts. Is this correct? Also, for these timeouts, I used a very
conservative 10000 iterations, would it be better to decrease that?
Also, do my error strings appear acceptable?

And finally, are timeouts here even necessary or helpful, or are there
any better ways to do it?

Thanks,
Chase

drivers/staging/comedi/drivers/s626.c | 49 ++++++++++++++++++++++++++++-------
1 file changed, 39 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c
index 5ba4b4a..282636b 100644
--- a/drivers/staging/comedi/drivers/s626.c
+++ b/drivers/staging/comedi/drivers/s626.c
@@ -209,6 +209,8 @@ static const struct comedi_lrange s626_range_table = {
static void s626_debi_transfer(struct comedi_device *dev)
{
struct s626_private *devpriv = dev->private;
+ static const int timeout = 10000;
+ int i;

/* Initiate upload of shadow RAM to DEBI control register */
s626_mc_enable(dev, S626_MC2_UPLD_DEBI, S626_P_MC2);
@@ -221,8 +223,13 @@ static void s626_debi_transfer(struct comedi_device *dev)
;

/* Wait until DEBI transfer is done */
- while (readl(devpriv->mmio + S626_P_PSR) & S626_PSR_DEBI_S)
- ;
+ for (i = 0; i < timeout; i++) {
+ if (!(readl(devpriv->mmio + S626_P_PSR) & S626_PSR_DEBI_S))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ comedi_error(dev, "DEBI transfer timeout.");
}

/*
@@ -359,6 +366,8 @@ static const uint8_t s626_trimadrs[] = {
static void s626_send_dac(struct comedi_device *dev, uint32_t val)
{
struct s626_private *devpriv = dev->private;
+ static const int timeout = 10000;
+ int i;

/* START THE SERIAL CLOCK RUNNING ------------- */

@@ -404,8 +413,13 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val)
* Done by polling the DMAC enable flag; this flag is automatically
* cleared when the transfer has finished.
*/
- while (readl(devpriv->mmio + S626_P_MC1) & S626_MC1_A2OUT)
- ;
+ for (i = 0; i < timeout; i++) {
+ if (!(readl(devpriv->mmio + S626_P_MC1) & S626_MC1_A2OUT))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ comedi_error(dev, "DMA transfer timeout.");

/* START THE OUTPUT STREAM TO THE TARGET DAC -------------------- */

@@ -425,8 +439,13 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val)
* finished transferring the DAC's data DWORD from the output FIFO
* to the output buffer register.
*/
- while (!(readl(devpriv->mmio + S626_P_SSR) & S626_SSR_AF2_OUT))
- ;
+ for (i = 0; i < timeout; i++) {
+ if (readl(devpriv->mmio + S626_P_SSR) & S626_SSR_AF2_OUT)
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ comedi_error(dev, "TSL timeout waiting for slot 1 to execute.");

/*
* Set up to trap execution at slot 0 when the TSL sequencer cycles
@@ -466,8 +485,13 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val)
* from 0xFF to 0x00, which slot 0 causes to happen by shifting
* out/in on SD2 the 0x00 that is always referenced by slot 5.
*/
- while (readl(devpriv->mmio + S626_P_FB_BUFFER2) & 0xff000000)
- ;
+ for (i = 0; i < timeout; i++) {
+ if (!(readl(devpriv->mmio + S626_P_FB_BUFFER2) & 0xff000000))
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ comedi_error(dev, "TSL timeout waiting for slot 0 to execute.");
}
/*
* Either (1) we were too late setting the slot 0 trap; the TSL
@@ -486,8 +510,13 @@ static void s626_send_dac(struct comedi_device *dev, uint32_t val)
* the next DAC write. This is detected when FB_BUFFER2 MSB changes
* from 0x00 to 0xFF.
*/
- while (!(readl(devpriv->mmio + S626_P_FB_BUFFER2) & 0xff000000))
- ;
+ for (i = 0; i < timeout; i++) {
+ if (readl(devpriv->mmio + S626_P_FB_BUFFER2) & 0xff000000)
+ break;
+ udelay(1);
+ }
+ if (i == timeout)
+ comedi_error(dev, "TLS timeout waiting for slot 0 to execute.");
}

/*
--
1.8.5.3


2014-02-28 17:18:17

by Ian Abbott

[permalink] [raw]
Subject: Re: [PATCH] Staging: comedi: add timeouts to while loops in s626.c

On 2014-02-28 07:35, Chase Southwood wrote:
> Smatch located a handful of while loops testing readl calls in s626.c.
> Since these while loops depend on readl succeeding, it's safer to make
> sure they time out eventually.
>
> Signed-off-by: Chase Southwood <[email protected]>
> ---
> Ian and/or Hartley, I'd love your comments on this. It seems to me that
> we want these kinds of while loops properly timed out, but I want to make
> sure I'm doing everything properly. First off, s626_debi_transfer() says
> directly that it is called from within critical sections, so I assume
> that means that the new comedi_timeout() function is no good here, and
> s626_send_dac() looked equally suspicious, so I opted for iterative
> timeouts. Is this correct? Also, for these timeouts, I used a very
> conservative 10000 iterations, would it be better to decrease that?

Well 10000 iterations is an improvement on infinity! If the hardware is
working, you'd expect it to go round a lot fewer iterations than that,
but if the hardware is broken all bets are off, especially if it is
generating interrupts.

> Also, do my error strings appear acceptable?

Mostly. There's a type in one of the strings that says "TLS" instead of
"TSL".

> And finally, are timeouts here even necessary or helpful, or are there
> any better ways to do it?

In the case of s626_send_dac(), it doesn't seem to be used in any
critical sections, so it could make use of Hartley's comedi_timeout().

Some of the timeout errors could be propagated, especially for
s626_send_dac() which is only reachable from very few paths.

There are other infinite loops involving calls to the s626_mc_test()
function, but those could be dealt with by other patches.

--
-=( Ian Abbott @ MEV Ltd. E-mail: <[email protected]> )=-
-=( Tel: +44 (0)161 477 1898 FAX: +44 (0)161 718 3587 )=-