Break //pkg/tcpip/link/rawfile's dep on //pkg/tcpip & move it to //pkg.

`//pkg/tcpip/link/rawfile` is a package to deal with raw socket and file FDs.
It is not only used for Netstack, but rather just generally useful raw file
manipulation stuff.

This change removes the Unix-error-to-`//pkg/tcpip`-error translation step
from its functions; this is now the responsibility of its callers. Callers
within Netstack now do the translation by themselves; the translation
function is moved to `//pkg/tcpip`.

This allows the `//pkg/tcpip/link/rawfile` package to not depend on
`//pkg/tcpip`, which in turn means the `//pkg/eventfd` package
(which depends on `rawfile`) no longer transitively depends on
`//pkg/tcpip`, which in turns means the `//pkg/unet` package (which
depends on `//pkg/eventfd`) no longer transitively depends on
`//pkg/tcpip`, which in turns means that the `//pkg/eventchannel`
package (which depends on `//pkg/unet`) no longer transitively
depends on `//pkg/tcpip`, which in turns means that the `//pkg/metric`
package (which depends on `//pkg/eventchannel`) no longer transitively
depends on `//pkg/tcpip`, which finally means that the `//pkg/metric`
package can be used within `//pkg/tcpip`. \o/

This changes does not make it use it, it just moves `rawfile`.

