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