Commit 8d6a455d authored by Andrei Tudor Călin's avatar Andrei Tudor Călin Committed by Brad Fitzpatrick

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: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent c22c7607
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment