aboutsummaryrefslogtreecommitdiff
path: root/src/os
diff options
context:
space:
mode:
authorAndy Pan <i@andypan.me>2024-08-10 11:26:02 +0800
committerGopher Robot <gobot@golang.org>2024-08-15 19:23:02 +0000
commitf7cdadafbee0c7d78fcfd6c5281a82c6c7ac2a50 (patch)
tree76c5707803833139e186f06a4e903b1b32791dab /src/os
parent2693f77b3583585172810427e12a634b28d34493 (diff)
downloadgo-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.go9
-rw-r--r--src/os/readfrom_freebsd_test.go57
-rw-r--r--src/os/readfrom_unix_test.go2
-rw-r--r--src/os/zero_copy_freebsd.go57
-rw-r--r--src/os/zero_copy_stub.go2
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