PiperOrigin-RevId: 647943618
This commit is contained in:
Etienne Perot
2024-06-29 05:11:53 -07:00
committed by gVisor bot
parent e36c063c7c
commit d59375d82e
26 changed files with 137 additions and 155 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ go_library(
],
deps = [
"//pkg/hostarch",
"//pkg/tcpip/link/rawfile",
"//pkg/rawfile",
"@org_golang_x_sys//unix:go_default_library",
],
)
+4 -4
View File
@@ -21,7 +21,7 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/hostarch"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/rawfile"
)
const sizeofUint64 = 8
@@ -95,9 +95,9 @@ func (ev Eventfd) Wait() error {
// and returns the value read.
func (ev Eventfd) Read() (uint64, error) {
var tmp [sizeofUint64]byte
n, err := rawfile.BlockingReadUntranslated(ev.fd, tmp[:])
if err != 0 {
return 0, err
n, errno := rawfile.BlockingRead(ev.fd, tmp[:])
if errno != 0 {
return 0, errno
}
if n == 0 {
return 0, io.EOF
+19
View File
@@ -0,0 +1,19 @@
load("//tools:defs.bzl", "go_library")
package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)
go_library(
name = "rawfile",
srcs = [
"blockingpoll_amd64.s",
"blockingpoll_arm64.s",
"blockingpoll_noyield_unsafe.go",
"blockingpoll_yield_unsafe.go",
"rawfile_unsafe.go",
],
visibility = ["//visibility:public"],
deps = ["@org_golang_x_sys//unix:go_default_library"],
)
@@ -15,8 +15,7 @@
//go:build linux
// +build linux
// Package rawfile contains utilities for using the netstack with raw host
// files on Linux hosts.
// Package rawfile contains utilities for using raw host files on Linux hosts.
package rawfile
import (
@@ -24,7 +23,6 @@ import (
"unsafe"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip"
)
// SizeofIovec is the size of a unix.Iovec in bytes.
@@ -106,39 +104,28 @@ func GetMTU(name string) (uint32, error) {
// NonBlockingWrite writes the given buffer to a file descriptor. It fails if
// partial data is written.
func NonBlockingWrite(fd int, buf []byte) tcpip.Error {
func NonBlockingWrite(fd int, buf []byte) unix.Errno {
var ptr unsafe.Pointer
if len(buf) > 0 {
ptr = unsafe.Pointer(&buf[0])
}
_, _, e := unix.RawSyscall(unix.SYS_WRITE, uintptr(fd), uintptr(ptr), uintptr(len(buf)))
if e != 0 {
return TranslateErrno(e)
}
return nil
return e
}
// NonBlockingWriteIovec writes iovec to a file descriptor in a single unix.
// It fails if partial data is written.
func NonBlockingWriteIovec(fd int, iovec []unix.Iovec) tcpip.Error {
func NonBlockingWriteIovec(fd int, iovec []unix.Iovec) unix.Errno {
iovecLen := uintptr(len(iovec))
_, _, e := unix.RawSyscall(unix.SYS_WRITEV, uintptr(fd), uintptr(unsafe.Pointer(&iovec[0])), iovecLen)
if e != 0 {
return TranslateErrno(e)
}
return nil
return e
}
// NonBlockingSendMMsg sends multiple messages on a socket.
func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, tcpip.Error) {
func NonBlockingSendMMsg(fd int, msgHdrs []MMsgHdr) (int, unix.Errno) {
n, _, e := unix.RawSyscall6(unix.SYS_SENDMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), unix.MSG_DONTWAIT, 0, 0)
if e != 0 {
return 0, TranslateErrno(e)
}
return int(n), nil
return int(n), e
}
// PollEvent represents the pollfd structure passed to a poll() system call.
@@ -148,22 +135,10 @@ type PollEvent struct {
Revents int16
}
// BlockingRead reads from a file descriptor that is set up as non-blocking. If
// no data is available, it will block in a poll() syscall until the file
// BlockingRead reads from a file descriptor that is set up as non-blocking.
// If no data is available, it will block in a poll() syscall until the file
// descriptor becomes readable.
func BlockingRead(fd int, b []byte) (int, tcpip.Error) {
n, err := BlockingReadUntranslated(fd, b)
if err != 0 {
return n, TranslateErrno(err)
}
return n, nil
}
// BlockingReadUntranslated reads from a file descriptor that is set up as
// non-blocking. If no data is available, it will block in a poll() syscall
// until the file descriptor becomes readable. It returns the raw unix.Errno
// value returned by the underlying syscalls.
func BlockingReadUntranslated(fd int, b []byte) (int, unix.Errno) {
func BlockingRead(fd int, b []byte) (int, unix.Errno) {
for {
n, _, e := unix.RawSyscall(unix.SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
if e == 0 {
@@ -187,21 +162,21 @@ func BlockingReadUntranslated(fd int, b []byte) (int, unix.Errno) {
// available, it will block in a poll() syscall until the file descriptor
// becomes readable or stop is signalled (efd becomes readable). Returns -1 in
// the latter case.
func BlockingReadvUntilStopped(efd int, fd int, iovecs []unix.Iovec) (int, tcpip.Error) {
func BlockingReadvUntilStopped(efd int, fd int, iovecs []unix.Iovec) (int, unix.Errno) {
for {
n, _, e := unix.RawSyscall(unix.SYS_READV, uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
if e == 0 {
return int(n), nil
return int(n), 0
}
if e != 0 && e != unix.EWOULDBLOCK {
return 0, TranslateErrno(e)
return 0, e
}
stopped, e := BlockingPollUntilStopped(efd, fd, unix.POLLIN)
if stopped {
return -1, nil
return -1, e
}
if e != 0 && e != unix.EINTR {
return 0, TranslateErrno(e)
return 0, e
}
}
}
@@ -211,23 +186,23 @@ func BlockingReadvUntilStopped(efd int, fd int, iovecs []unix.Iovec) (int, tcpip
// structures. If no data is available, it will block in a poll() syscall until
// the file descriptor becomes readable or stop is signalled (efd becomes
// readable). Returns -1 in the latter case.
func BlockingRecvMMsgUntilStopped(efd int, fd int, msgHdrs []MMsgHdr) (int, tcpip.Error) {
func BlockingRecvMMsgUntilStopped(efd int, fd int, msgHdrs []MMsgHdr) (int, unix.Errno) {
for {
n, _, e := unix.RawSyscall6(unix.SYS_RECVMMSG, uintptr(fd), uintptr(unsafe.Pointer(&msgHdrs[0])), uintptr(len(msgHdrs)), unix.MSG_DONTWAIT, 0, 0)
if e == 0 {
return int(n), nil
return int(n), e
}
if e != 0 && e != unix.EWOULDBLOCK {
return 0, TranslateErrno(e)
return 0, e
}
stopped, e := BlockingPollUntilStopped(efd, fd, unix.POLLIN)
if stopped {
return -1, nil
return -1, e
}
if e != 0 && e != unix.EINTR {
return 0, TranslateErrno(e)
return 0, e
}
}
}
+11 -2
View File
@@ -35,6 +35,7 @@ go_library(
name = "tcpip",
srcs = [
"errors.go",
"errors_linux.go",
"route_list.go",
"sock_err_list.go",
"socketops.go",
@@ -50,6 +51,7 @@ go_library(
"//pkg/buffer",
"//pkg/sync",
"//pkg/waiter",
"@org_golang_x_sys//unix:go_default_library",
],
)
@@ -75,6 +77,7 @@ deps_test(
"//pkg/log",
"//pkg/pool",
"//pkg/rand",
"//pkg/rawfile",
"//pkg/refs",
"//pkg/sleep",
"//pkg/state",
@@ -117,9 +120,15 @@ deps_test(
go_test(
name = "tcpip_test",
size = "small",
srcs = ["tcpip_test.go"],
srcs = [
"errors_test.go",
"tcpip_test.go",
],
library = ":tcpip",
deps = ["@com_github_google_go_cmp//cmp:go_default_library"],
deps = [
"@com_github_google_go_cmp//cmp:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_test(
+2
View File
@@ -32,6 +32,8 @@ type Error interface {
fmt.Stringer
}
const maxErrno = 134
// LINT.IfChange
// ErrAborted indicates the operation was aborted.
@@ -1,4 +1,4 @@
// Copyright 2018 The gVisor Authors.
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,63 +15,60 @@
//go:build linux
// +build linux
package rawfile
package tcpip
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip"
)
const maxErrno = 134
// TranslateErrno translate an errno from the syscall package into a
// tcpip.Error.
// tcpip Error.
//
// Valid, but unrecognized errnos will be translated to
// *tcpip.ErrInvalidEndpointState (EINVAL).
func TranslateErrno(e unix.Errno) tcpip.Error {
// *ErrInvalidEndpointState (EINVAL). This includes the "zero" value.
func TranslateErrno(e unix.Errno) Error {
switch e {
case unix.EEXIST:
return &tcpip.ErrDuplicateAddress{}
return &ErrDuplicateAddress{}
case unix.ENETUNREACH:
return &tcpip.ErrHostUnreachable{}
return &ErrHostUnreachable{}
case unix.EINVAL:
return &tcpip.ErrInvalidEndpointState{}
return &ErrInvalidEndpointState{}
case unix.EALREADY:
return &tcpip.ErrAlreadyConnecting{}
return &ErrAlreadyConnecting{}
case unix.EISCONN:
return &tcpip.ErrAlreadyConnected{}
return &ErrAlreadyConnected{}
case unix.EADDRINUSE:
return &tcpip.ErrPortInUse{}
return &ErrPortInUse{}
case unix.EADDRNOTAVAIL:
return &tcpip.ErrBadLocalAddress{}
return &ErrBadLocalAddress{}
case unix.EPIPE:
return &tcpip.ErrClosedForSend{}
return &ErrClosedForSend{}
case unix.EWOULDBLOCK:
return &tcpip.ErrWouldBlock{}
return &ErrWouldBlock{}
case unix.ECONNREFUSED:
return &tcpip.ErrConnectionRefused{}
return &ErrConnectionRefused{}
case unix.ETIMEDOUT:
return &tcpip.ErrTimeout{}
return &ErrTimeout{}
case unix.EINPROGRESS:
return &tcpip.ErrConnectStarted{}
return &ErrConnectStarted{}
case unix.EDESTADDRREQ:
return &tcpip.ErrDestinationRequired{}
return &ErrDestinationRequired{}
case unix.ENOTSUP:
return &tcpip.ErrNotSupported{}
return &ErrNotSupported{}
case unix.ENOTTY:
return &tcpip.ErrQueueSizeNotSupported{}
return &ErrQueueSizeNotSupported{}
case unix.ENOTCONN:
return &tcpip.ErrNotConnected{}
return &ErrNotConnected{}
case unix.ECONNRESET:
return &tcpip.ErrConnectionReset{}
return &ErrConnectionReset{}
case unix.ECONNABORTED:
return &tcpip.ErrConnectionAborted{}
return &ErrConnectionAborted{}
case unix.EMSGSIZE:
return &tcpip.ErrMessageTooLong{}
return &ErrMessageTooLong{}
case unix.ENOBUFS:
return &tcpip.ErrNoBufferSpace{}
return &ErrNoBufferSpace{}
default:
return &tcpip.ErrInvalidEndpointState{}
return &ErrInvalidEndpointState{}
}
}
@@ -1,4 +1,4 @@
// Copyright 2020 The gVisor Authors.
// Copyright 2024 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,36 +15,35 @@
//go:build linux
// +build linux
package rawfile
package tcpip
import (
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip"
)
func TestTranslateErrno(t *testing.T) {
for _, test := range []struct {
errno unix.Errno
translated tcpip.Error
translated Error
}{
{
errno: unix.Errno(0),
translated: &tcpip.ErrInvalidEndpointState{},
translated: &ErrInvalidEndpointState{},
},
{
errno: unix.Errno(maxErrno),
translated: &tcpip.ErrInvalidEndpointState{},
translated: &ErrInvalidEndpointState{},
},
{
errno: unix.Errno(514),
translated: &tcpip.ErrInvalidEndpointState{},
translated: &ErrInvalidEndpointState{},
},
{
errno: unix.EEXIST,
translated: &tcpip.ErrDuplicateAddress{},
translated: &ErrDuplicateAddress{},
},
} {
got := TranslateErrno(test.errno)
+1 -1
View File
@@ -21,12 +21,12 @@ go_library(
"//pkg/atomicbitops",
"//pkg/buffer",
"//pkg/rand",
"//pkg/rawfile",
"//pkg/sleep",
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/hash/jenkins",
"//pkg/tcpip/header",
"//pkg/tcpip/link/rawfile",
"//pkg/tcpip/link/stopfd",
"//pkg/tcpip/stack",
"//pkg/tcpip/stack/gro",
+12 -6
View File
@@ -47,10 +47,10 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/rawfile"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -622,7 +622,10 @@ func (e *endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
for _, v := range views {
iovecs = rawfile.AppendIovecFromBytes(iovecs, v, numIovecs)
}
return rawfile.NonBlockingWriteIovec(fd, iovecs)
if errno := rawfile.NonBlockingWriteIovec(fd, iovecs); errno != 0 {
return tcpip.TranslateErrno(errno)
}
return nil
}
func (e *endpoint) sendBatch(batchFDInfo fdInfo, pkts []*stack.PacketBuffer) (int, tcpip.Error) {
@@ -729,9 +732,9 @@ func (e *endpoint) sendBatch(batchFDInfo fdInfo, pkts []*stack.PacketBuffer) (in
packets++
} else {
for len(mmsgHdrs) > 0 {
sent, err := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs)
if err != nil {
return packets, err
sent, errno := rawfile.NonBlockingSendMMsg(batchFD, mmsgHdrs)
if errno != 0 {
return packets, tcpip.TranslateErrno(errno)
}
packets += sent
mmsgHdrs = mmsgHdrs[sent:]
@@ -786,7 +789,10 @@ func (e *endpoint) WritePackets(pkts stack.PacketBufferList) (int, tcpip.Error)
// InjectOutbound implements stack.InjectableEndpoint.InjectOutbound.
func (e *endpoint) InjectOutbound(dest tcpip.Address, packet *buffer.View) tcpip.Error {
return rawfile.NonBlockingWrite(e.fds[0].fd, packet.AsSlice())
if errno := rawfile.NonBlockingWrite(e.fds[0].fd, packet.AsSlice()); errno != 0 {
return tcpip.TranslateErrno(errno)
}
return nil
}
// dispatchLoop reads packets from the file descriptor in a loop and dispatches
+2 -2
View File
@@ -23,9 +23,9 @@ import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/rawfile"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/link/stopfd"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -148,7 +148,7 @@ func (d *packetMMapDispatcher) readMMappedPackets() (stack.PacketBufferList, boo
if errno == unix.EINTR {
continue
}
return pkts, stopped, rawfile.TranslateErrno(errno)
return pkts, stopped, tcpip.TranslateErrno(errno)
}
if stopped {
return pkts, true, nil
+10 -7
View File
@@ -20,9 +20,9 @@ package fdbased
import (
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/rawfile"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/link/stopfd"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/stack/gro"
@@ -184,9 +184,9 @@ func (d *readVDispatcher) release() {
// dispatch reads one packet from the file descriptor and dispatches it.
func (d *readVDispatcher) dispatch() (bool, tcpip.Error) {
n, err := rawfile.BlockingReadvUntilStopped(d.EFD, d.fd, d.buf.nextIovecs())
if n <= 0 || err != nil {
return false, err
n, errno := rawfile.BlockingReadvUntilStopped(d.EFD, d.fd, d.buf.nextIovecs())
if n <= 0 || errno != 0 {
return false, tcpip.TranslateErrno(errno)
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
@@ -285,9 +285,12 @@ func (d *recvMMsgDispatcher) dispatch() (bool, tcpip.Error) {
d.msgHdrs[k].Msg.SetIovlen(iovLen)
}
nMsgs, err := rawfile.BlockingRecvMMsgUntilStopped(d.EFD, d.fd, d.msgHdrs)
if nMsgs == -1 || err != nil {
return false, err
nMsgs, errno := rawfile.BlockingRecvMMsgUntilStopped(d.EFD, d.fd, d.msgHdrs)
if errno != 0 {
return false, tcpip.TranslateErrno(errno)
}
if nMsgs == -1 {
return false, nil
}
// Process each of received packets.
-36
View File
@@ -1,36 +0,0 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)
go_library(
name = "rawfile",
srcs = [
"blockingpoll_amd64.s",
"blockingpoll_arm64.s",
"blockingpoll_noyield_unsafe.go",
"blockingpoll_yield_unsafe.go",
"errors.go",
"rawfile_unsafe.go",
],
visibility = ["//visibility:public"],
deps = [
"//pkg/tcpip",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_test(
name = "rawfile_test",
srcs = [
"errors_test.go",
],
library = "rawfile",
deps = [
"//pkg/tcpip",
"@com_github_google_go_cmp//cmp:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
+1 -1
View File
@@ -27,10 +27,10 @@ go_library(
"//pkg/eventfd",
"//pkg/log",
"//pkg/memutil",
"//pkg/rawfile",
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/header",
"//pkg/tcpip/link/rawfile",
"//pkg/tcpip/link/sharedmem/pipe",
"//pkg/tcpip/link/sharedmem/queue",
"//pkg/tcpip/stack",
+7 -3
View File
@@ -30,10 +30,10 @@ import (
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/eventfd"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/rawfile"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/link/sharedmem/queue"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -300,9 +300,13 @@ func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
b := make([]byte, 1)
// When sharedmem endpoint is in use the peerFD is never used for any data
// transfer and this Read should only return if the peer is shutting down.
_, err := rawfile.BlockingRead(e.peerFD, b)
_, errno := rawfile.BlockingRead(e.peerFD, b)
if e.onClosed != nil {
e.onClosed(err)
if errno == 0 {
e.onClosed(nil)
} else {
e.onClosed(tcpip.TranslateErrno(errno))
}
}
}()
}
+7 -3
View File
@@ -20,10 +20,10 @@ package sharedmem
import (
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/rawfile"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -158,9 +158,13 @@ func (e *serverEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
// When sharedmem endpoint is in use the peerFD is never used for any
// data transfer and this Read should only return if the peer is
// shutting down.
_, err := rawfile.BlockingRead(e.peerFD, b)
_, errno := rawfile.BlockingRead(e.peerFD, b)
if e.onClosed != nil {
e.onClosed(err)
if errno == 0 {
e.onClosed(nil)
} else {
e.onClosed(tcpip.TranslateErrno(errno))
}
}
e.completed.Done()
}()

Some files were not shown because too many files have changed in this diff Show More