github.com/flowerwrong/netstack@v0.0.0-20191009141956-e5848263af28/tcpip/link/sharedmem/queue/rx.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 queue provides the implementation of transmit and receive queues 16 // based on shared memory ring buffers. 17 package queue 18 19 import ( 20 "encoding/binary" 21 "sync/atomic" 22 23 "github.com/FlowerWrong/netstack/tcpip/link/sharedmem/pipe" 24 "log" 25 ) 26 27 const ( 28 // Offsets within a posted buffer. 29 postedOffset = 0 30 postedSize = 8 31 postedRemainingInGroup = 12 32 postedUserData = 16 33 postedID = 24 34 35 sizeOfPostedBuffer = 32 36 37 // Offsets within a received packet header. 38 consumedPacketSize = 0 39 consumedPacketReserved = 4 40 41 sizeOfConsumedPacketHeader = 8 42 43 // Offsets within a consumed buffer. 44 consumedOffset = 0 45 consumedSize = 8 46 consumedUserData = 12 47 consumedID = 20 48 49 sizeOfConsumedBuffer = 28 50 51 // The following are the allowed states of the shared data area. 52 eventFDUninitialized = 0 53 eventFDDisabled = 1 54 eventFDEnabled = 2 55 ) 56 57 // RxBuffer is the descriptor of a receive buffer. 58 type RxBuffer struct { 59 Offset uint64 60 Size uint32 61 ID uint64 62 UserData uint64 63 } 64 65 // Rx is a receive queue. It is implemented with one tx and one rx pipe: the tx 66 // pipe is used to "post" buffers, while the rx pipe is used to receive packets 67 // whose contents have been written to previously posted buffers. 68 // 69 // This struct is thread-compatible. 70 type Rx struct { 71 tx pipe.Tx 72 rx pipe.Rx 73 sharedEventFDState *uint32 74 } 75 76 // Init initializes the receive queue with the given pipes, and shared state 77 // pointer -- the latter is used to enable/disable eventfd notifications. 78 func (r *Rx) Init(tx, rx []byte, sharedEventFDState *uint32) { 79 r.sharedEventFDState = sharedEventFDState 80 r.tx.Init(tx) 81 r.rx.Init(rx) 82 } 83 84 // EnableNotification updates the shared state such that the peer will notify 85 // the eventfd when there are packets to be dequeued. 86 func (r *Rx) EnableNotification() { 87 atomic.StoreUint32(r.sharedEventFDState, eventFDEnabled) 88 } 89 90 // DisableNotification updates the shared state such that the peer will not 91 // notify the eventfd. 92 func (r *Rx) DisableNotification() { 93 atomic.StoreUint32(r.sharedEventFDState, eventFDDisabled) 94 } 95 96 // PostedBuffersLimit returns the maximum number of buffers that can be posted 97 // before the tx queue fills up. 98 func (r *Rx) PostedBuffersLimit() uint64 { 99 return r.tx.Capacity(sizeOfPostedBuffer) 100 } 101 102 // PostBuffers makes the given buffers available for receiving data from the 103 // peer. Once they are posted, the peer is free to write to them and will 104 // eventually post them back for consumption. 105 func (r *Rx) PostBuffers(buffers []RxBuffer) bool { 106 for i := range buffers { 107 b := r.tx.Push(sizeOfPostedBuffer) 108 if b == nil { 109 r.tx.Abort() 110 return false 111 } 112 113 pb := &buffers[i] 114 binary.LittleEndian.PutUint64(b[postedOffset:], pb.Offset) 115 binary.LittleEndian.PutUint32(b[postedSize:], pb.Size) 116 binary.LittleEndian.PutUint32(b[postedRemainingInGroup:], 0) 117 binary.LittleEndian.PutUint64(b[postedUserData:], pb.UserData) 118 binary.LittleEndian.PutUint64(b[postedID:], pb.ID) 119 } 120 121 r.tx.Flush() 122 123 return true 124 } 125 126 // Dequeue receives buffers that have been previously posted by PostBuffers() 127 // and that have been filled by the peer and posted back. 128 // 129 // This is similar to append() in that new buffers are appended to "bufs", with 130 // reallocation only if "bufs" doesn't have enough capacity. 131 func (r *Rx) Dequeue(bufs []RxBuffer) ([]RxBuffer, uint32) { 132 for { 133 outBufs := bufs 134 135 // Pull the next descriptor from the rx pipe. 136 b := r.rx.Pull() 137 if b == nil { 138 return bufs, 0 139 } 140 141 if len(b) < sizeOfConsumedPacketHeader { 142 log.Printf("Ignoring packet header: size (%v) is less than header size (%v)", len(b), sizeOfConsumedPacketHeader) 143 r.rx.Flush() 144 continue 145 } 146 147 totalDataSize := binary.LittleEndian.Uint32(b[consumedPacketSize:]) 148 149 // Calculate the number of buffer descriptors and copy them 150 // over to the output. 151 count := (len(b) - sizeOfConsumedPacketHeader) / sizeOfConsumedBuffer 152 offset := sizeOfConsumedPacketHeader 153 buffersSize := uint32(0) 154 for i := count; i > 0; i-- { 155 s := binary.LittleEndian.Uint32(b[offset+consumedSize:]) 156 buffersSize += s 157 if buffersSize < s { 158 // The buffer size overflows an unsigned 32-bit 159 // integer, so break out and force it to be 160 // ignored. 161 totalDataSize = 1 162 buffersSize = 0 163 break 164 } 165 166 outBufs = append(outBufs, RxBuffer{ 167 Offset: binary.LittleEndian.Uint64(b[offset+consumedOffset:]), 168 Size: s, 169 ID: binary.LittleEndian.Uint64(b[offset+consumedID:]), 170 }) 171 172 offset += sizeOfConsumedBuffer 173 } 174 175 r.rx.Flush() 176 177 if buffersSize < totalDataSize { 178 // The descriptor is corrupted, ignore it. 179 log.Printf("Ignoring packet: actual data size (%v) less than expected size (%v)", buffersSize, totalDataSize) 180 continue 181 } 182 183 return outBufs, totalDataSize 184 } 185 } 186 187 // Bytes returns the byte slices on which the queue operates. 188 func (r *Rx) Bytes() (tx, rx []byte) { 189 return r.tx.Bytes(), r.rx.Bytes() 190 } 191 192 // DecodeRxBufferHeader decodes the header of a buffer posted on an rx queue. 193 func DecodeRxBufferHeader(b []byte) RxBuffer { 194 return RxBuffer{ 195 Offset: binary.LittleEndian.Uint64(b[postedOffset:]), 196 Size: binary.LittleEndian.Uint32(b[postedSize:]), 197 ID: binary.LittleEndian.Uint64(b[postedID:]), 198 UserData: binary.LittleEndian.Uint64(b[postedUserData:]), 199 } 200 } 201 202 // RxCompletionSize returns the number of bytes needed to encode an rx 203 // completion containing "count" buffers. 204 func RxCompletionSize(count int) uint64 { 205 return sizeOfConsumedPacketHeader + uint64(count)*sizeOfConsumedBuffer 206 } 207 208 // EncodeRxCompletion encodes an rx completion header. 209 func EncodeRxCompletion(b []byte, size, reserved uint32) { 210 binary.LittleEndian.PutUint32(b[consumedPacketSize:], size) 211 binary.LittleEndian.PutUint32(b[consumedPacketReserved:], reserved) 212 } 213 214 // EncodeRxCompletionBuffer encodes the i-th rx completion buffer header. 215 func EncodeRxCompletionBuffer(b []byte, i int, rxb RxBuffer) { 216 b = b[RxCompletionSize(i):] 217 binary.LittleEndian.PutUint64(b[consumedOffset:], rxb.Offset) 218 binary.LittleEndian.PutUint32(b[consumedSize:], rxb.Size) 219 binary.LittleEndian.PutUint64(b[consumedUserData:], rxb.UserData) 220 binary.LittleEndian.PutUint64(b[consumedID:], rxb.ID) 221 }