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
|
||||
// setting SetNonblock and CloseOnExec.
|
||||
|
||||
//go:build dragonfly || freebsd || (linux && !arm) || netbsd || openbsd || solaris
|
||||
//go:build dragonfly || freebsd || (linux && !arm) || netbsd || openbsd
|
||||
|
||||
package poll
|
||||
|
||||
|
|
@ -15,35 +15,8 @@ import "syscall"
|
|||
// descriptor as nonblocking and close-on-exec.
|
||||
func accept(s int) (int, syscall.Sockaddr, string, error) {
|
||||
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 {
|
||||
return -1, nil, "accept", err
|
||||
}
|
||||
if err = syscall.SetNonblock(ns, true); err != nil {
|
||||
CloseFunc(ns)
|
||||
return -1, nil, "setnonblock", err
|
||||
return -1, nil, "accept4", err
|
||||
}
|
||||
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
|
||||
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.
|
||||
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_openat openat "libc.so"
|
||||
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.so"
|
||||
//go:cgo_import_dynamic libc_uname uname "libc.so"
|
||||
|
||||
const (
|
||||
AT_REMOVEDIR = 0x1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !linux
|
||||
//go:build !linux && !solaris
|
||||
|
||||
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
|
||||
// setting SetNonblock and CloseOnExec.
|
||||
|
||||
//go:build dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
//go:build dragonfly || freebsd || linux || netbsd || openbsd
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"internal/poll"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
|
@ -19,30 +18,8 @@ import (
|
|||
// descriptor as nonblocking and close-on-exec.
|
||||
func sysSocket(family, sotype, proto int) (int, error) {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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