[PATCH v19 00/14] arm64: entry: Convert to Generic Entry

Jinjie Ruan posted 14 patches 2 days, 12 hours ago
arch/arm64/Kconfig                    |   2 +-
arch/arm64/include/asm/entry-common.h |  77 +++++++++++++++++
arch/arm64/include/asm/syscall.h      |   5 +-
arch/arm64/include/asm/thread_info.h  |  16 +---
arch/arm64/kernel/debug-monitors.c    |   8 ++
arch/arm64/kernel/entry-common.c      |   2 +-
arch/arm64/kernel/ptrace.c            | 115 --------------------------
arch/arm64/kernel/signal.c            |   2 +-
arch/arm64/kernel/syscall.c           |  34 +++-----
9 files changed, 102 insertions(+), 159 deletions(-)
[PATCH v19 00/14] arm64: entry: Convert to Generic Entry
Posted by Jinjie Ruan 2 days, 12 hours ago
This series converts arm64 to the generic entry infrastructure.

Currently, architectures such as x86, RISC-V, LoongArch, Powerprc
and S390 use generic entry, which significantly reduces maintenance
burden and improves code elegance. arm64 already adopted generic IRQ
entry in commit b3cf07851b6c ("arm64: entry: Switch to generic IRQ entry"),
making it the right time to complete the conversion.

This work brings arm64 in line with other major architectures,
reducing duplicated code and enabling future improvements like
Syscall User Dispatch and rseq time slice extension optimizations.

The series is rebased on v7.3-rc4 and has been tested with
stress-ng, hackbench, kselftests (ptrace, breakpoints, arm64/abi,
arm64/fp, vDSO), ptrace stress test, and Pseudo-NMI load test.

On gVisor with systrap mode, the Syscall User Dispatch (SUD) feature
yields a slightly lower performance gain on arm64 (3% ~ 5%), similar to
that on x86.

Result: the average over 1M getpid calls, ns/op

| KVM VM Configuration | SUD    | no-SUD | Savings % |
| -------------------  | -----  | -------| --------- |
| 2 vCPU, pinned       | 4269.8 | 4509.5 | 5.3%      |
| 4 vCPU, pinned       | 4175.4 | 4369.1 | 4.4%      |
| 8 vCPU, pinned       | 4352.6 | 4485.4 | 3.0%      |

Link: https://github.com/google/gvisor/commit/a45d6227b521012109cde9a942d644d778eb773b

Changes in v19:
- Rebased on v7.3-rc4.
- Fix build problem as Karl pointed out.
- Remove has_syscall_work() later to make it more clear.
- Add gvisor test data.
- Link to v18: https://lore.kernel.org/all/20260902095537.602517-2-ruanjinjie@huawei.com/

Changes in v18:
- Rebased on v7.3-rc1.
- Drop the SUD patch as Mark suggeted.
- Update the commit message.
- Add Reviewed-by.
- Link to v17: https://lore.kernel.org/all/20260721081858.1169276-1-ruanjinjie@huawei.com/

For changes in v7 to v16, please refer to the individual patch threads:
- v16: https://lore.kernel.org/all/20260629130616.642022-1-ruanjinjie@huawei.com/
- v15: https://lore.kernel.org/all/20260511092103.1974980-1-ruanjinjie@huawei.com/
- v14: https://lore.kernel.org/all/20260320102620.1336796-1-ruanjinjie@huawei.com/
- v13: https://lore.kernel.org/all/20260317082020.737779-1-ruanjinjie@huawei.com/
- v12: https://lore.kernel.org/all/20260203133728.848283-1-ruanjinjie@huawei.com/
- v11: https://lore.kernel.org/all/20260128031934.3906955-1-ruanjinjie@huawei.com/
- v10: https://lore.kernel.org/all/20251222114737.1334364-1-ruanjinjie@huawei.com/
- v9: https://lore.kernel.org/all/20251204082123.2792067-1-ruanjinjie@huawei.com/
- v8: https://lore.kernel.org/all/20251126071446.3234218-1-ruanjinjie@huawei.com/
- v7: https://lore.kernel.org/all/20251117133048.53182-1-ruanjinjie@huawei.com/

