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