interface: add multi tx-queues support for new tx infra
Type: feature Change-Id: I231f782b3c56dc2b10321e4569ac7acdad1c11da Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
This commit is contained in:

committed by
Damjan Marion

parent
5d5f85f5e4
commit
0d05c0d214
@ -855,10 +855,10 @@ memif_delete_if (vlib_main_t * vm, memif_if_t * mif)
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
VNET_HW_INTERFACE_CLASS (memif_ip_hw_if_class, static) =
|
||||
{
|
||||
VNET_HW_INTERFACE_CLASS (memif_ip_hw_if_class, static) = {
|
||||
.name = "memif-ip",
|
||||
.flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
|
||||
.tx_hash_fn_type = VNET_HASH_FN_TYPE_IP,
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
|
@ -58,13 +58,11 @@ tap_main_t tap_main;
|
||||
goto error; \
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
VNET_HW_INTERFACE_CLASS (tun_device_hw_interface_class, static) =
|
||||
{
|
||||
VNET_HW_INTERFACE_CLASS (tun_device_hw_interface_class, static) = {
|
||||
.name = "tun-device",
|
||||
.flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
|
||||
.tx_hash_fn_type = VNET_HASH_FN_TYPE_IP,
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
#define TUN_MAX_PACKET_BYTES 65355
|
||||
#define TUN_MIN_PACKET_BYTES 64
|
||||
|
@ -313,6 +313,7 @@ ethernet_mac_change (vnet_hw_interface_t * hi,
|
||||
/* *INDENT-OFF* */
|
||||
VNET_HW_INTERFACE_CLASS (ethernet_hw_interface_class) = {
|
||||
.name = "Ethernet",
|
||||
.tx_hash_fn_type = VNET_HASH_FN_TYPE_ETHERNET,
|
||||
.format_address = format_ethernet_address,
|
||||
.format_header = format_ethernet_header_with_length,
|
||||
.unformat_hw_address = unformat_ethernet_address,
|
||||
|
@ -458,6 +458,29 @@ autoreply define sw_interface_set_rx_placement
|
||||
bool is_main;
|
||||
};
|
||||
|
||||
/** \brief Set an interface's tx-placement
|
||||
Tx-Queue placement on specific thread is operational for only hardware
|
||||
interface. It will not set queue - thread placement for sub-interfaces,
|
||||
p2p and pipe interfaces.
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param sw_if_index - the interface whose tx-placement will be set
|
||||
@param queue_id - the queue number whose tx-placement will be set.
|
||||
@param array_size - the size of the thread indexes array
|
||||
@param threads - the thread indexes of main and worker(s) threads
|
||||
whom tx-placement will be at.
|
||||
*/
|
||||
autoendian autoreply define sw_interface_set_tx_placement
|
||||
{
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
vl_api_interface_index_t sw_if_index;
|
||||
u32 queue_id;
|
||||
u32 array_size;
|
||||
u32 threads[array_size];
|
||||
option vat_help = "<interface | sw_if_index <index>> queue <n> [threads <list> | mask <hex>]";
|
||||
};
|
||||
|
||||
/** \brief Set custom interface name
|
||||
Set custom interface name for the interface.
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@ -512,6 +535,60 @@ define sw_interface_rx_placement_details
|
||||
vl_api_rx_mode_t mode;
|
||||
};
|
||||
|
||||
service {
|
||||
rpc sw_interface_tx_placement_get returns sw_interface_tx_placement_get_reply
|
||||
stream sw_interface_tx_placement_details;
|
||||
};
|
||||
|
||||
/** \brief get the tx queue placement of interface(s)
|
||||
@param cursor - optional, it allows client to continue a dump
|
||||
@param sw_if_index - optional interface index for which queue placement to
|
||||
be requested. sw_if_index = ~0 will get the placement information for all
|
||||
interfaces. It will not get information related to sub-interfaces, p2p
|
||||
and pipe interfaces.
|
||||
*/
|
||||
autoendian define sw_interface_tx_placement_get
|
||||
{
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
u32 cursor;
|
||||
vl_api_interface_index_t sw_if_index;
|
||||
option vat_help = "[interface | sw_if_index <index>]";
|
||||
};
|
||||
|
||||
autoendian define sw_interface_tx_placement_get_reply
|
||||
{
|
||||
u32 context;
|
||||
i32 retval;
|
||||
u32 cursor;
|
||||
};
|
||||
|
||||
/** \brief show the interface's queue - thread placement
|
||||
This api is used to display the interface and queue worker
|
||||
thread placement. One message per tx-queue per interface will
|
||||
be sent to client.
|
||||
Each message will contain information about tx-queue id of an
|
||||
interface, interface index, thread on which this tx-queue is
|
||||
placed and mode of tx-queue.
|
||||
@param client_index - opaque cookie to identify the sender
|
||||
@param context - sender context, to match reply w/ request
|
||||
@param sw_if_index - the interface whose tx-placement will be dumped
|
||||
@param queue_id - the queue id
|
||||
@param shared - the queue is shared on other threads
|
||||
@param array_size - the size of the threads array
|
||||
@param threads - the main and worker(s) thread index(es) whom tx-placement are at.
|
||||
*/
|
||||
autoendian define sw_interface_tx_placement_details
|
||||
{
|
||||
u32 client_index;
|
||||
u32 context;
|
||||
vl_api_interface_index_t sw_if_index;
|
||||
u32 queue_id;
|
||||
u8 shared;
|
||||
u32 array_size;
|
||||
u32 threads[array_size];
|
||||
};
|
||||
|
||||
/* Gross kludge, DGMS */
|
||||
autoreply define interface_name_renumber
|
||||
{
|
||||
|
@ -863,6 +863,10 @@ vnet_register_interface (vnet_main_t * vnm,
|
||||
hw->hw_if_index = hw_index;
|
||||
hw->default_rx_mode = VNET_HW_IF_RX_MODE_POLLING;
|
||||
|
||||
if (hw_class->tx_hash_fn_type == VNET_HASH_FN_TYPE_ETHERNET ||
|
||||
hw_class->tx_hash_fn_type == VNET_HASH_FN_TYPE_IP)
|
||||
hw->hf = vnet_hash_default_function (hw_class->tx_hash_fn_type);
|
||||
|
||||
if (dev_class->format_device_name)
|
||||
hw->name = format (0, "%U", dev_class->format_device_name, dev_instance);
|
||||
else if (hw_class->format_interface_name)
|
||||
@ -1020,6 +1024,7 @@ vnet_register_interface (vnet_main_t * vnm,
|
||||
static char *e[] = {
|
||||
"interface is down",
|
||||
"interface is deleted",
|
||||
"no tx queue available",
|
||||
};
|
||||
|
||||
r.n_errors = ARRAY_LEN (e);
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <vppinfra/pcap.h>
|
||||
#include <vnet/l3_types.h>
|
||||
#include <vppinfra/lock.h>
|
||||
#include <vnet/hash/hash.h>
|
||||
|
||||
struct vnet_main_t;
|
||||
struct vnet_hw_interface_t;
|
||||
@ -410,6 +411,9 @@ typedef struct _vnet_hw_interface_class
|
||||
/* Flags */
|
||||
vnet_hw_interface_class_flags_t flags;
|
||||
|
||||
/* tx hash type for interfaces of this hw class */
|
||||
vnet_hash_fn_type_t tx_hash_fn_type;
|
||||
|
||||
/* Function to call when hardware interface is added/deleted. */
|
||||
vnet_interface_function_t *interface_add_del_function;
|
||||
|
||||
@ -641,8 +645,9 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
|
||||
vnet_hw_if_tx_frame_t frame;
|
||||
u32 n_threads;
|
||||
vnet_hw_if_tx_frame_t *frame;
|
||||
u32 *lookup_table;
|
||||
u32 n_queues;
|
||||
} vnet_hw_if_output_node_runtime_t;
|
||||
|
||||
/* Hardware-interface. This corresponds to a physical wire
|
||||
@ -696,6 +701,9 @@ typedef struct vnet_hw_interface_t
|
||||
used by node function vnet_per_buffer_interface_output() */
|
||||
u32 output_node_next_index;
|
||||
|
||||
/* called when hw interface is using transmit side packet steering */
|
||||
vnet_hash_fn_t hf;
|
||||
|
||||
/* Maximum transmit rate for this interface in bits/sec. */
|
||||
f64 max_rate_bits_per_sec;
|
||||
|
||||
|
@ -184,39 +184,73 @@ vnet_hw_if_update_runtime_data (vnet_main_t *vnm, u32 hw_if_index)
|
||||
}
|
||||
}
|
||||
|
||||
new_out_runtimes =
|
||||
vec_dup_aligned (hi->output_node_thread_runtimes, CLIB_CACHE_LINE_BYTES);
|
||||
vec_validate_aligned (new_out_runtimes, n_threads - 1,
|
||||
CLIB_CACHE_LINE_BYTES);
|
||||
|
||||
if (vec_len (hi->output_node_thread_runtimes) != vec_len (new_out_runtimes))
|
||||
something_changed_on_tx = 1;
|
||||
|
||||
for (int i = 0; i < vec_len (hi->tx_queue_indices); i++)
|
||||
if (vec_len (hi->tx_queue_indices) > 0)
|
||||
{
|
||||
u32 thread_index;
|
||||
u32 queue_index = hi->tx_queue_indices[i];
|
||||
vnet_hw_if_tx_queue_t *txq = vnet_hw_if_get_tx_queue (vnm, queue_index);
|
||||
uword n_threads = clib_bitmap_count_set_bits (txq->threads);
|
||||
new_out_runtimes = vec_dup_aligned (hi->output_node_thread_runtimes,
|
||||
CLIB_CACHE_LINE_BYTES);
|
||||
vec_validate_aligned (new_out_runtimes, n_threads - 1,
|
||||
CLIB_CACHE_LINE_BYTES);
|
||||
|
||||
clib_bitmap_foreach (thread_index, txq->threads)
|
||||
for (u32 i = 0; i < vec_len (new_out_runtimes); i++)
|
||||
{
|
||||
vnet_hw_if_output_node_runtime_t *rt;
|
||||
rt = vec_elt_at_index (new_out_runtimes, thread_index);
|
||||
if ((rt->frame.queue_id != txq->queue_id) ||
|
||||
(rt->n_threads != n_threads))
|
||||
rt = vec_elt_at_index (new_out_runtimes, i);
|
||||
u32 n_queues = 0, total_queues = vec_len (hi->tx_queue_indices);
|
||||
rt->frame = 0;
|
||||
rt->lookup_table = 0;
|
||||
|
||||
for (u32 j = 0; j < total_queues; j++)
|
||||
{
|
||||
u32 queue_index = hi->tx_queue_indices[j];
|
||||
vnet_hw_if_tx_frame_t frame = { .shared_queue = 0,
|
||||
.hints = 7,
|
||||
.queue_id = ~0 };
|
||||
vnet_hw_if_tx_queue_t *txq =
|
||||
vnet_hw_if_get_tx_queue (vnm, queue_index);
|
||||
if (!clib_bitmap_get (txq->threads, i))
|
||||
continue;
|
||||
|
||||
log_debug ("tx queue data changed for interface %v, thread %u "
|
||||
"(queue_id %u -> %u, n_threads %u -> %u)",
|
||||
hi->name, thread_index, rt->frame.queue_id,
|
||||
txq->queue_id, rt->n_threads, n_threads);
|
||||
"(queue_id %u)",
|
||||
hi->name, i, txq->queue_id);
|
||||
something_changed_on_tx = 1;
|
||||
rt->frame.queue_id = txq->queue_id;
|
||||
rt->frame.shared_queue = txq->shared_queue;
|
||||
rt->n_threads = n_threads;
|
||||
|
||||
frame.queue_id = txq->queue_id;
|
||||
frame.shared_queue = txq->shared_queue;
|
||||
vec_add1 (rt->frame, frame);
|
||||
n_queues++;
|
||||
}
|
||||
|
||||
// don't initialize rt->n_queues above
|
||||
if (rt->n_queues != n_queues)
|
||||
{
|
||||
something_changed_on_tx = 1;
|
||||
rt->n_queues = n_queues;
|
||||
}
|
||||
/*
|
||||
* It is only used in case of multiple txq.
|
||||
*/
|
||||
if (rt->n_queues > 0)
|
||||
{
|
||||
if (!is_pow2 (n_queues))
|
||||
n_queues = max_pow2 (n_queues);
|
||||
|
||||
vec_validate_aligned (rt->lookup_table, n_queues - 1,
|
||||
CLIB_CACHE_LINE_BYTES);
|
||||
|
||||
for (u32 k = 0; k < vec_len (rt->lookup_table); k++)
|
||||
{
|
||||
rt->lookup_table[k] = rt->frame[k % rt->n_queues].queue_id;
|
||||
log_debug ("tx queue lookup table changed for interface %v, "
|
||||
"(lookup table [%u]=%u)",
|
||||
hi->name, k, rt->lookup_table[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
/* interface deleted */
|
||||
something_changed_on_tx = 1;
|
||||
|
||||
if (something_changed_on_rx || something_changed_on_tx)
|
||||
{
|
||||
@ -303,6 +337,11 @@ vnet_hw_if_update_runtime_data (vnet_main_t *vnm, u32 hw_if_index)
|
||||
{
|
||||
vec_free (d[i]);
|
||||
vec_free (a[i]);
|
||||
if (new_out_runtimes)
|
||||
{
|
||||
vec_free (new_out_runtimes[i].frame);
|
||||
vec_free (new_out_runtimes[i].lookup_table);
|
||||
}
|
||||
}
|
||||
|
||||
vec_free (d);
|
||||
|
@ -27,3 +27,20 @@ vnet_hw_if_get_tx_queue (vnet_main_t *vnm, u32 queue_index)
|
||||
return 0;
|
||||
return pool_elt_at_index (im->hw_if_tx_queues, queue_index);
|
||||
}
|
||||
|
||||
static_always_inline int
|
||||
vnet_hw_if_txq_cmp_cli_api (vnet_hw_if_tx_queue_t **a,
|
||||
vnet_hw_if_tx_queue_t **b)
|
||||
{
|
||||
if (*a == *b)
|
||||
return 0;
|
||||
|
||||
if (a[0]->hw_if_index != b[0]->hw_if_index)
|
||||
return 2 * (a[0]->hw_if_index > b[0]->hw_if_index) - 1;
|
||||
|
||||
if (a[0]->queue_id != b[0]->queue_id)
|
||||
return 2 * (a[0]->queue_id > b[0]->queue_id) - 1;
|
||||
|
||||
ASSERT (0);
|
||||
return ~0;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <vnet/interface.h>
|
||||
#include <vnet/interface/rx_queue_funcs.h>
|
||||
#include <vnet/interface/tx_queue_funcs.h>
|
||||
#include <vnet/api_errno.h>
|
||||
#include <vnet/ethernet/ethernet.h>
|
||||
#include <vnet/ip/ip.h>
|
||||
@ -56,7 +57,9 @@ vpe_api_main_t vpe_api_main;
|
||||
_ (SW_INTERFACE_ADD_DEL_ADDRESS, sw_interface_add_del_address) \
|
||||
_ (SW_INTERFACE_SET_RX_MODE, sw_interface_set_rx_mode) \
|
||||
_ (SW_INTERFACE_RX_PLACEMENT_DUMP, sw_interface_rx_placement_dump) \
|
||||
_ (SW_INTERFACE_TX_PLACEMENT_GET, sw_interface_tx_placement_get) \
|
||||
_ (SW_INTERFACE_SET_RX_PLACEMENT, sw_interface_set_rx_placement) \
|
||||
_ (SW_INTERFACE_SET_TX_PLACEMENT, sw_interface_set_tx_placement) \
|
||||
_ (SW_INTERFACE_SET_TABLE, sw_interface_set_table) \
|
||||
_ (SW_INTERFACE_GET_TABLE, sw_interface_get_table) \
|
||||
_ (SW_INTERFACE_SET_UNNUMBERED, sw_interface_set_unnumbered) \
|
||||
@ -1215,6 +1218,168 @@ out:
|
||||
REPLY_MACRO (VL_API_SW_INTERFACE_SET_RX_PLACEMENT_REPLY);
|
||||
}
|
||||
|
||||
static void
|
||||
send_interface_tx_placement_details (vnet_hw_if_tx_queue_t **all_queues,
|
||||
u32 index, vl_api_registration_t *rp,
|
||||
u32 context)
|
||||
{
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
vl_api_sw_interface_tx_placement_details_t *rmp;
|
||||
u32 n_bits = 0, v = ~0;
|
||||
vnet_hw_if_tx_queue_t **q = vec_elt_at_index (all_queues, index);
|
||||
uword *bitmap = q[0]->threads;
|
||||
u32 hw_if_index = q[0]->hw_if_index;
|
||||
vnet_hw_interface_t *hw_if = vnet_get_hw_interface (vnm, hw_if_index);
|
||||
|
||||
n_bits = clib_bitmap_count_set_bits (bitmap);
|
||||
u32 n = n_bits * sizeof (u32);
|
||||
|
||||
/*
|
||||
* FIXME: Use the REPLY_MACRO_DETAILS5_END once endian handler is registered
|
||||
* and available.
|
||||
*/
|
||||
REPLY_MACRO_DETAILS5 (
|
||||
VL_API_SW_INTERFACE_TX_PLACEMENT_DETAILS, n, rp, context, ({
|
||||
rmp->sw_if_index = clib_host_to_net_u32 (hw_if->sw_if_index);
|
||||
rmp->queue_id = clib_host_to_net_u32 (q[0]->queue_id);
|
||||
rmp->shared = q[0]->shared_queue;
|
||||
rmp->array_size = clib_host_to_net_u32 (n_bits);
|
||||
|
||||
v = clib_bitmap_first_set (bitmap);
|
||||
for (u32 i = 0; i < n_bits; i++)
|
||||
{
|
||||
rmp->threads[i] = clib_host_to_net_u32 (v);
|
||||
v = clib_bitmap_next_set (bitmap, v + 1);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_sw_interface_tx_placement_get_t_handler (
|
||||
vl_api_sw_interface_tx_placement_get_t *mp)
|
||||
{
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
vl_api_sw_interface_tx_placement_get_reply_t *rmp = 0;
|
||||
vnet_hw_if_tx_queue_t **all_queues = 0;
|
||||
vnet_hw_if_tx_queue_t *q;
|
||||
u32 sw_if_index = mp->sw_if_index;
|
||||
i32 rv = 0;
|
||||
|
||||
if (pool_elts (vnm->interface_main.hw_if_tx_queues) == 0)
|
||||
{
|
||||
rv = VNET_API_ERROR_NO_SUCH_ENTRY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (sw_if_index == ~0)
|
||||
{
|
||||
pool_foreach (q, vnm->interface_main.hw_if_tx_queues)
|
||||
vec_add1 (all_queues, q);
|
||||
vec_sort_with_function (all_queues, vnet_hw_if_txq_cmp_cli_api);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 qi = ~0;
|
||||
vnet_sw_interface_t *si;
|
||||
|
||||
if (!vnet_sw_if_index_is_api_valid (sw_if_index))
|
||||
{
|
||||
clib_warning ("sw_if_index %u does not exist", sw_if_index);
|
||||
rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
|
||||
goto err;
|
||||
}
|
||||
|
||||
si = vnet_get_sw_interface (vnm, sw_if_index);
|
||||
if (si->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
|
||||
{
|
||||
clib_warning ("interface type is not HARDWARE! P2P, PIPE and SUB"
|
||||
" interfaces are not supported");
|
||||
rv = VNET_API_ERROR_INVALID_INTERFACE;
|
||||
goto err;
|
||||
}
|
||||
|
||||
vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, si->hw_if_index);
|
||||
for (qi = 0; qi < vec_len (hw->tx_queue_indices); qi++)
|
||||
{
|
||||
q = vnet_hw_if_get_tx_queue (vnm, hw->tx_queue_indices[qi]);
|
||||
vec_add1 (all_queues, q);
|
||||
}
|
||||
}
|
||||
|
||||
REPLY_AND_DETAILS_VEC_MACRO_END (VL_API_SW_INTERFACE_TX_PLACEMENT_GET_REPLY,
|
||||
all_queues, mp, rmp, rv, ({
|
||||
send_interface_tx_placement_details (
|
||||
all_queues, cursor, rp, mp->context);
|
||||
}));
|
||||
|
||||
vec_free (all_queues);
|
||||
return;
|
||||
|
||||
err:
|
||||
REPLY_MACRO_END (VL_API_SW_INTERFACE_TX_PLACEMENT_GET_REPLY);
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_sw_interface_set_tx_placement_t_handler (
|
||||
vl_api_sw_interface_set_tx_placement_t *mp)
|
||||
{
|
||||
vl_api_sw_interface_set_tx_placement_reply_t *rmp;
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
u32 sw_if_index = mp->sw_if_index;
|
||||
vnet_sw_interface_t *si;
|
||||
uword *bitmap = 0;
|
||||
u32 queue_id = ~0;
|
||||
u32 size = 0;
|
||||
clib_error_t *error = 0;
|
||||
int rv = 0;
|
||||
|
||||
VALIDATE_SW_IF_INDEX_END (mp);
|
||||
|
||||
si = vnet_get_sw_interface (vnm, sw_if_index);
|
||||
if (si->type != VNET_SW_INTERFACE_TYPE_HARDWARE)
|
||||
{
|
||||
rv = VNET_API_ERROR_INVALID_VALUE;
|
||||
goto bad_sw_if_index;
|
||||
}
|
||||
|
||||
size = mp->array_size;
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
u32 thread_index = mp->threads[i];
|
||||
bitmap = clib_bitmap_set (bitmap, thread_index, 1);
|
||||
}
|
||||
|
||||
queue_id = mp->queue_id;
|
||||
rv = set_hw_interface_tx_queue (si->hw_if_index, queue_id, bitmap);
|
||||
|
||||
switch (rv)
|
||||
{
|
||||
case VNET_API_ERROR_INVALID_VALUE:
|
||||
error = clib_error_return (
|
||||
0, "please specify valid thread(s) - last thread index %u",
|
||||
clib_bitmap_last_set (bitmap));
|
||||
break;
|
||||
case VNET_API_ERROR_INVALID_QUEUE:
|
||||
error = clib_error_return (
|
||||
0, "unknown queue %u on interface %s", queue_id,
|
||||
vnet_get_hw_interface (vnet_get_main (), si->hw_if_index)->name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
clib_error_report (error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
BAD_SW_IF_INDEX_LABEL;
|
||||
out:
|
||||
REPLY_MACRO_END (VL_API_SW_INTERFACE_SET_TX_PLACEMENT_REPLY);
|
||||
clib_bitmap_free (bitmap);
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_create_vlan_subif_t_handler (vl_api_create_vlan_subif_t * mp)
|
||||
{
|
||||
@ -1474,6 +1639,10 @@ interface_api_hookup (vlib_main_t * vm)
|
||||
/* Do not replay VL_API_SW_INTERFACE_DUMP messages */
|
||||
am->api_trace_cfg[VL_API_SW_INTERFACE_DUMP].replay_enable = 0;
|
||||
|
||||
/* Mark these APIs as autoendian */
|
||||
am->is_autoendian[VL_API_SW_INTERFACE_SET_TX_PLACEMENT] = 1;
|
||||
am->is_autoendian[VL_API_SW_INTERFACE_TX_PLACEMENT_GET] = 1;
|
||||
|
||||
/*
|
||||
* Set up the (msg_name, crc, message-id) table
|
||||
*/
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include <vnet/classify/vnet_classify.h>
|
||||
#include <vnet/interface/rx_queue_funcs.h>
|
||||
#include <vnet/interface/tx_queue_funcs.h>
|
||||
#include <vnet/hash/hash.h>
|
||||
static int
|
||||
compare_interface_names (void *a1, void *a2)
|
||||
{
|
||||
@ -1840,28 +1841,24 @@ VLIB_CLI_COMMAND (cmd_set_if_rx_placement,static) = {
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
clib_error_t *
|
||||
int
|
||||
set_hw_interface_tx_queue (u32 hw_if_index, u32 queue_id, uword *bitmap)
|
||||
{
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
vnet_device_main_t *vdm = &vnet_device_main;
|
||||
vnet_hw_interface_t *hw;
|
||||
vlib_thread_main_t *vtm = vlib_get_thread_main ();
|
||||
vnet_hw_if_tx_queue_t *txq;
|
||||
u32 queue_index;
|
||||
u32 thread_index;
|
||||
|
||||
hw = vnet_get_hw_interface (vnm, hw_if_index);
|
||||
|
||||
/* highest set bit in bitmap should not exceed last worker thread index */
|
||||
thread_index = clib_bitmap_last_set (bitmap);
|
||||
if ((thread_index != ~0) && (thread_index > vdm->last_worker_thread_index))
|
||||
return clib_error_return (0, "please specify valid thread(s)");
|
||||
if ((thread_index != ~0) && (thread_index >= vtm->n_vlib_mains))
|
||||
return VNET_API_ERROR_INVALID_VALUE;
|
||||
|
||||
queue_index =
|
||||
vnet_hw_if_get_tx_queue_index_by_id (vnm, hw_if_index, queue_id);
|
||||
if (queue_index == ~0)
|
||||
return clib_error_return (0, "unknown queue %u on interface %s", queue_id,
|
||||
hw->name);
|
||||
return VNET_API_ERROR_INVALID_QUEUE;
|
||||
|
||||
txq = vnet_hw_if_get_tx_queue (vnm, queue_index);
|
||||
|
||||
@ -1889,6 +1886,7 @@ set_interface_tx_queue (vlib_main_t *vm, unformat_input_t *input,
|
||||
u32 hw_if_index = (u32) ~0;
|
||||
u32 queue_id = (u32) 0;
|
||||
uword *bitmap = 0;
|
||||
int rv = 0;
|
||||
|
||||
if (!unformat_user (input, unformat_line_input, line_input))
|
||||
return 0;
|
||||
@ -1920,7 +1918,23 @@ set_interface_tx_queue (vlib_main_t *vm, unformat_input_t *input,
|
||||
goto error;
|
||||
}
|
||||
|
||||
error = set_hw_interface_tx_queue (hw_if_index, queue_id, bitmap);
|
||||
rv = set_hw_interface_tx_queue (hw_if_index, queue_id, bitmap);
|
||||
|
||||
switch (rv)
|
||||
{
|
||||
case VNET_API_ERROR_INVALID_VALUE:
|
||||
error = clib_error_return (
|
||||
0, "please specify valid thread(s) - last thread index %u",
|
||||
clib_bitmap_last_set (bitmap));
|
||||
break;
|
||||
case VNET_API_ERROR_INVALID_QUEUE:
|
||||
error = clib_error_return (
|
||||
0, "unknown queue %u on interface %s", queue_id,
|
||||
vnet_get_hw_interface (vnet_get_main (), hw_if_index)->name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
error:
|
||||
clib_bitmap_free (bitmap);
|
||||
@ -2467,6 +2481,132 @@ VLIB_CLI_COMMAND (cmd_set_if_name, static) = {
|
||||
.function = set_interface_name,
|
||||
.is_mp_safe = 1,
|
||||
};
|
||||
|
||||
static clib_error_t *
|
||||
set_interface_tx_hash_cmd (vlib_main_t *vm, unformat_input_t *input,
|
||||
vlib_cli_command_t *cmd)
|
||||
{
|
||||
clib_error_t *error = 0;
|
||||
unformat_input_t _line_input, *line_input = &_line_input;
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
vnet_hw_interface_t *hi;
|
||||
u8 *hash_name = 0;
|
||||
u32 hw_if_index = (u32) ~0;
|
||||
vnet_hash_fn_t hf;
|
||||
vnet_hash_fn_type_t ftype;
|
||||
|
||||
if (!unformat_user (input, unformat_line_input, line_input))
|
||||
return 0;
|
||||
|
||||
while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
|
||||
{
|
||||
if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
|
||||
&hw_if_index))
|
||||
;
|
||||
else if (unformat (line_input, "hash-name %s", &hash_name))
|
||||
;
|
||||
else
|
||||
{
|
||||
error = clib_error_return (0, "parse error: '%U'",
|
||||
format_unformat_error, line_input);
|
||||
unformat_free (line_input);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
unformat_free (line_input);
|
||||
|
||||
if (hw_if_index == (u32) ~0)
|
||||
{
|
||||
error = clib_error_return (0, "please specify valid interface name");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hi = vnet_get_hw_interface (vnm, hw_if_index);
|
||||
ftype =
|
||||
vnet_get_hw_interface_class (vnm, hi->hw_class_index)->tx_hash_fn_type;
|
||||
hf = vnet_hash_function_from_name ((const char *) hash_name, ftype);
|
||||
|
||||
if (!hf)
|
||||
{
|
||||
error = clib_error_return (0, "please specify valid hash name");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hi->hf = hf;
|
||||
error:
|
||||
vec_free (hash_name);
|
||||
return (error);
|
||||
}
|
||||
|
||||
VLIB_CLI_COMMAND (cmd_set_if_tx_hash, static) = {
|
||||
.path = "set interface tx-hash",
|
||||
.short_help = "set interface tx-hash <interface> hash-name <hash-name>",
|
||||
.function = set_interface_tx_hash_cmd,
|
||||
};
|
||||
|
||||
static clib_error_t *
|
||||
show_tx_hash (vlib_main_t *vm, unformat_input_t *input,
|
||||
vlib_cli_command_t *cmd)
|
||||
{
|
||||
clib_error_t *error = 0;
|
||||
unformat_input_t _line_input, *line_input = &_line_input;
|
||||
vnet_main_t *vnm = vnet_get_main ();
|
||||
vnet_hw_interface_t *hi;
|
||||
vnet_hash_function_registration_t *hash;
|
||||
u32 hw_if_index = (u32) ~0;
|
||||
vnet_hash_fn_type_t ftype;
|
||||
|
||||
if (!unformat_user (input, unformat_line_input, line_input))
|
||||
return 0;
|
||||
|
||||
while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
|
||||
{
|
||||
if (unformat (line_input, "%U", unformat_vnet_hw_interface, vnm,
|
||||
&hw_if_index))
|
||||
;
|
||||
else
|
||||
{
|
||||
error = clib_error_return (0, "parse error: '%U'",
|
||||
format_unformat_error, line_input);
|
||||
unformat_free (line_input);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
unformat_free (line_input);
|
||||
|
||||
if (hw_if_index == (u32) ~0)
|
||||
{
|
||||
error = clib_error_return (0, "please specify valid interface name");
|
||||
goto error;
|
||||
}
|
||||
|
||||
hi = vnet_get_hw_interface (vnm, hw_if_index);
|
||||
ftype =
|
||||
vnet_get_hw_interface_class (vnm, hi->hw_class_index)->tx_hash_fn_type;
|
||||
|
||||
if (hi->hf)
|
||||
{
|
||||
hash = vnet_hash_function_from_func (hi->hf, ftype);
|
||||
if (hash)
|
||||
vlib_cli_output (vm, "%U", format_vnet_hash, hash);
|
||||
else
|
||||
vlib_cli_output (vm, "no matching hash function found");
|
||||
}
|
||||
else
|
||||
vlib_cli_output (vm, "no hashing function set");
|
||||
|
||||
error:
|
||||
return (error);
|
||||
}
|
||||
|
||||
VLIB_CLI_COMMAND (cmd_show_tx_hash, static) = {
|
||||
.path = "show interface tx-hash",
|
||||
.short_help = "show interface tx-hash [interface]",
|
||||
.function = show_tx_hash,
|
||||
};
|
||||
|
||||
/*
|
||||
* fd.io coding-style-patch-verification: ON
|
||||
*
|
||||
|
@ -212,6 +212,9 @@ format_vnet_hw_interface (u8 * s, va_list * args)
|
||||
if (vec_len (hi->tx_queue_indices))
|
||||
{
|
||||
s = format (s, "\n%UTX Queues:", format_white_space, indent + 2);
|
||||
s = format (
|
||||
s, "\n%UTX Hash: %U", format_white_space, indent + 4, format_vnet_hash,
|
||||
vnet_hash_function_from_func (hi->hf, hw_class->tx_hash_fn_type));
|
||||
s = format (s, "\n%U%-6s%-7s%-15s", format_white_space, indent + 4,
|
||||
"queue", "shared", "thread(s)");
|
||||
for (int i = 0; i < vec_len (hi->tx_queue_indices); i++)
|
||||
|
@ -427,6 +427,8 @@ clib_error_t *set_hw_interface_change_rx_mode (vnet_main_t * vnm,
|
||||
/* Set rx-placement on the interface */
|
||||
clib_error_t *set_hw_interface_rx_placement (u32 hw_if_index, u32 queue_id,
|
||||
u32 thread_index, u8 is_main);
|
||||
/* Set tx-queue placement on the interface */
|
||||
int set_hw_interface_tx_queue (u32 hw_if_index, u32 queue_id, uword *bitmap);
|
||||
|
||||
/* Set the MTU on the HW interface */
|
||||
void vnet_hw_interface_set_mtu (vnet_main_t * vnm, u32 hw_if_index, u32 mtu);
|
||||
@ -509,6 +511,7 @@ typedef enum
|
||||
{
|
||||
VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN,
|
||||
VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED,
|
||||
VNET_INTERFACE_OUTPUT_ERROR_NO_TX_QUEUE,
|
||||
} vnet_interface_output_error_t;
|
||||
|
||||
/* Format for interface output traces. */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -569,6 +569,63 @@ api_sw_interface_set_rx_placement (vat_main_t *vam)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
api_sw_interface_set_tx_placement (vat_main_t *vam)
|
||||
{
|
||||
unformat_input_t *i = vam->input;
|
||||
vl_api_sw_interface_set_tx_placement_t *mp;
|
||||
u32 sw_if_index;
|
||||
u8 sw_if_index_set = 0;
|
||||
int ret;
|
||||
uword *bitmap = 0;
|
||||
u32 queue_id, n_bits = 0;
|
||||
u32 v;
|
||||
|
||||
/* Parse args required to build the message */
|
||||
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
|
||||
{
|
||||
if (unformat (i, "queue %d", &queue_id))
|
||||
;
|
||||
else if (unformat (i, "threads %U", unformat_bitmap_list, &bitmap))
|
||||
;
|
||||
else if (unformat (i, "mask %U", unformat_bitmap_mask, &bitmap))
|
||||
;
|
||||
else if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index))
|
||||
sw_if_index_set = 1;
|
||||
else if (unformat (i, "sw_if_index %d", &sw_if_index))
|
||||
sw_if_index_set = 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (sw_if_index_set == 0)
|
||||
{
|
||||
errmsg ("missing interface name or sw_if_index");
|
||||
return -99;
|
||||
}
|
||||
|
||||
n_bits = clib_bitmap_count_set_bits (bitmap);
|
||||
/* Construct the API message */
|
||||
M2 (SW_INTERFACE_SET_TX_PLACEMENT, mp, sizeof (u32) * n_bits);
|
||||
mp->sw_if_index = htonl (sw_if_index);
|
||||
mp->queue_id = htonl (queue_id);
|
||||
mp->array_size = htonl (n_bits);
|
||||
|
||||
v = clib_bitmap_first_set (bitmap);
|
||||
for (u32 j = 0; j < n_bits; j++)
|
||||
{
|
||||
mp->threads[j] = htonl (v);
|
||||
v = clib_bitmap_next_set (bitmap, v + 1);
|
||||
}
|
||||
|
||||
/* send it... */
|
||||
S (mp);
|
||||
/* Wait for a reply, return the good/bad news... */
|
||||
W (ret);
|
||||
clib_bitmap_free (bitmap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
api_interface_name_renumber (vat_main_t *vam)
|
||||
{
|
||||
@ -844,6 +901,25 @@ vl_api_sw_interface_rx_placement_details_t_handler (
|
||||
((mp->mode == 2) ? "interrupt" : "adaptive"));
|
||||
}
|
||||
|
||||
static __clib_unused void
|
||||
vl_api_sw_interface_tx_placement_details_t_handler (
|
||||
vl_api_sw_interface_tx_placement_details_t *mp)
|
||||
{
|
||||
vat_main_t *vam = interface_test_main.vat_main;
|
||||
u32 size = ntohl (mp->array_size);
|
||||
uword *bitmap = 0;
|
||||
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
u32 thread_index = ntohl (mp->threads[i]);
|
||||
bitmap = clib_bitmap_set (bitmap, thread_index, 1);
|
||||
}
|
||||
|
||||
print (vam->ofp, "\n%-11d %-6d %-7s %U", ntohl (mp->sw_if_index),
|
||||
ntohl (mp->queue_id), (mp->shared == 1) ? "yes" : "no",
|
||||
format_bitmap_list, bitmap);
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_create_vlan_subif_reply_t_handler (vl_api_create_vlan_subif_reply_t *mp)
|
||||
{
|
||||
@ -960,6 +1036,52 @@ api_sw_interface_rx_placement_dump (vat_main_t *vam)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
api_sw_interface_tx_placement_get (vat_main_t *vam)
|
||||
{
|
||||
unformat_input_t *i = vam->input;
|
||||
vl_api_sw_interface_tx_placement_get_t *mp;
|
||||
vl_api_control_ping_t *mp_ping;
|
||||
int ret;
|
||||
u32 sw_if_index;
|
||||
u8 sw_if_index_set = 0;
|
||||
|
||||
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
|
||||
{
|
||||
if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index))
|
||||
sw_if_index_set++;
|
||||
else if (unformat (i, "sw_if_index %d", &sw_if_index))
|
||||
sw_if_index_set++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
fformat (vam->ofp, "\n%-11s %-6s %-7s %-11s", "sw_if_index", "queue",
|
||||
"shared", "threads");
|
||||
|
||||
/* Dump Interface tx placement */
|
||||
M (SW_INTERFACE_TX_PLACEMENT_GET, mp);
|
||||
|
||||
if (sw_if_index_set)
|
||||
mp->sw_if_index = htonl (sw_if_index);
|
||||
else
|
||||
mp->sw_if_index = ~0;
|
||||
|
||||
S (mp);
|
||||
|
||||
/* Use a control ping for synchronization */
|
||||
PING (&interface_test_main, mp_ping);
|
||||
S (mp_ping);
|
||||
|
||||
W (ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
vl_api_sw_interface_tx_placement_get_reply_t_handler ()
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
api_sw_interface_clear_stats (vat_main_t *vam)
|
||||
{
|
||||
|
@ -245,6 +245,7 @@ VNET_HW_INTERFACE_CLASS (pg_tun_hw_interface_class) = {
|
||||
.build_rewrite = NULL,
|
||||
//.update_adjacency = gre_update_adj,
|
||||
.flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
|
||||
.tx_hash_fn_type = VNET_HASH_FN_TYPE_IP,
|
||||
};
|
||||
|
||||
u32
|
||||
|
Reference in New Issue
Block a user