Jinjie Ruan (14):
  arm64: ptrace: Fix redundant syscall exit stop for
    PTRACE_SYSEMU_SINGLESTEP
  arm64: ptrace: Rework audit_syscall_entry()
  arm64: ptrace: Open-code seccomp check in syscall_trace_enter()
  arm64: ptrace: Rename and clean up syscall_trace_enter()
  arm64: ptrace: Protect rseq_syscall() from tracer PC modifications
  arm64: syscall: Rework the syscall exit path in el0_svc_common()
  arm64: ptrace: Pass thread flags to trace enter/exit
  arm64: ptrace: Extract arm64_syscall_exit_to_user_mode_work() helper
  arm64: ptrace: Align syscall exit work semantics with generic entry
  arm64: syscall: Use exit-specific flags check in el0_svc_common()
  arm64: syscall: Simplify el0_svc_common() syscall exit path
  arm64: ptrace: Make return type of arm64_syscall_trace_enter() bool
  arm64: entry: Convert to generic entry
  arm64: Inline el0_svc_common()

 arch/arm64/Kconfig                    |   2 +-
 arch/arm64/include/asm/entry-common.h |  77 +++++++++++++++++
 arch/arm64/include/asm/syscall.h      |   5 +-
 arch/arm64/include/asm/thread_info.h  |  16 +---
 arch/arm64/kernel/debug-monitors.c    |   8 ++
 arch/arm64/kernel/entry-common.c      |   2 +-
 arch/arm64/kernel/ptrace.c            | 115 --------------------------
 arch/arm64/kernel/signal.c            |   2 +-
 arch/arm64/kernel/syscall.c           |  34 +++-----
 9 files changed, 102 insertions(+), 159 deletions(-)

-- 
2.34.1
Re: [PATCH v19 00/14] arm64: entry: Convert to Generic Entry
Posted by Kees Cook 1 day, 21 hours ago
On Tue, Sep 22, 2026 at 11:54:56AM +0800, Jinjie Ruan wrote:
> This series converts arm64 to the generic entry infrastructure.

With my trusty LLM driving a bunch of orchestration, I gave this
a fairly wide before/after run under qemu-system-aarch64, trying
to cover things beyond the ptrace/breakpoints/abi/fp/vDSO list in
the cover letter. But since this is all emulated, it's mainly basic
correctness coverage, without any meaningful concurrency coverage. I
just wanted to find stuff that maybe hadn't been exercised yet.

tl;dr: the only behavioral difference I could find anywhere is what
patch 1 fixed, and I found no meaningful regressions.

Setup: the series applies cleanly to v7.3-rc2 (I only noticed later
the series was actually based on -rc4), so "base" is v7.3-rc2 and
"patched" is the same tree plus the 14 patches, with each pair built
from the same config with GCC 16.1.0. The guest was QEMU 11.1.0:

  -M virt,gic-version=max,mte=on -cpu max,pauth-impdef=on -smp 4

I used four kernel configs, each built for both base and patched:

  plain    defconfig + SECCOMP, USER_NS, AUDITSYSCALL, KUNIT_ALL_TESTS
  lockdep  plain + PROVE_LOCKING, TRACE_IRQFLAGS, PROVE_RCU,
             DEBUG_ATOMIC_SLEEP, DEBUG_PREEMPT
  hooks    lockdep + the options that add work to syscall entry/exit:
             FTRACE_SYSCALLS, KSTACK_ERASE, LKDTM, NO_HZ_FULL,
             CONTEXT_TRACKING_USER (booted nohz_full=2-3)
  rt       hooks + PREEMPT_RT

and ran userspace tests from both an AArch64 and an AArch32 (armhf)
userspace, since the latter takes the is_compat_task() side of
ptrace_save_reg() and the cover letter didn't mentioned COMPAT.

