From fa95a1d8e7eda9ab90a7fd29785cad0ae7d816e2 Mon Sep 17 00:00:00 2001
From: jingrui <jingrui@huawei.com>
Date: Wed, 27 Nov 2019 09:54:22 +0800
Subject: [PATCH 1/6] 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 <howard.zhang@arm.com> and Bin Lu <bin.lu@arm.com>

Change-Id: Ia99d81d877f564ec60d19f17e596276836576eaf
Reviewed-on: https://go-review.googlesource.com/c/go/+/189418
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
---
 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 7edeafc..fb22f8d 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 a2242b2..3540d51 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 48ad0bb..89b2ab2 100644
--- a/src/syscall/syscall_linux_arm64.go
+++ b/src/syscall/syscall_linux_arm64.go
@@ -154,6 +154,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)
-- 
2.17.1

