nfp: core: allow 4-byte aligned accesses to Memory Units

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>
This commit is contained in:
Jakub Kicinski 2018-05-21 22:12:46 -07:00 committed by David S. Miller
parent a0d163f432
commit b586c77b3c
1 changed files with 48 additions and 54 deletions

View File

@ -933,7 +933,6 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
u32 *wrptr32 = kernel_vaddr; u32 *wrptr32 = kernel_vaddr;
const u32 __iomem *rdptr32; const u32 __iomem *rdptr32;
int n, width; int n, width;
bool is_64;
priv = nfp_cpp_area_priv(area); priv = nfp_cpp_area_priv(area);
rdptr64 = priv->iomem + offset; rdptr64 = priv->iomem + offset;
@ -943,10 +942,15 @@ static int nfp6000_area_read(struct nfp_cpp_area *area, void *kernel_vaddr,
return -EFAULT; return -EFAULT;
width = priv->width.read; width = priv->width.read;
if (width <= 0) if (width <= 0)
return -EINVAL; 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 */ /* Unaligned? Translate to an explicit access */
if ((priv->offset + offset) & (width - 1)) if ((priv->offset + offset) & (width - 1))
return nfp_cpp_explicit_read(nfp_cpp_area_cpp(area), 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, priv->offset + offset,
kernel_vaddr, length, width); 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 (is_64) {
if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
return -EINVAL;
} else {
if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
return -EINVAL;
}
if (WARN_ON(!priv->bar)) if (WARN_ON(!priv->bar))
return -EFAULT; return -EFAULT;
if (is_64) switch (width) {
#ifndef __raw_readq case TARGET_WIDTH_32:
return -EINVAL; if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
#else return -EINVAL;
for (n = 0; n < length; n += sizeof(u64))
*wrptr64++ = __raw_readq(rdptr64++);
#endif
else
for (n = 0; n < length; n += sizeof(u32)) for (n = 0; n < length; n += sizeof(u32))
*wrptr32++ = __raw_readl(rdptr32++); *wrptr32++ = __raw_readl(rdptr32++);
return n;
#ifdef __raw_readq
case TARGET_WIDTH_64:
if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
return -EINVAL;
return n; for (n = 0; n < length; n += sizeof(u64))
*wrptr64++ = __raw_readq(rdptr64++);
return n;
#endif
default:
return -EINVAL;
}
} }
static int static int
@ -999,7 +996,6 @@ nfp6000_area_write(struct nfp_cpp_area *area,
struct nfp6000_area_priv *priv; struct nfp6000_area_priv *priv;
u32 __iomem *wrptr32; u32 __iomem *wrptr32;
int n, width; int n, width;
bool is_64;
priv = nfp_cpp_area_priv(area); priv = nfp_cpp_area_priv(area);
wrptr64 = priv->iomem + offset; wrptr64 = priv->iomem + offset;
@ -1009,10 +1005,15 @@ nfp6000_area_write(struct nfp_cpp_area *area,
return -EFAULT; return -EFAULT;
width = priv->width.write; width = priv->width.write;
if (width <= 0) if (width <= 0)
return -EINVAL; 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 */ /* Unaligned? Translate to an explicit access */
if ((priv->offset + offset) & (width - 1)) if ((priv->offset + offset) & (width - 1))
return nfp_cpp_explicit_write(nfp_cpp_area_cpp(area), 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, priv->offset + offset,
kernel_vaddr, length, width); 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 (is_64) {
if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
return -EINVAL;
} else {
if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
return -EINVAL;
}
if (WARN_ON(!priv->bar)) if (WARN_ON(!priv->bar))
return -EFAULT; return -EFAULT;
if (is_64) switch (width) {
#ifndef __raw_writeq case TARGET_WIDTH_32:
return -EINVAL; if (offset % sizeof(u32) != 0 || length % sizeof(u32) != 0)
#else return -EINVAL;
for (n = 0; n < length; n += sizeof(u64)) {
__raw_writeq(*rdptr64++, wrptr64++);
wmb();
}
#endif
else
for (n = 0; n < length; n += sizeof(u32)) { for (n = 0; n < length; n += sizeof(u32)) {
__raw_writel(*rdptr32++, wrptr32++); __raw_writel(*rdptr32++, wrptr32++);
wmb(); wmb();
} }
return n;
#ifdef __raw_writeq
case TARGET_WIDTH_64:
if (offset % sizeof(u64) != 0 || length % sizeof(u64) != 0)
return -EINVAL;
return n; for (n = 0; n < length; n += sizeof(u64)) {
__raw_writeq(*rdptr64++, wrptr64++);
wmb();
}
return n;
#endif
default:
return -EINVAL;
}
} }
struct nfp6000_explicit_priv { struct nfp6000_explicit_priv {