tio_poll_linux.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. //go:build linux && !android
  2. // +build linux,!android
  3. package tio
  4. import (
  5. "fmt"
  6. "os"
  7. "sync/atomic"
  8. "golang.org/x/sys/unix"
  9. )
  10. // Maximum size we accept for a single read from a TUN with IFF_VNET_HDR. A
  11. // TSO superpacket can be up to 64KiB of payload plus a single L2/L3/L4 header
  12. // prefix plus the virtio header.
  13. const tunReadBufSize = 65535
  14. type Poll struct {
  15. fd int
  16. shutdownFd int
  17. closed atomic.Bool
  18. readBuf []byte
  19. batchRet [1]Packet
  20. }
  21. // newPoll wraps an existing tun fd. On failure it does NOT close fd: the
  22. // caller owns fd and is the sole closer (see pollQueueSet.Add callers in
  23. // overlay/tun_linux.go, which unix.Close on Add error). This matches the
  24. // newOffload convention and keeps closes at exactly one on every path.
  25. func newPoll(fd int, shutdownFd int) (*Poll, error) {
  26. if err := unix.SetNonblock(fd, true); err != nil {
  27. return nil, fmt.Errorf("failed to set Poll device as nonblocking: %w", err)
  28. }
  29. out := &Poll{
  30. fd: fd,
  31. shutdownFd: shutdownFd,
  32. readBuf: make([]byte, tunReadBufSize),
  33. }
  34. return out, nil
  35. }
  36. // blockOnRead waits until the Poll fd is readable or shutdown has been signaled.
  37. // Returns os.ErrClosed if Close was called.
  38. func (t *Poll) blockOnRead() error {
  39. return blockOn(int32(t.fd), int32(t.shutdownFd), unix.POLLIN)
  40. }
  41. func (t *Poll) blockOnWrite() error {
  42. return blockOn(int32(t.fd), int32(t.shutdownFd), unix.POLLOUT)
  43. }
  44. func (t *Poll) Read() ([]Packet, error) {
  45. n, err := t.readOne(t.readBuf)
  46. if err != nil {
  47. return nil, err
  48. }
  49. t.batchRet[0] = Packet{Bytes: t.readBuf[:n]}
  50. return t.batchRet[:], nil
  51. }
  52. func (t *Poll) readOne(to []byte) (int, error) {
  53. for {
  54. n, errno := unix.Read(t.fd, to)
  55. if errno == nil {
  56. return n, nil
  57. }
  58. switch errno {
  59. case unix.EAGAIN:
  60. if err := t.blockOnRead(); err != nil {
  61. return 0, err
  62. }
  63. case unix.EINTR:
  64. // retry
  65. case unix.EBADF:
  66. return 0, os.ErrClosed
  67. default:
  68. return 0, errno
  69. }
  70. }
  71. }
  72. // Write is safe for concurrent use
  73. func (t *Poll) Write(from []byte) (int, error) {
  74. for {
  75. n, errno := unix.Write(t.fd, from)
  76. if errno == nil {
  77. return n, nil
  78. }
  79. switch errno {
  80. case unix.EAGAIN:
  81. if err := t.blockOnWrite(); err != nil {
  82. return 0, err
  83. }
  84. case unix.EINTR:
  85. // retry
  86. case unix.EBADF:
  87. return 0, os.ErrClosed
  88. default:
  89. return 0, errno
  90. }
  91. }
  92. }
  93. func (t *Poll) Close() error {
  94. if t.closed.Swap(true) {
  95. return nil
  96. }
  97. //shutdownFd is owned by the container, so we should not close it
  98. // Close the underlying fd but do NOT null t.fd: a reader may still be
  99. // loading it in readOne, and mutating the field would race that load.
  100. // It gets EBADF -> os.ErrClosed (or wakes via the shutdown eventfd's
  101. // ppoll first). closed.Swap already guarantees we only close once.
  102. return unix.Close(t.fd)
  103. }