+static void
+vhost_user_tx_thread_placement (vhost_user_intf_t * vui)
+{
+ //Let's try to assign one queue to each thread
+ u32 qid = 0;
+ u32 cpu_index = 0;
+ vui->use_tx_spinlock = 0;
+ while (1)
+ {
+ for (qid = 0; qid < VHOST_VRING_MAX_N / 2; qid++)
+ {
+ vhost_user_vring_t *rxvq = &vui->vrings[VHOST_VRING_IDX_RX (qid)];
+ if (!rxvq->started || !rxvq->enabled)
+ continue;
+
+ vui->per_cpu_tx_qid[cpu_index] = qid;
+ cpu_index++;
+ if (cpu_index == vlib_get_thread_main ()->n_vlib_mains)
+ return;
+ }
+ //We need to loop, meaning the spinlock has to be used
+ vui->use_tx_spinlock = 1;
+ if (cpu_index == 0)
+ {
+ //Could not find a single valid one
+ for (cpu_index = 0;
+ cpu_index < vlib_get_thread_main ()->n_vlib_mains; cpu_index++)
+ {
+ vui->per_cpu_tx_qid[cpu_index] = 0;
+ }
+ return;
+ }
+ }
+}
+
+static void
+vhost_user_rx_thread_placement ()
+{
+ vhost_user_main_t *vum = &vhost_user_main;
+ vhost_user_intf_t *vui;
+ vhost_cpu_t *vhc;
+ u32 *workers = 0;
+
+ //Let's list all workers cpu indexes
+ u32 i;
+ for (i = vum->input_cpu_first_index;
+ i < vum->input_cpu_first_index + vum->input_cpu_count; i++)
+ {
+ vlib_node_set_state (vlib_mains ? vlib_mains[i] : &vlib_global_main,
+ vhost_user_input_node.index,
+ VLIB_NODE_STATE_DISABLED);
+ vec_add1 (workers, i);
+ }
+
+ vec_foreach (vhc, vum->cpus)
+ {
+ vec_reset_length (vhc->rx_queues);
+ }
+
+ i = 0;
+ vec_foreach (vui, vum->vhost_user_interfaces)
+ {
+ if (!vui->active)
+ continue;
+
+ u32 *vui_workers = vec_len (vui->workers) ? vui->workers : workers;
+ u32 qid;
+ for (qid = 0; qid < VHOST_VRING_MAX_N / 2; qid++)
+ {
+ vhost_user_vring_t *txvq = &vui->vrings[VHOST_VRING_IDX_TX (qid)];
+ if (!txvq->started)
+ continue;
+
+ i %= vec_len (vui_workers);
+ u32 cpu_index = vui_workers[i];
+ i++;
+ vhc = &vum->cpus[cpu_index];
+
+ vhost_iface_and_queue_t iaq = {
+ .qid = qid,
+ .vhost_iface_index = vui - vum->vhost_user_interfaces,
+ };
+ vec_add1 (vhc->rx_queues, iaq);
+ vlib_node_set_state (vlib_mains ? vlib_mains[cpu_index] :
+ &vlib_global_main, vhost_user_input_node.index,
+ VLIB_NODE_STATE_POLLING);
+ }
+ }
+}
+
+static int
+vhost_user_thread_placement (u32 sw_if_index, u32 worker_thread_index, u8 del)
+{
+ vhost_user_main_t *vum = &vhost_user_main;
+ vhost_user_intf_t *vui;
+ vnet_hw_interface_t *hw;
+
+ if (worker_thread_index < vum->input_cpu_first_index ||
+ worker_thread_index >=
+ vum->input_cpu_first_index + vum->input_cpu_count)
+ return -1;
+
+ if (!(hw = vnet_get_sup_hw_interface (vnet_get_main (), sw_if_index)))
+ return -2;
+
+ vui = vec_elt_at_index (vum->vhost_user_interfaces, hw->dev_instance);
+ u32 found = ~0, *w;
+ vec_foreach (w, vui->workers)
+ {
+ if (*w == worker_thread_index)
+ {
+ found = w - vui->workers;
+ break;
+ }
+ }
+
+ if (del)
+ {
+ if (found == ~0)
+ return -3;
+ vec_del1 (vui->workers, found);
+ }
+ else if (found == ~0)
+ {
+ vec_add1 (vui->workers, worker_thread_index);
+ }
+
+ vhost_user_rx_thread_placement ();
+ return 0;
+}
+
+/** @brief Returns whether at least one TX and one RX vring are enabled */
+int
+vhost_user_intf_ready (vhost_user_intf_t * vui)
+{
+ int i, found[2] = { }; //RX + TX
+
+ for (i = 0; i < VHOST_VRING_MAX_N; i++)
+ if (vui->vrings[i].started && vui->vrings[i].enabled)
+ found[i & 1] = 1;
+
+ return found[0] && found[1];
+}
+
+static void
+vhost_user_update_iface_state (vhost_user_intf_t * vui)
+{
+ /* if we have pointers to descriptor table, go up */
+ int is_up = vhost_user_intf_ready (vui);
+ if (is_up != vui->is_up)
+ {
+ DBG_SOCK ("interface %d %s", vui->sw_if_index,
+ is_up ? "ready" : "down");
+ vnet_hw_interface_set_flags (vnet_get_main (), vui->hw_if_index,
+ is_up ? VNET_HW_INTERFACE_FLAG_LINK_UP :
+ 0);
+ vui->is_up = is_up;
+ }
+ vhost_user_rx_thread_placement ();
+ vhost_user_tx_thread_placement (vui);
+}