github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/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 "github.com/ttpreport/gvisor-ligolo/pkg/atomicbitops" 22 "golang.org/x/sys/unix" 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 }