[PATCH] arm64: syscall: Fixed the problem that local_daif_mask was called twice in a row when there was no _TIF_SYSCALL_WORK when the do_el0_svc call returned.

yushengjin yushengjin at uniontech.com
Fri Dec 3 04:17:54 PST 2021


This commit f37099b6992a ("arm64: convert syscall trace logic to C") is
a patch that converts syscall trace to C. It was submitted to the v4.18
branch in 2018, but there is a small problem in the conversion process,
which is still there.

After calling disable_daif, the previous assembly code ret_fast_syscall
will directly call kernel_exit to exit the kernel state if there is no
_TIF_SYSCALL_WORK flag bit,Instead of calling  ret_to_user.But after
converting this assembly code into C,After checking the flag bit and
finding no _TIF_SYSCALL_WORK and it returns, ret_to_user will be called
unconditionally, and ret_to_user also has a code of disable_daif, which
will cause ret_to_user to be called twice in a row in this program execution
flow.

Although calling twice doesn't have much impact, I found that this disable_daif
code has some impact on the performance of arm64 machines.

Taking the gitpid test of "Unixbench syscall" as an example, we know that if
getpid is not specially optimized in C library, the performance of getpid
is almost the performance of system call.

The performance test after patching is as follows:

    ~/UnixBench$ ./pgms/syscall 10 g
    COUNT|83397768|1|lps
    ~/UnixBench$ ./pgms/syscall 10 g
    COUNT|83521095|1|lps

At the same time, I add a line of repeated code under XXX, similar to this
-----------------
local_daif_mask();
local_daif_mask();
-----------------
    ~/UnixBench$ ./pgms/syscall 10 g
    COUNT|78040911|1|lps
    ~/UnixBench$ ./pgms/syscall 10 g
    COUNT|78046938|1|lps

This shows that the operation of this daIf has a loss of performance,
Therefore, after fixing the problem of repeated calls twice, the performance
is as follows.

    ~/UnixBench$ ./pgms/syscall 10 g
    COUNT|93664649|1|lps
    ~/UnixBench$ ./pgms/syscall 10 g
    COUNT|94852639|1|lps

Signed-off-by: yushengjin <yushengjin at uniontech.com>
---
 arch/arm64/kernel/entry-common.c | 13 ++++++++++++-
 arch/arm64/kernel/syscall.c      |  1 +
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index f7408edf8571..99baf32b3e3d 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -141,6 +141,17 @@ static __always_inline void exit_to_user_mode(struct pt_regs *regs)
 	__exit_to_user_mode();
 }
 
+static __always_inline void el0_svc_exit_to_user_mode(struct pt_regs *regs)
+{
+	unsigned long flags;
+
+	flags = READ_ONCE(current_thread_info()->flags);
+	if (unlikely(flags & _TIF_WORK_MASK))
+		do_notify_resume(regs, flags);
+	mte_check_tfsr_exit();
+	__exit_to_user_mode();
+}
+
 asmlinkage void noinstr asm_exit_to_user_mode(struct pt_regs *regs)
 {
 	exit_to_user_mode(regs);
@@ -601,7 +612,7 @@ static void noinstr el0_svc(struct pt_regs *regs)
 	enter_from_user_mode(regs);
 	cortex_a76_erratum_1463225_svc_handler();
 	do_el0_svc(regs);
-	exit_to_user_mode(regs);
+	el0_svc_exit_to_user_mode(regs);
 }
 
 static void noinstr el0_fpac(struct pt_regs *regs, unsigned long esr)
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index 50a0f1a38e84..4095786c85f1 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -156,6 +156,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
 
 trace_exit:
 	syscall_trace_exit(regs);
+	local_daif_mask();
 }
 
 static inline void sve_user_discard(void)
-- 
2.20.1






More information about the linux-arm-kernel mailing list