+static void
+send_interface_rx_placement_details (vpe_api_main_t * am,
+ vl_api_registration_t * rp,
+ u32 sw_if_index, u32 worker_id,
+ u32 queue_id, u8 mode, u32 context)
+{
+ vl_api_sw_interface_rx_placement_details_t *mp;
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+
+ mp->_vl_msg_id =
+ htons (REPLY_MSG_ID_BASE + VL_API_SW_INTERFACE_RX_PLACEMENT_DETAILS);
+ mp->sw_if_index = htonl (sw_if_index);
+ mp->queue_id = htonl (queue_id);
+ mp->worker_id = htonl (worker_id);
+ mp->mode = htonl (mode);
+ mp->context = context;
+
+ vl_api_send_msg (rp, (u8 *) mp);
+}
+
+static void vl_api_sw_interface_rx_placement_dump_t_handler
+ (vl_api_sw_interface_rx_placement_dump_t * mp)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ vpe_api_main_t *am = &vpe_api_main;
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ if (sw_if_index == ~0)
+ {
+ vnet_hw_if_rx_queue_t **all_queues = 0;
+ vnet_hw_if_rx_queue_t **qptr;
+ vnet_hw_if_rx_queue_t *q;
+ pool_foreach (q, vnm->interface_main.hw_if_rx_queues)
+ vec_add1 (all_queues, q);
+ vec_sort_with_function (all_queues, vnet_hw_if_rxq_cmp_cli_api);
+
+ vec_foreach (qptr, all_queues)
+ {
+ u32 current_thread = qptr[0]->thread_index;
+ u32 hw_if_index = qptr[0]->hw_if_index;
+ vnet_hw_interface_t *hw_if =
+ vnet_get_hw_interface (vnm, hw_if_index);
+ send_interface_rx_placement_details (
+ am, reg, hw_if->sw_if_index, current_thread, qptr[0]->queue_id,
+ qptr[0]->mode, mp->context);
+ }
+ vec_free (all_queues);
+ }
+ else
+ {
+ int i;
+ 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);
+ goto bad_sw_if_index;
+ }
+
+ 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");
+ goto bad_sw_if_index;
+ }
+
+ vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, si->hw_if_index);
+
+ for (i = 0; i < vec_len (hw->rx_queue_indices); i++)
+ {
+ vnet_hw_if_rx_queue_t *rxq =
+ vnet_hw_if_get_rx_queue (vnm, hw->rx_queue_indices[i]);
+ send_interface_rx_placement_details (
+ am, reg, hw->sw_if_index, rxq->thread_index, rxq->queue_id,
+ rxq->mode, mp->context);
+ }
+ }
+
+ BAD_SW_IF_INDEX_LABEL;
+}
+
+static void vl_api_sw_interface_set_rx_placement_t_handler
+ (vl_api_sw_interface_set_rx_placement_t * mp)
+{
+ vl_api_sw_interface_set_rx_placement_reply_t *rmp;
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+ vnet_sw_interface_t *si;
+ clib_error_t *error = 0;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (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;
+ }
+
+ error = set_hw_interface_rx_placement (si->hw_if_index,
+ ntohl (mp->queue_id),
+ ntohl (mp->worker_id), mp->is_main);
+ if (error)
+ {
+ rv = VNET_API_ERROR_UNIMPLEMENTED;
+ clib_error_report (error);
+ goto out;
+ }
+
+ BAD_SW_IF_INDEX_LABEL;
+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 native_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);
+ u32 context = clib_host_to_net_u32 (native_context);
+
+ 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);
+}
+