diff options
| author | Andy Pan <i@andypan.me> | 2024-08-10 11:26:02 +0800 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-08-15 19:23:02 +0000 |
| commit | f7cdadafbee0c7d78fcfd6c5281a82c6c7ac2a50 (patch) | |
| tree | 76c5707803833139e186f06a4e903b1b32791dab /src/os | |
| parent | 2693f77b3583585172810427e12a634b28d34493 (diff) | |
| download | go-f7cdadafbee0c7d78fcfd6c5281a82c6c7ac2a50.tar.xz | |
internal,os: employ copy_file_range(2) for file-to-file copying on FreeBSD
FreeBSD 13.0 introduced the Linux-compatible copy_file_range(2) system call,
we should make use of it.
Ref:
https://www.gnu.org/software/gnulib/manual/html_node/copy_005ffile_005frange.html
https://reviews.freebsd.org/D20584?id=60021
https://man.freebsd.org/cgi/man.cgi?copy_file_range(2)
Change-Id: I75edb5629717289c8887be436613d3a8b3820bdc
Reviewed-on: https://go-review.googlesource.com/c/go/+/604655
Run-TryBot: Andy Pan <panjf2000@gmail.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Diffstat (limited to 'src/os')
| -rw-r--r-- | src/os/export_freebsd_test.go | 9 | ||||
| -rw-r--r-- | src/os/readfrom_freebsd_test.go | 57 | ||||
| -rw-r--r-- | src/os/readfrom_unix_test.go | 2 | ||||
| -rw-r--r-- | src/os/zero_copy_freebsd.go | 57 | ||||
| -rw-r--r-- | src/os/zero_copy_stub.go | 2 |
5 files changed, 125 insertions, 2 deletions
diff --git a/src/os/export_freebsd_test.go b/src/os/export_freebsd_test.go new file mode 100644 index 0000000000..56bfcc6c71 --- /dev/null +++ b/src/os/export_freebsd_test.go @@ -0,0 +1,9 @@ +// Copyright 2024 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 os + +var ( + PollCopyFileRangeP = &pollCopyFileRange +) diff --git a/src/os/readfrom_freebsd_test.go b/src/os/readfrom_freebsd_test.go new file mode 100644 index 0000000000..186049951f --- /dev/null +++ b/src/os/readfrom_freebsd_test.go @@ -0,0 +1,57 @@ +// Copyright 2024 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 os_test + +import ( + "internal/poll" + . "os" + "testing" +) + +var ( + copyFileTests = []copyFileTestFunc{newCopyFileRangeTest} + copyFileHooks = []copyFileTestHook{hookCopyFileRange} +) + +func testCopyFiles(t *testing.T, size, limit int64) { + testCopyFileRange(t, size, limit) +} + +func testCopyFileRange(t *testing.T, size int64, limit int64) { + dst, src, data, hook, name := newCopyFileRangeTest(t, size) + testCopyFile(t, dst, src, data, hook, limit, name) +} + +// newCopyFileRangeTest initializes a new test for copy_file_range. +// It hooks package os' call to poll.CopyFileRange and returns the hook, +// so it can be inspected. +func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte, hook *copyFileHook, name string) { + t.Helper() + + name = "newCopyFileRangeTest" + + dst, src, data = newCopyFileTest(t, size) + hook, _ = hookCopyFileRange(t) + + return +} + +func hookCopyFileRange(t *testing.T) (hook *copyFileHook, name string) { + name = "hookCopyFileRange" + + hook = new(copyFileHook) + orig := *PollCopyFileRangeP + t.Cleanup(func() { + *PollCopyFileRangeP = orig + }) + *PollCopyFileRangeP = func(dst, src *poll.FD, remain int64) (int64, bool, error) { + hook.called = true + hook.dstfd = dst.Sysfd + hook.srcfd = src.Sysfd + hook.written, hook.handled, hook.err = orig(dst, src, remain) + return hook.written, hook.handled, hook.err + } + return +} diff --git a/src/os/readfrom_unix_test.go b/src/os/readfrom_unix_test.go index 9ed633639a..98a4e6af81 100644 --- a/src/os/readfrom_unix_test.go +++ b/src/os/readfrom_unix_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux || solaris +//go:build freebsd || linux || solaris package os_test diff --git a/src/os/zero_copy_freebsd.go b/src/os/zero_copy_freebsd.go new file mode 100644 index 0000000000..4751ca46be --- /dev/null +++ b/src/os/zero_copy_freebsd.go @@ -0,0 +1,57 @@ +// Copyright 2024 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 os + +import ( + "internal/poll" + "io" +) + +var pollCopyFileRange = poll.CopyFileRange + +func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) { + return 0, false, nil +} + +func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) { + // copy_file_range(2) doesn't supports destinations opened with + // O_APPEND, so don't bother to try zero-copy with these system calls. + // + // Visit https://man.freebsd.org/cgi/man.cgi?copy_file_range(2)#ERRORS for details. + if f.appendMode { + return 0, false, nil + } + + var ( + remain int64 + lr *io.LimitedReader + ) + if lr, r, remain = tryLimitedReader(r); remain <= 0 { + return 0, true, nil + } + + var src *File + switch v := r.(type) { + case *File: + src = v + case fileWithoutWriteTo: + src = v.File + default: + return 0, false, nil + } + + if src.checkValid("ReadFrom") != nil { + // Avoid returning the error as we report handled as false, + // leave further error handling as the responsibility of the caller. + return 0, false, nil + } + + written, handled, err = pollCopyFileRange(&f.pfd, &src.pfd, remain) + if lr != nil { + lr.N -= written + } + + return written, handled, wrapSyscallError("copy_file_range", err) +} diff --git a/src/os/zero_copy_stub.go b/src/os/zero_copy_stub.go index fb70124fca..0470a205ef 100644 --- a/src/os/zero_copy_stub.go +++ b/src/os/zero_copy_stub.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !linux && !solaris +//go:build !freebsd && !linux && !solaris package os |
