mirror of
https://github.com/slackhq/nebula.git
synced 2026-08-15 15:37:03 +02:00
913a37cfee
Device loses io.ReadWriteCloser + NewMultiQueueReader in favor of Queues(n), which returns up to n tio.Queue objects; platforms without multiqueue hand back their single queue and the interface sizes its reader routines to what it actually got. Queue.Read returns a batch of borrowed packets (single-element for every current backend) so a future backend can deliver more than one packet per syscall without another interface change. The Linux poll/eventfd machinery moves out of tun_linux.go into the new overlay/tio package: nonblocking fds, a shared shutdown eventfd owned by the queue set, and pollfd arrays built on the stack so concurrent writers parked in blockOnWrite no longer share Revents storage. Other platforms wrap their existing one-datagram Read/Write in a singleQueue adapter that owns a private scratch buffer, so multiqueue-by-sharing devices (user, disabled) no longer race concurrent readers on one buffer. This is the tun-interface subset of better-tun-interface-ordering, extracted at 18dc13b with none of the GSO/GRO offload mechanics and no udp/sendmmsg changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
51 lines
1.7 KiB
Go
51 lines
1.7 KiB
Go
package tio
|
|
|
|
import "io"
|
|
|
|
// singleQueue adapts a legacy one-datagram-per-Read source into a Queue.
|
|
// Read fills a private scratch buffer and returns exactly one Packet whose
|
|
// Bytes borrow from that buffer, valid only until the next Read, per the
|
|
// Queue contract. Single-reader like every Queue; Write is exactly as safe
|
|
// for concurrent use as the underlying source's Write.
|
|
type singleQueue struct {
|
|
rw io.ReadWriter
|
|
closer io.Closer // nil: Close is a no-op (the source is shared and owned elsewhere)
|
|
buf []byte
|
|
ret [1]Packet
|
|
}
|
|
|
|
// NewSingleQueue wraps a one-datagram-per-Read ReadWriteCloser (a legacy tun
|
|
// device) into a Queue. bufSize is the per-queue read scratch size and must
|
|
// be at least the largest datagram the source can return. Close closes rwc.
|
|
func NewSingleQueue(rwc io.ReadWriteCloser, bufSize int) Queue {
|
|
return &singleQueue{rw: rwc, closer: rwc, buf: make([]byte, bufSize)}
|
|
}
|
|
|
|
// NewSingleQueueNoClose is NewSingleQueue for a source owned by someone else,
|
|
// e.g. several queues sharing one device. Close on the returned Queue is a
|
|
// no-op so one queue can't tear the shared source out from under its
|
|
// siblings; the owner remains responsible for closing the source itself.
|
|
func NewSingleQueueNoClose(rw io.ReadWriter, bufSize int) Queue {
|
|
return &singleQueue{rw: rw, buf: make([]byte, bufSize)}
|
|
}
|
|
|
|
func (q *singleQueue) Read() ([]Packet, error) {
|
|
n, err := q.rw.Read(q.buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q.ret[0] = Packet{Bytes: q.buf[:n]}
|
|
return q.ret[:], nil
|
|
}
|
|
|
|
func (q *singleQueue) Write(p []byte) (int, error) {
|
|
return q.rw.Write(p)
|
|
}
|
|
|
|
func (q *singleQueue) Close() error {
|
|
if q.closer == nil {
|
|
return nil
|
|
}
|
|
return q.closer.Close()
|
|
}
|