From 8ab37b1bafdd0040530f976f45c417029cda6965 Mon Sep 17 00:00:00 2001 From: Jeremy Faller Date: Fri, 17 Apr 2020 16:11:28 -0400 Subject: [PATCH] [dev.link] cmd/link: fallocate space, and remove all msync calls MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The fallocate calls will lower the chances of SIGBUS in the linker, but it might still happen on other unsupported platforms and filesystems. Darwin cmd/compile stats: Munmap 16.0ms ± 8% 0.8ms ± 3% -95.19% (p=0.000 n=8+10) TotalTime 484ms ± 2% 462ms ± 2% -4.52% (p=0.000 n=10+9) Updates #37310 Change-Id: I41c6e490adec26fa1ebee49a5b268828f5ba05e1 Reviewed-on: https://go-review.googlesource.com/c/go/+/228385 Run-TryBot: Jeremy Faller TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/outbuf.go | 9 +++++- src/cmd/link/internal/ld/outbuf_darwin.go | 26 ++++++++++++++++ src/cmd/link/internal/ld/outbuf_linux.go | 11 +++++++ src/cmd/link/internal/ld/outbuf_mmap.go | 31 +++++++------------ .../link/internal/ld/outbuf_nofallocate.go | 13 ++++++++ src/cmd/link/internal/ld/outbuf_nommap.go | 3 +- src/cmd/link/internal/ld/outbuf_test.go | 3 ++ src/cmd/link/internal/ld/outbuf_windows.go | 7 ----- 8 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 src/cmd/link/internal/ld/outbuf_darwin.go create mode 100644 src/cmd/link/internal/ld/outbuf_linux.go create mode 100644 src/cmd/link/internal/ld/outbuf_nofallocate.go diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index f043168f1a..b58dee368b 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -13,6 +13,13 @@ import ( "os" ) +// If fallocate is not supported on this platform, return this error. +// Note this is the same error returned by filesystems that don't support +// fallocate, and that is intentional. The error is ignored where needed, and +// OutBuf writes to heap memory. +const fallocateNotSupportedErr = "operation not supported" +const outbufMode = 0775 + // OutBuf is a buffered file writer. // // It is simlar to the Writer in cmd/internal/bio with a few small differences. @@ -70,7 +77,7 @@ func (out *OutBuf) Open(name string) error { if out.f != nil { return errors.New("cannot open more than one file") } - f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775) + f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, outbufMode) if err != nil { return err } diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go new file mode 100644 index 0000000000..299902ec62 --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_darwin.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import ( + "syscall" + "unsafe" +) + +func (out *OutBuf) fallocate(size uint64) error { + store := &syscall.Fstore_t{ + Flags: syscall.F_ALLOCATEALL, + Posmode: syscall.F_PEOFPOSMODE, + Offset: 0, + Length: int64(size), + } + + _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(out.f.Fd()), syscall.F_PREALLOCATE, uintptr(unsafe.Pointer(store))) + if err != 0 { + return err + } + + return nil +} diff --git a/src/cmd/link/internal/ld/outbuf_linux.go b/src/cmd/link/internal/ld/outbuf_linux.go new file mode 100644 index 0000000000..93e621a70f --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_linux.go @@ -0,0 +1,11 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ld + +import "syscall" + +func (out *OutBuf) fallocate(size uint64) error { + return syscall.Fallocate(int(out.f.Fd()), outbufMode, 0, int64(size)) +} diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go index a2493d7d16..e6ee041abb 100644 --- a/src/cmd/link/internal/ld/outbuf_mmap.go +++ b/src/cmd/link/internal/ld/outbuf_mmap.go @@ -8,11 +8,19 @@ package ld import ( "syscall" - "unsafe" ) func (out *OutBuf) Mmap(filesize uint64) error { - err := out.f.Truncate(int64(filesize)) + err := out.fallocate(filesize) + if err != nil { + // Some file systems do not support fallocate. We ignore that error as linking + // can still take place, but you might SIGBUS when you write to the mmapped + // area. + if err.Error() != fallocateNotSupportedErr { + return err + } + } + err = out.f.Truncate(int64(filesize)) if err != nil { Exitf("resize output file failed: %v", err) } @@ -24,27 +32,10 @@ func (out *OutBuf) munmap() { if out.buf == nil { return } - err := out.Msync() - if err != nil { - Exitf("msync output file failed: %v", err) - } syscall.Munmap(out.buf) out.buf = nil - _, err = out.f.Seek(out.off, 0) + _, err := out.f.Seek(out.off, 0) if err != nil { Exitf("seek output file failed: %v", err) } } - -func (out *OutBuf) Msync() error { - if out.buf == nil { - return nil - } - // TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC. - // It is excluded from the build tag for now. - _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_SYNC) - if errno != 0 { - return errno - } - return nil -} diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go new file mode 100644 index 0000000000..51b4fe7aff --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nofallocate.go @@ -0,0 +1,13 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !darwin,!linux + +package ld + +import "errors" + +func (out *OutBuf) fallocate(size uint64) error { + return errors.New(fallocateNotSupportedErr) +} diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go index 472fca22d7..51218d8ae7 100644 --- a/src/cmd/link/internal/ld/outbuf_nommap.go +++ b/src/cmd/link/internal/ld/outbuf_nommap.go @@ -12,5 +12,4 @@ func (out *OutBuf) Mmap(filesize uint64) error { return nil } -func (out *OutBuf) munmap() { panic("unreachable") } -func (out *OutBuf) Msync() error { panic("unreachable") } +func (out *OutBuf) munmap() { panic("unreachable") } diff --git a/src/cmd/link/internal/ld/outbuf_test.go b/src/cmd/link/internal/ld/outbuf_test.go index 58f9b10cfa..d8c21426b3 100644 --- a/src/cmd/link/internal/ld/outbuf_test.go +++ b/src/cmd/link/internal/ld/outbuf_test.go @@ -33,6 +33,9 @@ func TestMMap(t *testing.T) { if err := ob.Mmap(1 << 20); err != nil { t.Errorf("error mmapping file %v", err) } + if !ob.isMmapped() { + t.Errorf("should be mmapped") + } } // TestWriteLoc ensures that the math surrounding writeLoc is correct. diff --git a/src/cmd/link/internal/ld/outbuf_windows.go b/src/cmd/link/internal/ld/outbuf_windows.go index fc4fc5fb3b..a7140cce38 100644 --- a/src/cmd/link/internal/ld/outbuf_windows.go +++ b/src/cmd/link/internal/ld/outbuf_windows.go @@ -41,10 +41,3 @@ func (out *OutBuf) munmap() { Exitf("UnmapViewOfFile failed: %v", err) } } - -func (out *OutBuf) Msync() error { - if out.buf == nil { - return nil - } - return syscall.FlushViewOfFile(uintptr(unsafe.Pointer(&out.buf[0])), 0) -} -- 2.50.0