mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-16 01:07:01 +02:00
102 lines
2.5 KiB
Go
102 lines
2.5 KiB
Go
//go:build linux && !android
|
|
// +build linux,!android
|
|
|
|
package tio
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"sync/atomic"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
type offloadQueueSet struct {
|
|
pq []*Offload
|
|
// pqi is exactly the same as pq, but stored as the interface type
|
|
pqi []Queue
|
|
shutdownFd int
|
|
// usoEnabled is true when newTun successfully negotiated TUN_F_USO4|6 with the kernel.
|
|
// Queues created by Add inherit this and surface it via Offload.USOSupported so coalescers can gate USO emission.
|
|
usoEnabled bool
|
|
closed atomic.Bool
|
|
// l is handed to each queue for its bad-vnet-header drop logging.
|
|
l *slog.Logger
|
|
}
|
|
|
|
// NewOffloadQueueSet creates a QueueSet that uses virtio_net_hdr to do TSO segmentation.
|
|
// usoEnabled tells downstream queues whether the kernel agreed to deliver/accept GSO_UDP_L4 superpackets.
|
|
func NewOffloadQueueSet(usoEnabled bool, l *slog.Logger) (QueueSet, error) {
|
|
shutdownFd, err := unix.Eventfd(0, unix.EFD_NONBLOCK|unix.EFD_CLOEXEC)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create eventfd: %w", err)
|
|
}
|
|
|
|
out := &offloadQueueSet{
|
|
pq: []*Offload{},
|
|
pqi: []Queue{},
|
|
shutdownFd: shutdownFd,
|
|
usoEnabled: usoEnabled,
|
|
l: l,
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (c *offloadQueueSet) Queues() []Queue {
|
|
return c.pqi
|
|
}
|
|
|
|
func (c *offloadQueueSet) Add(fd int) error {
|
|
if c.closed.Load() {
|
|
return errors.New("queue set already closed")
|
|
}
|
|
x, err := newOffload(fd, c.shutdownFd, c.usoEnabled, c.l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.pq = append(c.pq, x)
|
|
c.pqi = append(c.pqi, x)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *offloadQueueSet) wakeForShutdown() error {
|
|
var buf [8]byte
|
|
binary.NativeEndian.PutUint64(buf[:], 1)
|
|
_, err := unix.Write(c.shutdownFd, buf[:])
|
|
return err
|
|
}
|
|
|
|
func (c *offloadQueueSet) Close() error {
|
|
if c.closed.Swap(true) {
|
|
return nil
|
|
}
|
|
|
|
errs := []error{}
|
|
|
|
// Signal all readers blocked in poll to wake up and exit.
|
|
// They observe POLLIN on the shutdown eventfd and return os.ErrClosed.
|
|
if err := c.wakeForShutdown(); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
|
|
// Close the per-queue tun fds; this also unblocks any in-flight reads.
|
|
for _, x := range c.pq {
|
|
if err := x.Close(); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
|
|
// Close the shutdown eventfd last: every reader's pollfd set references it,
|
|
// so it must outlive the wake + per-queue teardown above.
|
|
if err := unix.Close(c.shutdownFd); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
c.shutdownFd = -1
|
|
|
|
return errors.Join(errs...)
|
|
}
|