put_sigset_t() calls copy_to_user() for copying two words.

Because INLINE_COPY_TO_USER is not defined on powerpc,
copy_to_user() doesn't get optimised and falls back to
copy_tofrom_user() with the relevant glue. This is terribly
inefficient for copying two words.

By switching to unsafe_put_user(), we end up with something as
simple as:

 3cc:   81 3d 00 00     lwz     r9,0(r29)
 3d0:   91 26 00 b4     stw     r9,180(r6)
 3d4:   81 3d 00 04     lwz     r9,4(r29)
 3d8:   91 26 00 b8     stw     r9,184(r6)

Signed-off-by: Christophe Leroy <christophe.le...@csgroup.eu>
---
 arch/powerpc/kernel/signal_32.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index d03ba3d8eb68..6cbff0293ff4 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -87,6 +87,8 @@ static inline int put_sigset_t(compat_sigset_t __user *uset, 
sigset_t *set)
        return put_compat_sigset(uset, set, sizeof(*uset));
 }
 
+#define unsafe_put_sigset_t    unsafe_put_compat_sigset
+
 static inline int get_sigset_t(sigset_t *set,
                               const compat_sigset_t __user *uset)
 {
@@ -143,6 +145,13 @@ static inline int put_sigset_t(sigset_t __user *uset, 
sigset_t *set)
        return copy_to_user(uset, set, sizeof(*uset));
 }
 
+#define unsafe_put_sigset_t(uset, set, label) do {                     \
+       sigset_t __user *__us = uset    ;                               \
+       const sigset_t *__s = set;                                      \
+                                                                       \
+       unsafe_copy_to_user(__us, __s, sizeof(*__us), label);           \
+} while (0)
+
 static inline int get_sigset_t(sigset_t *set, const sigset_t __user *uset)
 {
        return copy_from_user(set, uset, sizeof(*uset));
@@ -820,10 +829,11 @@ int handle_rt_signal32(struct ksignal *ksig, sigset_t 
*oldset,
        {
                unsafe_put_user(0, &rt_sf->uc.uc_link, failed);
        }
+
+       unsafe_put_sigset_t(&rt_sf->uc.uc_sigmask, oldset, failed);
+
        user_write_access_end();
 
-       if (put_sigset_t(&rt_sf->uc.uc_sigmask, oldset))
-               goto badframe;
        if (copy_siginfo_to_user(&rt_sf->info, &ksig->info))
                goto badframe;
 
-- 
2.25.0

Reply via email to