1
0

queueset_gso_linux.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. //go:build linux && !android
  2. package tio
  3. import (
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "log/slog"
  8. "sync/atomic"
  9. "golang.org/x/sys/unix"
  10. )
  11. type offloadQueueSet struct {
  12. pq []*Offload
  13. // pqi is exactly the same as pq, but stored as the interface type
  14. pqi []Queue
  15. shutdownFd int
  16. // usoEnabled is true when newTun successfully negotiated TUN_F_USO4|6 with the kernel.
  17. // Queues created by Add inherit this and surface it via Offload.USOSupported so coalescers can gate USO emission.
  18. usoEnabled bool
  19. closed atomic.Bool
  20. // l is handed to each queue for its bad-vnet-header drop logging.
  21. l *slog.Logger
  22. }
  23. // NewOffloadQueueSet creates a QueueSet that uses virtio_net_hdr to do TSO segmentation.
  24. // usoEnabled tells downstream queues whether the kernel agreed to deliver/accept GSO_UDP_L4 superpackets.
  25. func NewOffloadQueueSet(usoEnabled bool, l *slog.Logger) (QueueSet, error) {
  26. shutdownFd, err := unix.Eventfd(0, unix.EFD_NONBLOCK|unix.EFD_CLOEXEC)
  27. if err != nil {
  28. return nil, fmt.Errorf("failed to create eventfd: %w", err)
  29. }
  30. out := &offloadQueueSet{
  31. pq: []*Offload{},
  32. pqi: []Queue{},
  33. shutdownFd: shutdownFd,
  34. usoEnabled: usoEnabled,
  35. l: l,
  36. }
  37. return out, nil
  38. }
  39. func (c *offloadQueueSet) Queues() []Queue {
  40. return c.pqi
  41. }
  42. func (c *offloadQueueSet) Add(fd int) error {
  43. if c.closed.Load() {
  44. return errors.New("queue set already closed")
  45. }
  46. x, err := newOffload(fd, c.shutdownFd, c.usoEnabled, c.l)
  47. if err != nil {
  48. return err
  49. }
  50. c.pq = append(c.pq, x)
  51. c.pqi = append(c.pqi, x)
  52. return nil
  53. }
  54. func (c *offloadQueueSet) wakeForShutdown() error {
  55. var buf [8]byte
  56. binary.NativeEndian.PutUint64(buf[:], 1)
  57. _, err := unix.Write(c.shutdownFd, buf[:])
  58. return err
  59. }
  60. func (c *offloadQueueSet) Close() error {
  61. if c.closed.Swap(true) {
  62. return nil
  63. }
  64. errs := []error{}
  65. // Signal all readers blocked in poll to wake up and exit.
  66. // They observe POLLIN on the shutdown eventfd and return os.ErrClosed.
  67. if err := c.wakeForShutdown(); err != nil {
  68. errs = append(errs, err)
  69. }
  70. // Close the per-queue tun fds; this also unblocks any in-flight reads.
  71. for _, x := range c.pq {
  72. if err := x.Close(); err != nil {
  73. errs = append(errs, err)
  74. }
  75. }
  76. // Close the shutdown eventfd last: every reader's pollfd set references it,
  77. // so it must outlive the wake + per-queue teardown above.
  78. if err := unix.Close(c.shutdownFd); err != nil {
  79. errs = append(errs, err)
  80. }
  81. c.shutdownFd = -1
  82. return errors.Join(errs...)
  83. }