diff options
Diffstat (limited to 'src/vnet/interface')
-rw-r--r-- | src/vnet/interface/caps.c | 63 | ||||
-rw-r--r-- | src/vnet/interface/monitor.c | 121 | ||||
-rw-r--r-- | src/vnet/interface/runtime.c | 90 | ||||
-rw-r--r-- | src/vnet/interface/rx_queue.c | 21 | ||||
-rw-r--r-- | src/vnet/interface/rx_queue_funcs.h | 7 | ||||
-rw-r--r-- | src/vnet/interface/stats.c | 84 | ||||
-rw-r--r-- | src/vnet/interface/tx_queue.rst | 159 | ||||
-rw-r--r-- | src/vnet/interface/tx_queue_funcs.h | 17 |
8 files changed, 528 insertions, 34 deletions
diff --git a/src/vnet/interface/caps.c b/src/vnet/interface/caps.c new file mode 100644 index 00000000000..54e8d90c471 --- /dev/null +++ b/src/vnet/interface/caps.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2021 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vnet/vnet.h> +#include <vnet/interface.h> + +VLIB_REGISTER_LOG_CLASS (if_caps_log, static) = { + .class_name = "interface", + .subclass_name = "caps", +}; + +#define log_debug(fmt, ...) \ + vlib_log_debug (if_caps_log.class, fmt, __VA_ARGS__) + +format_function_t format_vnet_hw_if_caps; + +void +vnet_hw_if_change_caps (vnet_main_t *vnm, u32 hw_if_index, + vnet_hw_if_caps_change_t *caps) +{ + vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); + vnet_hw_if_caps_t old = hi->caps; + + hi->caps = (hi->caps & ~caps->mask) | caps->val; + + log_debug ("change: interface %U, set: %U, cleared: %U", + format_vnet_hw_if_index_name, vnm, hw_if_index, + format_vnet_hw_if_caps, (old ^ hi->caps) & caps->val, + format_vnet_hw_if_caps, (old ^ hi->caps) & ~caps->val); +} + +u8 * +format_vnet_hw_if_caps (u8 *s, va_list *va) +{ + vnet_hw_if_caps_t caps = va_arg (*va, vnet_hw_if_caps_t); + + const char *strings[sizeof (vnet_hw_if_caps_t) * 8] = { +#define _(bit, sfx, str) [bit] = (str), + foreach_vnet_hw_if_caps +#undef _ + }; + + if (caps == 0) + return format (s, "none"); + + while (caps) + { + int bit = get_lowest_set_bit_index (caps); + + if (strings[bit]) + s = format (s, "%s", strings[bit]); + else + s = format (s, "unknown-%u", bit); + + caps = clear_lowest_set_bit (caps); + if (caps) + vec_add1 (s, ' '); + } + + return s; +} diff --git a/src/vnet/interface/monitor.c b/src/vnet/interface/monitor.c new file mode 100644 index 00000000000..3ae1fd29156 --- /dev/null +++ b/src/vnet/interface/monitor.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 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 <vnet/vnet.h> +#include <vlib/vlib.h> + +static clib_error_t * +monitor_interface_command_fn (vlib_main_t *vm, unformat_input_t *input, + vlib_cli_command_t *cmd) +{ + const vnet_main_t *vnm = vnet_get_main (); + const vlib_combined_counter_main_t *counters = + vnm->interface_main.combined_sw_if_counters; + f64 refresh_interval = 1.0; + u32 refresh_count = ~0; + clib_error_t *error = 0; + vlib_counter_t vrx[2], vtx[2]; + f64 ts[2]; + u32 hw_if_index = ~0; + u8 spin = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "%U", unformat_vnet_hw_interface, vnm, + &hw_if_index)) + ; + else if (unformat (input, "interval %f", &refresh_interval)) + ; + else if (unformat (input, "count %u", &refresh_count)) + ; + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + goto done; + } + } + + if (hw_if_index == ~0) + { + error = clib_error_return (0, "no interface passed"); + goto done; + } + + vlib_get_combined_counter (counters + VNET_INTERFACE_COUNTER_RX, hw_if_index, + &vrx[spin]); + vlib_get_combined_counter (counters + VNET_INTERFACE_COUNTER_TX, hw_if_index, + &vtx[spin]); + ts[spin] = vlib_time_now (vm); + + while (refresh_count--) + { + f64 sleep_interval, tsd; + + while (((sleep_interval = + ts[spin] + refresh_interval - vlib_time_now (vm)) > 0.0)) + { + uword event_type, *event_data = 0; + vlib_process_wait_for_event_or_clock (vm, sleep_interval); + event_type = vlib_process_get_events (vm, &event_data); + switch (event_type) + { + case ~0: /* no events => timeout */ + break; + default: + /* someone pressed a key, abort */ + vlib_cli_output (vm, "Aborted due to a keypress."); + goto done; + } + vec_free (event_data); + } + spin ^= 1; + vlib_get_combined_counter (counters + VNET_INTERFACE_COUNTER_RX, + hw_if_index, &vrx[spin]); + vlib_get_combined_counter (counters + VNET_INTERFACE_COUNTER_TX, + hw_if_index, &vtx[spin]); + ts[spin] = vlib_time_now (vm); + + tsd = ts[spin] - ts[spin ^ 1]; + vlib_cli_output ( + vm, "rx: %Upps %Ubps tx: %Upps %Ubps", format_base10, + (u64) ((vrx[spin].packets - vrx[spin ^ 1].packets) / tsd), + format_base10, + (u64) (8 * (vrx[spin].bytes - vrx[spin ^ 1].bytes) / tsd), + format_base10, + (u64) ((vtx[spin].packets - vtx[spin ^ 1].packets) / tsd), + format_base10, + (u64) (8 * (vtx[spin].bytes - vtx[spin ^ 1].bytes) / tsd)); + } + +done: + return error; +} + +VLIB_CLI_COMMAND (monitor_interface_command, static) = { + .path = "monitor interface", + .short_help = + "monitor interface <interface> [interval <intv>] [count <count>]", + .function = monitor_interface_command_fn, + .is_mp_safe = 1, +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet/interface/runtime.c b/src/vnet/interface/runtime.c index e63f1ecc2fc..a88a23bd4c9 100644 --- a/src/vnet/interface/runtime.c +++ b/src/vnet/interface/runtime.c @@ -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; + + 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->frame.queue_id = txq->queue_id; - rt->frame.shared_queue = txq->shared_queue; - rt->n_threads = n_threads; + 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) { @@ -255,10 +289,9 @@ vnet_hw_if_update_runtime_data (vnet_main_t *vnm, u32 hw_if_index) { void *in = rt->rxq_interrupts; int int_num = -1; - while ((int_num = clib_interrupt_get_next (in, int_num)) != - -1) + while ((int_num = clib_interrupt_get_next_and_clear ( + in, int_num)) != -1) { - clib_interrupt_clear (in, int_num); pending_int = clib_bitmap_set (pending_int, int_num, 1); last_int = clib_max (last_int, int_num); } @@ -303,6 +336,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); diff --git a/src/vnet/interface/rx_queue.c b/src/vnet/interface/rx_queue.c index 7632b190705..b1fc82f38e9 100644 --- a/src/vnet/interface/rx_queue.c +++ b/src/vnet/interface/rx_queue.c @@ -124,7 +124,10 @@ vnet_hw_if_unregister_all_rx_queues (vnet_main_t *vnm, u32 hw_if_index) vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); vnet_interface_main_t *im = &vnm->interface_main; vnet_hw_if_rx_queue_t *rxq; + vlib_main_t *vm; + vnet_hw_if_rx_node_runtime_t *rt; u64 key; + u32 queue_index; log_debug ("unregister_all: interface %v", hi->name); @@ -132,6 +135,15 @@ vnet_hw_if_unregister_all_rx_queues (vnet_main_t *vnm, u32 hw_if_index) { rxq = vnet_hw_if_get_rx_queue (vnm, hi->rx_queue_indices[i]); key = rx_queue_key (rxq->hw_if_index, rxq->queue_id); + if (PREDICT_FALSE (rxq->mode == VNET_HW_IF_RX_MODE_INTERRUPT || + rxq->mode == VNET_HW_IF_RX_MODE_ADAPTIVE)) + { + vm = vlib_get_main_by_index (rxq->thread_index); + queue_index = vnet_hw_if_get_rx_queue_index_by_id (vnm, hw_if_index, + rxq->queue_id); + rt = vlib_node_get_runtime_data (vm, hi->input_node_index); + clib_interrupt_clear (rt->rxq_interrupts, queue_index); + } hash_unset_mem_free (&im->rxq_index_by_hw_if_index_and_queue_id, &key); pool_put_index (im->hw_if_rx_queues, hi->rx_queue_indices[i]); @@ -228,7 +240,7 @@ vnet_hw_if_set_rx_queue_thread_index (vnet_main_t *vnm, u32 queue_index, hi->name, rxq->queue_id, thread_index); } -void +vnet_hw_if_rxq_poll_vector_t * vnet_hw_if_generate_rxq_int_poll_vector (vlib_main_t *vm, vlib_node_runtime_t *node) { @@ -240,18 +252,17 @@ vnet_hw_if_generate_rxq_int_poll_vector (vlib_main_t *vm, vec_reset_length (rt->rxq_vector_int); - while ((int_num = clib_interrupt_get_next (rt->rxq_interrupts, int_num)) != - -1) + while ((int_num = clib_interrupt_get_next_and_clear (rt->rxq_interrupts, + int_num)) != -1) { vnet_hw_if_rx_queue_t *rxq = vnet_hw_if_get_rx_queue (vnm, int_num); vnet_hw_if_rxq_poll_vector_t *pv; - clib_interrupt_clear (rt->rxq_interrupts, int_num); - vec_add2 (rt->rxq_vector_int, pv, 1); pv->dev_instance = rxq->dev_instance; pv->queue_id = rxq->queue_id; } + return rt->rxq_vector_int; } /* diff --git a/src/vnet/interface/rx_queue_funcs.h b/src/vnet/interface/rx_queue_funcs.h index 83f1bac4e94..906d7118296 100644 --- a/src/vnet/interface/rx_queue_funcs.h +++ b/src/vnet/interface/rx_queue_funcs.h @@ -33,8 +33,9 @@ vnet_hw_if_rx_mode vnet_hw_if_get_rx_queue_mode (vnet_main_t *vnm, u32 queue_index); void vnet_hw_if_set_rx_queue_thread_index (vnet_main_t *vnm, u32 queue_index, u32 thread_index); -void vnet_hw_if_generate_rxq_int_poll_vector (vlib_main_t *vm, - vlib_node_runtime_t *node); +vnet_hw_if_rxq_poll_vector_t * +vnet_hw_if_generate_rxq_int_poll_vector (vlib_main_t *vm, + vlib_node_runtime_t *node); /* inline functions */ @@ -72,7 +73,7 @@ vnet_hw_if_get_rxq_poll_vector (vlib_main_t *vm, vlib_node_runtime_t *node) vnet_hw_if_rxq_poll_vector_t *pv = rt->rxq_vector_int; if (PREDICT_FALSE (node->state == VLIB_NODE_STATE_INTERRUPT)) - vnet_hw_if_generate_rxq_int_poll_vector (vm, node); + pv = vnet_hw_if_generate_rxq_int_poll_vector (vm, node); else if (node->flags & VLIB_NODE_FLAG_ADAPTIVE_MODE) pv = rt->rxq_vector_poll; diff --git a/src/vnet/interface/stats.c b/src/vnet/interface/stats.c new file mode 100644 index 00000000000..4f3213aafc3 --- /dev/null +++ b/src/vnet/interface/stats.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2022 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vlib/unix/unix.h> +#include <vlib/stats/stats.h> +#include <vnet/vnet.h> +#include <vnet/devices/devices.h> /* vnet_get_aggregate_rx_packets */ +#include <vnet/interface.h> + +vlib_stats_string_vector_t if_names = 0; +static u32 **dir_entry_indices = 0; + +static struct +{ + char *prefix, *name; + u32 index; +} if_counters[] = { +#define _(e, n, p) { .prefix = #p, .name = #n }, + foreach_simple_interface_counter_name foreach_combined_interface_counter_name +#undef _ +}; + +static clib_error_t * +statseg_sw_interface_add_del (vnet_main_t *vnm, u32 sw_if_index, u32 is_add) +{ + u8 *name = 0; + + if (if_names == 0) + { + if_names = vlib_stats_add_string_vector ("/if/names"); + + for (int i = 0; i < ARRAY_LEN (if_counters); i++) + if_counters[i].index = vlib_stats_find_entry_index ( + "/%s/%s", if_counters[i].prefix, if_counters[i].name); + } + + vec_validate (dir_entry_indices, sw_if_index); + + vlib_stats_segment_lock (); + + if (is_add) + { + vnet_sw_interface_t *si, *si_sup; + vnet_hw_interface_t *hi_sup; + + si = vnet_get_sw_interface (vnm, sw_if_index); + si_sup = vnet_get_sup_sw_interface (vnm, si->sw_if_index); + ASSERT (si_sup->type == VNET_SW_INTERFACE_TYPE_HARDWARE); + hi_sup = vnet_get_hw_interface (vnm, si_sup->hw_if_index); + + name = format (0, "%v", hi_sup->name); + if (si->type != VNET_SW_INTERFACE_TYPE_HARDWARE) + name = format (name, ".%d", si->sub.id); + + vlib_stats_set_string_vector (&if_names, sw_if_index, "%v", name); + + for (u32 index, i = 0; i < ARRAY_LEN (if_counters); i++) + { + index = vlib_stats_add_symlink ( + if_counters[i].index, sw_if_index, "/interfaces/%U/%s", + format_vlib_stats_symlink, name, if_counters[i].name); + ASSERT (index != ~0); + vec_add1 (dir_entry_indices[sw_if_index], index); + } + } + else + { + name = format (0, "%s", "deleted"); + vlib_stats_set_string_vector (&if_names, sw_if_index, "%v", name); + for (u32 i = 0; i < vec_len (dir_entry_indices[sw_if_index]); i++) + vlib_stats_remove_entry (dir_entry_indices[sw_if_index][i]); + vec_free (dir_entry_indices[sw_if_index]); + } + + vec_free (name); + + vlib_stats_segment_unlock (); + + return 0; +} + +VNET_SW_INTERFACE_ADD_DEL_FUNCTION (statseg_sw_interface_add_del); diff --git a/src/vnet/interface/tx_queue.rst b/src/vnet/interface/tx_queue.rst new file mode 100644 index 00000000000..e8f0e039b8e --- /dev/null +++ b/src/vnet/interface/tx_queue.rst @@ -0,0 +1,159 @@ +.. _TX_Queue_doc: + +Transmit Queues +=============== + +Overview +________ + +VPP implements Transmit queues infra to access and manage them. It provides +common registration functions to register or unregister interfaces’ transmit +queues. It also provides functions for queues placement on given thread(s). + +The TXQ Infrastructure +_______________________ + +Infra registers each queue using a unique key which is formed by concatenating +the hardware interface index ``hw_if_index`` and unique queue identifier for +given interface ``queue_id``. As a result of registration of queue, infra +returns back a unique global ``queue_index`` which can be used by driver to +access that queue later. + +Interface output node uses pre-computed ``output_node_thread_runtime`` data +which provides essential information related to queue placements on given +thread of given interface. Transmit queue infra implements an algorithm to +pre-compute this information. It also pre-computes scalar arguments of frame +``vnet_hw_if_tx_frame_t``. It also pre-calculates a ``lookup_table`` for +thread if there are multiple transmit queues are placed on that thread. +Interface drivers call ``vnet_hw_if_update_runtime_data()`` to execute that +algorithm after registering the transmit queues to TXQ infra. + +The algorithm makes the copy of existing runtime data and iterate through them +for each vpp main and worker thread. In each iteration, algorithm loop through +all the tx queues of given interface to fill the information in the frame data +structure ``vnet_hw_if_tx_frame_t``. Algorithm also updates the information +related to number of transmit queues of given interface on given vpp thread in +data structure ``output_node_thread_runtime``. As a consequence of any update +to the copy, triggers the function to update the actual working copy by taking +the worker barrier and free the old copy of ``output_node_thread_runtime``. + +Multi-TXQ infra +^^^^^^^^^^^^^^^ + +Interface output node uses packet flow hash using hash infra in case of multi-txq +on given thread. Each hardware interface class contains type of the hash required +for interfaces from that hardware interface class i.e. ethernet interface hardware +class contains type ``VNET_HASH_FN_TYPE_ETHERNET``. Though, the hash function +itself is contained by hardware interface data structure of given interface. Default +hashing function is selected upon interface creation based on priority. User can +configure a different hash to an interface for multi-txq use case. + +Interface output node uses packet flow hash as an index to the pre-calculated lookup +table to get the queue identifier for given transmit queue. Interface output node +enqueues the packets to respective frame and also copies the ``vnet_hw_if_tx_frame_t`` +to frame scalar arguments. Drivers use scalar arguments ``vnet_hw_if_tx_frame_t`` +of the given frame to extract the information about the transmit queue to be used to +transmit the packets. Drivers may need to acquire a lock on given queue before +transmitting the packets based on the ``shared_queue`` bit status. + +Data structures +^^^^^^^^^^^^^^^ + +Queue information is stored in data structure ``vnet_hw_if_tx_queue_t``: + +.. code:: c + + typedef struct + { + /* either this queue is shared among multiple threads */ + u8 shared_queue : 1; + /* hw interface index */ + u32 hw_if_index; + + /* hardware queue identifier */ + u32 queue_id; + + /* bitmap of threads which use this queue */ + clib_bitmap_t *threads; + } vnet_hw_if_tx_queue_t; + + +Frame information is stored in data structure: ``vnet_hw_if_tx_frame_t``: + +.. code:: c + + typedef enum + { + VNET_HW_IF_TX_FRAME_HINT_NOT_CHAINED = (1 << 0), + VNET_HW_IF_TX_FRAME_HINT_NO_GSO = (1 << 1), + VNET_HW_IF_TX_FRAME_HINT_NO_CKSUM_OFFLOAD = (1 << 2), + } vnet_hw_if_tx_frame_hint_t; + + typedef struct + { + u8 shared_queue : 1; + vnet_hw_if_tx_frame_hint_t hints : 16; + u32 queue_id; + } vnet_hw_if_tx_frame_t; + +Output node runtime information is stored in data structure: ``output_node_thread_runtime``: + +.. code:: c + + typedef struct + { + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + vnet_hw_if_tx_frame_t *frame; + u32 *lookup_table; + u32 n_queues; + } vnet_hw_if_output_node_runtime_t; + + +MultiTXQ API +^^^^^^^^^^^^ + +This API message is used to place tx queue of an interface to vpp main or worker(s) thread(s). + +.. code:: c + + 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>]"; + }; + +Multi-TXQ CLI +^^^^^^^^^^^^^ + +:: + + set interface tx-queue set interface tx-queue <interface> queue <n> [threads <list>] + set interface tx-hash set interface tx-hash <interface> hash-name <hash-name> + +:: + + show hardware-interfaces + + Name Idx Link Hardware + tap0 1 up tap0 + Link speed: unknown + RX Queues: + queue thread mode + 0 main (0) polling + TX Queues: + TX Hash: [name: crc32c-5tuple priority: 50 description: IPv4/IPv6 header and TCP/UDP ports] + queue shared thread(s) + 0 no 0 + Ethernet address 02:fe:27:69:5a:b5 + VIRTIO interface + instance 0 + RX QUEUE : Total Packets + 0 : 0 + TX QUEUE : Total Packets + 0 : 0 + diff --git a/src/vnet/interface/tx_queue_funcs.h b/src/vnet/interface/tx_queue_funcs.h index 22956a4eb9b..8fcf7c336a8 100644 --- a/src/vnet/interface/tx_queue_funcs.h +++ b/src/vnet/interface/tx_queue_funcs.h @@ -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; +} |