aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Burkert <ben@benburkert.com>2018-05-21 19:28:19 -0700
committerBrad Fitzpatrick <bradfitz@golang.org>2018-05-23 01:45:00 +0000
commit92bdfab795a1245d8b81a6e841d5e5aa52f3a790 (patch)
treee55617aed5b678258acb87e150c46889e49a5346 /src
parent132900982c2b28470559afcdc43f517cdf285e9c (diff)
downloadgo-92bdfab795a1245d8b81a6e841d5e5aa52f3a790.tar.xz
internal/poll: disable splice on old linux versions
The splice syscall is buggy prior to linux 2.6.29. Instead of returning 0 when reading a closed socket, it returns EAGAIN. While it is possible to detect this (HAProxy falls back to recv), it is simpiler to avoid using splice all together. the "fcntl(fd, F_GETPIPE_SZ)" syscall is used detect buggy versions of splice as the syscall returns EINVAL on versions prior to 2.6.35. Fixes #25486 Change-Id: I860c029f13de2b09e95a7ba39b76ac7fca91a195 Reviewed-on: https://go-review.googlesource.com/113999 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/internal/poll/splice_linux.go57
1 files changed, 28 insertions, 29 deletions
diff --git a/src/internal/poll/splice_linux.go b/src/internal/poll/splice_linux.go
index 7ebd548a97..5874f79a56 100644
--- a/src/internal/poll/splice_linux.go
+++ b/src/internal/poll/splice_linux.go
@@ -4,7 +4,11 @@
package poll
-import "syscall"
+import (
+ "sync/atomic"
+ "syscall"
+ "unsafe"
+)
const (
// spliceNonblock makes calls to splice(2) non-blocking.
@@ -134,43 +138,38 @@ func splice(out int, in int, max int, flags int) (int, error) {
return int(n), err
}
+var disableSplice unsafe.Pointer
+
// newTempPipe sets up a temporary pipe for a splice operation.
func newTempPipe() (prfd, pwfd int, sc string, err error) {
+ p := (*bool)(atomic.LoadPointer(&disableSplice))
+ if p != nil && *p {
+ return -1, -1, "splice", syscall.EINVAL
+ }
+
var fds [2]int
+ // pipe2 was added in 2.6.27 and our minimum requirement is 2.6.23, so it
+ // might not be implemented. Falling back to pipe is possible, but prior to
+ // 2.6.29 splice returns -EAGAIN instead of 0 when the connection is
+ // closed.
const flags = syscall.O_CLOEXEC | syscall.O_NONBLOCK
if err := syscall.Pipe2(fds[:], flags); err != nil {
- // pipe2 was added in 2.6.27 and our minimum requirement
- // is 2.6.23, so it might not be implemented.
- if err == syscall.ENOSYS {
- return newTempPipeFallback(fds[:])
- }
return -1, -1, "pipe2", err
}
- return fds[0], fds[1], "", nil
-}
-// newTempPipeFallback is a fallback for newTempPipe, for systems
-// which do not support pipe2.
-func newTempPipeFallback(fds []int) (prfd, pwfd int, sc string, err error) {
- syscall.ForkLock.RLock()
- defer syscall.ForkLock.RUnlock()
- if err := syscall.Pipe(fds); err != nil {
- return -1, -1, "pipe", err
- }
- prfd, pwfd = fds[0], fds[1]
- syscall.CloseOnExec(prfd)
- syscall.CloseOnExec(pwfd)
- if err := syscall.SetNonblock(prfd, true); err != nil {
- CloseFunc(prfd)
- CloseFunc(pwfd)
- return -1, -1, "setnonblock", err
- }
- if err := syscall.SetNonblock(pwfd, true); err != nil {
- CloseFunc(prfd)
- CloseFunc(pwfd)
- return -1, -1, "setnonblock", err
+ if p == nil {
+ p = new(bool)
+ defer atomic.StorePointer(&disableSplice, unsafe.Pointer(p))
+
+ // F_GETPIPE_SZ was added in 2.6.35, which does not have the -EAGAIN bug.
+ if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fds[0]), syscall.F_GETPIPE_SZ, 0); errno != 0 {
+ *p = true
+ destroyTempPipe(fds[0], fds[1])
+ return -1, -1, "fcntl", errno
+ }
}
- return prfd, pwfd, "", nil
+
+ return fds[0], fds[1], "", nil
}
// destroyTempPipe destroys a temporary pipe.