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  }