Files
vpp/src/plugins/vmxnet3/output.c
Steven Luong a57a7005d6 vmxnet3: support manual thread assignment to tx queue
Thread assignment to tx queue has always been automatic and there
was no way to modify it. With this patch, it is now possible to use
the cli "set interface tx-queue" to change the thread assignment to
tx queue for vmxnet3 interface, thanks to the new tx infra.

Type: feature

Signed-off-by: Steven Luong <sluong@cisco.com>
Change-Id: I1544e3557f70251d4bd423cc3d9f28ee1d44db4a
2021-07-01 12:55:29 +00:00

246 lines
6.6 KiB
C

/*
*------------------------------------------------------------------
* Copyright (c) 2018 Cisco and/or its affiliates.
* 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.
*------------------------------------------------------------------
*/
#include <vlib/vlib.h>
#include <vlib/unix/unix.h>
#include <vlib/pci/pci.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/devices/devices.h>
#include <vnet/ip/ip6_packet.h>
#include <vnet/ip/ip4_packet.h>
#include <vmxnet3/vmxnet3.h>
static_always_inline void
vmxnet3_tx_comp_ring_advance_next (vmxnet3_txq_t * txq)
{
vmxnet3_tx_comp_ring *comp_ring = &txq->tx_comp_ring;
comp_ring->next++;
if (PREDICT_FALSE (comp_ring->next == txq->size))
{
comp_ring->next = 0;
comp_ring->gen ^= VMXNET3_TXCF_GEN;
}
}
static_always_inline void
vmxnet3_tx_ring_advance_produce (vmxnet3_txq_t * txq)
{
txq->tx_ring.produce++;
if (PREDICT_FALSE (txq->tx_ring.produce == txq->size))
{
txq->tx_ring.produce = 0;
txq->tx_ring.gen ^= VMXNET3_TXF_GEN;
}
}
static_always_inline void
vmxnet3_tx_ring_advance_consume (vmxnet3_txq_t * txq)
{
txq->tx_ring.consume++;
txq->tx_ring.consume &= txq->size - 1;
}
static_always_inline void
vmxnet3_txq_release (vlib_main_t * vm, vmxnet3_device_t * vd,
vmxnet3_txq_t * txq)
{
vmxnet3_tx_comp *tx_comp;
vmxnet3_tx_comp_ring *comp_ring;
comp_ring = &txq->tx_comp_ring;
tx_comp = &txq->tx_comp[comp_ring->next];
while ((tx_comp->flags & VMXNET3_TXCF_GEN) == comp_ring->gen)
{
u16 eop_idx = tx_comp->index & VMXNET3_TXC_INDEX;
u32 bi0 = txq->tx_ring.bufs[txq->tx_ring.consume];
vlib_buffer_free_one (vm, bi0);
while (txq->tx_ring.consume != eop_idx)
{
vmxnet3_tx_ring_advance_consume (txq);
}
vmxnet3_tx_ring_advance_consume (txq);
vmxnet3_tx_comp_ring_advance_next (txq);
tx_comp = &txq->tx_comp[comp_ring->next];
}
}
static_always_inline u16
vmxnet3_tx_ring_space_left (vmxnet3_txq_t * txq)
{
u16 count;
count = (txq->tx_ring.consume - txq->tx_ring.produce - 1);
/* Wrapped? */
if (txq->tx_ring.produce >= txq->tx_ring.consume)
count += txq->size;
return count;
}
VNET_DEVICE_CLASS_TX_FN (vmxnet3_device_class) (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
vmxnet3_main_t *vmxm = &vmxnet3_main;
vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
vmxnet3_device_t *vd = pool_elt_at_index (vmxm->devices, rd->dev_instance);
u32 *buffers = vlib_frame_vector_args (frame);
u32 bi0;
vlib_buffer_t *b0;
vmxnet3_tx_desc *txd = 0;
u32 desc_idx, generation, first_idx;
u16 space_left;
u16 n_left = frame->n_vectors;
vmxnet3_txq_t *txq;
vnet_hw_if_tx_frame_t *tf = vlib_frame_scalar_args (frame);
u16 qid = tf->queue_id, produce;
if (PREDICT_FALSE (!(vd->flags & VMXNET3_DEVICE_F_LINK_UP)))
{
vlib_buffer_free (vm, buffers, n_left);
vlib_error_count (vm, node->node_index, VMXNET3_TX_ERROR_LINK_DOWN,
n_left);
return (0);
}
txq = vec_elt_at_index (vd->txqs, qid);
if (tf->shared_queue)
clib_spinlock_lock (&txq->lock);
vmxnet3_txq_release (vm, vd, txq);
produce = txq->tx_ring.produce;
while (PREDICT_TRUE (n_left))
{
u16 space_needed = 1, i;
u32 gso_size = 0;
u32 l4_hdr_sz;
vlib_buffer_t *b;
u32 hdr_len = 0;
bi0 = buffers[0];
b0 = vlib_get_buffer (vm, bi0);
b = b0;
space_left = vmxnet3_tx_ring_space_left (txq);
while (b->flags & VLIB_BUFFER_NEXT_PRESENT)
{
u32 next_buffer = b->next_buffer;
b = vlib_get_buffer (vm, next_buffer);
space_needed++;
}
if (PREDICT_FALSE (space_left < space_needed))
{
vmxnet3_txq_release (vm, vd, txq);
space_left = vmxnet3_tx_ring_space_left (txq);
if (PREDICT_FALSE (space_left < space_needed))
{
vlib_buffer_free_one (vm, bi0);
vlib_error_count (vm, node->node_index,
VMXNET3_TX_ERROR_NO_FREE_SLOTS, 1);
buffers++;
n_left--;
/*
* Drop this packet. But we may have enough room for the next
* packet
*/
continue;
}
}
/*
* Toggle the generation bit for SOP fragment to avoid device starts
* reading incomplete packet
*/
generation = txq->tx_ring.gen ^ VMXNET3_TXF_GEN;
first_idx = txq->tx_ring.produce;
for (i = 0; i < space_needed; i++)
{
b0 = vlib_get_buffer (vm, bi0);
desc_idx = txq->tx_ring.produce;
vmxnet3_tx_ring_advance_produce (txq);
txq->tx_ring.bufs[desc_idx] = bi0;
txd = &txq->tx_desc[desc_idx];
txd->address = vlib_buffer_get_current_pa (vm, b0);
txd->flags[0] = generation | b0->current_length;
txd->flags[1] = 0;
if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_GSO))
{
/*
* We should not be getting GSO outbound traffic unless it is
* lro is enable
*/
ASSERT (vd->gso_enable == 1);
gso_size = vnet_buffer2 (b0)->gso_size;
l4_hdr_sz = vnet_buffer2 (b0)->gso_l4_hdr_sz;
if (b0->flags & VNET_BUFFER_F_IS_IP6)
hdr_len = sizeof (ethernet_header_t) + sizeof (ip6_header_t) +
l4_hdr_sz;
else
hdr_len = sizeof (ethernet_header_t) + sizeof (ip4_header_t) +
l4_hdr_sz;
}
generation = txq->tx_ring.gen;
bi0 = b0->next_buffer;
}
if (PREDICT_FALSE (gso_size != 0))
{
txq->tx_desc[first_idx].flags[1] = hdr_len;
txq->tx_desc[first_idx].flags[1] |= VMXNET3_TXF_OM (VMXNET3_OM_TSO);
txq->tx_desc[first_idx].flags[0] |= VMXNET3_TXF_MSSCOF (gso_size);
}
txd->flags[1] |= VMXNET3_TXF_CQ | VMXNET3_TXF_EOP;
asm volatile ("":::"memory");
/*
* Now toggle back the generation bit for the first segment.
* Device can start reading the packet
*/
txq->tx_desc[first_idx].flags[0] ^= VMXNET3_TXF_GEN;
buffers++;
n_left--;
}
if (PREDICT_TRUE (produce != txq->tx_ring.produce))
vmxnet3_reg_write_inline (vd, 0, txq->reg_txprod, txq->tx_ring.produce);
if (tf->shared_queue)
clib_spinlock_unlock (&txq->lock);
return (frame->n_vectors - n_left);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/