diff mbox series

[v8,01/14] Consolidate IO memcpy/memset into iomap_copy.c

Message ID 20241008075023.3052370-2-jvetter@kalrayinc.com (mailing list archive)
State New
Headers show
Series Consolidate IO memcpy functions | expand

Commit Message

Julian Vetter Oct. 8, 2024, 7:50 a.m. UTC
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(-)

Comments

Arnd Bergmann Oct. 8, 2024, 9:27 a.m. UTC | #1
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
Christoph Hellwig Oct. 8, 2024, 9:32 a.m. UTC | #2
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.
Christoph Hellwig Oct. 8, 2024, 11:46 a.m. UTC | #3
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.
Arnd Bergmann Oct. 8, 2024, 12:29 p.m. UTC | #4
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
diff mbox series

Patch

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