This document describes how to test that the Linux I2C core properly handles DMA buffers. The I2C IP cores of a Renesas R-Car M3-W serve as examples here.
- 1 Setup
- 2 Testing
- 3 Conclusion
Kernel Version and Configuration
Support for this feature is expected to land upstream in v4.13. It is currently available in a topic branch:
The following kernel options need to be set in addition to the defconfig:
Test were largely done on a
- Salvator-X/r8a7796 (R-Car M3-W SoC)
but also verified on a
- Lager/r8a7790 (R-Car H2 SoC)
Besides a busybox, the i2cdump and i2ctransfer utilities from the i2c-tools-package package are needed.
We use the following commands to to trigger DMA:
# SMBus block transfer -> msg buffer will be on stack i2cdump -y -f <bus> <addr> i # I2C block transfer -> msg buffer will be from heap i2ctransfer -y -f <bus> r32@<addr>
before this task
On the host, to get the base Kernel tree without the I2C core DMA improvements:
$ git checkout b41c5497755a66 Note: checking out 'b41c5497755a66'. ... HEAD is now at b41c5497755a66... Merge remote-tracking branch 'renesas/arm64-dt-for-v4.13' into renesas/topic/i2c-core-dma
On the target, with this Kernel, triggering an I2C access using SMBus block with DMA gives a stack dump:
# i2cdump -y -f 7 0x50 i ... rcar-dmac e6700000.dma-controller: DMA-API: device driver maps memory from stack [addr=ffffffc636997cb0] ------------[ cut here ]------------ WARNING: CPU: 0 PID: 877 at lib/dma-debug.c:1188 check_for_stack+0xb0/0x100 ...
This driver suffers from the same problem, yet DMA_API_DEBUG does not trigger. i2c-rcar maps new DMA buffers in interrupt context where likely a different task stack is in use. DMA_API_DEBUG can only detect stack usage from the current task. Still, the problem is present for i2c-rcar, too. The following patch makes it visible by temporarily mapping the buffer from the expected context:
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 8be3e6cb8fe686..6d10de8447066d 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -674,6 +674,9 @@ static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv, priv->dma_rx = chan; else priv->dma_tx = chan; + + dma_addr_t dma_addr = dma_map_single(chan->device->dev, msg->buf, msg->len, DMA_TO_DEVICE); + dma_unmap_single(chan->device->dev, dma_addr, msg->len, DMA_TO_DEVICE); } static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
# i2cdump -f -y 2 0x10 i ... rcar-dmac e7310000.dma-controller: DMA-API: device driver maps memory from stack [addr=ffffffc63683bcd8] ------------[ cut here ]------------ WARNING: CPU: 1 PID: 877 at lib/dma-debug.c:1188 check_for_stack+0xb0/0x100 ...
So, for both cases, DMA_API_DEBUG detected the usage of the stack which is strongly discouraged. Still, DMA was used as one could see from /proc/interrupts because DMA interrupt counters increased.
The topic branch fully applied is about improving the situation.
after this task
Now, the tests with the full branch applied:
The same command as before does not trigger a DMA_API_DEBUG warning anymore. The I2C core detected the stack usage and applied a bounce buffer as requested by the i2c-sh_mobile driver:
# i2cdump -y -f 7 0x50 i ...
If you enable CONFIG_DEBUG, you can see this message as well:
msg buffer to 0x0050 is not DMA safe, trying bounce buffer
The same command as before does not trigger a DMA_API_DEBUG warning anymore. The I2C core detected the stack usage and falls back to PIO as implemented in the i2c-rcar driver:
# i2cdump -f -y 2 0x10 i
If you enable CONFIG_DEBUG, you can see these messages as well:
msg buffer to 0x0010 is not DMA safe i2c-rcar e6510000.i2c: skipping DMA for this whole transfer
However, DMA still works with proper buffers:
# i2ctransfer -y 2 r32@0x10 0x07 0x3f 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0f 0x07 0x3f 0x20 0x20 0x55 0x05 0x07 0x0f 0x07 0x3f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
DMA was used as one could see from /proc/interrupts because DMA interrupt counters increased.
The previously unsafe DMA operations on stack memory are now avoided. The I2C core can now detect this and offer suitable alternatives to drivers, either bounce buffers or PIO fallbacks.
The above tests show that for I2C reads with buffers on the stack, the I2C drivers can use Linux I2C core helpers to detect improper DMA buffers and work around them. Further tests have been done with I2C writes, vmalloced buffers, and buffers being static in kernel memory. The results of these tests all have been successful. However, they are more complicated and, thus, skipped here for brevity, but are available if desired. Documentation about DMA handling within the Linux I2C core has been added to the kernel tree, as well.