s390/pci: fix max size calculation in zpci_memcpy_toio()
authorNiklas Schnelle <schnelle@linux.ibm.com>
Tue, 28 Nov 2023 15:22:49 +0000 (16:22 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jan 2024 23:35:56 +0000 (15:35 -0800)
[ Upstream commit 80df7d6af7f6d229b34cf237b2cc9024c07111cd ]

The zpci_get_max_write_size() helper is used to determine the maximum
size a PCI store or load can use at a given __iomem address.

For the PCI block store the following restrictions apply:

1. The dst + len must not cross a 4K boundary in the (pseudo-)MMIO space
2. len must not exceed ZPCI_MAX_WRITE_SIZE
3. len must be a multiple of 8 bytes
4. The src address must be double word (8 byte) aligned
5. The dst address must be double word (8 byte) aligned

Otherwise only a normal PCI store which takes its src value from
a register can be used. For these PCI store restriction 1 still applies.
Similarly 1 also applies to PCI loads.

It turns out zpci_max_write_size() instead implements stricter
conditions which prevents PCI block stores from being used where they
can and should be used. In particular instead of conditions 4 and 5 it
wrongly enforces both dst and src to be size aligned. This indirectly
covers condition 1 but also prevents many legal PCI block stores.

On top of the functional shortcomings the zpci_get_max_write_size() is
misnamed as it is used for both read and write size calculations. Rename
it to zpci_get_max_io_size() and implement the listed conditions
explicitly.

Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Fixes: cd24834130ac ("s390/pci: base support")
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
[agordeev@linux.ibm.com replaced spaces with tabs]
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
arch/s390/include/asm/pci_io.h
arch/s390/pci/pci_mmio.c

index 287bb88..2686bee 100644 (file)
@@ -11,6 +11,8 @@
 /* I/O size constraints */
 #define ZPCI_MAX_READ_SIZE     8
 #define ZPCI_MAX_WRITE_SIZE    128
+#define ZPCI_BOUNDARY_SIZE     (1 << 12)
+#define ZPCI_BOUNDARY_MASK     (ZPCI_BOUNDARY_SIZE - 1)
 
 /* I/O Map */
 #define ZPCI_IOMAP_SHIFT               48
@@ -125,16 +127,18 @@ out:
 int zpci_write_block(volatile void __iomem *dst, const void *src,
                     unsigned long len);
 
-static inline u8 zpci_get_max_write_size(u64 src, u64 dst, int len, int max)
+static inline int zpci_get_max_io_size(u64 src, u64 dst, int len, int max)
 {
-       int count = len > max ? max : len, size = 1;
+       int offset = dst & ZPCI_BOUNDARY_MASK;
+       int size;
 
-       while (!(src & 0x1) && !(dst & 0x1) && ((size << 1) <= count)) {
-               dst = dst >> 1;
-               src = src >> 1;
-               size = size << 1;
-       }
-       return size;
+       size = min3(len, ZPCI_BOUNDARY_SIZE - offset, max);
+       if (IS_ALIGNED(src, 8) && IS_ALIGNED(dst, 8) && IS_ALIGNED(size, 8))
+               return size;
+
+       if (size >= 8)
+               return 8;
+       return rounddown_pow_of_two(size);
 }
 
 static inline int zpci_memcpy_fromio(void *dst,
@@ -144,9 +148,9 @@ static inline int zpci_memcpy_fromio(void *dst,
        int size, rc = 0;
 
        while (n > 0) {
-               size = zpci_get_max_write_size((u64 __force) src,
-                                              (u64) dst, n,
-                                              ZPCI_MAX_READ_SIZE);
+               size = zpci_get_max_io_size((u64 __force) src,
+                                           (u64) dst, n,
+                                           ZPCI_MAX_READ_SIZE);
                rc = zpci_read_single(dst, src, size);
                if (rc)
                        break;
@@ -166,9 +170,9 @@ static inline int zpci_memcpy_toio(volatile void __iomem *dst,
                return -EINVAL;
 
        while (n > 0) {
-               size = zpci_get_max_write_size((u64 __force) dst,
-                                              (u64) src, n,
-                                              ZPCI_MAX_WRITE_SIZE);
+               size = zpci_get_max_io_size((u64 __force) dst,
+                                           (u64) src, n,
+                                           ZPCI_MAX_WRITE_SIZE);
                if (size > 8) /* main path */
                        rc = zpci_write_block(dst, src, size);
                else
index 5880893..a90499c 100644 (file)
@@ -97,9 +97,9 @@ static inline int __memcpy_toio_inuser(void __iomem *dst,
                return -EINVAL;
 
        while (n > 0) {
-               size = zpci_get_max_write_size((u64 __force) dst,
-                                              (u64 __force) src, n,
-                                              ZPCI_MAX_WRITE_SIZE);
+               size = zpci_get_max_io_size((u64 __force) dst,
+                                           (u64 __force) src, n,
+                                           ZPCI_MAX_WRITE_SIZE);
                if (size > 8) /* main path */
                        rc = __pcistb_mio_inuser(dst, src, size, &status);
                else
@@ -242,9 +242,9 @@ static inline int __memcpy_fromio_inuser(void __user *dst,
        u8 status;
 
        while (n > 0) {
-               size = zpci_get_max_write_size((u64 __force) src,
-                                              (u64 __force) dst, n,
-                                              ZPCI_MAX_READ_SIZE);
+               size = zpci_get_max_io_size((u64 __force) src,
+                                           (u64 __force) dst, n,
+                                           ZPCI_MAX_READ_SIZE);
                rc = __pcilg_mio_inuser(dst, src, size, &status);
                if (rc)
                        break;