github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/pkg/tcpip/link/sharedmem/tx.go (about) 1 // Copyright 2018 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 package sharedmem 16 17 import ( 18 "math" 19 20 "github.com/MerlinKodo/gvisor/pkg/buffer" 21 "github.com/MerlinKodo/gvisor/pkg/eventfd" 22 "github.com/MerlinKodo/gvisor/pkg/tcpip/link/sharedmem/queue" 23 "golang.org/x/sys/unix" 24 ) 25 26 const ( 27 nilID = math.MaxUint64 28 ) 29 30 // tx holds all state associated with a tx queue. 31 type tx struct { 32 data []byte 33 q queue.Tx 34 ids idManager 35 bufs bufferManager 36 eventFD eventfd.Eventfd 37 sharedData []byte 38 sharedDataFD int 39 } 40 41 // init initializes all state needed by the tx queue based on the information 42 // provided. 43 // 44 // The caller always retains ownership of all file descriptors passed in. The 45 // queue implementation will duplicate any that it may need in the future. 46 func (t *tx) init(bufferSize uint32, c *QueueConfig) error { 47 // Map in all buffers. 48 txPipe, err := getBuffer(c.TxPipeFD) 49 if err != nil { 50 return err 51 } 52 53 rxPipe, err := getBuffer(c.RxPipeFD) 54 if err != nil { 55 unix.Munmap(txPipe) 56 return err 57 } 58 59 data, err := getBuffer(c.DataFD) 60 if err != nil { 61 unix.Munmap(txPipe) 62 unix.Munmap(rxPipe) 63 return err 64 } 65 66 sharedData, err := getBuffer(c.SharedDataFD) 67 if err != nil { 68 unix.Munmap(txPipe) 69 unix.Munmap(rxPipe) 70 unix.Munmap(data) 71 } 72 73 // Initialize state based on buffers. 74 t.q.Init(txPipe, rxPipe, sharedDataPointer(sharedData)) 75 t.ids.init() 76 t.bufs.init(0, len(data), int(bufferSize)) 77 t.data = data 78 t.eventFD = c.EventFD 79 t.sharedDataFD = c.SharedDataFD 80 t.sharedData = sharedData 81 82 return nil 83 } 84 85 // cleanup releases all resources allocated during init(). It must only be 86 // called if init() has previously succeeded. 87 func (t *tx) cleanup() { 88 a, b := t.q.Bytes() 89 unix.Munmap(a) 90 unix.Munmap(b) 91 unix.Munmap(t.data) 92 } 93 94 // transmit sends a packet made of bufs. Returns a boolean that specifies 95 // whether the packet was successfully transmitted. 96 func (t *tx) transmit(transmitBuf buffer.Buffer) bool { 97 // Pull completions from the tx queue and add their buffers back to the 98 // pool so that we can reuse them. 99 for { 100 id, ok := t.q.CompletedPacket() 101 if !ok { 102 break 103 } 104 105 if buf := t.ids.remove(id); buf != nil { 106 t.bufs.free(buf) 107 } 108 } 109 110 bSize := t.bufs.entrySize 111 total := uint32(transmitBuf.Size()) 112 bufCount := (total + bSize - 1) / bSize 113 114 // Allocate enough buffers to hold all the data. 115 var buf *queue.TxBuffer 116 for i := bufCount; i != 0; i-- { 117 b := t.bufs.alloc() 118 if b == nil { 119 // Failed to get all buffers. Return to the pool 120 // whatever we had managed to get. 121 if buf != nil { 122 t.bufs.free(buf) 123 } 124 return false 125 } 126 b.Next = buf 127 buf = b 128 } 129 130 // Copy data into allocated buffers. 131 nBuf := buf 132 var dBuf []byte 133 transmitBuf.Apply(func(v *buffer.View) { 134 for v.Size() > 0 { 135 if len(dBuf) == 0 { 136 dBuf = t.data[nBuf.Offset:][:nBuf.Size] 137 nBuf = nBuf.Next 138 } 139 n := copy(dBuf, v.AsSlice()) 140 v.TrimFront(n) 141 dBuf = dBuf[n:] 142 } 143 }) 144 145 // Get an id for this packet and send it out. 146 id := t.ids.add(buf) 147 if !t.q.Enqueue(id, total, bufCount, buf) { 148 t.ids.remove(id) 149 t.bufs.free(buf) 150 return false 151 } 152 153 return true 154 } 155 156 // notify writes to the tx.eventFD to indicate to the peer that there is data to 157 // be read. 158 func (t *tx) notify() { 159 if t.q.NotificationsEnabled() { 160 t.eventFD.Notify() 161 } 162 } 163 164 // idDescriptor is used by idManager to either point to a tx buffer (in case 165 // the ID is assigned) or to the next free element (if the id is not assigned). 166 type idDescriptor struct { 167 buf *queue.TxBuffer 168 nextFree uint64 169 } 170 171 // idManager is a manager of tx buffer identifiers. It assigns unique IDs to 172 // tx buffers that are added to it; the IDs can only be reused after they have 173 // been removed. 174 // 175 // The ID assignments are stored so that the tx buffers can be retrieved from 176 // the IDs previously assigned to them. 177 type idManager struct { 178 // ids is a slice containing all tx buffers. The ID is the index into 179 // this slice. 180 ids []idDescriptor 181 182 // freeList a list of free IDs. 183 freeList uint64 184 } 185 186 // init initializes the id manager. 187 func (m *idManager) init() { 188 m.freeList = nilID 189 } 190 191 // add assigns an ID to the given tx buffer. 192 func (m *idManager) add(b *queue.TxBuffer) uint64 { 193 if i := m.freeList; i != nilID { 194 // There is an id available in the free list, just use it. 195 m.ids[i].buf = b 196 m.freeList = m.ids[i].nextFree 197 return i 198 } 199 200 // We need to expand the id descriptor. 201 m.ids = append(m.ids, idDescriptor{buf: b}) 202 return uint64(len(m.ids) - 1) 203 } 204 205 // remove retrieves the tx buffer associated with the given ID, and removes the 206 // ID from the assigned table so that it can be reused in the future. 207 func (m *idManager) remove(i uint64) *queue.TxBuffer { 208 if i >= uint64(len(m.ids)) { 209 return nil 210 } 211 212 desc := &m.ids[i] 213 b := desc.buf 214 if b == nil { 215 // The provided id is not currently assigned. 216 return nil 217 } 218 219 desc.buf = nil 220 desc.nextFree = m.freeList 221 m.freeList = i 222 223 return b 224 } 225 226 // bufferManager manages a buffer region broken up into smaller, equally sized 227 // buffers. Smaller buffers can be allocated and freed. 228 type bufferManager struct { 229 freeList *queue.TxBuffer 230 curOffset uint64 231 limit uint64 232 entrySize uint32 233 } 234 235 // init initializes the buffer manager. 236 func (b *bufferManager) init(initialOffset, size, entrySize int) { 237 b.freeList = nil 238 b.curOffset = uint64(initialOffset) 239 b.limit = uint64(initialOffset + size/entrySize*entrySize) 240 b.entrySize = uint32(entrySize) 241 } 242 243 // alloc allocates a buffer from the manager, if one is available. 244 func (b *bufferManager) alloc() *queue.TxBuffer { 245 if b.freeList != nil { 246 // There is a descriptor ready for reuse in the free list. 247 d := b.freeList 248 b.freeList = d.Next 249 d.Next = nil 250 return d 251 } 252 253 if b.curOffset < b.limit { 254 // There is room available in the never-used range, so create 255 // a new descriptor for it. 256 d := &queue.TxBuffer{ 257 Offset: b.curOffset, 258 Size: b.entrySize, 259 } 260 b.curOffset += uint64(b.entrySize) 261 return d 262 } 263 264 return nil 265 } 266 267 // free returns all buffers in the list to the buffer manager so that they can 268 // be reused. 269 func (b *bufferManager) free(d *queue.TxBuffer) { 270 // Find the last buffer in the list. 271 last := d 272 for last.Next != nil { 273 last = last.Next 274 } 275 276 // Push list onto free list. 277 last.Next = b.freeList 278 b.freeList = d 279 }