Skip to contentSkip to conversation

i2c: omap: ensure writes to dev->buf_len are ordered

PATCH New Felipe Balbi 6 participants 14 messages Oct 25, 2012 – Nov 14, 2012

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

 drivers/i2c/busses/i2c-omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +1 -1
index db31eae..1ec4e6e 100644
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
 	dev->buf = msg->buf;
 	dev->buf_len = msg->len;
+	wmb();
 
 	omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 
@@ -579,7 +580,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	 */
 	timeout = wait_for_completion_timeout(&dev->cmd_complete,
 						OMAP_I2C_TIMEOUT);
-	dev->buf_len = 0;
 	if (timeout == 0) {
 		dev_err(dev->dev, "controller timed out\n");
 		omap_i2c_init(dev);
Signature · 1 line
1.8.0
#1 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Quoted 17 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

Quoted 8 lines

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

looks good to me

Quoted 39 lines

---

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

 drivers/i2c/busses/i2c-omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +1 -2
index db31eae..1ec4e6e 100644
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
        /* REVISIT: Could the STB bit of I2C_CON be used with probing? */
        dev->buf = msg->buf;
        dev->buf_len = msg->len;
+       wmb();
 
        omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 
@@ -579,7 +580,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
         */
        timeout = wait_for_completion_timeout(&dev->cmd_complete,
                                                OMAP_I2C_TIMEOUT);
-       dev->buf_len = 0;
        if (timeout == 0) {
                dev_err(dev->dev, "controller timed out\n");
                omap_i2c_init(dev);
--

1.8.0

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

#2 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

In reply to Felipe Balbi

+Paul

Quoted 3 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

Any chance this would help with the bug Paul found with I2C timeouts on beagle userspace startup?

Kevin

Quoted 53 lines

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

 drivers/i2c/busses/i2c-omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +1 -1
index db31eae..1ec4e6e 100644
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
 	dev->buf = msg->buf;
 	dev->buf_len = msg->len;
+	wmb();
 
 	omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 
@@ -579,7 +580,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	 */
 	timeout = wait_for_completion_timeout(&dev->cmd_complete,
 						OMAP_I2C_TIMEOUT);
-	dev->buf_len = 0;
 	if (timeout == 0) {
 		dev_err(dev->dev, "controller timed out\n");
 		omap_i2c_init(dev);
#3 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Hi,

Quoted 10 lines · Kevin Hilman

+Paul

Quoted 3 lines · Felipe Balbi

if we allow compiler reorder our writes, we could
fall into a situation where dev->buf_len is reset
for no apparent reason.

Any chance this would help with the bug Paul found with I2C timeouts on beagle userspace startup?

I replied to the original thread asking the same ;-)

Signature · 8 lines
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121025/8550556d/attachment-0001.sig>
#4 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

In reply to Felipe Balbi

Hi Felipe

just two quick comments

Quoted 42 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

 drivers/i2c/busses/i2c-omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +0 -0
index db31eae..1ec4e6e 100644

drivers/i2c/busses/i2c-omap.c static int omap_i2c_xfer_msg(struct i2c_adapter *adap, · line 527

Quoted 7 lines
drivers/i2c/busses/i2c-omap.c +1 -0
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
 	dev->buf = msg->buf;
 	dev->buf_len = msg->len;
+	wmb();
 
 	omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 

Would suggest moving the wmb() immediately before the point at which the interrupt can occur. Looks to me that's when the OMAP_I2C_CON_REG write occurs.

Also would suggest adding a comment to clarify what the wmb() is intended to do. Maybe something like 'Prevent the compiler from moving earlier changes to dev->buf and dev->buf_len after the write to CON_REG. This write enables interrupts and those variables are used in the interrupt handler'.

checkpatch is supposed to flag uncommented barriers, but maybe that's only with --strict.

# check for memory barriers without a comment.
		if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) {
			if (!ctx_has_comment($first_line, $linenr)) {
				CHK("MEMORY_BARRIER",
				    "memory barrier without comment\n" . 
$herecurr);
			}
		}

- Paul

#5 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Quoted 60 lines · Paul Walmsley

Hi Felipe

just two quick comments

Quoted 42 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

  drivers/i2c/busses/i2c-omap.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +0 -0
index db31eae..1ec4e6e 100644

drivers/i2c/busses/i2c-omap.c static int omap_i2c_xfer_msg(struct i2c_adapter *adap, · line 527

Quoted 7 lines
drivers/i2c/busses/i2c-omap.c +1 -0
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
  	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
  	dev->buf = msg->buf;
  	dev->buf_len = msg->len;
+	wmb();
 
  	omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 

Would suggest moving the wmb() immediately before the point at which the interrupt can occur. Looks to me that's when the OMAP_I2C_CON_REG write occurs.

Quoted 6 lines

Also would suggest adding a comment to clarify what the wmb() is intended to do. Maybe something like 'Prevent the compiler from moving earlier changes to dev->buf and dev->buf_len after the write to CON_REG. This write enables interrupts and those variables are used in the interrupt handler'.

Another alternative, which I will recommend to just make use of the read*/wrire* instead __raw versions. The barriers are taken care already and driver point of view, it is transparent.

-->

drivers/i2c/busses/i2c-omap.c +2 -2
index db31eae..0cd6365 100644
@@ -265,13 +265,13 @@ static const u8 reg_map_ip_v2[] = {
  static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
  				      int reg, u16 val)
  {
-	__raw_writew(val, i2c_dev->base +
+	writew(val, i2c_dev->base +
  			(i2c_dev->regs[reg] << i2c_dev->reg_shift));
  }
 
  static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
  {
-	return __raw_readw(i2c_dev->base +
+	return readw(i2c_dev->base +
  				(i2c_dev->regs[reg] << i2c_dev->reg_shift));
  }
 

Patch might be damaged because of copy paste.

Regards
Santosh

#6 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Quoted 3 lines · Santosh Shilimkar

Another alternative, which I will recommend to just make use of the read*/wrire* instead __raw versions. The barriers are taken care already and driver point of view, it is transparent.

Those barriers will disappear if CONFIG_ARM_DMA_MEM_BUFFERABLE is set to N, so that's probably not the right thing to do in this case. The barrier here isn't DMA-related, it's needed due to the design of the driver.

In fact the wmb() is probably overkill, since only a compiler reordering barrier is needed. It can probably just be barrier().

- Paul

#7 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Quoted 10 lines · Paul Walmsley
Quoted 3 lines · Santosh Shilimkar

Another alternative, which I will recommend to just make use of the read*/wrire* instead __raw versions. The barriers are taken care already and driver point of view, it is transparent.

Those barriers will disappear if CONFIG_ARM_DMA_MEM_BUFFERABLE is set to N, so that's probably not the right thing to do in this case. The barrier here isn't DMA-related, it's needed due to the design of the driver.

Good point.

Quoted 3 lines

In fact the wmb() is probably overkill, since only a compiler reordering barrier is needed. It can probably just be barrier().

I agree. Just barrier() is enough to avoid compiler re-ordering.

Regards
Santosh

#8 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

In reply to Felipe Balbi
Quoted 26 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

Quoted 7 lines

---

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

I would add this into 3.7, but what about the comments suggesting to use barrier()?

Signature · 9 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121101/928fba26/attachment.sig>
#9 lore raw

PATCH 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Hi,

Quoted 30 lines · Wolfram Sang
Quoted 29 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

Quoted 7 lines
Quoted 4 lines

This bug has been there forever, but it's quite annoying. I think it deserves being pushed upstream during this -rc cycle, but if Wolfram decides to wait until v3.8, I don't mind.

I would add this into 3.7, but what about the comments suggesting to use barrier()?

I was waiting for more comments, will respin the patch next week.

cheers

Signature · 8 lines
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121102/1206e9bb/attachment.sig>
#10 lore raw

PATCH v2 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

Changes since v1:
- use barrier() instead of wmb()

Note: this version was compile-tested only

 drivers/i2c/busses/i2c-omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +1 -1
index db31eae..ba03bec 100644
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
 	dev->buf = msg->buf;
 	dev->buf_len = msg->len;
+	barrier();
 
 	omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 
@@ -579,7 +580,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	 */
 	timeout = wait_for_completion_timeout(&dev->cmd_complete,
 						OMAP_I2C_TIMEOUT);
-	dev->buf_len = 0;
 	if (timeout == 0) {
 		dev_err(dev->dev, "controller timed out\n");
 		omap_i2c_init(dev);
Signature · 1 line
1.8.0
#11 lore raw

PATCH v2 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Quoted 42 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

Changes since v1:
- use barrier() instead of wmb()

Note: this version was compile-tested only

 drivers/i2c/busses/i2c-omap.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +0 -0
index db31eae..ba03bec 100644

drivers/i2c/busses/i2c-omap.c static int omap_i2c_xfer_msg(struct i2c_adapter *adap, · line 524

Quoted 4 lines
drivers/i2c/busses/i2c-omap.c +1 -0
@@ -521,6 +521,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
 	dev->buf = msg->buf;
 	dev->buf_len = msg->len;
+	barrier();

I agree adding a comment here is a good idea.

Signature · 9 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121114/5b44e45c/attachment.sig>
#12 lore raw

PATCH v3 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

---

Changes since v2:
- add a comment

Changes since v1:
- use barrier() instead of wmb()

 drivers/i2c/busses/i2c-omap.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/i2c/busses/i2c-omap.c +3 -1
index db31eae..e88fc26 100644
@@ -522,6 +522,9 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	dev->buf = msg->buf;
 	dev->buf_len = msg->len;
 
+	/* make sure writes to dev->buf_len are ordered */
+	barrier();
+
 	omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
 
 	/* Clear the FIFO Buffers */
@@ -579,7 +582,6 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	 */
 	timeout = wait_for_completion_timeout(&dev->cmd_complete,
 						OMAP_I2C_TIMEOUT);
-	dev->buf_len = 0;
 	if (timeout == 0) {
 		dev_err(dev->dev, "controller timed out\n");
 		omap_i2c_init(dev);
Signature · 1 line
1.8.0
Applied
#13 lore raw

PATCH v3 1/1 i2c: omap: ensure writes to dev->buf_len are ordered

Quoted 23 lines · Felipe Balbi

if we allow compiler reorder our writes, we could fall into a situation where dev->buf_len is reset for no apparent reason.

This bug was found with a simple script which would transfer data to an i2c client from 1 to 1024 bytes (a simple for loop), when we got to transfer sizes bigger than the fifo size, dev->buf_len was reset to zero before we had an oportunity to handle XDR Interrupt. Because dev->buf_len was zero, we entered omap_i2c_transmit_data() to transfer zero bytes, which would mean we would just silently exit omap_i2c_transmit_data() without actually writing anything to DATA register. That would cause XDR IRQ to trigger forever and we would never transfer the remaining bytes.

After adding the memory barrier, we also drop resetting dev->buf_len to zero in omap_i2c_xfer_msg() because both omap_i2c_transmit_data() and omap_i2c_receive_data() will act until dev->buf_len reaches zero, rendering the other write in omap_i2c_xfer_msg() redundant.

Quoted 4 lines

This patch has been tested with pandaboard for a few iterations of the script mentioned above.

Applied to for-current, thanks!

Signature · 9 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121114/0b0eb476/attachment.sig>