From: Ben Burkert Date: Tue, 22 May 2018 02:28:19 +0000 (-0700) Subject: internal/poll: disable splice on old linux versions X-Git-Tag: go1.11beta1~344 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=92bdfab795a1245d8b81a6e841d5e5aa52f3a790;p=gostls13.git 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 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- 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.