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  }