mirror of
https://github.com/golang/go
synced 2024-09-06 00:19:45 +00:00
net: don't use splice for unix{packet,gram} connections
As pointed out in the aftermath of CL 113997, splice is not supported for SOCK_SEQPACKET or SOCK_DGRAM unix sockets. Don't call poll.Splice in those cases. Change-Id: Ieab18fb0ae706fdeb249e3f54d51a3292e3ead62 Reviewed-on: https://go-review.googlesource.com/136635 Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
c22c7607d3
commit
8d6a455df4
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
// splice transfers data from r to c using the splice system call to minimize
|
||||
// copies from and to userspace. c must be a TCP connection. Currently, splice
|
||||
// is only enabled if r is a TCP or Unix connection.
|
||||
// is only enabled if r is a TCP or a stream-oriented Unix connection.
|
||||
//
|
||||
// If splice returns handled == false, it has performed no work.
|
||||
func splice(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
||||
|
@ -28,6 +28,9 @@ func splice(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
|||
if tc, ok := r.(*TCPConn); ok {
|
||||
s = tc.fd
|
||||
} else if uc, ok := r.(*UnixConn); ok {
|
||||
if uc.fd.net != "unix" {
|
||||
return 0, nil, false
|
||||
}
|
||||
s = uc.fd
|
||||
} else {
|
||||
return 0, nil, false
|
||||
|
|
|
@ -24,6 +24,8 @@ func TestSplice(t *testing.T) {
|
|||
t.Skip("skipping unix-to-tcp tests")
|
||||
}
|
||||
t.Run("unix-to-tcp", func(t *testing.T) { testSplice(t, "unix", "tcp") })
|
||||
t.Run("no-unixpacket", testSpliceNoUnixpacket)
|
||||
t.Run("no-unixgram", testSpliceNoUnixgram)
|
||||
}
|
||||
|
||||
func testSplice(t *testing.T, upNet, downNet string) {
|
||||
|
@ -208,6 +210,56 @@ func testSpliceIssue25985(t *testing.T, upNet, downNet string) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func testSpliceNoUnixpacket(t *testing.T) {
|
||||
clientUp, serverUp, err := spliceTestSocketPair("unixpacket")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer clientUp.Close()
|
||||
defer serverUp.Close()
|
||||
clientDown, serverDown, err := spliceTestSocketPair("tcp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer clientDown.Close()
|
||||
defer serverDown.Close()
|
||||
// If splice called poll.Splice here, we'd get err == syscall.EINVAL
|
||||
// and handled == false. If poll.Splice gets an EINVAL on the first
|
||||
// try, it assumes the kernel it's running on doesn't support splice
|
||||
// for unix sockets and returns handled == false. This works for our
|
||||
// purposes by somewhat of an accident, but is not entirely correct.
|
||||
//
|
||||
// What we want is err == nil and handled == false, i.e. we never
|
||||
// called poll.Splice, because we know the unix socket's network.
|
||||
_, err, handled := splice(serverDown.(*TCPConn).fd, serverUp)
|
||||
if err != nil || handled != false {
|
||||
t.Fatalf("got err = %v, handled = %t, want nil error, handled == false", err, handled)
|
||||
}
|
||||
}
|
||||
|
||||
func testSpliceNoUnixgram(t *testing.T) {
|
||||
addr, err := ResolveUnixAddr("unixgram", testUnixAddr())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
up, err := ListenUnixgram("unixgram", addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer up.Close()
|
||||
clientDown, serverDown, err := spliceTestSocketPair("tcp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer clientDown.Close()
|
||||
defer serverDown.Close()
|
||||
// Analogous to testSpliceNoUnixpacket.
|
||||
_, err, handled := splice(serverDown.(*TCPConn).fd, up)
|
||||
if err != nil || handled != false {
|
||||
t.Fatalf("got err = %v, handled = %t, want nil error, handled == false", err, handled)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSplice(b *testing.B) {
|
||||
testHookUninstaller.Do(uninstallTestHooks)
|
||||
|
||||
|
|
Loading…
Reference in a new issue