af_xdp: fix init lock for shared txq
[vpp.git] / src / plugins / af_xdp / device.c
index 5bc7e30..30d3c95 100644 (file)
 
 #include <stdio.h>
 #include <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/ethtool.h>
 #include <linux/if_link.h>
+#include <linux/sockios.h>
 #include <bpf/libbpf.h>
 #include <vlib/vlib.h>
 #include <vlib/unix/unix.h>
@@ -101,9 +104,6 @@ af_xdp_delete_if (vlib_main_t * vm, af_xdp_device_t * ad)
       ethernet_delete_interface (vnm, ad->hw_if_index);
     }
 
-  for (i = 0; i < ad->rxq_num; i++)
-    clib_file_del_by_index (&file_main, vec_elt (ad->rxqs, i).file_index);
-
   for (i = 0; i < ad->txq_num; i++)
     clib_spinlock_free (&vec_elt (ad->txqs, i).lock);
 
@@ -113,6 +113,9 @@ af_xdp_delete_if (vlib_main_t * vm, af_xdp_device_t * ad)
   vec_foreach (umem, ad->umem)
     xsk_umem__delete (*umem);
 
+  for (i = 0; i < ad->rxq_num; i++)
+    clib_file_del_by_index (&file_main, vec_elt (ad->rxqs, i).file_index);
+
   if (ad->bpf_obj)
     {
       bpf_set_link_xdp_fd (ad->linux_ifindex, -1, 0);
@@ -188,16 +191,9 @@ af_xdp_create_queue (vlib_main_t *vm, af_xdp_create_if_args_t *args,
   const int is_rx = qid < ad->rxq_num;
   const int is_tx = qid < ad->txq_num;
 
-  vec_validate_aligned (ad->umem, qid, CLIB_CACHE_LINE_BYTES);
   umem = vec_elt_at_index (ad->umem, qid);
-
-  vec_validate_aligned (ad->xsk, qid, CLIB_CACHE_LINE_BYTES);
   xsk = vec_elt_at_index (ad->xsk, qid);
-
-  vec_validate_aligned (ad->rxqs, qid, CLIB_CACHE_LINE_BYTES);
   rxq = vec_elt_at_index (ad->rxqs, qid);
-
-  vec_validate_aligned (ad->txqs, qid, CLIB_CACHE_LINE_BYTES);
   txq = vec_elt_at_index (ad->txqs, qid);
 
   /*
@@ -215,18 +211,7 @@ af_xdp_create_queue (vlib_main_t *vm, af_xdp_create_if_args_t *args,
   umem_config.comp_size = args->txq_size;
   umem_config.frame_size =
     sizeof (vlib_buffer_t) + vlib_buffer_get_default_data_size (vm);
-  /*
-   * Note about headroom: for some reasons, there seem to be a discrepency
-   * between 0-copy and copy mode:
-   *   - 0-copy: XDP_PACKET_HEADROOM will be added to the user headroom
-   *   - copy: nothing is added to the user headroom
-   * We privileged 0-copy and set headroom so that frame_headroom +
-   * XDP_PACKET_HEADROOM == sizeof(vlib_buffer_t), ie data will correctly
-   * point to vlib_buffer_t->data for 0-copy. In copy mode, we have to add
-   * XDP_PACKET_HEADROOM to desc offset during refill.
-   */
-  STATIC_ASSERT (sizeof (vlib_buffer_t) >= XDP_PACKET_HEADROOM, "wrong size");
-  umem_config.frame_headroom = sizeof (vlib_buffer_t) - XDP_PACKET_HEADROOM;
+  umem_config.frame_headroom = sizeof (vlib_buffer_t);
   umem_config.flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG;
   if (xsk_umem__create
       (umem, uword_to_pointer (vm->buffer_main->buffer_mem_start, void *),
@@ -332,6 +317,31 @@ af_xdp_get_numa (const char *ifname)
   return numa;
 }
 
+static void
+af_xdp_get_q_count (const char *ifname, int *rxq_num, int *txq_num)
+{
+  struct ethtool_channels ec = { .cmd = ETHTOOL_GCHANNELS };
+  struct ifreq ifr = { .ifr_data = (void *) &ec };
+  int fd, err;
+
+  *rxq_num = *txq_num = 1;
+
+  fd = socket (AF_INET, SOCK_DGRAM, 0);
+  if (fd < 0)
+    return;
+
+  snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", ifname);
+  err = ioctl (fd, SIOCETHTOOL, &ifr);
+
+  close (fd);
+
+  if (err)
+    return;
+
+  *rxq_num = clib_max (ec.combined_count, ec.rx_count);
+  *txq_num = clib_max (ec.combined_count, ec.tx_count);
+}
+
 static clib_error_t *
 af_xdp_device_rxq_read_ready (clib_file_t * f)
 {
@@ -386,8 +396,7 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args)
 
   args->rxq_size = args->rxq_size ? args->rxq_size : 2 * VLIB_FRAME_SIZE;
   args->txq_size = args->txq_size ? args->txq_size : 2 * VLIB_FRAME_SIZE;
-  rxq_num = args->rxq_num ? args->rxq_num : 1;
-  txq_num = tm->n_vlib_mains;
+  args->rxq_num = args->rxq_num ? args->rxq_num : 1;
 
   if (!args->linux_ifname)
     {
@@ -408,6 +417,17 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args)
       goto err0;
     }
 
+  af_xdp_get_q_count (args->linux_ifname, &rxq_num, &txq_num);
+  if (args->rxq_num > rxq_num && AF_XDP_NUM_RX_QUEUES_ALL != args->rxq_num)
+    {
+      args->rv = VNET_API_ERROR_INVALID_VALUE;
+      args->error = clib_error_create ("too many rxq requested (%d > %d)",
+                                      args->rxq_num, rxq_num);
+      goto err0;
+    }
+  rxq_num = clib_min (rxq_num, args->rxq_num);
+  txq_num = clib_min (txq_num, tm->n_vlib_mains);
+
   pool_get_zero (am->devices, ad);
 
   if (tm->n_vlib_mains > 1 &&
@@ -423,6 +443,12 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args)
   q_num = clib_max (rxq_num, txq_num);
   ad->rxq_num = rxq_num;
   ad->txq_num = txq_num;
+
+  vec_validate_aligned (ad->umem, q_num - 1, CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (ad->xsk, q_num - 1, CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (ad->rxqs, q_num - 1, CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (ad->txqs, q_num - 1, CLIB_CACHE_LINE_BYTES);
+
   for (i = 0; i < q_num; i++)
     {
       if (af_xdp_create_queue (vm, args, ad, i))
@@ -444,19 +470,13 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args)
          ad->rxq_num = clib_min (i, rxq_num);
          ad->txq_num = clib_min (i, txq_num);
 
-         if (i < rxq_num && AF_XDP_NUM_RX_QUEUES_ALL != rxq_num)
+         if (i < rxq_num && AF_XDP_NUM_RX_QUEUES_ALL != args->rxq_num)
            {
              ad->rxq_num = ad->txq_num = 0;
              goto err1; /* failed creating requested rxq: fatal error, bailing
                            out */
            }
 
-         if (i < txq_num)
-           {
-             /* we created less txq than threads not an error but initialize lock for shared txq */
-             for (i = 0; i < ad->txq_num; i++)
-               clib_spinlock_init (&vec_elt (ad->txqs, i).lock);
-           }
 
          args->rv = 0;
          clib_error_free (args->error);
@@ -464,6 +484,13 @@ af_xdp_create_if (vlib_main_t * vm, af_xdp_create_if_args_t * args)
        }
     }
 
+  if (ad->txq_num < tm->n_vlib_mains)
+    {
+      /* initialize lock for shared txq */
+      for (i = 0; i < ad->txq_num; i++)
+       clib_spinlock_init (&vec_elt (ad->txqs, i).lock);
+    }
+
   ad->dev_instance = ad - am->devices;
   ad->per_interface_next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
   ad->pool =
@@ -550,6 +577,7 @@ af_xdp_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
       vnet_hw_interface_set_flags (vnm, ad->hw_if_index,
                                   VNET_HW_INTERFACE_FLAG_LINK_UP);
       ad->flags |= AF_XDP_DEVICE_F_ADMIN_UP;
+      af_xdp_device_input_refill (ad);
     }
   else
     {