aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Klauser <tklauser@distanz.ch>2020-11-09 11:25:38 +0100
committerDmitri Shuralyov <dmitshur@golang.org>2020-11-20 21:54:27 +0000
commit20f50bba49e6338793a5711dc1c6ed60e4b10df8 (patch)
tree9840852e4e7783d567492fc51ca507aca18df968
parentefd204ccf78e16ee6792079ba70456ed425cb9c3 (diff)
downloadgo-20f50bba49e6338793a5711dc1c6ed60e4b10df8.tar.gz
go-20f50bba49e6338793a5711dc1c6ed60e4b10df8.zip
[release-branch.go1.15] internal/poll: use copy_file_range only on Linux kernel >= 5.3
https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS states: A major rework of the kernel implementation occurred in 5.3. Areas of the API that weren't clearly defined were clarified and the API bounds are much more strictly checked than on earlier kernels. Applications should target the behaviour and requirements of 5.3 kernels. Rather than attempting to detect the file system for source and destination files (which means two additional statfs syscalls) and skip copy_file_range in case of known defects (e.g. CIFS -> CIFS), just assume copy_file_range to be broken on kernels < 5.3. Fixes #42550 Change-Id: I3a531296182c1d6e341772cc9d2be5bf83e52575 Reviewed-on: https://go-review.googlesource.com/c/go/+/268338 Trust: Tobias Klauser <tobias.klauser@gmail.com> Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> (cherry picked from commit 1c7650aa93bd53b7df0bbb34693fc5a16d9f67af) Reviewed-on: https://go-review.googlesource.com/c/go/+/269759 Trust: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
-rw-r--r--src/internal/poll/copy_file_range_linux.go50
1 files changed, 48 insertions, 2 deletions
diff --git a/src/internal/poll/copy_file_range_linux.go b/src/internal/poll/copy_file_range_linux.go
index 24bee614a68..fc34aef4cba 100644
--- a/src/internal/poll/copy_file_range_linux.go
+++ b/src/internal/poll/copy_file_range_linux.go
@@ -10,15 +10,61 @@ import (
"syscall"
)
-var copyFileRangeSupported int32 = 1 // accessed atomically
+var copyFileRangeSupported int32 = -1 // accessed atomically
const maxCopyFileRangeRound = 1 << 30
+func kernelVersion() (major int, minor int) {
+ var uname syscall.Utsname
+ if err := syscall.Uname(&uname); err != nil {
+ return
+ }
+
+ rl := uname.Release
+ var values [2]int
+ vi := 0
+ value := 0
+ for _, c := range rl {
+ if '0' <= c && c <= '9' {
+ value = (value * 10) + int(c-'0')
+ } else {
+ // Note that we're assuming N.N.N here. If we see anything else we are likely to
+ // mis-parse it.
+ values[vi] = value
+ vi++
+ if vi >= len(values) {
+ break
+ }
+ value = 0
+ }
+ }
+ switch vi {
+ case 0:
+ return 0, 0
+ case 1:
+ return values[0], 0
+ case 2:
+ return values[0], values[1]
+ }
+ return
+}
+
// CopyFileRange copies at most remain bytes of data from src to dst, using
// the copy_file_range system call. dst and src must refer to regular files.
func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err error) {
- if atomic.LoadInt32(&copyFileRangeSupported) == 0 {
+ if supported := atomic.LoadInt32(&copyFileRangeSupported); supported == 0 {
return 0, false, nil
+ } else if supported == -1 {
+ major, minor := kernelVersion()
+ if major > 5 || (major == 5 && minor >= 3) {
+ atomic.StoreInt32(&copyFileRangeSupported, 1)
+ } else {
+ // copy_file_range(2) is broken in various ways on kernels older than 5.3,
+ // see issue #42400 and
+ // https://man7.org/linux/man-pages/man2/copy_file_range.2.html#VERSIONS
+ atomic.StoreInt32(&copyFileRangeSupported, 0)
+ return 0, false, nil
+ }
}
for remain > 0 {
max := remain