// Copyright 2018 The gVisor Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package queue import ( "encoding/binary" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/tcpip/link/sharedmem/pipe" ) const ( // Offsets within a packet header. packetID = 0 packetSize = 8 packetReserved = 12 sizeOfPacketHeader = 16 // Offsets with a buffer descriptor bufferOffset = 0 bufferSize = 8 sizeOfBufferDescriptor = 12 ) // TxBuffer is the descriptor of a transmit buffer. type TxBuffer struct { Next *TxBuffer Offset uint64 Size uint32 } // Tx is a transmit queue. It is implemented with one tx and one rx pipe: the // tx pipe is used to request the transmission of packets, while the rx pipe // is used to receive which transmissions have completed. // // This struct is thread-compatible. type Tx struct { tx pipe.Tx rx pipe.Rx } // Init initializes the transmit queue with the given pipes. func (t *Tx) Init(tx, rx []byte) { t.tx.Init(tx) t.rx.Init(rx) } // Enqueue queues the given linked list of buffers for transmission as one // packet. While it is queued, the caller must not modify them. func (t *Tx) Enqueue(id uint64, totalDataLen, bufferCount uint32, buffer *TxBuffer) bool { // Reserve room in the tx pipe. totalLen := sizeOfPacketHeader + uint64(bufferCount)*sizeOfBufferDescriptor b := t.tx.Push(totalLen) if b == nil { return false } // Initialize the packet and buffer descriptors. binary.LittleEndian.PutUint64(b[packetID:], id) binary.LittleEndian.PutUint32(b[packetSize:], totalDataLen) binary.LittleEndian.PutUint32(b[packetReserved:], 0) offset := sizeOfPacketHeader for i := bufferCount; i != 0; i-- { binary.LittleEndian.PutUint64(b[offset+bufferOffset:], buffer.Offset) binary.LittleEndian.PutUint32(b[offset+bufferSize:], buffer.Size) offset += sizeOfBufferDescriptor buffer = buffer.Next } t.tx.Flush() return true } // CompletedPacket returns the id of the last completed transmission. The // returned id, if any, refers to a value passed on a previous call to // Enqueue(). func (t *Tx) CompletedPacket() (id uint64, ok bool) { for { b := t.rx.Pull() if b == nil { return 0, false } if len(b) != 8 { t.rx.Flush() log.Warningf("Ignoring completed packet: size (%v) is less than expected (%v)", len(b), 8) continue } v := binary.LittleEndian.Uint64(b) t.rx.Flush() return v, true } } // Bytes returns the byte slices on which the queue operates. func (t *Tx) Bytes() (tx, rx []byte) { return t.tx.Bytes(), t.rx.Bytes() } // TxPacketInfo holds information about a packet sent on a tx queue. type TxPacketInfo struct { ID uint64 Size uint32 Reserved uint32 BufferCount int } // DecodeTxPacketHeader decodes the header of a packet sent over a tx queue. func DecodeTxPacketHeader(b []byte) TxPacketInfo { return TxPacketInfo{ ID: binary.LittleEndian.Uint64(b[packetID:]), Size: binary.LittleEndian.Uint32(b[packetSize:]), Reserved: binary.LittleEndian.Uint32(b[packetReserved:]), BufferCount: (len(b) - sizeOfPacketHeader) / sizeOfBufferDescriptor, } } // DecodeTxBufferHeader decodes the header of the i-th buffer of a packet sent // over a tx queue. func DecodeTxBufferHeader(b []byte, i int) TxBuffer { b = b[sizeOfPacketHeader+i*sizeOfBufferDescriptor:] return TxBuffer{ Offset: binary.LittleEndian.Uint64(b[bufferOffset:]), Size: binary.LittleEndian.Uint32(b[bufferSize:]), } } // EncodeTxCompletion encodes a tx completion header. func EncodeTxCompletion(b []byte, id uint64) { binary.LittleEndian.PutUint64(b, id) }