Various architectures have almost the same implementations for
memcpy_{to,from}io and memset_io functions. So, consolidate them
into the existing lib/iomap_copy.c.
Reviewed-by: Yann Sionneau <ysionneau@kalrayinc.com>
Signed-off-by: Julian Vetter <jvetter@kalrayinc.com>
---
Changes for v8:
- Replaced shifts by 'qc *= ~0UL / 0xff'
- Modification in v6.12-rc2: Include 'linux/unaligned.h' instead of
'asm/unaligned.h'
---
include/asm-generic/io.h | 58 ++----------------
lib/iomap_copy.c | 127 +++++++++++++++++++++++++++++++++++++++
2 files changed, 133 insertions(+), 52 deletions(-)
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 80de699bf6af..f14655ed4d9d 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -102,6 +102,12 @@ static inline void log_post_read_mmio(u64 val, u8 width, const volatile void __i
#endif /* CONFIG_TRACE_MMIO_ACCESS */
+extern void memcpy_fromio(void *to, const volatile void __iomem *from,
+ size_t count);
+extern void memcpy_toio(volatile void __iomem *to, const void *from,
+ size_t count);
+extern void memset_io(volatile void __iomem *dst, int c, size_t count);
+
/*
* __raw_{read,write}{b,w,l,q}() access memory in native endianness.
*
@@ -1150,58 +1156,6 @@ static inline void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr)
}
#endif
-#ifndef memset_io
-#define memset_io memset_io
-/**
- * memset_io Set a range of I/O memory to a constant value
- * @addr: The beginning of the I/O-memory range to set
- * @val: The value to set the memory to
- * @count: The number of bytes to set
- *
- * Set a range of I/O memory to a given value.
- */
-static inline void memset_io(volatile void __iomem *addr, int value,
- size_t size)
-{
- memset(__io_virt(addr), value, size);
-}
-#endif
-
-#ifndef memcpy_fromio
-#define memcpy_fromio memcpy_fromio
-/**
- * memcpy_fromio Copy a block of data from I/O memory
- * @dst: The (RAM) destination for the copy
- * @src: The (I/O memory) source for the data
- * @count: The number of bytes to copy
- *
- * Copy a block of data from I/O memory.
- */
-static inline void memcpy_fromio(void *buffer,
- const volatile void __iomem *addr,
- size_t size)
-{
- memcpy(buffer, __io_virt(addr), size);
-}
-#endif
-
-#ifndef memcpy_toio
-#define memcpy_toio memcpy_toio
-/**
- * memcpy_toio Copy a block of data into I/O memory
- * @dst: The (I/O memory) destination for the copy
- * @src: The (RAM) source for the data
- * @count: The number of bytes to copy
- *
- * Copy a block of data to I/O memory.
- */
-static inline void memcpy_toio(volatile void __iomem *addr, const void *buffer,
- size_t size)
-{
- memcpy(__io_virt(addr), buffer, size);
-}
-#endif
-
extern int devmem_is_allowed(unsigned long pfn);
#endif /* __KERNEL__ */
diff --git a/lib/iomap_copy.c b/lib/iomap_copy.c
index 2fd5712fb7c0..175d6930c293 100644
--- a/lib/iomap_copy.c
+++ b/lib/iomap_copy.c
@@ -3,8 +3,11 @@
* Copyright 2006 PathScale, Inc. All Rights Reserved.
*/
+#include <linux/align.h>
#include <linux/export.h>
#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
/**
* __iowrite32_copy - copy data to MMIO space, in 32-bit units
@@ -76,3 +79,127 @@ void __iowrite64_copy(void __iomem *to, const void *from, size_t count)
}
EXPORT_SYMBOL_GPL(__iowrite64_copy);
#endif
+
+#ifndef memcpy_fromio
+/**
+ * memcpy_fromio Copy a block of data from I/O memory
+ * @to: The (RAM) destination for the copy
+ * @from: The (I/O memory) source for the data
+ * @count: The number of bytes to copy
+ *
+ * Copy a block of data from I/O memory.
+ */
+void memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
+{
+ while (count && !IS_ALIGNED((long)from, sizeof(long))) {
+ *(u8 *)to = __raw_readb(from);
+ from++;
+ to++;
+ count--;
+ }
+
+ while (count >= sizeof(long)) {
+#ifdef CONFIG_64BIT
+ long val = __raw_readq(from);
+#else
+ long val = __raw_readl(from);
+#endif
+ put_unaligned(val, (long *)to);
+
+
+ from += sizeof(long);
+ to += sizeof(long);
+ count -= sizeof(long);
+ }
+
+ while (count) {
+ *(u8 *)to = __raw_readb(from);
+ from++;
+ to++;
+ count--;
+ }
+}
+EXPORT_SYMBOL(memcpy_fromio);
+#endif
+
+#ifndef memcpy_toio
+/**
+ * memcpy_toio Copy a block of data into I/O memory
+ * @to: The (I/O memory) destination for the copy
+ * @from: The (RAM) source for the data
+ * @count: The number of bytes to copy
+ *
+ * Copy a block of data to I/O memory.
+ */
+void memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
+{
+ while (count && !IS_ALIGNED((long)to, sizeof(long))) {
+ __raw_writeb(*(u8 *)from, to);
+ from++;
+ to++;
+ count--;
+ }
+
+ while (count >= sizeof(long)) {
+ long val = get_unaligned((long *)from);
+#ifdef CONFIG_64BIT
+ __raw_writeq(val, to);
+#else
+ __raw_writel(val, to);
+#endif
+
+ from += sizeof(long);
+ to += sizeof(long);
+ count -= sizeof(long);
+ }
+
+ while (count) {
+ __raw_writeb(*(u8 *)from, to);
+ from++;
+ to++;
+ count--;
+ }
+}
+EXPORT_SYMBOL(memcpy_toio);
+#endif
+
+#ifndef memset_io
+/**
+ * memset_io Set a range of I/O memory to a constant value
+ * @dst: The beginning of the I/O-memory range to set
+ * @c: The value to set the memory to
+ * @count: The number of bytes to set
+ *
+ * Set a range of I/O memory to a given value.
+ */
+void memset_io(volatile void __iomem *dst, int c, size_t count)
+{
+ long qc = (u8)c;
+
+ qc *= ~0UL / 0xff;
+
+ while (count && !IS_ALIGNED((long)dst, sizeof(long))) {
+ __raw_writeb(c, dst);
+ dst++;
+ count--;
+ }
+
+ while (count >= sizeof(long)) {
+#ifdef CONFIG_64BIT
+ __raw_writeq(qc, dst);
+#else
+ __raw_writel(qc, dst);
+#endif
+
+ dst += sizeof(long);
+ count -= sizeof(long);
+ }
+
+ while (count) {
+ __raw_writeb(c, dst);
+ dst++;
+ count--;
+ }
+}
+EXPORT_SYMBOL(memset_io);
+#endif
--
2.34.1
On Tue, Oct 08, 2024 at 09:50:09AM +0200, Julian Vetter wrote: > lib/iomap_copy.c | 127 +++++++++++++++++++++++++++++++++++++++ On top of the previous comments: this really should be iomem_copy.c instead.
On Tue, Oct 8, 2024, at 11:46, Christoph Hellwig wrote: > On Tue, Oct 08, 2024 at 09:50:09AM +0200, Julian Vetter wrote: >> lib/iomap_copy.c | 127 +++++++++++++++++++++++++++++++++++++++ > > On top of the previous comments: this really should be iomem_copy.c > instead. Right, I suggested adding it to the existing file since the functions are logically related, but the naming of that file identifiers in it is unfortunate: __iowrite32_copy/__iowrite64_copy/__ioread32_copy sound like they are meant to work on both IORESOURCE_MEM and IORESOURCE_IO mappings the same way that iowrite64/ioread64/ioread32 do, but actually using them on x86 port I/O (from pci_iomap or ioport_map) would lead to a NULL pointer dereference. Arnd
On Tue, Oct 8, 2024, at 07:50, Julian Vetter wrote: > diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h > index 80de699bf6af..f14655ed4d9d 100644 > --- a/include/asm-generic/io.h > +++ b/include/asm-generic/io.h > @@ -102,6 +102,12 @@ static inline void log_post_read_mmio(u64 val, u8 > width, const volatile void __i > > #endif /* CONFIG_TRACE_MMIO_ACCESS */ > > +extern void memcpy_fromio(void *to, const volatile void __iomem *from, > + size_t count); > +extern void memcpy_toio(volatile void __iomem *to, const void *from, > + size_t count); > +extern void memset_io(volatile void __iomem *dst, int c, size_t count); > + I think having this globally visible is the reason you are running into the mismatched prototypes. The patches to change the architecture specific implementations are all good, but I would instead add #ifdef checks around the prototypes the same way you do for the implementation, to make the series bisectible and shorter. include/asm-generic/io.h | 58 ++---------------- lib/iomap_copy.c | 127 +++++++++++++++++++++++++++++++++++++++ Along the same lines, I would change lib/Makefile to build this file unconditionally even on architectures that don't set CONFIG_HAS_IOMEM. Again, strengthening the driver dependencies is good, but it feels like a distraction here when we just need the common implementation to be available. Arnd
On Tue, Oct 08, 2024 at 09:27:20AM +0000, Arnd Bergmann wrote: > > #endif /* CONFIG_TRACE_MMIO_ACCESS */ > > > > +extern void memcpy_fromio(void *to, const volatile void __iomem *from, > > + size_t count); > > +extern void memcpy_toio(volatile void __iomem *to, const void *from, > > + size_t count); > > +extern void memset_io(volatile void __iomem *dst, int c, size_t count); > > + > > I think having this globally visible is the reason you are running > into the mismatched prototypes. Yes, especially as architectures sometimes actually implement this as macro or inline function. Please also drop the pointless externs while you're at it.
© 2016 - 2024 Red Hat, Inc.