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  }