Results were identical on base and patched (pass/fail/skip):

  seccomp_bpf                   98/1/12 (AArch64), 94/2/15 (AArch32)
  ptrace selftests              get/set_syscall_info, peeksiginfo,
                                vmaccess: no change
  breakpoint_test_arm64         213/0/0
  rseq basic, percpu_ops        pass
  KUnit (KUNIT_ALL_TESTS)       ~1500 results, no change
  MTE selftests                 117/0/0 (but see below)
  strace 6.18 test suite        81/6/0 (ptrace/seccomp tests)
  audit (a0 filter)             pass
  syscall tracepoints           pass (argument recorded correctly)
  lkdtm KSTACK_ERASE            pass
  lkdtm stack-entropy.sh        7 bits
  lockdep/RCU                   0 splats in lockdep, hooks, and rt

The only delta I could find was the expected one:

  sysemu_singlestep (new test)  FAIL on base, pass on patched

So patch 1 fixes the described bug, but it had no in-tree test, so I
wrote one. I'll send that separately.

Various things I noticed along the way:

- The set of traceable symbols on the syscall path changes:

    removed: syscall_trace_enter, syscall_trace_exit,
             el0_svc_common.constprop.0
    added:   trace_syscall_enter, trace_syscall_exit,
             syscall_enter_audit

  These aren't ABI, and the new names are the same ones x86,
  riscv, loongarch, and s390 already expose, so this is really an
  improvement. But it might be worth a sentence in the cover letter,
  since patch 13's "[Compatibility]" note could be read as "nothing
  observable changes", which isn't _strictly_ true. :)

- Syscall-path stack use grows by 48 bytes. Base do_el0_svc() has a
  16-byte frame and calls el0_svc_common() with a 48-byte frame; with
  patch 14 inlining it (plus the __always_inline generic enter/exit
  helpers), do_el0_svc() grows from 48 to 808 bytes and 1 to 13 calls,
  with a single 112-byte frame: 112 - 64 = 48. lkdtm KSTACK_ERASE with
  randomize_kstack_offset=off confirms exactly that at runtime (2288 vs
  2336 bytes, ten samples, no variance), on both userspaces and in
  both the hooks and rt configs. It's small, but it's not mentioned as
  a (minor) trade-off for patch 14's ~1% speedup. Other size deltas:
  .text +12K, ptrace.o -3.6K, and +3.1K of shared
  kernel/entry/syscall-common.o (all seems expected/unremarkable).

- Patch 14 also moves that inlined code into .noinstr.text (+896
  bytes). Since arm64 doesn't have objtool noinstr validation like x86,
  I checked the images directly: no calls from .noinstr.text to
  instrumentation outside the noinstr range, and no __mcount_loc entry
  falls inside it, on either kernel. But we don't seem to have anything
  that will continue to enforce this?

- CONFIG_DEBUG_RSEQ "depends on ... && !GENERIC_ENTRY", so patch 13
  makes it unselectable on arm64 and rseq_syscall() becomes the no-op
  stub. Patches 5 and 9 carefully reposition that call, and then patch
  13 removes its effect. Not a bug (generic entry does the equivalent
  via rseq_debug_enabled, and I can see __rseq_debug_syscall_return()
  called from the new do_el0_svc()), but patch 5 reads like a
  standalone fix that disappears eight patches later, so a note in the
  cover letter might help make sense of this?

- The cover letter includes the gvisor SUD numbers, but SUD needs
  ARCH_SUPPORTS_SYSCALL_USER_DISPATCH (as well as GENERIC_ENTRY),
  and with the SUD patch dropped in v18, arm64 doesn't select it,
  so CONFIG_SYSCALL_USER_DISPATCH can't be enabled yet. (rseq slice
  extension is similarly blocked on HAVE_GENERIC_TIF_BITS.) A reader
  could take those numbers as something this series delivers.

