Esempio n. 1
0
        private void Forward(IxgbeDevice rxDev, int rxQueue, IxgbeDevice txDev, int txQueue, Span <PacketBuffer> buffers)
        {
            var rxBuffCount = rxDev.RxBatch(rxQueue, buffers);

            if (rxBuffCount > 0)
            {
                var rxBuffers = buffers.Slice(0, rxBuffCount);
                //Touch all buffers to simulate a realistic scenario
                foreach (var buffer in rxBuffers)
                {
                    buffer.Touch();
                }

                var txBuffCount = txDev.TxBatch(txQueue, rxBuffers);
                _mempool = (_mempool == null) ? Mempool.FindPool(rxBuffers[0].MempoolId) : _mempool;

                //Drop unsent packets
                foreach (var buffer in rxBuffers.Slice(txBuffCount))
                {
                    _mempool.FreeBuffer(buffer);
                }
            }
        }
Esempio n. 2
0
        public override int TxBatch(int queueId, Span <PacketBuffer> buffers)
        {
            if (queueId < 0 || queueId >= RxQueues.Length)
            {
                throw new ArgumentOutOfRangeException("Queue id out of bounds");
            }

            var    queue        = TxQueues[queueId] as IxgbeTxQueue;
            ushort cleanIndex   = queue.CleanIndex;
            ushort currentIndex = (ushort)queue.Index;
            var    cmdTypeFlags = IxgbeDefs.ADVTXD_DCMD_EOP | IxgbeDefs.ADVTXD_DCMD_RS | IxgbeDefs.ADVTXD_DCMD_IFCS |
                                  IxgbeDefs.ADVTXD_DCMD_DEXT | IxgbeDefs.ADVTXD_DTYP_DATA;
            //All packet buffers that will be handled here will belong to the same mempool
            Mempool pool = null;

            //Step 1: Clean up descriptors that were sent out by the hardware and return them to the mempool
            //Start by reading step 2 which is done first for each packet
            //Cleaning up must be done in batches for performance reasons, so this is unfortunately somewhat complicated
            while (true)
            {
                //currentIndex is always ahead of clean (invariant of our queue)
                int cleanable = currentIndex - cleanIndex;
                if (cleanable < 0)
                {
                    cleanable = queue.EntriesCount + cleanable;
                }
                if (cleanable < TxCleanBatch)
                {
                    break;
                }

                //Calculate the index of the last transcriptor in the clean batch
                //We can't check all descriptors for performance reasons
                int cleanupTo = cleanIndex + TxCleanBatch - 1;
                if (cleanupTo >= queue.EntriesCount)
                {
                    cleanupTo -= queue.EntriesCount;
                }

                var  descAddr = queue.GetDescriptorAddress((ushort)cleanupTo);
                uint status   = queue.ReadWbStatus(descAddr);

                //Hardware sets this flag as soon as it's sent out, we can give back all bufs in the batch back to the mempool
                if ((status & IxgbeDefs.ADVTXD_STAT_DD) != 0)
                {
                    int i = cleanIndex;
                    while (true)
                    {
                        var packetBuffer = new PacketBuffer(queue.VirtualAddresses[i]);
                        if (pool == null)
                        {
                            pool = Mempool.FindPool(packetBuffer.MempoolId);
                            if (pool == null)
                            {
                                throw new NullReferenceException("Could not find mempool with id specified by PacketBuffer");
                            }
                        }
                        pool.FreeBufferFast(packetBuffer);
                        if (i == cleanupTo)
                        {
                            break;
                        }
                        i = WrapRing(i, queue.EntriesCount);
                    }
                    //Next descriptor to be cleaned up is one after the one we just cleaned
                    cleanIndex = (ushort)WrapRing(cleanupTo, queue.EntriesCount);
                }
                //Clean the whole batch or nothing. This will leave some packets in the queue forever
                //if you stop transmitting but that's not a real concern
                else
                {
                    break;
                }
            }
            queue.CleanIndex = cleanIndex;

            //Step 2: Send out as many of our packets as possible
            int sent;

            for (sent = 0; sent < buffers.Length; sent++)
            {
                var    descAddr  = queue.GetDescriptorAddress(currentIndex);
                ushort nextIndex = WrapRing(currentIndex, (ushort)queue.EntriesCount);
                //We are full if the next index is the one we are trying to reclaim
                if (cleanIndex == nextIndex)
                {
                    break;
                }

                var buffer = buffers[sent];
                //Remember virtual address to clean it up later
                queue.VirtualAddresses[currentIndex] = buffer.VirtualAddress;
                queue.Index = WrapRing(queue.Index, queue.EntriesCount);
                //NIC reads from here
                queue.WriteBufferAddress(descAddr, buffer.PhysicalAddress + PacketBuffer.DataOffset);
                //Always the same flags: One buffer (EOP), advanced data descriptor, CRC offload, data length
                var bufSize = buffer.Size;
                queue.WriteCmdTypeLength(descAddr, cmdTypeFlags | bufSize);
                //No fancy offloading - only the total payload length
                //implement offloading flags here:
                // * ip checksum offloading is trivial: just set the offset
                // * tcp/udp checksum offloading is more annoying, you have to precalculate the pseudo-header checksum
                queue.WriteOlInfoStatus(descAddr, bufSize << (int)IxgbeDefs.ADVTXD_PAYLEN_SHIFT);
                currentIndex = nextIndex;
            }

            //Send out by advancing tail, i.e. pass control of the bus to the NIC
            SetReg(IxgbeDefs.TDT((uint)queueId), (uint)queue.Index);
            return(sent);
        }