mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Allow waiting for LinkEndpoint worker goroutines to finish.
Previously, the only safe way to use an fdbased endpoint was to leak the FD. This change makes it possible to safely close the FD. This is the first step towards having stoppable stacks. Updates #837 PiperOrigin-RevId: 270346582
This commit is contained in:
@@ -133,3 +133,6 @@ func (e *Endpoint) WritePacket(_ *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*Endpoint) Wait() {}
|
||||
|
||||
@@ -41,6 +41,7 @@ package fdbased
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -81,6 +82,7 @@ const (
|
||||
PacketMMap
|
||||
)
|
||||
|
||||
// An endpoint implements the link-layer using a message-oriented file descriptor.
|
||||
type endpoint struct {
|
||||
// fds is the set of file descriptors each identifying one inbound/outbound
|
||||
// channel. The endpoint will dispatch from all inbound channels as well as
|
||||
@@ -114,6 +116,9 @@ type endpoint struct {
|
||||
// gsoMaxSize is the maximum GSO packet size. It is zero if GSO is
|
||||
// disabled.
|
||||
gsoMaxSize uint32
|
||||
|
||||
// wg keeps track of running goroutines.
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// Options specify the details about the fd-based endpoint to be created.
|
||||
@@ -164,7 +169,8 @@ type Options struct {
|
||||
// New creates a new fd-based endpoint.
|
||||
//
|
||||
// Makes fd non-blocking, but does not take ownership of fd, which must remain
|
||||
// open for the lifetime of the returned endpoint.
|
||||
// open for the lifetime of the returned endpoint (until after the endpoint has
|
||||
// stopped being using and Wait returns).
|
||||
func New(opts *Options) (stack.LinkEndpoint, error) {
|
||||
caps := stack.LinkEndpointCapabilities(0)
|
||||
if opts.RXChecksumOffload {
|
||||
@@ -290,7 +296,11 @@ func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
// saved, they stop sending outgoing packets and all incoming packets
|
||||
// are rejected.
|
||||
for i := range e.inboundDispatchers {
|
||||
go e.dispatchLoop(e.inboundDispatchers[i]) // S/R-SAFE: See above.
|
||||
e.wg.Add(1)
|
||||
go func(i int) { // S/R-SAFE: See above.
|
||||
e.dispatchLoop(e.inboundDispatchers[i])
|
||||
e.wg.Done()
|
||||
}(i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,6 +330,12 @@ func (e *endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
return e.addr
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait. It waits for the endpoint to stop
|
||||
// reading from its FD.
|
||||
func (e *endpoint) Wait() {
|
||||
e.wg.Wait()
|
||||
}
|
||||
|
||||
// virtioNetHdr is declared in linux/virtio_net.h.
|
||||
type virtioNetHdr struct {
|
||||
flags uint8
|
||||
|
||||
@@ -85,3 +85,6 @@ func (e *endpoint) WritePacket(_ *stack.Route, _ *stack.GSO, hdr buffer.Prependa
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
@@ -104,6 +104,13 @@ func (m *InjectableEndpoint) WriteRawPacket(dest tcpip.Address, packet []byte) *
|
||||
return endpoint.WriteRawPacket(dest, packet)
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (m *InjectableEndpoint) Wait() {
|
||||
for _, ep := range m.routes {
|
||||
ep.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
// NewInjectableEndpoint creates a new multi-endpoint injectable endpoint.
|
||||
func NewInjectableEndpoint(routes map[tcpip.Address]stack.InjectableLinkEndpoint) *InjectableEndpoint {
|
||||
return &InjectableEndpoint{
|
||||
|
||||
@@ -132,7 +132,8 @@ func (e *endpoint) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
// Wait waits until all workers have stopped after a Close() call.
|
||||
// Wait implements stack.LinkEndpoint.Wait. It waits until all workers have
|
||||
// stopped after a Close() call.
|
||||
func (e *endpoint) Wait() {
|
||||
e.completed.Wait()
|
||||
}
|
||||
|
||||
@@ -240,6 +240,9 @@ func (e *endpoint) WritePacket(r *stack.Route, gso *stack.GSO, hdr buffer.Prepen
|
||||
return e.lower.WritePacket(r, gso, hdr, payload, protocol)
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func logPacket(prefix string, protocol tcpip.NetworkProtocolNumber, b buffer.View, gso *stack.GSO) {
|
||||
// Figure out the network layer info.
|
||||
var transProto uint8
|
||||
|
||||
@@ -120,3 +120,6 @@ func (e *Endpoint) WaitWrite() {
|
||||
func (e *Endpoint) WaitDispatch() {
|
||||
e.dispatchGate.Close()
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (e *Endpoint) Wait() {}
|
||||
|
||||
@@ -70,6 +70,9 @@ func (e *countedEndpoint) WritePacket(r *stack.Route, _ *stack.GSO, hdr buffer.P
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*countedEndpoint) Wait() {}
|
||||
|
||||
func TestWaitWrite(t *testing.T) {
|
||||
ep := &countedEndpoint{}
|
||||
wep := New(ep)
|
||||
|
||||
@@ -144,6 +144,9 @@ func (*testObject) LinkAddress() tcpip.LinkAddress {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Wait implements stack.LinkEndpoint.Wait.
|
||||
func (*testObject) Wait() {}
|
||||
|
||||
// WritePacket is called by network endpoints after producing a packet and
|
||||
// writing it to the link endpoint. This is used by the test object to verify
|
||||
// that the produced packet is as expected.
|
||||
|
||||
@@ -295,6 +295,15 @@ type LinkEndpoint interface {
|
||||
// IsAttached returns whether a NetworkDispatcher is attached to the
|
||||
// endpoint.
|
||||
IsAttached() bool
|
||||
|
||||
// Wait waits for any worker goroutines owned by the endpoint to stop.
|
||||
//
|
||||
// For now, requesting that an endpoint's worker goroutine(s) stop is
|
||||
// implementation specific.
|
||||
//
|
||||
// Wait will not block if the endpoint hasn't started any goroutines
|
||||
// yet, even if it might later.
|
||||
Wait()
|
||||
}
|
||||
|
||||
// InjectableLinkEndpoint is a LinkEndpoint where inbound packets are
|
||||
|
||||
Reference in New Issue
Block a user