nfp: core: allow 4-byte aligned accesses to Memory Units
authorJakub Kicinski <jakub.kicinski@netronome.com>
Tue, 22 May 2018 05:12:46 +0000 (22:12 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 May 2018 18:26:18 +0000 (14:26 -0400)
Current code doesn't enforce length requirements on 32bit accesses
with action NFP_CPP_ACTION_RW to memory units, but if the access
is only aligned to 4 bytes as well we will fall into the explicit
access case and error out.  Such accesses are correct, allow them
by lowering the width earlier.

While at it use a switch statement to improve readability.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/nfpcore/nfp6000_pcie.c

index a0e336bd1d85798bb295bdc1be538bd4efb67e38..749655c329b240021a34e99612412626c26e8855 100644 (file)
@@ -933,7 +933,6 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
        u32 *wrptr32 = kernel_vaddr;
        const u32 __iomem *rdptr32;
        int n, width;
-       bool is_64;
 
        priv = nfp_cpp_area_priv(area);
        rdptr64 = priv->iomem + offset;
@@ -943,10 +942,15 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
                return -EFAULT;
 
        width = priv->width.read;
-
        if (width <= 0)
                return -EINVAL;
 
+       /* MU reads via a PCIe2CPP BAR support 32bit (and other) lengths */
+       if (priv->target == (NFP_CPP_TARGET_MU & NFP_CPP_TARGET_ID_MASK) &&
+           priv->action == NFP_CPP_ACTION_RW &&
+           (offset % sizeof(u64) == 4 || length % sizeof(u64) == 4))
+               width = TARGET_WIDTH_32;
+
        /* Unaligned? Translate to an explicit access */
        if ((priv->offset + offset) & (width - 1))
                return nfp_cpp_explicit_read(nfp_cpp_area_cpp(area),
@@ -956,36 +960,29 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
                                             priv->offset + offset,
                                             kernel_vaddr, length, width);
 
-       is_64 = width == TARGET_WIDTH_64;
-
-       /* MU reads via a PCIe2CPP BAR supports 32bit (and other) lengths */
-       if (priv->target == (NFP_CPP_TARGET_ID_MASK & NFP_CPP_TARGET_MU) &&
-           priv->action == NFP_CPP_ACTION_RW)
-               is_64 = false;
+       if (WARN_ON(!priv->bar))
+               return -EFAULT;
 
-       if (is_64) {
-               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
-                       return -EINVAL;
-       } else {
+       switch (width) {
+       case TARGET_WIDTH_32:
                if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
                        return -EINVAL;
-       }
 
-       if (WARN_ON(!priv->bar))
-               return -EFAULT;
+               for (n = 0; n < length; n += sizeof(u32))
+                       *wrptr32++ = __raw_readl(rdptr32++);
+               return n;
+#ifdef __raw_readq
+       case TARGET_WIDTH_64:
+               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
+                       return -EINVAL;
 
-       if (is_64)
-#ifndef __raw_readq
-               return -EINVAL;
-#else
                for (n = 0; n < length; n += sizeof(u64))
                        *wrptr64++ = __raw_readq(rdptr64++);
+               return n;
 #endif
-       else
-               for (n = 0; n < length; n += sizeof(u32))
-                       *wrptr32++ = __raw_readl(rdptr32++);
-
-       return n;
+       default:
+               return -EINVAL;
+       }
 }
 
 static int
@@ -999,7 +996,6 @@ nfp6000_area_write(struct nfp_cpp_area *area,
        struct nfp6000_area_priv *priv;
        u32 __iomem *wrptr32;
        int n, width;
-       bool is_64;
 
        priv = nfp_cpp_area_priv(area);
        wrptr64 = priv->iomem + offset;
@@ -1009,10 +1005,15 @@ nfp6000_area_write(struct nfp_cpp_area *area,
                return -EFAULT;
 
        width = priv->width.write;
-
        if (width <= 0)
                return -EINVAL;
 
+       /* MU writes via a PCIe2CPP BAR support 32bit (and other) lengths */
+       if (priv->target == (NFP_CPP_TARGET_ID_MASK & NFP_CPP_TARGET_MU) &&
+           priv->action == NFP_CPP_ACTION_RW &&
+           (offset % sizeof(u64) == 4 || length % sizeof(u64) == 4))
+               width = TARGET_WIDTH_32;
+
        /* Unaligned? Translate to an explicit access */
        if ((priv->offset + offset) & (width - 1))
                return nfp_cpp_explicit_write(nfp_cpp_area_cpp(area),
@@ -1022,40 +1023,33 @@ nfp6000_area_write(struct nfp_cpp_area *area,
                                              priv->offset + offset,
                                              kernel_vaddr, length, width);
 
-       is_64 = width == TARGET_WIDTH_64;
-
-       /* MU writes via a PCIe2CPP BAR supports 32bit (and other) lengths */
-       if (priv->target == (NFP_CPP_TARGET_ID_MASK & NFP_CPP_TARGET_MU) &&
-           priv->action == NFP_CPP_ACTION_RW)
-               is_64 = false;
+       if (WARN_ON(!priv->bar))
+               return -EFAULT;
 
-       if (is_64) {
-               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
-                       return -EINVAL;
-       } else {
+       switch (width) {
+       case TARGET_WIDTH_32:
                if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
                        return -EINVAL;
-       }
 
-       if (WARN_ON(!priv->bar))
-               return -EFAULT;
+               for (n = 0; n < length; n += sizeof(u32)) {
+                       __raw_writel(*rdptr32++, wrptr32++);
+                       wmb();
+               }
+               return n;
+#ifdef __raw_writeq
+       case TARGET_WIDTH_64:
+               if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
+                       return -EINVAL;
 
-       if (is_64)
-#ifndef __raw_writeq
-               return -EINVAL;
-#else
                for (n = 0; n < length; n += sizeof(u64)) {
                        __raw_writeq(*rdptr64++, wrptr64++);
                        wmb();
                }
+               return n;
 #endif
-       else
-               for (n = 0; n < length; n += sizeof(u32)) {
-                       __raw_writel(*rdptr32++, wrptr32++);
-                       wmb();
-               }
-
-       return n;
+       default:
+               return -EINVAL;
+       }
 }
 
 struct nfp6000_explicit_priv {