From b25ec50b693eae68de1f020a9566fa14dea47888 Mon Sep 17 00:00:00 2001 From: Howard Zhang Date: Tue, 6 Aug 2019 02:42:37 -0700 Subject: [PATCH] syscall: implement rawVforkSyscall for linux/arm64 This allows the use of CLONE_VFORK and CLONE_VM for fork/exec, preventing "fork/exec ...: cannot allocate memory" failures from occuring when attempting to execute commands from a Go process that has a large memory footprint. Additionally, this should reduce the latency of fork/exec on linux/arm64. With CLONE_VM the child process shares the same memory with the parent process. On its own this would lead to conflicting use of the same memory, so CLONE_VFORK is used to suspend the parent process until the child releases the memory when switching to the new program binary via the exec syscall. When the parent process continues to run, one has to consider the changes to memory that the child process did, namely the return address of the syscall function needs to be restored from a register. exec.Command() callers can start in a faster manner, as child process who do exec commands job can be cloned faster via vfork than via fork on arm64. The same problem was addressed on linux/amd64 via issue #5838. Updates #31936 Contributed by Howard Zhang and Bin Lu Change-Id: Ia99d81d877f564ec60d19f17e596276836576eaf Reviewed-on: https://go-review.googlesource.com/c/go/+/189418 Run-TryBot: Tobias Klauser TryBot-Result: Gobot Gobot Reviewed-by: Tobias Klauser Reviewed-by: Cherry Zhang --- src/syscall/asm_linux_arm64.s | 23 +++++++++++++++++++++++ src/syscall/exec_linux.go | 2 +- src/syscall/syscall_linux_arm64.go | 4 +--- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/syscall/asm_linux_arm64.s b/src/syscall/asm_linux_arm64.s index 7edeafca81..fb22f8d547 100644 --- a/src/syscall/asm_linux_arm64.s +++ b/src/syscall/asm_linux_arm64.s @@ -103,6 +103,29 @@ ok: MOVD ZR, err+72(FP) // errno RET +// func rawVforkSyscall(trap, a1 uintptr) (r1, err uintptr) +TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-32 + MOVD a1+8(FP), R0 + MOVD $0, R1 + MOVD $0, R2 + MOVD $0, R3 + MOVD $0, R4 + MOVD $0, R5 + MOVD trap+0(FP), R8 // syscall entry + SVC + CMN $4095, R0 + BCC ok + MOVD $-1, R4 + MOVD R4, r1+16(FP) // r1 + NEG R0, R0 + MOVD R0, err+24(FP) // errno + RET +ok: + MOVD R0, r1+16(FP) // r1 + MOVD ZR, err+24(FP) // errno + RET + + // func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr); TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48 MOVD a1+8(FP), R0 diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index a2242b2057..3540d511bf 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -196,7 +196,7 @@ func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, att } } - hasRawVforkSyscall := runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "s390x" + hasRawVforkSyscall := runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64" // About to call fork. // No more allocation or calls of non-assembly functions. diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index 1ea48892ba..95065bfe2d 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -155,6 +155,4 @@ const ( SYS_EPOLL_WAIT = 1069 ) -func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno) { - panic("not implemented") -} +func rawVforkSyscall(trap, a1 uintptr) (r1 uintptr, err Errno)