diff mbox

[2/2] i2c: octeon: Fix waiting for operation completion

Message ID 20161107200921.30284-2-paul.burton@imgtec.com
State Deferred
Headers show

Commit Message

Paul Burton Nov. 7, 2016, 8:09 p.m. UTC
Commit 1bb1ff3e7c74 ("i2c: octeon: Improve performance if interrupt is
early") modified octeon_i2c_wait() & octeon_i2c_hlc_wait() to attempt to
check for a valid bit being clear & if not to sleep for a while then try
again before waiting on a waitqueue which may time out. However it does
so by sleeping within a function called as the condition provided to
wait_event_timeout() which seems to cause strange behaviour, with the
system hanging during boot with the condition being checked constantly &
the timeout not seeming to have any effect.

Fix this by instead checking for the valid bit being clear in the
octeon_i2c(_hlc)_wait() functions & sleeping there if that condition is
not met, then calling the wait_event_timeout with a condition that does
not sleep.

Tested on a Rhino Labs UTM-8 with Octeon CN7130.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: David Daney <david.daney@cavium.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Peter Swain <pswain@cavium.com>
Cc: Wolfram Sang <wsa@the-dreams.de>
Cc: linux-i2c@vger.kernel.org
---

 drivers/i2c/busses/i2c-octeon-core.c | 58 ++++++++++--------------------------
 1 file changed, 15 insertions(+), 43 deletions(-)

Comments

Jan Glauber Nov. 8, 2016, 9:20 a.m. UTC | #1
On Mon, Nov 07, 2016 at 08:09:21PM +0000, Paul Burton wrote:
> Commit 1bb1ff3e7c74 ("i2c: octeon: Improve performance if interrupt is
> early") modified octeon_i2c_wait() & octeon_i2c_hlc_wait() to attempt to
> check for a valid bit being clear & if not to sleep for a while then try
> again before waiting on a waitqueue which may time out. However it does
> so by sleeping within a function called as the condition provided to
> wait_event_timeout() which seems to cause strange behaviour, with the
> system hanging during boot with the condition being checked constantly &
> the timeout not seeming to have any effect.
> 
> Fix this by instead checking for the valid bit being clear in the
> octeon_i2c(_hlc)_wait() functions & sleeping there if that condition is
> not met, then calling the wait_event_timeout with a condition that does
> not sleep.
> 
> Tested on a Rhino Labs UTM-8 with Octeon CN7130.

This patch breaks ipmi on ThunderX (CN88xx). We also have not seen the
problems you mention, although I must admit that I don't like the
complicated nested waits.

--Jan

> Signed-off-by: Paul Burton <paul.burton@imgtec.com>
> Cc: David Daney <david.daney@cavium.com>
> Cc: Jan Glauber <jglauber@cavium.com>
> Cc: Peter Swain <pswain@cavium.com>
> Cc: Wolfram Sang <wsa@the-dreams.de>
> Cc: linux-i2c@vger.kernel.org
> ---
> 
>  drivers/i2c/busses/i2c-octeon-core.c | 58 ++++++++++--------------------------
>  1 file changed, 15 insertions(+), 43 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
> index 419b54b..2e270a1 100644
> --- a/drivers/i2c/busses/i2c-octeon-core.c
> +++ b/drivers/i2c/busses/i2c-octeon-core.c
> @@ -36,24 +36,6 @@ static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c)
>  	return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG);
>  }
>  
> -static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first)
> -{
> -	if (octeon_i2c_test_iflg(i2c))
> -		return true;
> -
> -	if (*first) {
> -		*first = false;
> -		return false;
> -	}
> -
> -	/*
> -	 * IRQ has signaled an event but IFLG hasn't changed.
> -	 * Sleep and retry once.
> -	 */
> -	usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
> -	return octeon_i2c_test_iflg(i2c);
> -}
> -
>  /**
>   * octeon_i2c_wait - wait for the IFLG to be set
>   * @i2c: The struct octeon_i2c
> @@ -80,8 +62,13 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
>  	}
>  
>  	i2c->int_enable(i2c);
> -	time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first),
> -				       i2c->adap.timeout);
> +	time_left = i2c->adap.timeout;
> +	if (!octeon_i2c_test_iflg(i2c)) {
> +		usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
> +		time_left = wait_event_timeout(i2c->queue,
> +					       octeon_i2c_test_iflg(i2c),
> +					       time_left);
> +	}
>  	i2c->int_disable(i2c);
>  
>  	if (i2c->broken_irq_check && !time_left &&
> @@ -99,26 +86,8 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c)
>  
>  static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c)
>  {
> -	return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
> -}
> -
> -static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first)
> -{
>  	/* check if valid bit is cleared */
> -	if (octeon_i2c_hlc_test_valid(i2c))
> -		return true;
> -
> -	if (*first) {
> -		*first = false;
> -		return false;
> -	}
> -
> -	/*
> -	 * IRQ has signaled an event but valid bit isn't cleared.
> -	 * Sleep and retry once.
> -	 */
> -	usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
> -	return octeon_i2c_hlc_test_valid(i2c);
> +	return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
>  }
>  
>  static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c)
> @@ -176,7 +145,6 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
>   */
>  static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
>  {
> -	bool first = true;
>  	int time_left;
>  
>  	/*
> @@ -194,9 +162,13 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
>  	}
>  
>  	i2c->hlc_int_enable(i2c);
> -	time_left = wait_event_timeout(i2c->queue,
> -				       octeon_i2c_hlc_test_ready(i2c, &first),
> -				       i2c->adap.timeout);
> +	time_left = i2c->adap.timeout;
> +	if (!octeon_i2c_hlc_test_valid(i2c)) {
> +		usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
> +		time_left = wait_event_timeout(i2c->queue,
> +					       octeon_i2c_hlc_test_valid(i2c),
> +					       time_left);
> +	}
>  	i2c->hlc_int_disable(i2c);
>  	if (!time_left)
>  		octeon_i2c_hlc_int_clear(i2c);
> -- 
> 2.10.2
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Glauber Nov. 9, 2016, 1:41 p.m. UTC | #2
Hi Paul,

I think we should revert commit "70121f7 i2c: octeon: thunderx: Limit
register access retries". With debugging enabled I'm getting:

[   78.871568] ipmi_ssif: Trying hotmod-specified SSIF interface at i2c address 0x12, adapter Cavium ThunderX i2c adapter at 0000:01:09.4, slave address 0x0
[   78.886107] do not call blocking ops when !TASK_RUNNING; state=2 set at [<fffffc00080e0088>] prepare_to_wait_event+0x58/0x10c
[   78.897436] ------------[ cut here ]------------
[   78.902050] WARNING: CPU: 6 PID: 2235 at kernel/sched/core.c:7718 __might_sleep+0x80/0x88
[   78.910218] Modules linked in: ipmi_ssif i2c_thunderx i2c_smbus nicvf nicpf thunder_bgx thunder_xcv mdio_thunder

[   78.921916] CPU: 6 PID: 2235 Comm: bash Tainted: G        W       4.9.0-rc3-jang+ #17
[   78.929737] Hardware name: www.cavium.com ThunderX CRB-1S/ThunderX CRB-1S, BIOS 0.3 Aug 24 2016
[   78.938426] task: fffffe1fdd554500 task.stack: fffffe1fe384c000
[   78.944338] PC is at __might_sleep+0x80/0x88
[   78.948601] LR is at __might_sleep+0x80/0x88
[   78.952863] pc : [<fffffc00080c3aac>] lr : [<fffffc00080c3aac>] pstate: 80000145
[   78.960250] sp : fffffe1fe384f600
[   78.963557] x29: fffffe1fe384f600 x28: fffffe1fe384f860
[   78.968875] x27: fffffe1fd07fa018 x26: fffffe1fe384f968
[   78.974193] x25: fffffc0009a2b000 x24: 00000000ffff26d6
[   78.979510] x23: fffffe1fe384f860 x22: fffffe1fe384f860
[   78.984827] x21: 0000000000000000 x20: 00000000000000b1
[   78.990144] x19: fffffc0000e330b8 x18: 0000000000005bb0
[   78.995461] x17: fffffc0009669ca8 x16: 0000000000000000
[   79.000779] x15: 0000000000000539 x14: 66663c5b20746120
[   79.006097] x13: 74657320323d6574 x12: 617473203b474e49
[   79.011415] x11: 4e4e55525f4b5341 x10: 5421206e65687720
[   79.016732] x9 : 73706f20676e696b x8 : 0000000000000000
[   79.022049] x7 : fffffc00080f5740 x6 : fffffc00080f5740
[   79.027367] x5 : ffffffffffffff80 x4 : 0000000000000060
[   79.032684] x3 : 0000000000000000 x2 : 0000000000000001
[   79.038001] x1 : 0000000000000000 x0 : 0000000000000071

[   79.044803] ---[ end trace d8af6005f683d444 ]---
[   79.049413] Call trace:
[   79.051853] Exception stack(0xfffffe1fe384f420 to 0xfffffe1fe384f550)
[   79.058287] f420: fffffc0000e330b8 0000040000000000 fffffe1fe384f600 fffffc00080c3aac
[   79.066109] f440: 0000000080000145 000000000000003d 0000000000000000 fffffc0008853920
[   79.073931] f460: 0000040000000000 0000000100000001 fffffe1fe384f520 fffffc00080f60d8
[   79.081752] f480: fffffc0000e330b8 00000000000000b1 0000000000000000 fffffe1fe384f860
[   79.089574] f4a0: fffffe1fe384f860 00000000ffff26d6 fffffc0009a2b000 fffffe1fe384f968
[   79.097396] f4c0: fffffe1fd07fa018 fffffe1fe384f860 0000000000000071 0000000000000000
[   79.105218] f4e0: 0000000000000001 0000000000000000 0000000000000060 ffffffffffffff80
[   79.113040] f500: fffffc00080f5740 fffffc00080f5740 0000000000000000 73706f20676e696b
[   79.120861] f520: 5421206e65687720 4e4e55525f4b5341 617473203b474e49 74657320323d6574
[   79.128683] f540: 66663c5b20746120 0000000000000539
[   79.133553] [<fffffc00080c3aac>] __might_sleep+0x80/0x88
[   79.138862] [<fffffc0000e30138>] octeon_i2c_test_iflg+0x4c/0xbc [i2c_thunderx]
[   79.146077] [<fffffc0000e30958>] octeon_i2c_test_ready+0x18/0x70 [i2c_thunderx]
[   79.153379] [<fffffc0000e30b04>] octeon_i2c_wait+0x154/0x1a4 [i2c_thunderx]
[   79.160334] [<fffffc0000e310bc>] octeon_i2c_xfer+0xf4/0xf60 [i2c_thunderx]

This is not caused by the usleep inside the wait_event but by readq_poll_timeout().
Could you try if it works for you if you only revert this patch?

Thanks,
Jan
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Burton Nov. 9, 2016, 2:07 p.m. UTC | #3
On Wednesday, 9 November 2016 14:41:03 GMT Jan Glauber wrote:
> Hi Paul,
> 
> I think we should revert commit "70121f7 i2c: octeon: thunderx: Limit
> register access retries". With debugging enabled I'm getting:
> 
> <snip>
> 
> This is not caused by the usleep inside the wait_event but by
> readq_poll_timeout(). Could you try if it works for you if you only revert
> this patch?
> 
> Thanks,
> Jan

Hi Jan,

If I drop both my patches & just revert 70121f7f3725 ("i2c: octeon: thunderx: 
Limit register access retries") sadly it doesn't fix my system. A boot of a 
cavium_octeon_defconfig kernel with initcall_debug ends with:

  calling  octeon_mgmt_mod_init+0x0/0x28 @ 1
  initcall octeon_mgmt_mod_init+0x0/0x28 returned 0 after 67 usecs
  calling  ds1307_driver_init+0x0/0x10 @ 1
  initcall ds1307_driver_init+0x0/0x10 returned 0 after 19 usecs
  calling  octeon_i2c_driver_init+0x0/0x10 @ 1
  ata1: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
  ata1.00: ATA-9: SanDisk SDSSDA240G, Z22000RL, max UDMA/133
  ata1.00: 468862128 sectors, multi 1: LBA48 NCQ (depth 31/32)
  ata1.00: configured for UDMA/133
  scsi 0:0:0:0: Direct-Access     ATA      SanDisk SDSSDA24 00RL PQ: 0 ANSI: 5
  sd 0:0:0:0: [sda] 468862128 512-byte logical blocks: (240 GB/224 GiB)
  sd 0:0:0:0: [sda] Write Protect is off
  sd 0:0:0:0: [sda] Mode Sense: 00 3a 00 00
  sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support 
DPO or FUA
   sda: sda1 sda2 sda3 sda4
  sd 0:0:0:0: [sda] Attached SCSI disk
  ata2: SATA link down (SStatus 0 SControl 300)
  random: crng init done

As you can see octeon_i2c_driver_init never returns. Are you able to test on 
one of your MIPS-based systems?

Thanks,
    Paul
Jan Glauber Nov. 9, 2016, 2:38 p.m. UTC | #4
On Wed, Nov 09, 2016 at 02:07:58PM +0000, Paul Burton wrote:
> On Wednesday, 9 November 2016 14:41:03 GMT Jan Glauber wrote:
> > Hi Paul,
> > 
> > I think we should revert commit "70121f7 i2c: octeon: thunderx: Limit
> > register access retries". With debugging enabled I'm getting:
> > 
> > <snip>
> > 
> > This is not caused by the usleep inside the wait_event but by
> > readq_poll_timeout(). Could you try if it works for you if you only revert
> > this patch?
> > 
> > Thanks,
> > Jan
> 
> Hi Jan,
> 
> If I drop both my patches & just revert 70121f7f3725 ("i2c: octeon: thunderx: 
> Limit register access retries") sadly it doesn't fix my system. A boot of a 
> cavium_octeon_defconfig kernel with initcall_debug ends with:
> 
>   calling  octeon_mgmt_mod_init+0x0/0x28 @ 1
>   initcall octeon_mgmt_mod_init+0x0/0x28 returned 0 after 67 usecs
>   calling  ds1307_driver_init+0x0/0x10 @ 1
>   initcall ds1307_driver_init+0x0/0x10 returned 0 after 19 usecs
>   calling  octeon_i2c_driver_init+0x0/0x10 @ 1
>   ata1: SATA link up 6.0 Gbps (SStatus 133 SControl 300)
>   ata1.00: ATA-9: SanDisk SDSSDA240G, Z22000RL, max UDMA/133
>   ata1.00: 468862128 sectors, multi 1: LBA48 NCQ (depth 31/32)
>   ata1.00: configured for UDMA/133
>   scsi 0:0:0:0: Direct-Access     ATA      SanDisk SDSSDA24 00RL PQ: 0 ANSI: 5
>   sd 0:0:0:0: [sda] 468862128 512-byte logical blocks: (240 GB/224 GiB)
>   sd 0:0:0:0: [sda] Write Protect is off
>   sd 0:0:0:0: [sda] Mode Sense: 00 3a 00 00
>   sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support 
> DPO or FUA
>    sda: sda1 sda2 sda3 sda4
>   sd 0:0:0:0: [sda] Attached SCSI disk
>   ata2: SATA link down (SStatus 0 SControl 300)
>   random: crng init done
> 
> As you can see octeon_i2c_driver_init never returns. Are you able to test on 
> one of your MIPS-based systems?

CC'ing Steven who might be able to test on MIPS.

> Thanks,
>     Paul


--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c
index 419b54b..2e270a1 100644
--- a/drivers/i2c/busses/i2c-octeon-core.c
+++ b/drivers/i2c/busses/i2c-octeon-core.c
@@ -36,24 +36,6 @@  static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c)
 	return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG);
 }
 
-static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first)
-{
-	if (octeon_i2c_test_iflg(i2c))
-		return true;
-
-	if (*first) {
-		*first = false;
-		return false;
-	}
-
-	/*
-	 * IRQ has signaled an event but IFLG hasn't changed.
-	 * Sleep and retry once.
-	 */
-	usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
-	return octeon_i2c_test_iflg(i2c);
-}
-
 /**
  * octeon_i2c_wait - wait for the IFLG to be set
  * @i2c: The struct octeon_i2c
@@ -80,8 +62,13 @@  static int octeon_i2c_wait(struct octeon_i2c *i2c)
 	}
 
 	i2c->int_enable(i2c);
-	time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first),
-				       i2c->adap.timeout);
+	time_left = i2c->adap.timeout;
+	if (!octeon_i2c_test_iflg(i2c)) {
+		usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
+		time_left = wait_event_timeout(i2c->queue,
+					       octeon_i2c_test_iflg(i2c),
+					       time_left);
+	}
 	i2c->int_disable(i2c);
 
 	if (i2c->broken_irq_check && !time_left &&
@@ -99,26 +86,8 @@  static int octeon_i2c_wait(struct octeon_i2c *i2c)
 
 static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c)
 {
-	return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
-}
-
-static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first)
-{
 	/* check if valid bit is cleared */
-	if (octeon_i2c_hlc_test_valid(i2c))
-		return true;
-
-	if (*first) {
-		*first = false;
-		return false;
-	}
-
-	/*
-	 * IRQ has signaled an event but valid bit isn't cleared.
-	 * Sleep and retry once.
-	 */
-	usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
-	return octeon_i2c_hlc_test_valid(i2c);
+	return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0;
 }
 
 static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c)
@@ -176,7 +145,6 @@  static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c)
  */
 static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
 {
-	bool first = true;
 	int time_left;
 
 	/*
@@ -194,9 +162,13 @@  static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c)
 	}
 
 	i2c->hlc_int_enable(i2c);
-	time_left = wait_event_timeout(i2c->queue,
-				       octeon_i2c_hlc_test_ready(i2c, &first),
-				       i2c->adap.timeout);
+	time_left = i2c->adap.timeout;
+	if (!octeon_i2c_hlc_test_valid(i2c)) {
+		usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT);
+		time_left = wait_event_timeout(i2c->queue,
+					       octeon_i2c_hlc_test_valid(i2c),
+					       time_left);
+	}
 	i2c->hlc_int_disable(i2c);
 	if (!time_left)
 		octeon_i2c_hlc_int_clear(i2c);