github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/tcpip/link/sharedmem/queue/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 queue
    16  
    17  import (
    18  	"encoding/binary"
    19  
    20  	"github.com/sagernet/gvisor/pkg/atomicbitops"
    21  	"github.com/sagernet/gvisor/pkg/log"
    22  	"github.com/sagernet/gvisor/pkg/tcpip/link/sharedmem/pipe"
    23  )
    24  
    25  const (
    26  	// Offsets within a packet header.
    27  	packetID       = 0
    28  	packetSize     = 8
    29  	packetReserved = 12
    30  
    31  	sizeOfPacketHeader = 16
    32  
    33  	// Offsets with a buffer descriptor
    34  	bufferOffset = 0
    35  	bufferSize   = 8
    36  
    37  	sizeOfBufferDescriptor = 12
    38  )
    39  
    40  // TxBuffer is the descriptor of a transmit buffer.
    41  type TxBuffer struct {
    42  	Next   *TxBuffer
    43  	Offset uint64
    44  	Size   uint32
    45  }
    46  
    47  // Tx is a transmit queue. It is implemented with one tx and one rx pipe: the
    48  // tx pipe is used to request the transmission of packets, while the rx pipe
    49  // is used to receive which transmissions have completed.
    50  //
    51  // This struct is thread-compatible.
    52  type Tx struct {
    53  	tx                 pipe.Tx
    54  	rx                 pipe.Rx
    55  	sharedEventFDState *atomicbitops.Uint32
    56  }
    57  
    58  // Init initializes the transmit queue with the given pipes.
    59  func (t *Tx) Init(tx, rx []byte, sharedEventFDState *atomicbitops.Uint32) {
    60  	t.tx.Init(tx)
    61  	t.rx.Init(rx)
    62  	t.sharedEventFDState = sharedEventFDState
    63  }
    64  
    65  // NotificationsEnabled returns true if eventFD should be used to notify the
    66  // peer of events (eg. packet transmit etc).
    67  func (t *Tx) NotificationsEnabled() bool {
    68  	// Notifications are considered enabled unless explicitly disabled.
    69  	return t.sharedEventFDState.Load() != EventFDDisabled
    70  }
    71  
    72  // Enqueue queues the given linked list of buffers for transmission as one
    73  // packet. While it is queued, the caller must not modify them.
    74  func (t *Tx) Enqueue(id uint64, totalDataLen, bufferCount uint32, buffer *TxBuffer) bool {
    75  	// Reserve room in the tx pipe.
    76  	totalLen := sizeOfPacketHeader + uint64(bufferCount)*sizeOfBufferDescriptor
    77  
    78  	b := t.tx.Push(totalLen)
    79  	if b == nil {
    80  		return false
    81  	}
    82  
    83  	// Initialize the packet and buffer descriptors.
    84  	binary.LittleEndian.PutUint64(b[packetID:], id)
    85  	binary.LittleEndian.PutUint32(b[packetSize:], totalDataLen)
    86  	binary.LittleEndian.PutUint32(b[packetReserved:], 0)
    87  
    88  	offset := sizeOfPacketHeader
    89  	for i := bufferCount; i != 0; i-- {
    90  		binary.LittleEndian.PutUint64(b[offset+bufferOffset:], buffer.Offset)
    91  		binary.LittleEndian.PutUint32(b[offset+bufferSize:], buffer.Size)
    92  		offset += sizeOfBufferDescriptor
    93  		buffer = buffer.Next
    94  	}
    95  
    96  	t.tx.Flush()
    97  
    98  	return true
    99  }
   100  
   101  // CompletedPacket returns the id of the last completed transmission. The
   102  // returned id, if any, refers to a value passed on a previous call to
   103  // Enqueue().
   104  func (t *Tx) CompletedPacket() (id uint64, ok bool) {
   105  	for {
   106  		b := t.rx.Pull()
   107  		if b == nil {
   108  			return 0, false
   109  		}
   110  
   111  		if len(b) != 8 {
   112  			t.rx.Flush()
   113  			log.Warningf("Ignoring completed packet: size (%v) is less than expected (%v)", len(b), 8)
   114  			continue
   115  		}
   116  
   117  		v := binary.LittleEndian.Uint64(b)
   118  
   119  		t.rx.Flush()
   120  
   121  		return v, true
   122  	}
   123  }
   124  
   125  // Bytes returns the byte slices on which the queue operates.
   126  func (t *Tx) Bytes() (tx, rx []byte) {
   127  	return t.tx.Bytes(), t.rx.Bytes()
   128  }
   129  
   130  // TxPacketInfo holds information about a packet sent on a tx queue.
   131  type TxPacketInfo struct {
   132  	ID          uint64
   133  	Size        uint32
   134  	Reserved    uint32
   135  	BufferCount int
   136  }
   137  
   138  // DecodeTxPacketHeader decodes the header of a packet sent over a tx queue.
   139  func DecodeTxPacketHeader(b []byte) TxPacketInfo {
   140  	return TxPacketInfo{
   141  		ID:          binary.LittleEndian.Uint64(b[packetID:]),
   142  		Size:        binary.LittleEndian.Uint32(b[packetSize:]),
   143  		Reserved:    binary.LittleEndian.Uint32(b[packetReserved:]),
   144  		BufferCount: (len(b) - sizeOfPacketHeader) / sizeOfBufferDescriptor,
   145  	}
   146  }
   147  
   148  // DecodeTxBufferHeader decodes the header of the i-th buffer of a packet sent
   149  // over a tx queue.
   150  func DecodeTxBufferHeader(b []byte, i int) TxBuffer {
   151  	b = b[sizeOfPacketHeader+i*sizeOfBufferDescriptor:]
   152  	return TxBuffer{
   153  		Offset: binary.LittleEndian.Uint64(b[bufferOffset:]),
   154  		Size:   binary.LittleEndian.Uint32(b[bufferSize:]),
   155  	}
   156  }
   157  
   158  // EncodeTxCompletion encodes a tx completion header.
   159  func EncodeTxCompletion(b []byte, id uint64) {
   160  	binary.LittleEndian.PutUint64(b, id)
   161  }