| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- //go: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...)
- }
|