So, after revisiting old mail threads and taking part in a similar discussion
on the USB list, here is what I cooked up to document and ease DMA handling for
I2C within Linux. Please have a look at the documentation introduced in patch 2
for further details.
All patches have been tested with a Renesas Salvator-X board (r8a7796/M3-W) and
a Renesas Lager board (r8a7790/H2). A more detailed test description can be
found here: http://elinux.org/Tests:I2C-core-DMA
The branch can be found here:
git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/topic/i2c-core-dma-v3
And big kudos to Renesas Electronics for funding this work, thank you very much!
Regards,
Wolfram
Changes since v2:
* rebased to v4.13-rc1
* helper functions are not inlined anymore but moved to i2c core
* __must_check has been added to the buffer check helper
* the release function has been renamed to contain 'dma' as well
* documentation updates. Hopefully better wording now
* removed the doubled Signed-offs
* adding more potentially interested parties to CC
Wolfram Sang (4):
i2c: add helpers to ease DMA handling
i2c: add docs to clarify DMA handling
i2c: sh_mobile: use helper to decide if DMA is useful
i2c: rcar: check for DMA-capable buffers
Documentation/i2c/DMA-considerations | 38 ++++++++++++++++++++
drivers/i2c/busses/i2c-rcar.c | 18 +++++++---
drivers/i2c/busses/i2c-sh_mobile.c | 8 +++--
drivers/i2c/i2c-core-base.c | 68 ++++++++++++++++++++++++++++++++++++
include/linux/i2c.h | 5 +++
5 files changed, 130 insertions(+), 7 deletions(-)
create mode 100644 Documentation/i2c/DMA-considerations
--
2.11.0
Signed-off-by: Wolfram Sang <[email protected]>
---
Changes since v2:
* documentation updates. Hopefully better wording now
Documentation/i2c/DMA-considerations | 38 ++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 Documentation/i2c/DMA-considerations
diff --git a/Documentation/i2c/DMA-considerations b/Documentation/i2c/DMA-considerations
new file mode 100644
index 00000000000000..e46c24d65c8556
--- /dev/null
+++ b/Documentation/i2c/DMA-considerations
@@ -0,0 +1,38 @@
+Linux I2C and DMA
+-----------------
+
+Given that I2C is a low-speed bus where largely small messages are transferred,
+it is not considered a prime user of DMA access. At this time of writing, only
+10% of I2C bus master drivers have DMA support implemented. And the vast
+majority of transactions are so small that setting up DMA for it will likely
+add more overhead than a plain PIO transfer.
+
+Therefore, it is *not* mandatory that the buffer of an I2C message is DMA safe.
+It does not seem reasonable to apply additional burdens when the feature is so
+rarely used. However, it is recommended to use a DMA-safe buffer if your
+message size is likely applicable for DMA. Most drivers have this threshold
+around 8 bytes. As of today, this is mostly an educated guess, however.
+
+To support this scenario, drivers wishing to implement DMA can use helper
+functions from the I2C core. One checks if a message is DMA capable in terms of
+size and memory type. It can optionally also create a bounce buffer:
+
+ i2c_check_msg_for_dma(msg, threshold, &bounce_buf);
+
+The bounce buffer handling from the core is generic and simple. It will always
+allocate a new bounce buffer. If you want a more sophisticated handling (e.g.
+reusing pre-allocated buffers), you can leave the pointer to the bounce buffer
+empty and implement your own handling based on the return value of the above
+function.
+
+The other helper function releases the bounce buffer. It ensures data is copied
+back to the message:
+
+ i2c_release_dma_bounce_buf(msg, bounce_buf);
+
+Please check the in-kernel documentation for details. The i2c-sh_mobile driver
+can be used as a reference example.
+
+If you plan to use DMA with I2C (or with any other bus, actually) make sure you
+have CONFIG_DMA_API_DEBUG enabled during development. It can help you find
+various issues which can be complex to debug otherwise.
--
2.11.0
One helper checks if DMA is suitable and optionally creates a bounce
buffer, if not. The other function returns the bounce buffer and makes
sure the data is properly copied back to the message.
Signed-off-by: Wolfram Sang <[email protected]>
---
Changes since v2:
* rebased to v4.13-rc1
* helper functions are not inlined anymore but moved to i2c core
* __must_check has been added to the buffer check helper
* the release function has been renamed to contain 'dma' as well
drivers/i2c/i2c-core-base.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/i2c.h | 5 ++++
2 files changed, 73 insertions(+)
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index c89dac7fd2e7b7..7326a9d2e4eb69 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -34,6 +34,7 @@
#include <linux/irqflags.h>
#include <linux/jump_label.h>
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
@@ -44,6 +45,7 @@
#include <linux/pm_wakeirq.h>
#include <linux/property.h>
#include <linux/rwsem.h>
+#include <linux/sched/task_stack.h>
#include <linux/slab.h>
#include "i2c-core.h"
@@ -2240,6 +2242,72 @@ void i2c_put_adapter(struct i2c_adapter *adap)
}
EXPORT_SYMBOL(i2c_put_adapter);
+/**
+ * i2c_check_msg_for_dma() - check if a message is suitable for DMA
+ * @msg: the message to be checked
+ * @threshold: the amount of byte from which using DMA makes sense
+ * @ptr_for_bounce_buf: if not NULL, a bounce buffer will be attached to this
+ * ptr, if needed. The bounce buffer must be freed by the
+ * caller using i2c_release_dma_bounce_buf().
+ *
+ * Return: -ERANGE if message is smaller than threshold
+ * -EFAULT if message buffer is not DMA capable and no bounce buffer
+ * was requested
+ * -ENOMEM if a bounce buffer could not be created
+ * 0 if message is suitable for DMA
+ *
+ * The return value must be checked.
+ *
+ * Note: This function should only be called from process context! It uses
+ * helper functions which work on the 'current' task.
+ */
+int i2c_check_msg_for_dma(struct i2c_msg *msg, unsigned int threshold,
+ u8 **ptr_for_bounce_buf)
+{
+ if (ptr_for_bounce_buf)
+ *ptr_for_bounce_buf = NULL;
+
+ if (msg->len < threshold)
+ return -ERANGE;
+
+ if (!virt_addr_valid(msg->buf) || object_is_on_stack(msg->buf)) {
+ pr_debug("msg buffer to 0x%04x is not DMA safe%s\n", msg->addr,
+ ptr_for_bounce_buf ? ", trying bounce buffer" : "");
+ if (ptr_for_bounce_buf) {
+ if (msg->flags & I2C_M_RD)
+ *ptr_for_bounce_buf = kzalloc(msg->len, GFP_KERNEL);
+ else
+ *ptr_for_bounce_buf = kmemdup(msg->buf, msg->len,
+ GFP_KERNEL);
+ if (!*ptr_for_bounce_buf)
+ return -ENOMEM;
+ } else {
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2c_check_msg_for_dma);
+
+/**
+ * i2c_release_bounce_buf - copy data back from bounce buffer and release it
+ * @msg: the message to be copied back to
+ * @bounce_buf: the bounce buffer obtained from i2c_check_msg_for_dma().
+ * May be NULL.
+ */
+void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf)
+{
+ if (!bounce_buf)
+ return;
+
+ if (msg->flags & I2C_M_RD)
+ memcpy(msg->buf, bounce_buf, msg->len);
+
+ kfree(bounce_buf);
+}
+EXPORT_SYMBOL_GPL(i2c_release_bounce_buf);
+
MODULE_AUTHOR("Simon G. Vogl <[email protected]>");
MODULE_DESCRIPTION("I2C-Bus main module");
MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index 00ca5b86a753f8..ac02287b6c0d8f 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -766,6 +766,11 @@ static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
}
+int __must_check i2c_check_msg_for_dma(struct i2c_msg *msg, unsigned int threshold,
+ u8 **ptr_for_bounce_buf);
+
+void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf);
+
int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
/**
* module_i2c_driver() - Helper macro for registering a modular I2C driver
--
2.11.0
Handling this is special for this driver. Because the hardware needs to
initialize the next message in interrupt context, we cannot use the
i2c_check_msg_for_dma() directly. This helper only works reliably in
process context. So, we need to check during initial preparation of the
whole transfer and need to disable DMA completely for the whole transfer
once a message with a not-DMA-capable buffer is found.
Signed-off-by: Wolfram Sang <[email protected]>
---
drivers/i2c/busses/i2c-rcar.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index 93c1a54981df08..5d0e820d708853 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -111,8 +111,11 @@
#define ID_ARBLOST (1 << 3)
#define ID_NACK (1 << 4)
/* persistent flags */
+#define ID_P_NODMA (1 << 30)
#define ID_P_PM_BLOCKED (1 << 31)
-#define ID_P_MASK ID_P_PM_BLOCKED
+#define ID_P_MASK (ID_P_PM_BLOCKED | ID_P_NODMA)
+
+#define RCAR_DMA_THRESHOLD 8
enum rcar_i2c_type {
I2C_RCAR_GEN1,
@@ -358,8 +361,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
unsigned char *buf;
int len;
- /* Do not use DMA if it's not available or for messages < 8 bytes */
- if (IS_ERR(chan) || msg->len < 8)
+ if (IS_ERR(chan) || msg->len < RCAR_DMA_THRESHOLD || priv->flags & ID_P_NODMA)
return;
if (read) {
@@ -657,11 +659,15 @@ static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv,
struct i2c_msg *msg)
{
struct device *dev = rcar_i2c_priv_to_dev(priv);
- bool read;
+ bool read = msg->flags & I2C_M_RD;
struct dma_chan *chan;
enum dma_transfer_direction dir;
- read = msg->flags & I2C_M_RD;
+ /* we need to check here because we need the 'current' context */
+ if (i2c_check_msg_for_dma(msg, RCAR_DMA_THRESHOLD, NULL) == -EFAULT) {
+ dev_dbg(dev, "skipping DMA for this whole transfer\n");
+ priv->flags |= ID_P_NODMA;
+ }
chan = read ? priv->dma_rx : priv->dma_tx;
if (PTR_ERR(chan) != -EPROBE_DEFER)
@@ -740,6 +746,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
if (ret < 0 && ret != -ENXIO)
dev_err(dev, "error %d : %x\n", ret, priv->flags);
+ priv->flags &= ~ID_P_NODMA;
+
return ret;
}
--
2.11.0
This ensures that we fall back to PIO if the buffer is too small for DMA
being useful. Otherwise, we use DMA. A bounce buffer might be applied if
the original message buffer is not DMA safe
Signed-off-by: Wolfram Sang <[email protected]>
---
drivers/i2c/busses/i2c-sh_mobile.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
index 2e097d97d258bc..19f45bcd9b35ca 100644
--- a/drivers/i2c/busses/i2c-sh_mobile.c
+++ b/drivers/i2c/busses/i2c-sh_mobile.c
@@ -145,6 +145,7 @@ struct sh_mobile_i2c_data {
struct dma_chan *dma_rx;
struct scatterlist sg;
enum dma_data_direction dma_direction;
+ u8 *bounce_buf;
};
struct sh_mobile_dt_config {
@@ -548,6 +549,8 @@ static void sh_mobile_i2c_dma_callback(void *data)
pd->pos = pd->msg->len;
pd->stop_after_dma = true;
+ i2c_release_dma_bounce_buf(pd->msg, pd->bounce_buf);
+
iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
}
@@ -595,6 +598,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
struct dma_async_tx_descriptor *txdesc;
dma_addr_t dma_addr;
dma_cookie_t cookie;
+ u8 *dma_buf = pd->bounce_buf ?: pd->msg->buf;
if (PTR_ERR(chan) == -EPROBE_DEFER) {
if (read)
@@ -608,7 +612,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
if (IS_ERR(chan))
return;
- dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir);
+ dma_addr = dma_map_single(chan->device->dev, dma_buf, pd->msg->len, dir);
if (dma_mapping_error(chan->device->dev, dma_addr)) {
dev_dbg(pd->dev, "dma map failed, using PIO\n");
return;
@@ -665,7 +669,7 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
pd->pos = -1;
pd->sr = 0;
- if (pd->msg->len > 8)
+ if (i2c_check_msg_for_dma(pd->msg, 8, &pd->bounce_buf) == 0)
sh_mobile_i2c_xfer_dma(pd);
/* Enable all interrupts to begin with */
--
2.11.0
Hi Wolfram,
On 2017-07-18 12:23:37 +0200, Wolfram Sang wrote:
> Signed-off-by: Wolfram Sang <[email protected]>
Reviewed-by: Niklas S?derlund <[email protected]>
> ---
> Changes since v2:
>
> * documentation updates. Hopefully better wording now
>
> Documentation/i2c/DMA-considerations | 38 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
> create mode 100644 Documentation/i2c/DMA-considerations
>
> diff --git a/Documentation/i2c/DMA-considerations b/Documentation/i2c/DMA-considerations
> new file mode 100644
> index 00000000000000..e46c24d65c8556
> --- /dev/null
> +++ b/Documentation/i2c/DMA-considerations
> @@ -0,0 +1,38 @@
> +Linux I2C and DMA
> +-----------------
> +
> +Given that I2C is a low-speed bus where largely small messages are transferred,
> +it is not considered a prime user of DMA access. At this time of writing, only
> +10% of I2C bus master drivers have DMA support implemented. And the vast
> +majority of transactions are so small that setting up DMA for it will likely
> +add more overhead than a plain PIO transfer.
> +
> +Therefore, it is *not* mandatory that the buffer of an I2C message is DMA safe.
> +It does not seem reasonable to apply additional burdens when the feature is so
> +rarely used. However, it is recommended to use a DMA-safe buffer if your
> +message size is likely applicable for DMA. Most drivers have this threshold
> +around 8 bytes. As of today, this is mostly an educated guess, however.
> +
> +To support this scenario, drivers wishing to implement DMA can use helper
> +functions from the I2C core. One checks if a message is DMA capable in terms of
> +size and memory type. It can optionally also create a bounce buffer:
> +
> + i2c_check_msg_for_dma(msg, threshold, &bounce_buf);
> +
> +The bounce buffer handling from the core is generic and simple. It will always
> +allocate a new bounce buffer. If you want a more sophisticated handling (e.g.
> +reusing pre-allocated buffers), you can leave the pointer to the bounce buffer
> +empty and implement your own handling based on the return value of the above
> +function.
> +
> +The other helper function releases the bounce buffer. It ensures data is copied
> +back to the message:
> +
> + i2c_release_dma_bounce_buf(msg, bounce_buf);
> +
> +Please check the in-kernel documentation for details. The i2c-sh_mobile driver
> +can be used as a reference example.
> +
> +If you plan to use DMA with I2C (or with any other bus, actually) make sure you
> +have CONFIG_DMA_API_DEBUG enabled during development. It can help you find
> +various issues which can be complex to debug otherwise.
> --
> 2.11.0
>
--
Regards,
Niklas S?derlund
Hi Wolfram,
On 2017-07-18 12:23:36 +0200, Wolfram Sang wrote:
> One helper checks if DMA is suitable and optionally creates a bounce
> buffer, if not. The other function returns the bounce buffer and makes
> sure the data is properly copied back to the message.
>
> Signed-off-by: Wolfram Sang <[email protected]>
Reviewed-by: Niklas S?derlund <[email protected]>
> ---
> Changes since v2:
>
> * rebased to v4.13-rc1
> * helper functions are not inlined anymore but moved to i2c core
> * __must_check has been added to the buffer check helper
> * the release function has been renamed to contain 'dma' as well
>
> drivers/i2c/i2c-core-base.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c.h | 5 ++++
> 2 files changed, 73 insertions(+)
>
> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
> index c89dac7fd2e7b7..7326a9d2e4eb69 100644
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> @@ -34,6 +34,7 @@
> #include <linux/irqflags.h>
> #include <linux/jump_label.h>
> #include <linux/kernel.h>
> +#include <linux/mm.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> #include <linux/of_device.h>
> @@ -44,6 +45,7 @@
> #include <linux/pm_wakeirq.h>
> #include <linux/property.h>
> #include <linux/rwsem.h>
> +#include <linux/sched/task_stack.h>
> #include <linux/slab.h>
>
> #include "i2c-core.h"
> @@ -2240,6 +2242,72 @@ void i2c_put_adapter(struct i2c_adapter *adap)
> }
> EXPORT_SYMBOL(i2c_put_adapter);
>
> +/**
> + * i2c_check_msg_for_dma() - check if a message is suitable for DMA
> + * @msg: the message to be checked
> + * @threshold: the amount of byte from which using DMA makes sense
> + * @ptr_for_bounce_buf: if not NULL, a bounce buffer will be attached to this
> + * ptr, if needed. The bounce buffer must be freed by the
> + * caller using i2c_release_dma_bounce_buf().
> + *
> + * Return: -ERANGE if message is smaller than threshold
> + * -EFAULT if message buffer is not DMA capable and no bounce buffer
> + * was requested
> + * -ENOMEM if a bounce buffer could not be created
> + * 0 if message is suitable for DMA
> + *
> + * The return value must be checked.
> + *
> + * Note: This function should only be called from process context! It uses
> + * helper functions which work on the 'current' task.
> + */
> +int i2c_check_msg_for_dma(struct i2c_msg *msg, unsigned int threshold,
> + u8 **ptr_for_bounce_buf)
> +{
> + if (ptr_for_bounce_buf)
> + *ptr_for_bounce_buf = NULL;
> +
> + if (msg->len < threshold)
> + return -ERANGE;
> +
> + if (!virt_addr_valid(msg->buf) || object_is_on_stack(msg->buf)) {
> + pr_debug("msg buffer to 0x%04x is not DMA safe%s\n", msg->addr,
> + ptr_for_bounce_buf ? ", trying bounce buffer" : "");
> + if (ptr_for_bounce_buf) {
> + if (msg->flags & I2C_M_RD)
> + *ptr_for_bounce_buf = kzalloc(msg->len, GFP_KERNEL);
> + else
> + *ptr_for_bounce_buf = kmemdup(msg->buf, msg->len,
> + GFP_KERNEL);
> + if (!*ptr_for_bounce_buf)
> + return -ENOMEM;
> + } else {
> + return -EFAULT;
> + }
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(i2c_check_msg_for_dma);
> +
> +/**
> + * i2c_release_bounce_buf - copy data back from bounce buffer and release it
> + * @msg: the message to be copied back to
> + * @bounce_buf: the bounce buffer obtained from i2c_check_msg_for_dma().
> + * May be NULL.
> + */
> +void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf)
> +{
> + if (!bounce_buf)
> + return;
> +
> + if (msg->flags & I2C_M_RD)
> + memcpy(msg->buf, bounce_buf, msg->len);
> +
> + kfree(bounce_buf);
> +}
> +EXPORT_SYMBOL_GPL(i2c_release_bounce_buf);
> +
> MODULE_AUTHOR("Simon G. Vogl <[email protected]>");
> MODULE_DESCRIPTION("I2C-Bus main module");
> MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> index 00ca5b86a753f8..ac02287b6c0d8f 100644
> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -766,6 +766,11 @@ static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
> return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
> }
>
> +int __must_check i2c_check_msg_for_dma(struct i2c_msg *msg, unsigned int threshold,
> + u8 **ptr_for_bounce_buf);
> +
> +void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf);
> +
> int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
> /**
> * module_i2c_driver() - Helper macro for registering a modular I2C driver
> --
> 2.11.0
>
--
Regards,
Niklas S?derlund
Hi Wolfram,
On 2017-07-18 12:23:38 +0200, Wolfram Sang wrote:
> This ensures that we fall back to PIO if the buffer is too small for DMA
> being useful. Otherwise, we use DMA. A bounce buffer might be applied if
> the original message buffer is not DMA safe
>
> Signed-off-by: Wolfram Sang <[email protected]>
> ---
> drivers/i2c/busses/i2c-sh_mobile.c | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c
> index 2e097d97d258bc..19f45bcd9b35ca 100644
> --- a/drivers/i2c/busses/i2c-sh_mobile.c
> +++ b/drivers/i2c/busses/i2c-sh_mobile.c
> @@ -145,6 +145,7 @@ struct sh_mobile_i2c_data {
> struct dma_chan *dma_rx;
> struct scatterlist sg;
> enum dma_data_direction dma_direction;
> + u8 *bounce_buf;
> };
>
> struct sh_mobile_dt_config {
> @@ -548,6 +549,8 @@ static void sh_mobile_i2c_dma_callback(void *data)
> pd->pos = pd->msg->len;
> pd->stop_after_dma = true;
>
> + i2c_release_dma_bounce_buf(pd->msg, pd->bounce_buf);
> +
> iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE);
> }
>
> @@ -595,6 +598,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
> struct dma_async_tx_descriptor *txdesc;
> dma_addr_t dma_addr;
> dma_cookie_t cookie;
> + u8 *dma_buf = pd->bounce_buf ?: pd->msg->buf;
This looked funny and I had to look it up, I learnt something new today
:-)
>
> if (PTR_ERR(chan) == -EPROBE_DEFER) {
> if (read)
> @@ -608,7 +612,7 @@ static void sh_mobile_i2c_xfer_dma(struct sh_mobile_i2c_data *pd)
> if (IS_ERR(chan))
> return;
>
> - dma_addr = dma_map_single(chan->device->dev, pd->msg->buf, pd->msg->len, dir);
> + dma_addr = dma_map_single(chan->device->dev, dma_buf, pd->msg->len, dir);
> if (dma_mapping_error(chan->device->dev, dma_addr)) {
> dev_dbg(pd->dev, "dma map failed, using PIO\n");
> return;
> @@ -665,7 +669,7 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg,
> pd->pos = -1;
> pd->sr = 0;
>
> - if (pd->msg->len > 8)
> + if (i2c_check_msg_for_dma(pd->msg, 8, &pd->bounce_buf) == 0)
Maybe the 8 should be declared in a define to explain the value, like
you do in patch 4/4?
This nitpick aside:
Reviewed-by: Niklas S?derlund <[email protected]>
> sh_mobile_i2c_xfer_dma(pd);
>
> /* Enable all interrupts to begin with */
> --
> 2.11.0
>
--
Regards,
Niklas S?derlund
Hi Wolfram,
On 2017-07-18 12:23:39 +0200, Wolfram Sang wrote:
> Handling this is special for this driver. Because the hardware needs to
> initialize the next message in interrupt context, we cannot use the
> i2c_check_msg_for_dma() directly. This helper only works reliably in
> process context. So, we need to check during initial preparation of the
> whole transfer and need to disable DMA completely for the whole transfer
> once a message with a not-DMA-capable buffer is found.
>
> Signed-off-by: Wolfram Sang <[email protected]>
> ---
> drivers/i2c/busses/i2c-rcar.c | 18 +++++++++++++-----
> 1 file changed, 13 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
> index 93c1a54981df08..5d0e820d708853 100644
> --- a/drivers/i2c/busses/i2c-rcar.c
> +++ b/drivers/i2c/busses/i2c-rcar.c
> @@ -111,8 +111,11 @@
> #define ID_ARBLOST (1 << 3)
> #define ID_NACK (1 << 4)
> /* persistent flags */
> +#define ID_P_NODMA (1 << 30)
> #define ID_P_PM_BLOCKED (1 << 31)
> -#define ID_P_MASK ID_P_PM_BLOCKED
> +#define ID_P_MASK (ID_P_PM_BLOCKED | ID_P_NODMA)
> +
> +#define RCAR_DMA_THRESHOLD 8
>
> enum rcar_i2c_type {
> I2C_RCAR_GEN1,
> @@ -358,8 +361,7 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
> unsigned char *buf;
> int len;
>
> - /* Do not use DMA if it's not available or for messages < 8 bytes */
> - if (IS_ERR(chan) || msg->len < 8)
> + if (IS_ERR(chan) || msg->len < RCAR_DMA_THRESHOLD || priv->flags & ID_P_NODMA)
> return;
>
> if (read) {
> @@ -657,11 +659,15 @@ static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv,
> struct i2c_msg *msg)
> {
> struct device *dev = rcar_i2c_priv_to_dev(priv);
> - bool read;
> + bool read = msg->flags & I2C_M_RD;
> struct dma_chan *chan;
> enum dma_transfer_direction dir;
>
> - read = msg->flags & I2C_M_RD;
> + /* we need to check here because we need the 'current' context */
Maybe extend the comment here explaining that the check is primary here
to make sure the msg->buf is valid for DMA and that the bounce buffer
can't be used due to the special hardware feature? Else someone might be
tempted to try and enable the bounce buffer feature in the future?
Nitpicking aside:
Reviewed-by: Niklas S?derlund <[email protected]>
> + if (i2c_check_msg_for_dma(msg, RCAR_DMA_THRESHOLD, NULL) == -EFAULT) {
> + dev_dbg(dev, "skipping DMA for this whole transfer\n");
> + priv->flags |= ID_P_NODMA;
> + }
>
> chan = read ? priv->dma_rx : priv->dma_tx;
> if (PTR_ERR(chan) != -EPROBE_DEFER)
> @@ -740,6 +746,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
> if (ret < 0 && ret != -ENXIO)
> dev_err(dev, "error %d : %x\n", ret, priv->flags);
>
> + priv->flags &= ~ID_P_NODMA;
> +
> return ret;
> }
>
> --
> 2.11.0
>
--
Regards,
Niklas S?derlund
On Tue, 18 Jul 2017 12:23:36 +0200
Wolfram Sang <[email protected]> wrote:
> One helper checks if DMA is suitable and optionally creates a bounce
> buffer, if not. The other function returns the bounce buffer and makes
> sure the data is properly copied back to the message.
>
> Signed-off-by: Wolfram Sang <[email protected]>
My knowledge of the exact requirements for dma on all platforms isn't
all that good. Mostly it's based around the assumption that if one
forces a heap allocation and makes sure nothing else is in the
cacheline all is good.
I like the basic idea of this patch set a lot btw!
Jonathan
> ---
> Changes since v2:
>
> * rebased to v4.13-rc1
> * helper functions are not inlined anymore but moved to i2c core
> * __must_check has been added to the buffer check helper
> * the release function has been renamed to contain 'dma' as well
>
> drivers/i2c/i2c-core-base.c | 68 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/i2c.h | 5 ++++
> 2 files changed, 73 insertions(+)
>
> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
> index c89dac7fd2e7b7..7326a9d2e4eb69 100644
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> @@ -34,6 +34,7 @@
> #include <linux/irqflags.h>
> #include <linux/jump_label.h>
> #include <linux/kernel.h>
> +#include <linux/mm.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> #include <linux/of_device.h>
> @@ -44,6 +45,7 @@
> #include <linux/pm_wakeirq.h>
> #include <linux/property.h>
> #include <linux/rwsem.h>
> +#include <linux/sched/task_stack.h>
> #include <linux/slab.h>
>
> #include "i2c-core.h"
> @@ -2240,6 +2242,72 @@ void i2c_put_adapter(struct i2c_adapter *adap)
> }
> EXPORT_SYMBOL(i2c_put_adapter);
>
> +/**
> + * i2c_check_msg_for_dma() - check if a message is suitable for DMA
> + * @msg: the message to be checked
> + * @threshold: the amount of byte from which using DMA makes sense
> + * @ptr_for_bounce_buf: if not NULL, a bounce buffer will be attached to this
> + * ptr, if needed. The bounce buffer must be freed by the
> + * caller using i2c_release_dma_bounce_buf().
> + *
> + * Return: -ERANGE if message is smaller than threshold
> + * -EFAULT if message buffer is not DMA capable and no bounce buffer
> + * was requested
> + * -ENOMEM if a bounce buffer could not be created
> + * 0 if message is suitable for DMA
> + *
> + * The return value must be checked.
> + *
> + * Note: This function should only be called from process context! It uses
> + * helper functions which work on the 'current' task.
> + */
> +int i2c_check_msg_for_dma(struct i2c_msg *msg, unsigned int threshold,
> + u8 **ptr_for_bounce_buf)
> +{
> + if (ptr_for_bounce_buf)
> + *ptr_for_bounce_buf = NULL;
> +
> + if (msg->len < threshold)
> + return -ERANGE;
> +
> + if (!virt_addr_valid(msg->buf) || object_is_on_stack(msg->buf)) {
> + pr_debug("msg buffer to 0x%04x is not DMA safe%s\n", msg->addr,
> + ptr_for_bounce_buf ? ", trying bounce buffer" : "");
> + if (ptr_for_bounce_buf) {
> + if (msg->flags & I2C_M_RD)
> + *ptr_for_bounce_buf = kzalloc(msg->len, GFP_KERNEL);
> + else
> + *ptr_for_bounce_buf = kmemdup(msg->buf, msg->len,
> + GFP_KERNEL);
> + if (!*ptr_for_bounce_buf)
> + return -ENOMEM;
> + } else {
> + return -EFAULT;
> + }
> + }
I'm trying to get my head around whether this is a sufficient set of conditions
for a dma safe buffer and I'm not sure it is.
We go to a lot of effort with SPI buffers to ensure they are in their own cache
lines. Do we not need to do the same here? The issue would be the
classic case of embedding a buffer inside a larger structure which is on
the stack. Need the magic of __cacheline_aligned to force it into it's
own line for devices with dma-incoherent caches.
DMA-API-HOWTO.txt (not read that for a while at it is a rather good
at describing this stuff these days!)
So that one is easy enough to check by checking if it is cache line aligned,
but what do you do to know there is nothing much after it?
Not sure there is a way around this short of auditing i2c drivers to
change any that do this.
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(i2c_check_msg_for_dma);
> +
> +/**
> + * i2c_release_bounce_buf - copy data back from bounce buffer and release it
> + * @msg: the message to be copied back to
> + * @bounce_buf: the bounce buffer obtained from i2c_check_msg_for_dma().
> + * May be NULL.
> + */
> +void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf)
> +{
> + if (!bounce_buf)
> + return;
> +
> + if (msg->flags & I2C_M_RD)
> + memcpy(msg->buf, bounce_buf, msg->len);
> +
> + kfree(bounce_buf);
> +}
> +EXPORT_SYMBOL_GPL(i2c_release_bounce_buf);
> +
> MODULE_AUTHOR("Simon G. Vogl <[email protected]>");
> MODULE_DESCRIPTION("I2C-Bus main module");
> MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c.h b/include/linux/i2c.h
> index 00ca5b86a753f8..ac02287b6c0d8f 100644
> --- a/include/linux/i2c.h
> +++ b/include/linux/i2c.h
> @@ -766,6 +766,11 @@ static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
> return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
> }
>
> +int __must_check i2c_check_msg_for_dma(struct i2c_msg *msg, unsigned int threshold,
> + u8 **ptr_for_bounce_buf);
> +
> +void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf);
> +
> int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr);
> /**
> * module_i2c_driver() - Helper macro for registering a modular I2C driver
On Tue, 18 Jul 2017 12:23:37 +0200
Wolfram Sang <[email protected]> wrote:
> Signed-off-by: Wolfram Sang <[email protected]>
Is this material not perhaps better placed in the sphinx docs?
Up to you of course as your subsystem ;)
Text is good though.
Acked-by: Jonathan Cameron <[email protected]>
> ---
> Changes since v2:
>
> * documentation updates. Hopefully better wording now
>
> Documentation/i2c/DMA-considerations | 38 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
> create mode 100644 Documentation/i2c/DMA-considerations
>
> diff --git a/Documentation/i2c/DMA-considerations b/Documentation/i2c/DMA-considerations
> new file mode 100644
> index 00000000000000..e46c24d65c8556
> --- /dev/null
> +++ b/Documentation/i2c/DMA-considerations
> @@ -0,0 +1,38 @@
> +Linux I2C and DMA
> +-----------------
> +
> +Given that I2C is a low-speed bus where largely small messages are transferred,
> +it is not considered a prime user of DMA access. At this time of writing, only
> +10% of I2C bus master drivers have DMA support implemented. And the vast
> +majority of transactions are so small that setting up DMA for it will likely
> +add more overhead than a plain PIO transfer.
> +
> +Therefore, it is *not* mandatory that the buffer of an I2C message is DMA safe.
> +It does not seem reasonable to apply additional burdens when the feature is so
> +rarely used. However, it is recommended to use a DMA-safe buffer if your
> +message size is likely applicable for DMA. Most drivers have this threshold
> +around 8 bytes. As of today, this is mostly an educated guess, however.
> +
> +To support this scenario, drivers wishing to implement DMA can use helper
> +functions from the I2C core. One checks if a message is DMA capable in terms of
> +size and memory type. It can optionally also create a bounce buffer:
> +
> + i2c_check_msg_for_dma(msg, threshold, &bounce_buf);
> +
> +The bounce buffer handling from the core is generic and simple. It will always
> +allocate a new bounce buffer. If you want a more sophisticated handling (e.g.
> +reusing pre-allocated buffers), you can leave the pointer to the bounce buffer
> +empty and implement your own handling based on the return value of the above
> +function.
> +
> +The other helper function releases the bounce buffer. It ensures data is copied
> +back to the message:
> +
> + i2c_release_dma_bounce_buf(msg, bounce_buf);
> +
> +Please check the in-kernel documentation for details. The i2c-sh_mobile driver
> +can be used as a reference example.
> +
> +If you plan to use DMA with I2C (or with any other bus, actually) make sure you
> +have CONFIG_DMA_API_DEBUG enabled during development. It can help you find
> +various issues which can be complex to debug otherwise.
Hi Wolfram,
On Tue, Jul 18, 2017 at 12:23 PM, Wolfram Sang
<[email protected]> wrote:
> One helper checks if DMA is suitable and optionally creates a bounce
> buffer, if not. The other function returns the bounce buffer and makes
> sure the data is properly copied back to the message.
>
> Signed-off-by: Wolfram Sang <[email protected]>
> ---
> Changes since v2:
>
> * rebased to v4.13-rc1
> * helper functions are not inlined anymore but moved to i2c core
> * __must_check has been added to the buffer check helper
> * the release function has been renamed to contain 'dma' as well
Right:
drivers/i2c/i2c-core-base.c:2310:15: error: 'i2c_release_bounce_buf'
undeclared here (not in a function)
EXPORT_SYMBOL_GPL(i2c_release_bounce_buf);
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> +/**
> + * i2c_release_bounce_buf - copy data back from bounce buffer and release it
^^^^^^^^^^^^^^^^^^^^^^
> + * @msg: the message to be copied back to
> + * @bounce_buf: the bounce buffer obtained from i2c_check_msg_for_dma().
> + * May be NULL.
> + */
> +void i2c_release_dma_bounce_buf(struct i2c_msg *msg, u8 *bounce_buf)
> +{
> + if (!bounce_buf)
> + return;
> +
> + if (msg->flags & I2C_M_RD)
> + memcpy(msg->buf, bounce_buf, msg->len);
> +
> + kfree(bounce_buf);
> +}
> +EXPORT_SYMBOL_GPL(i2c_release_bounce_buf);
^^^^^^^^^^^^^^^^^^^^^^
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
> Right:
>
> drivers/i2c/i2c-core-base.c:2310:15: error: 'i2c_release_bounce_buf'
> undeclared here (not in a function)
> EXPORT_SYMBOL_GPL(i2c_release_bounce_buf);
Thanks. I am just now working on V4 currently which is a redesign.
I'll write more in an hour or so.
Hi Jonathan,
> I like the basic idea of this patch set a lot btw!
Thanks!
> Jonathan
Could you delete irrelevant parts of the message, please? I nearly
missed your other comment which would have been a great loss!
> I'm trying to get my head around whether this is a sufficient set of conditions
> for a dma safe buffer and I'm not sure it is.
>
> We go to a lot of effort with SPI buffers to ensure they are in their own cache
> lines. Do we not need to do the same here? The issue would be the
> classic case of embedding a buffer inside a larger structure which is on
> the stack. Need the magic of __cacheline_aligned to force it into it's
> own line for devices with dma-incoherent caches.
> DMA-API-HOWTO.txt (not read that for a while at it is a rather good
> at describing this stuff these days!)
>
> So that one is easy enough to check by checking if it is cache line aligned,
> but what do you do to know there is nothing much after it?
Thank you, really!
This patch series addressed all cases I have created, but I also
wondered if there are cases left which I missed. Now, also because you
mentioned cacheline alignments, the faint idea of "maybe it could be
fragile" became a strong "it is too fragile"! lib/dma-debug.c is >40KB
for a reason. I just rewrote this series...
> Not sure there is a way around this short of auditing i2c drivers to
> change any that do this.
... to do something like this, more precisely: an opt-in approach. I
introduce a new flag for i2c_msg, namely I2C_M_DMA_SAFE which can opt-in
DMA if we know the i2c_msg buffer is DMA safe. I finished a
proof-of-concept this evening and pushed it here:
git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/topic/i2c-core-dma-rfc-v4
I will test it on HW tomorrow and send it out, then. There are still
some bits missing, but for getting opinions, it should be good enough.
Thanks and kind regards,
Wolfram