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