- arm64 defconfig has FTRACE_SYSCALLS=n, which means
  SYSCALL_WORK_SYSCALL_TRACEPOINT can never be set and the tracepoint
  paths touched by patches 2, 3, and 9 aren't even built. So anyone
  testing with defconfig isn't exercising those; you may want to test
  with it enabled. (Also, compat tasks never hit syscall tracepoints
  on arm64 at all, due to ARCH_TRACE_IGNORE_COMPAT_SYSCALLS, so that
  path can only be covered by native tasks, but that's unchanged by
  the series.)

- I verified that the entry work ordering is unchanged. It looks
  correct to me: arm64 did ptrace, seccomp, tracepoint, audit; generic
  entry does the same. (It also checks SUD and rseq-slice before those,
  but neither can be enabled on arm64 yet.)

Suggestions:

- rr: Since you have real hardware, could you also run the rr test
  suite? rr has been the most sensitive ptrace user I've encountered,
  and it depends on exactly the syscall-stop and single-step behavior
  this series touches. I couldn't run it: QEMU's TCG PMU exposes PMUv3
  but only counts CPU_CYCLES (BR_RETIRED always reads 0, on every
  CPU model I tried), so rr aborts in check_working_counters(). If
  you do, note that you'll need "proc_mem.force_override=always" (or
  CONFIG_PROC_MEM_ALWAYS_FORCE=y), or three rr tests fail for unrelated
  reasons (rr-debugger/rr#4093). Build and test instructions are here:
  https://github.com/rr-debugger/rr/wiki/Building-And-Installing#tests

- MTE: I didn't see MTE in the cover letter's test list, and it seems
  relevant since patch 13 puts _TIF_MTE_ASYNC_FAULT into
  ARCH_EXIT_TO_USER_MODE_WORK, so an async tag check fault lands
  on the reworked exit path. The MTE selftests showed no differences,
  but a third of them don't actually run for me under QEMU, so they'd
  be worth running on your hardware. (Testing MTE under QEMU saw
  check_mmap_options hang at test 5, the first test with tag checking
  on, and check_child_memory never produces output, on both base and
  patched, so only 117 of the 183 planned MTE tests ran. But this is,
  of course, a QEMU/selftest issue unrelated to the generic entry.)

So, for the series:

Tested-by: Kees Cook <kees@kernel.org>

-Kees

-- 
Kees Cook
Re: [PATCH v19 00/14] arm64: entry: Convert to Generic Entry
Posted by Jinjie Ruan 6 hours ago

在 2026/9/23 2:29, Kees Cook 写道:
> On Tue, Sep 22, 2026 at 11:54:56AM +0800, Jinjie Ruan wrote:
>> This series converts arm64 to the generic entry infrastructure.
> 
> With my trusty LLM driving a bunch of orchestration, I gave this
> a fairly wide before/after run under qemu-system-aarch64, trying
> to cover things beyond the ptrace/breakpoints/abi/fp/vDSO list in
> the cover letter. But since this is all emulated, it's mainly basic
> correctness coverage, without any meaningful concurrency coverage. I
> just wanted to find stuff that maybe hadn't been exercised yet.
> 
> tl;dr: the only behavioral difference I could find anywhere is what
> patch 1 fixed, and I found no meaningful regressions.
> 
> Setup: the series applies cleanly to v7.3-rc2 (I only noticed later
> the series was actually based on -rc4), so "base" is v7.3-rc2 and
> "patched" is the same tree plus the 14 patches, with each pair built
> from the same config with GCC 16.1.0. The guest was QEMU 11.1.0:
> 
>   -M virt,gic-version=max,mte=on -cpu max,pauth-impdef=on -smp 4
> 
> I used four kernel configs, each built for both base and patched:
> 
>   plain    defconfig + SECCOMP, USER_NS, AUDITSYSCALL, KUNIT_ALL_TESTS
>   lockdep  plain + PROVE_LOCKING, TRACE_IRQFLAGS, PROVE_RCU,
>              DEBUG_ATOMIC_SLEEP, DEBUG_PREEMPT
>   hooks    lockdep + the options that add work to syscall entry/exit:
>              FTRACE_SYSCALLS, KSTACK_ERASE, LKDTM, NO_HZ_FULL,
>              CONTEXT_TRACKING_USER (booted nohz_full=2-3)
>   rt       hooks + PREEMPT_RT
> 
> and ran userspace tests from both an AArch64 and an AArch32 (armhf)
> userspace, since the latter takes the is_compat_task() side of
> ptrace_save_reg() and the cover letter didn't mentioned COMPAT.
> 
> Results were identical on base and patched (pass/fail/skip):
> 
>   seccomp_bpf                   98/1/12 (AArch64), 94/2/15 (AArch32)
>   ptrace selftests              get/set_syscall_info, peeksiginfo,
>                                 vmaccess: no change
>   breakpoint_test_arm64         213/0/0
>   rseq basic, percpu_ops        pass
>   KUnit (KUNIT_ALL_TESTS)       ~1500 results, no change
>   MTE selftests                 117/0/0 (but see below)
>   strace 6.18 test suite        81/6/0 (ptrace/seccomp tests)
>   audit (a0 filter)             pass
>   syscall tracepoints           pass (argument recorded correctly)
>   lkdtm KSTACK_ERASE            pass
>   lkdtm stack-entropy.sh        7 bits
>   lockdep/RCU                   0 splats in lockdep, hooks, and rt
> 
> The only delta I could find was the expected one:
> 
>   sysemu_singlestep (new test)  FAIL on base, pass on patched
> 
> So patch 1 fixes the described bug, but it had no in-tree test, so I
> wrote one. I'll send that separately.
> 
> Various things I noticed along the way:
> 
> - The set of traceable symbols on the syscall path changes:
> 
>     removed: syscall_trace_enter, syscall_trace_exit,
>              el0_svc_common.constprop.0
>     added:   trace_syscall_enter, trace_syscall_exit,
>              syscall_enter_audit
> 
>   These aren't ABI, and the new names are the same ones x86,
>   riscv, loongarch, and s390 already expose, so this is really an
>   improvement. But it might be worth a sentence in the cover letter,
>   since patch 13's "[Compatibility]" note could be read as "nothing
>   observable changes", which isn't _strictly_ true. :)
> 
> - Syscall-path stack use grows by 48 bytes. Base do_el0_svc() has a
>   16-byte frame and calls el0_svc_common() with a 48-byte frame; with
>   patch 14 inlining it (plus the __always_inline generic enter/exit
>   helpers), do_el0_svc() grows from 48 to 808 bytes and 1 to 13 calls,
>   with a single 112-byte frame: 112 - 64 = 48. lkdtm KSTACK_ERASE with
>   randomize_kstack_offset=off confirms exactly that at runtime (2288 vs
>   2336 bytes, ten samples, no variance), on both userspaces and in
>   both the hooks and rt configs. It's small, but it's not mentioned as
>   a (minor) trade-off for patch 14's ~1% speedup. Other size deltas:
>   .text +12K, ptrace.o -3.6K, and +3.1K of shared
>   kernel/entry/syscall-common.o (all seems expected/unremarkable).
> 
> - Patch 14 also moves that inlined code into .noinstr.text (+896
>   bytes). Since arm64 doesn't have objtool noinstr validation like x86,
>   I checked the images directly: no calls from .noinstr.text to
>   instrumentation outside the noinstr range, and no __mcount_loc entry
>   falls inside it, on either kernel. But we don't seem to have anything
>   that will continue to enforce this?
> 
> - CONFIG_DEBUG_RSEQ "depends on ... && !GENERIC_ENTRY", so patch 13
>   makes it unselectable on arm64 and rseq_syscall() becomes the no-op
>   stub. Patches 5 and 9 carefully reposition that call, and then patch
>   13 removes its effect. Not a bug (generic entry does the equivalent
>   via rseq_debug_enabled, and I can see __rseq_debug_syscall_return()
>   called from the new do_el0_svc()), but patch 5 reads like a
>   standalone fix that disappears eight patches later, so a note in the
>   cover letter might help make sense of this?
> 
> - The cover letter includes the gvisor SUD numbers, but SUD needs
>   ARCH_SUPPORTS_SYSCALL_USER_DISPATCH (as well as GENERIC_ENTRY),
>   and with the SUD patch dropped in v18, arm64 doesn't select it,
>   so CONFIG_SYSCALL_USER_DISPATCH can't be enabled yet. (rseq slice
>   extension is similarly blocked on HAVE_GENERIC_TIF_BITS.) A reader
>   could take those numbers as something this series delivers.
> 
> - arm64 defconfig has FTRACE_SYSCALLS=n, which means
>   SYSCALL_WORK_SYSCALL_TRACEPOINT can never be set and the tracepoint
>   paths touched by patches 2, 3, and 9 aren't even built. So anyone
>   testing with defconfig isn't exercising those; you may want to test
>   with it enabled. (Also, compat tasks never hit syscall tracepoints
>   on arm64 at all, due to ARCH_TRACE_IGNORE_COMPAT_SYSCALLS, so that
>   path can only be covered by native tasks, but that's unchanged by
>   the series.)
> 
> - I verified that the entry work ordering is unchanged. It looks
>   correct to me: arm64 did ptrace, seccomp, tracepoint, audit; generic
>   entry does the same. (It also checks SUD and rseq-slice before those,
>   but neither can be enabled on arm64 yet.)
> 
> Suggestions:
> 
> - rr: Since you have real hardware, could you also run the rr test
>   suite? rr has been the most sensitive ptrace user I've encountered,
>   and it depends on exactly the syscall-stop and single-step behavior
>   this series touches. I couldn't run it: QEMU's TCG PMU exposes PMUv3
>   but only counts CPU_CYCLES (BR_RETIRED always reads 0, on every
>   CPU model I tried), so rr aborts in check_working_counters(). If
>   you do, note that you'll need "proc_mem.force_override=always" (or
>   CONFIG_PROC_MEM_ALWAYS_FORCE=y), or three rr tests fail for unrelated
>   reasons (rr-debugger/rr#4093). Build and test instructions are here:
>   https://github.com/rr-debugger/rr/wiki/Building-And-Installing#tests

Hi Kees,

Thank you for your detailed testing.

I tested the rr test cases on a Kunpeng HIP09 (with minor adaptations,
since Kunpeng CPUs aren't supported by default), and the results showed
no regressions.

|               |    7.3-rc4 baseline     |   Patched                 |

|  ------------ |------------------------ | ------------------------- |
|   result      |  87% passed, 190 failed |   88% passed, 186 failed  |


| Failure Mode   | 7.3-rc4 baseline |   Patched  |
| ------------   | ---------------- | ---------- |
| TICK_MISMATCH  |         76       |   74       |
| TIMEOUT        |         68       |   65       |
| GDB_SCRIPT     |         34       |   37       |
| PERF_EVENT_OPEN|         3        |   2        |
|   OVERSHOOT    |         1        |   0        |
|    OTHER       |         8        |   8        |
| -------------- |  --------------- | ---------- |
|  Total         |      190         |   186      |

> 
> - MTE: I didn't see MTE in the cover letter's test list, and it seems
>   relevant since patch 13 puts _TIF_MTE_ASYNC_FAULT into
>   ARCH_EXIT_TO_USER_MODE_WORK, so an async tag check fault lands
>   on the reworked exit path. The MTE selftests showed no differences,
>   but a third of them don't actually run for me under QEMU, so they'd
>   be worth running on your hardware. (Testing MTE under QEMU saw

Turns out our arm64 machines don't support MTE; I was testing it on QEMU.

>   check_mmap_options hang at test 5, the first test with tag checking
>   on, and check_child_memory never produces output, on both base and
>   patched, so only 117 of the 183 planned MTE tests ran. But this is,
>   of course, a QEMU/selftest issue unrelated to the generic entry.)
> 
> So, for the series:
> 
> Tested-by: Kees Cook <kees@kernel.org>
> 
> -Kees
> 

-- 
Best regards,
Jinjie