public inbox for linux-arch@vger.kernel.org
 help / color / mirror / Atom feed
* Attempting to unify some user space copying APIs
@ 2026-03-28  0:20 Linus Torvalds
  2026-03-28  2:46 ` Helge Deller
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Linus Torvalds @ 2026-03-28  0:20 UTC (permalink / raw)
  To: Linux-Arch
  Cc: Russell King, Madhavan Srinivasan, Vasily Gorbik, Helge Deller,
	Huacai Chen, Will Deacon

[-- Attachment #1: Type: text/plain, Size: 2822 bytes --]

I'm sending this out to the architecture maintainer list (and a random
collection of arch maintainers that I just took from "recent pull
requests") in the hope that people can test this on platforms that I'm
too incompetent to check myself (read: I haven't installed all the
cross-compiler environments).

The impetus for this was trying to have some shared code in
lib/iov_iter.c by having a common wrapper function for a couple of
user copy functions that most architectures don't even have. But when
doing so I noticed that even the common function that we all *do* have
doesn't have a consistent calling convention: raw_copy_from_user().

So the compiler ended up being unhappy just because I want to pass a
function pointer around, and different architectures had different
types for the functions (and in the case of arm64, used a macro to
implement it, which really didn't work for this).

So I fixed it all up, and I think the end result looks better, but I'd
like people to at least build-test this on the platforms that I didn't
have immediate access to. I've tested this on arm64, x86-64 and i386
(and then I made changes, and didn't necessarily test all the changes
on all plaforms, but it LooksGoodToMe(tm), and some earlier version of
this patch did work on those architectures, so it must be perfect).

The patch was generated on current tip-of-the-day, but I don't think
any of this has changed in a while, so it should apply and be testable
pretty much on any half-way modern code base.

I fixed up powerpc blindly (we really had very odd types for
__copy_from_user_flushcache), and as mentioned the arm64 and x86 side
should be tested. Everything else lacks the flushcache and nocache
versions of the user copy functions, and it *looks* like only arm64
had that odd pattern for raw_copy_from_user().

But maybe my grep missed some case... Considering that three
architectures needed some header file fixups, let's make sure it's
_only_ those three, ok?

My initial version tried to use 'size_t', but the type of that is
typically - but not always - 'unsigned int' on 32-bit architectures.
And since I didn't want to mess with this low-level architecture
function more than necessary, the traditional 'unsigned long' it is
and most architectures should JustWork(tm).

[ Traditional, that is, except for those cache control versions, which
 just used completely random types with wrong sizes and signs and
everything.

  Not that we even allow IO that big in the first place, but it was
still a 'that shouldn't even work' kind of reaction from me ]

Anyway, could I please ask architecture maintainers to check that
there isn't some odd corner case that I missed? I could put this in
linux-next, but even before I do that, I'd like to do the basic "it
won't break anything" smell test.

             Linus

[-- Attachment #2: patch.diff --]
[-- Type: text/x-patch, Size: 7879 bytes --]

 arch/arm64/include/asm/uaccess.h   | 36 ++++++++++++++----------------
 arch/powerpc/include/asm/uaccess.h |  4 ++--
 arch/powerpc/lib/pmem.c            |  4 ++--
 arch/x86/include/asm/uaccess_64.h  | 13 +++++------
 arch/x86/lib/usercopy_64.c         |  2 +-
 lib/iov_iter.c                     | 19 +++++++++++-----
 6 files changed, 42 insertions(+), 36 deletions(-)

diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 9810106a3f66..2d36a0c4ec6a 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -390,26 +390,24 @@ do {									\
 } while(0)
 
 extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);
-#define raw_copy_from_user(to, from, n)					\
-({									\
-	unsigned long __acfu_ret;					\
-	uaccess_ttbr0_enable();						\
-	__acfu_ret = __arch_copy_from_user((to),			\
-				      __uaccess_mask_ptr(from), (n));	\
-	uaccess_ttbr0_disable();					\
-	__acfu_ret;							\
-})
+static __must_check __always_inline unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+	unsigned long ret;
+	uaccess_ttbr0_enable();
+	ret = __arch_copy_from_user(to, __uaccess_mask_ptr(from), n);
+	uaccess_ttbr0_disable();
+	return ret;
+}
 
 extern unsigned long __must_check __arch_copy_to_user(void __user *to, const void *from, unsigned long n);
-#define raw_copy_to_user(to, from, n)					\
-({									\
-	unsigned long __actu_ret;					\
-	uaccess_ttbr0_enable();						\
-	__actu_ret = __arch_copy_to_user(__uaccess_mask_ptr(to),	\
-				    (from), (n));			\
-	uaccess_ttbr0_disable();					\
-	__actu_ret;							\
-})
+static __must_check __always_inline unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+	unsigned long ret;
+	uaccess_ttbr0_enable();
+	ret = __arch_copy_to_user(__uaccess_mask_ptr(to), from, n);
+	uaccess_ttbr0_disable();
+	return ret;
+}
 
 static __must_check __always_inline bool user_access_begin(const void __user *ptr, size_t len)
 {
@@ -478,7 +476,7 @@ extern __must_check long strnlen_user(const char __user *str, long n);
 #ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
 extern unsigned long __must_check __copy_user_flushcache(void *to, const void __user *from, unsigned long n);
 
-static inline int __copy_from_user_flushcache(void *dst, const void __user *src, unsigned size)
+static inline unsigned long __copy_from_user_flushcache(void *dst, const void __user *src, unsigned long size)
 {
 	kasan_check_write(dst, size);
 	return __copy_user_flushcache(dst, __uaccess_mask_ptr(src), size);
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 17e63244e885..8ae10aa5de63 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -434,8 +434,8 @@ copy_mc_to_user(void __user *to, const void *from, unsigned long n)
 }
 #endif
 
-extern long __copy_from_user_flushcache(void *dst, const void __user *src,
-		unsigned size);
+extern unsigned long __copy_from_user_flushcache(void *dst, const void __user *src,
+		unsigned long size);
 
 static __must_check __always_inline bool __user_access_begin(const void __user *ptr, size_t len,
 							     unsigned long dir)
diff --git a/arch/powerpc/lib/pmem.c b/arch/powerpc/lib/pmem.c
index 4e724c4c01ad..2e733ea9de3a 100644
--- a/arch/powerpc/lib/pmem.c
+++ b/arch/powerpc/lib/pmem.c
@@ -66,8 +66,8 @@ EXPORT_SYMBOL_GPL(arch_invalidate_pmem);
 /*
  * CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE symbols
  */
-long __copy_from_user_flushcache(void *dest, const void __user *src,
-		unsigned size)
+unsigned long __copy_from_user_flushcache(void *dest, const void __user *src,
+		unsigned long size)
 {
 	unsigned long copied, start = (unsigned long) dest;
 
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index 915124011c27..6c369481174a 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -147,12 +147,11 @@ raw_copy_to_user(void __user *dst, const void *src, unsigned long size)
 	return copy_user_generic((__force void *)dst, src, size);
 }
 
-extern long __copy_user_nocache(void *dst, const void __user *src, unsigned size);
-extern long __copy_user_flushcache(void *dst, const void __user *src, unsigned size);
+extern unsigned long __copy_user_nocache(void *dst, const void __user *src, unsigned long size);
+extern unsigned long __copy_user_flushcache(void *dst, const void __user *src, unsigned long size);
 
-static inline int
-__copy_from_user_inatomic_nocache(void *dst, const void __user *src,
-				  unsigned size)
+static inline unsigned long
+__copy_from_user_inatomic_nocache(void *dst, const void __user *src, unsigned long size)
 {
 	long ret;
 	kasan_check_write(dst, size);
@@ -162,8 +161,8 @@ __copy_from_user_inatomic_nocache(void *dst, const void __user *src,
 	return ret;
 }
 
-static inline int
-__copy_from_user_flushcache(void *dst, const void __user *src, unsigned size)
+static inline unsigned long
+__copy_from_user_flushcache(void *dst, const void __user *src, unsigned long size)
 {
 	kasan_check_write(dst, size);
 	return __copy_user_flushcache(dst, src, size);
diff --git a/arch/x86/lib/usercopy_64.c b/arch/x86/lib/usercopy_64.c
index 654280aaa3e9..10b0026e85b7 100644
--- a/arch/x86/lib/usercopy_64.c
+++ b/arch/x86/lib/usercopy_64.c
@@ -43,7 +43,7 @@ void arch_wb_cache_pmem(void *addr, size_t size)
 }
 EXPORT_SYMBOL_GPL(arch_wb_cache_pmem);
 
-long __copy_user_flushcache(void *dst, const void __user *src, unsigned size)
+unsigned long __copy_user_flushcache(void *dst, const void __user *src, unsigned long size)
 {
 	unsigned long flushed, dest = (unsigned long) dst;
 	long rc;
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 0a63c7fba313..9c8fe94c72b4 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -41,9 +41,11 @@ size_t copy_to_user_iter_nofault(void __user *iter_to, size_t progress,
 	return res < 0 ? len : res;
 }
 
+typedef unsigned long (*from_user_copy_t)(void *, const void __user *, unsigned long);
+
 static __always_inline
-size_t copy_from_user_iter(void __user *iter_from, size_t progress,
-			   size_t len, void *to, void *priv2)
+size_t copy_from_user_iter_boilerplate(void __user *iter_from, size_t progress,
+			   size_t len, void *to, from_user_copy_t actor)
 {
 	size_t res = len;
 
@@ -64,12 +66,19 @@ size_t copy_from_user_iter(void __user *iter_from, size_t progress,
 	}
 	to += progress;
 	instrument_copy_from_user_before(to, iter_from, len);
-	res = raw_copy_from_user(to, iter_from, len);
+	res = actor(to, iter_from, len);
 	instrument_copy_from_user_after(to, iter_from, len, res);
 
 	return res;
 }
 
+static __always_inline
+size_t copy_from_user_iter(void __user *iter_from, size_t progress,
+			   size_t len, void *to, void *priv2)
+{
+	return copy_from_user_iter_boilerplate(iter_from, progress, len, to, raw_copy_from_user);
+}
+
 static __always_inline
 size_t memcpy_to_iter(void *iter_to, size_t progress,
 		      size_t len, void *from, void *priv2)
@@ -277,7 +286,7 @@ static __always_inline
 size_t copy_from_user_iter_nocache(void __user *iter_from, size_t progress,
 				   size_t len, void *to, void *priv2)
 {
-	return __copy_from_user_inatomic_nocache(to + progress, iter_from, len);
+	return copy_from_user_iter_boilerplate(iter_from, progress, len, to, __copy_from_user_inatomic_nocache);
 }
 
 size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
@@ -296,7 +305,7 @@ static __always_inline
 size_t copy_from_user_iter_flushcache(void __user *iter_from, size_t progress,
 				      size_t len, void *to, void *priv2)
 {
-	return __copy_from_user_flushcache(to + progress, iter_from, len);
+	return copy_from_user_iter_boilerplate(iter_from, progress, len, to, __copy_from_user_flushcache);
 }
 
 static __always_inline
-- 
2.53.0.479.g83c58c3406


^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-03-29  2:18 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-28  0:20 Attempting to unify some user space copying APIs Linus Torvalds
2026-03-28  2:46 ` Helge Deller
2026-03-28  4:14 ` Vasily Gorbik
2026-03-28 16:46   ` Linus Torvalds
2026-03-28 18:52     ` Linus Torvalds
2026-03-29  2:18   ` Linus Torvalds
2026-03-28 12:16 ` Huacai Chen
2026-03-28 12:32 ` Madhavan Srinivasan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox