github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/xdp/txqueue.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build amd64 || arm64
    16  // +build amd64 arm64
    17  
    18  package xdp
    19  
    20  import (
    21  	"golang.org/x/sys/unix"
    22  	"github.com/sagernet/gvisor/pkg/atomicbitops"
    23  )
    24  
    25  // The TXQueue is how a process tells the kernel which buffers are available to
    26  // be sent via the NIC.
    27  //
    28  // TXQueue is not thread-safe and requires external synchronization
    29  type TXQueue struct {
    30  	// sockfd is the underlying AF_XDP socket.
    31  	sockfd uint32
    32  
    33  	// mem is the mmap'd area shared with the kernel. Many other fields of
    34  	// this struct point into mem.
    35  	mem []byte
    36  
    37  	// ring is the actual ring buffer. It is a list of XDP descriptors
    38  	// pointing to ready-to-transmit packets.
    39  	//
    40  	// len(ring) must be a power of 2.
    41  	ring []unix.XDPDesc
    42  
    43  	// mask is used whenever indexing into ring. It is always len(ring)-1.
    44  	// It prevents index out of bounds errors while allowing the producer
    45  	// and consumer pointers to repeatedly "overflow" and loop back around
    46  	// the ring.
    47  	mask uint32
    48  
    49  	// producer points to the shared atomic value that indicates the last
    50  	// produced descriptor. Only we update this value.
    51  	producer *atomicbitops.Uint32
    52  
    53  	// consumer points to the shared atomic value that indicates the last
    54  	// consumed descriptor. Only the kernel updates this value.
    55  	consumer *atomicbitops.Uint32
    56  
    57  	// flags points to the shared atomic value that holds flags for the
    58  	// queue.
    59  	flags *atomicbitops.Uint32
    60  
    61  	// Cached values are used to avoid relatively expensive atomic
    62  	// operations. They are used, incremented, and decremented multiple
    63  	// times with non-atomic operations, and then "batch-updated" by
    64  	// reading or writing atomically to synchronize with the kernel.
    65  
    66  	// cachedProducer is used to atomically write *producer.
    67  	cachedProducer uint32
    68  	// cachedConsumer is updated when we atomically read *consumer.
    69  	// cachedConsumer is actually len(ring) larger than the real consumer
    70  	// value. See free() for details.
    71  	cachedConsumer uint32
    72  }
    73  
    74  // Reserve reserves descriptors in the queue. If toReserve descriptors cannot
    75  // be reserved, none are reserved.
    76  //
    77  // +checklocks:umem.mu
    78  func (tq *TXQueue) Reserve(umem *UMEM, toReserve uint32) (nReserved, index uint32) {
    79  	if umem.nFreeFrames < toReserve || tq.free(toReserve) < toReserve {
    80  		return 0, 0
    81  	}
    82  	idx := tq.cachedProducer
    83  	tq.cachedProducer += toReserve
    84  	return toReserve, idx
    85  }
    86  
    87  // free returns the number of free descriptors in the TX queue.
    88  func (tq *TXQueue) free(toReserve uint32) uint32 {
    89  	// Try to find free descriptors without incurring an atomic operation.
    90  	//
    91  	// cachedConsumer is always len(tq.ring) larger than the real consumer
    92  	// value. This lets us, in the common case, compute the number of free
    93  	// descriptors simply via tq.cachedConsumer - tq.cachedProducer without
    94  	// also addign len(tq.ring).
    95  	if available := tq.cachedConsumer - tq.cachedProducer; available >= toReserve {
    96  		return available
    97  	}
    98  
    99  	// If we didn't already have enough descriptors available, check
   100  	// whether the kernel has returned some to us.
   101  	tq.cachedConsumer = tq.consumer.Load()
   102  	tq.cachedConsumer += uint32(len(tq.ring))
   103  	return tq.cachedConsumer - tq.cachedProducer
   104  }
   105  
   106  // Notify updates the producer such that it is visible to the kernel.
   107  func (tq *TXQueue) Notify() {
   108  	tq.producer.Store(tq.cachedProducer)
   109  	tq.kick()
   110  }
   111  
   112  // Set sets the TX queue's descriptor at index to addr.
   113  func (tq *TXQueue) Set(index uint32, desc unix.XDPDesc) {
   114  	// Use mask to avoid overflowing and loop back around the ring.
   115  	tq.ring[index&tq.mask] = desc
   116  }