mirror of https://github.com/golang/go.git
net: separate the Solaris fast/slow path of setting SOCK_* from others
Along with the removal of the slow path from Linux and *BSD. For #59359 Change-Id: I6c79594252e5e5f1c1c57c11e09458fcae3793d2 Reviewed-on: https://go-review.googlesource.com/c/go/+/577175 Reviewed-by: Damien Neil <dneil@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Andy Pan <panjf2000@gmail.com>
This commit is contained in:
parent
16df5330e4
commit
37f482223f
|
|
@ -5,7 +5,7 @@
|
||||||
// This file implements accept for platforms that provide a fast path for
|
// This file implements accept for platforms that provide a fast path for
|
||||||
// setting SetNonblock and CloseOnExec.
|
// setting SetNonblock and CloseOnExec.
|
||||||
|
|
||||||
//go:build dragonfly || freebsd || (linux && !arm) || netbsd || openbsd || solaris
|
//go:build dragonfly || freebsd || (linux && !arm) || netbsd || openbsd
|
||||||
|
|
||||||
package poll
|
package poll
|
||||||
|
|
||||||
|
|
@ -15,35 +15,8 @@ import "syscall"
|
||||||
// descriptor as nonblocking and close-on-exec.
|
// descriptor as nonblocking and close-on-exec.
|
||||||
func accept(s int) (int, syscall.Sockaddr, string, error) {
|
func accept(s int) (int, syscall.Sockaddr, string, error) {
|
||||||
ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
|
ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
|
||||||
// TODO: We can remove the fallback on Linux and *BSD,
|
|
||||||
// as currently supported versions all support accept4
|
|
||||||
// with SOCK_CLOEXEC, but Solaris does not. See issue #59359.
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return ns, sa, "", nil
|
|
||||||
default: // errors other than the ones listed
|
|
||||||
return -1, sa, "accept4", err
|
|
||||||
case syscall.ENOSYS: // syscall missing
|
|
||||||
case syscall.EINVAL: // some Linux use this instead of ENOSYS
|
|
||||||
case syscall.EACCES: // some Linux use this instead of ENOSYS
|
|
||||||
case syscall.EFAULT: // some Linux use this instead of ENOSYS
|
|
||||||
}
|
|
||||||
|
|
||||||
// See ../syscall/exec_unix.go for description of ForkLock.
|
|
||||||
// It is probably okay to hold the lock across syscall.Accept
|
|
||||||
// because we have put fd.sysfd into non-blocking mode.
|
|
||||||
// However, a call to the File method will put it back into
|
|
||||||
// blocking mode. We can't take that risk, so no use of ForkLock here.
|
|
||||||
ns, sa, err = AcceptFunc(s)
|
|
||||||
if err == nil {
|
|
||||||
syscall.CloseOnExec(ns)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, nil, "accept", err
|
return -1, nil, "accept4", err
|
||||||
}
|
|
||||||
if err = syscall.SetNonblock(ns, true); err != nil {
|
|
||||||
CloseFunc(ns)
|
|
||||||
return -1, nil, "setnonblock", err
|
|
||||||
}
|
}
|
||||||
return ns, sa, "", nil
|
return ns, sa, "", nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements accept for platforms that provide a fast path for
|
||||||
|
// setting SetNonblock and CloseOnExec, but don't necessarily have accept4.
|
||||||
|
// The accept4(3c) function was added to Oracle Solaris in the Solaris 11.4.0
|
||||||
|
// release. Thus, on releases prior to 11.4, we fall back to the combination
|
||||||
|
// of accept(3c) and fcntl(2).
|
||||||
|
|
||||||
|
package poll
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/syscall/unix"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wrapper around the accept system call that marks the returned file
|
||||||
|
// descriptor as nonblocking and close-on-exec.
|
||||||
|
func accept(s int) (int, syscall.Sockaddr, string, error) {
|
||||||
|
// Perform a cheap test and try the fast path first.
|
||||||
|
if unix.SupportAccept4() {
|
||||||
|
ns, sa, err := Accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
|
||||||
|
if err != nil {
|
||||||
|
return -1, nil, "accept4", err
|
||||||
|
}
|
||||||
|
return ns, sa, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See ../syscall/exec_unix.go for description of ForkLock.
|
||||||
|
// It is probably okay to hold the lock across syscall.Accept
|
||||||
|
// because we have put fd.sysfd into non-blocking mode.
|
||||||
|
// However, a call to the File method will put it back into
|
||||||
|
// blocking mode. We can't take that risk, so no use of ForkLock here.
|
||||||
|
ns, sa, err := AcceptFunc(s)
|
||||||
|
if err == nil {
|
||||||
|
syscall.CloseOnExec(ns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return -1, nil, "accept", err
|
||||||
|
}
|
||||||
|
if err = syscall.SetNonblock(ns, true); err != nil {
|
||||||
|
CloseFunc(ns)
|
||||||
|
return -1, nil, "setnonblock", err
|
||||||
|
}
|
||||||
|
return ns, sa, "", nil
|
||||||
|
}
|
||||||
|
|
@ -8,3 +8,6 @@
|
||||||
|
|
||||||
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
||||||
JMP syscall·sysvicall6(SB)
|
JMP syscall·sysvicall6(SB)
|
||||||
|
|
||||||
|
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·rawSysvicall6(SB)
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,13 @@ import "syscall"
|
||||||
// Implemented as sysvicall6 in runtime/syscall_solaris.go.
|
// Implemented as sysvicall6 in runtime/syscall_solaris.go.
|
||||||
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||||
|
|
||||||
|
// Implemented as rawsysvicall6 in runtime/syscall_solaris.go.
|
||||||
|
func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||||||
|
|
||||||
//go:cgo_import_dynamic libc_fstatat fstatat "libc.so"
|
//go:cgo_import_dynamic libc_fstatat fstatat "libc.so"
|
||||||
//go:cgo_import_dynamic libc_openat openat "libc.so"
|
//go:cgo_import_dynamic libc_openat openat "libc.so"
|
||||||
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.so"
|
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.so"
|
||||||
|
//go:cgo_import_dynamic libc_uname uname "libc.so"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AT_REMOVEDIR = 0x1
|
AT_REMOVEDIR = 0x1
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !linux
|
//go:build !linux && !solaris
|
||||||
|
|
||||||
package unix
|
package unix
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname procUname libc_uname
|
||||||
|
|
||||||
|
var procUname uintptr
|
||||||
|
|
||||||
|
// utsname represents the fields of a struct utsname defined in <sys/utsname.h>.
|
||||||
|
type utsname struct {
|
||||||
|
Sysname [257]byte
|
||||||
|
Nodename [257]byte
|
||||||
|
Release [257]byte
|
||||||
|
Version [257]byte
|
||||||
|
Machine [257]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// KernelVersion returns major and minor kernel version numbers
|
||||||
|
// parsed from the syscall.Uname's Version field, or (0, 0) if the
|
||||||
|
// version can't be obtained or parsed.
|
||||||
|
func KernelVersion() (major int, minor int) {
|
||||||
|
var un utsname
|
||||||
|
_, _, errno := rawSyscall6(uintptr(unsafe.Pointer(&procUname)), 1, uintptr(unsafe.Pointer(&un)), 0, 0, 0, 0, 0)
|
||||||
|
if errno != 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// The version string is in the form "<version>.<update>.<sru>.<build>.<reserved>"
|
||||||
|
// on Solaris: https://blogs.oracle.com/solaris/post/whats-in-a-uname-
|
||||||
|
// Therefore, we use the Version field on Solaris when available.
|
||||||
|
ver := un.Version[:]
|
||||||
|
if runtime.GOOS == "illumos" {
|
||||||
|
// Illumos distributions use different formats without a parsable
|
||||||
|
// and unified pattern for the Version field while Release level
|
||||||
|
// string is guaranteed to be in x.y or x.y.z format regardless of
|
||||||
|
// whether the kernel is Solaris or illumos.
|
||||||
|
ver = un.Release[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
parseNext := func() (n int) {
|
||||||
|
for i, c := range ver {
|
||||||
|
if c == '.' {
|
||||||
|
ver = ver[i+1:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
n = n*10 + int(c-'0')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ver = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
major = parseNext()
|
||||||
|
minor = parseNext()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportSockNonblockCloexec tests if SOCK_NONBLOCK and SOCK_CLOEXEC are supported
|
||||||
|
// for socket() system call, returns true if affirmative.
|
||||||
|
var SupportSockNonblockCloexec = sync.OnceValue(func() bool {
|
||||||
|
// First test if socket() supports SOCK_NONBLOCK and SOCK_CLOEXEC directly.
|
||||||
|
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
|
||||||
|
if err == nil {
|
||||||
|
syscall.Close(s)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL {
|
||||||
|
// Something wrong with socket(), fall back to checking the kernel version.
|
||||||
|
major, minor := KernelVersion()
|
||||||
|
if runtime.GOOS == "illumos" {
|
||||||
|
return major > 5 || (major == 5 && minor >= 11) // minimal requirement is SunOS 5.11
|
||||||
|
}
|
||||||
|
return major > 11 || (major == 11 && minor >= 4)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
// SupportAccept4 tests whether accept4 system call is available.
|
||||||
|
var SupportAccept4 = sync.OnceValue(func() bool {
|
||||||
|
for {
|
||||||
|
// Test if the accept4() is available.
|
||||||
|
_, _, err := syscall.Accept4(0, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
|
||||||
|
if err == syscall.EINTR {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err != syscall.ENOSYS
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build solaris
|
||||||
|
|
||||||
|
package unix_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/syscall/unix"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSupportSockNonblockCloexec(t *testing.T) {
|
||||||
|
// Test that SupportSockNonblockCloexec returns true if socket succeeds with SOCK_NONBLOCK and SOCK_CLOEXEC.
|
||||||
|
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, 0)
|
||||||
|
if err == nil {
|
||||||
|
syscall.Close(s)
|
||||||
|
}
|
||||||
|
wantSock := err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL
|
||||||
|
gotSock := unix.SupportSockNonblockCloexec()
|
||||||
|
if wantSock != gotSock {
|
||||||
|
t.Fatalf("SupportSockNonblockCloexec, got %t; want %t", gotSock, wantSock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that SupportAccept4 returns true if accept4 is available.
|
||||||
|
for {
|
||||||
|
_, _, err = syscall.Accept4(0, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
|
||||||
|
if err != syscall.EINTR {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wantAccept4 := err != syscall.ENOSYS
|
||||||
|
gotAccept4 := unix.SupportAccept4()
|
||||||
|
if wantAccept4 != gotAccept4 {
|
||||||
|
t.Fatalf("SupportAccept4, got %t; want %t", gotAccept4, wantAccept4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that the version returned by KernelVersion matches expectations.
|
||||||
|
major, minor := unix.KernelVersion()
|
||||||
|
t.Logf("Kernel version: %d.%d", major, minor)
|
||||||
|
if runtime.GOOS == "illumos" {
|
||||||
|
if gotSock && gotAccept4 && (major < 5 || (major == 5 && minor < 11)) {
|
||||||
|
t.Fatalf("SupportSockNonblockCloexec and SupportAccept4 are true, but kernel version is older than 5.11, SunOS version: %d.%d", major, minor)
|
||||||
|
}
|
||||||
|
if !gotSock && !gotAccept4 && (major > 5 || (major == 5 && minor >= 11)) {
|
||||||
|
t.Errorf("SupportSockNonblockCloexec and SupportAccept4 are false, but kernel version is 5.11 or newer, SunOS version: %d.%d", major, minor)
|
||||||
|
}
|
||||||
|
} else { // Solaris
|
||||||
|
if gotSock && gotAccept4 && (major < 11 || (major == 11 && minor < 4)) {
|
||||||
|
t.Fatalf("SupportSockNonblockCloexec and SupportAccept4 are true, but kernel version is older than 11.4, Solaris version: %d.%d", major, minor)
|
||||||
|
}
|
||||||
|
if !gotSock && !gotAccept4 && (major > 11 || (major == 11 && minor >= 4)) {
|
||||||
|
t.Errorf("SupportSockNonblockCloexec and SupportAccept4 are false, but kernel version is 11.4 or newer, Solaris version: %d.%d", major, minor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,12 +5,11 @@
|
||||||
// This file implements sysSocket for platforms that provide a fast path for
|
// This file implements sysSocket for platforms that provide a fast path for
|
||||||
// setting SetNonblock and CloseOnExec.
|
// setting SetNonblock and CloseOnExec.
|
||||||
|
|
||||||
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build dragonfly || freebsd || linux || netbsd || openbsd
|
||||||
|
|
||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"internal/poll"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
@ -19,30 +18,8 @@ import (
|
||||||
// descriptor as nonblocking and close-on-exec.
|
// descriptor as nonblocking and close-on-exec.
|
||||||
func sysSocket(family, sotype, proto int) (int, error) {
|
func sysSocket(family, sotype, proto int) (int, error) {
|
||||||
s, err := socketFunc(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
|
s, err := socketFunc(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
|
||||||
// TODO: We can remove the fallback on Linux and *BSD,
|
|
||||||
// as currently supported versions all support accept4
|
|
||||||
// with SOCK_CLOEXEC, but Solaris does not. See issue #59359.
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
return s, nil
|
|
||||||
default:
|
|
||||||
return -1, os.NewSyscallError("socket", err)
|
|
||||||
case syscall.EPROTONOSUPPORT, syscall.EINVAL:
|
|
||||||
}
|
|
||||||
|
|
||||||
// See ../syscall/exec_unix.go for description of ForkLock.
|
|
||||||
syscall.ForkLock.RLock()
|
|
||||||
s, err = socketFunc(family, sotype, proto)
|
|
||||||
if err == nil {
|
|
||||||
syscall.CloseOnExec(s)
|
|
||||||
}
|
|
||||||
syscall.ForkLock.RUnlock()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, os.NewSyscallError("socket", err)
|
return -1, os.NewSyscallError("socket", err)
|
||||||
}
|
}
|
||||||
if err = syscall.SetNonblock(s, true); err != nil {
|
|
||||||
poll.CloseFunc(s)
|
|
||||||
return -1, os.NewSyscallError("setnonblock", err)
|
|
||||||
}
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements sysSocket for platforms that provide a fast path for
|
||||||
|
// setting SetNonblock and CloseOnExec, but don't necessarily support it.
|
||||||
|
// Support for SOCK_* flags as part of the type parameter was added to Oracle
|
||||||
|
// Solaris in the 11.4 release. Thus, on releases prior to 11.4, we fall back
|
||||||
|
// to the combination of socket(3c) and fcntl(2).
|
||||||
|
|
||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"internal/poll"
|
||||||
|
"internal/syscall/unix"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wrapper around the socket system call that marks the returned file
|
||||||
|
// descriptor as nonblocking and close-on-exec.
|
||||||
|
func sysSocket(family, sotype, proto int) (int, error) {
|
||||||
|
// Perform a cheap test and try the fast path first.
|
||||||
|
if unix.SupportSockNonblockCloexec() {
|
||||||
|
s, err := socketFunc(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
|
||||||
|
if err != nil {
|
||||||
|
return -1, os.NewSyscallError("socket", err)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// See ../syscall/exec_unix.go for description of ForkLock.
|
||||||
|
syscall.ForkLock.RLock()
|
||||||
|
s, err := socketFunc(family, sotype, proto)
|
||||||
|
if err == nil {
|
||||||
|
syscall.CloseOnExec(s)
|
||||||
|
}
|
||||||
|
syscall.ForkLock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return -1, os.NewSyscallError("socket", err)
|
||||||
|
}
|
||||||
|
if err = syscall.SetNonblock(s, true); err != nil {
|
||||||
|
poll.CloseFunc(s)
|
||||||
|
return -1, os.NewSyscallError("setnonblock", err)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue