spi: spidev: Align buffers for DMA
authorChristian Eggers <ceggers@arri.de>
Tue, 28 Jul 2020 10:08:32 +0000 (12:08 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Aug 2020 07:48:15 +0000 (09:48 +0200)
commit aa9e862d7d5bcecd4dca9f39e8b684b93dd84ee7 upstream.

Simply copying all xfers from userspace into one bounce buffer causes
alignment problems if the SPI controller uses DMA.

Ensure that all transfer data blocks within the rx and tx bounce buffers
are aligned for DMA (according to ARCH_KMALLOC_MINALIGN).

Alignment may increase the usage of the bounce buffers. In some cases,
the buffers may need to be increased using the "bufsiz" module
parameter.

Signed-off-by: Christian Eggers <ceggers@arri.de>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20200728100832.24788-1-ceggers@arri.de
Signed-off-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/spi/spidev.c

index 167047760d79adec1f7ec5e2762f1ec27f491ab1..e444e7cc6968d24934bed2ab43a8fa4af33bb6a8 100644 (file)
@@ -232,6 +232,11 @@ static int spidev_message(struct spidev_data *spidev,
        for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
                        n;
                        n--, k_tmp++, u_tmp++) {
+               /* Ensure that also following allocations from rx_buf/tx_buf will meet
+                * DMA alignment requirements.
+                */
+               unsigned int len_aligned = ALIGN(u_tmp->len, ARCH_KMALLOC_MINALIGN);
+
                k_tmp->len = u_tmp->len;
 
                total += k_tmp->len;
@@ -247,17 +252,17 @@ static int spidev_message(struct spidev_data *spidev,
 
                if (u_tmp->rx_buf) {
                        /* this transfer needs space in RX bounce buffer */
-                       rx_total += k_tmp->len;
+                       rx_total += len_aligned;
                        if (rx_total > bufsiz) {
                                status = -EMSGSIZE;
                                goto done;
                        }
                        k_tmp->rx_buf = rx_buf;
-                       rx_buf += k_tmp->len;
+                       rx_buf += len_aligned;
                }
                if (u_tmp->tx_buf) {
                        /* this transfer needs space in TX bounce buffer */
-                       tx_total += k_tmp->len;
+                       tx_total += len_aligned;
                        if (tx_total > bufsiz) {
                                status = -EMSGSIZE;
                                goto done;
@@ -267,7 +272,7 @@ static int spidev_message(struct spidev_data *spidev,
                                                (uintptr_t) u_tmp->tx_buf,
                                        u_tmp->len))
                                goto done;
-                       tx_buf += k_tmp->len;
+                       tx_buf += len_aligned;
                }
 
                k_tmp->cs_change = !!u_tmp->cs_change;
@@ -297,16 +302,16 @@ static int spidev_message(struct spidev_data *spidev,
                goto done;
 
        /* copy any rx data out of bounce buffer */
-       rx_buf = spidev->rx_buffer;
-       for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
+       for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
+                       n;
+                       n--, k_tmp++, u_tmp++) {
                if (u_tmp->rx_buf) {
                        if (copy_to_user((u8 __user *)
-                                       (uintptr_t) u_tmp->rx_buf, rx_buf,
+                                       (uintptr_t) u_tmp->rx_buf, k_tmp->rx_buf,
                                        u_tmp->len)) {
                                status = -EFAULT;
                                goto done;
                        }
-                       rx_buf += u_tmp->len;
                }
        }
        status = total;