Skip to content

Commit

Permalink
[release-branch.go1.15] internal/poll: use copy_file_range only on Li…
Browse files Browse the repository at this point in the history
…nux 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 1c7650a)
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>
  • Loading branch information
tklauser authored and dmitshur committed Nov 20, 2020
1 parent efd204c commit 20f50bb
Showing 1 changed file with 48 additions and 2 deletions.
50 changes: 48 additions & 2 deletions src/internal/poll/copy_file_range_linux.go
Expand Up @@ -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
Expand Down

0 comments on commit 20f50bb

Please sign in to comment.