dpdk: do not crash when failing to bring interface up
[vpp.git] / src / plugins / dpdk / device / device.c
index eb2dbbc..6d130a9 100644 (file)
@@ -22,6 +22,7 @@
 #include <dpdk/device/dpdk.h>
 #include <dpdk/device/dpdk_priv.h>
 #include <vppinfra/error.h>
+#include <vlib/unix/unix.h>
 
 #define foreach_dpdk_tx_func_error                     \
   _(BAD_RETVAL, "DPDK tx function returned an error")  \
@@ -81,7 +82,7 @@ dpdk_set_mac_address (vnet_hw_interface_t * hi,
   else
     {
       vec_reset_length (xd->default_mac_address);
-      vec_add (xd->default_mac_address, address, sizeof (address));
+      vec_add (xd->default_mac_address, address, sizeof (mac_address_t));
       return NULL;
     }
 }
@@ -218,17 +219,21 @@ static_always_inline void
 dpdk_buffer_tx_offload (dpdk_device_t * xd, vlib_buffer_t * b,
                        struct rte_mbuf *mb)
 {
-  u32 ip_cksum = b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM;
-  u32 tcp_cksum = b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM;
-  u32 udp_cksum = b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
   int is_ip4 = b->flags & VNET_BUFFER_F_IS_IP4;
-  u32 tso = b->flags & VNET_BUFFER_F_GSO;
+  u32 tso = b->flags & VNET_BUFFER_F_GSO, max_pkt_len;
+  u32 ip_cksum, tcp_cksum, udp_cksum;
   u64 ol_flags;
+  vnet_buffer_oflags_t oflags = 0;
 
   /* Is there any work for us? */
-  if (PREDICT_TRUE ((ip_cksum | tcp_cksum | udp_cksum | tso) == 0))
+  if (PREDICT_TRUE (((b->flags & VNET_BUFFER_F_OFFLOAD) | tso) == 0))
     return;
 
+  oflags = vnet_buffer (b)->oflags;
+  ip_cksum = oflags & VNET_BUFFER_OFFLOAD_F_IP_CKSUM;
+  tcp_cksum = oflags & VNET_BUFFER_OFFLOAD_F_TCP_CKSUM;
+  udp_cksum = oflags & VNET_BUFFER_OFFLOAD_F_UDP_CKSUM;
+
   mb->l2_len = vnet_buffer (b)->l3_hdr_offset - b->current_data;
   mb->l3_len = vnet_buffer (b)->l4_hdr_offset -
     vnet_buffer (b)->l3_hdr_offset;
@@ -238,12 +243,15 @@ dpdk_buffer_tx_offload (dpdk_device_t * xd, vlib_buffer_t * b,
   ol_flags |= ip_cksum ? PKT_TX_IP_CKSUM : 0;
   ol_flags |= tcp_cksum ? PKT_TX_TCP_CKSUM : 0;
   ol_flags |= udp_cksum ? PKT_TX_UDP_CKSUM : 0;
-  ol_flags |= tso ? (tcp_cksum ? PKT_TX_TCP_SEG : PKT_TX_UDP_SEG) : 0;
 
   if (tso)
     {
       mb->l4_len = vnet_buffer2 (b)->gso_l4_hdr_sz;
       mb->tso_segsz = vnet_buffer2 (b)->gso_size;
+      /* ensure packet is large enough to require tso */
+      max_pkt_len = mb->l2_len + mb->l3_len + mb->l4_len + mb->tso_segsz;
+      if (mb->tso_segsz != 0 && mb->pkt_len > max_pkt_len)
+       ol_flags |= (tcp_cksum ? PKT_TX_TCP_SEG : PKT_TX_UDP_SEG);
     }
 
   mb->ol_flags |= ol_flags;
@@ -304,11 +312,6 @@ VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
       or_flags = b[0]->flags | b[1]->flags | b[2]->flags | b[3]->flags;
       all_or_flags |= or_flags;
 
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[1]);
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[2]);
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[3]);
-
       if (or_flags & VLIB_BUFFER_NEXT_PRESENT)
        {
          dpdk_validate_rte_mbuf (vm, b[0], 1);
@@ -325,10 +328,7 @@ VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
        }
 
       if (PREDICT_FALSE ((xd->flags & DPDK_DEVICE_FLAG_TX_OFFLOAD) &&
-                        (or_flags &
-                         (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM
-                          | VNET_BUFFER_F_OFFLOAD_IP_CKSUM
-                          | VNET_BUFFER_F_OFFLOAD_UDP_CKSUM))))
+                        (or_flags & VNET_BUFFER_F_OFFLOAD)))
        {
          dpdk_buffer_tx_offload (xd, b[0], mb[0]);
          dpdk_buffer_tx_offload (xd, b[1], mb[1]);
@@ -370,9 +370,6 @@ VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
       or_flags = b[0]->flags | b[1]->flags;
       all_or_flags |= or_flags;
 
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[1]);
-
       if (or_flags & VLIB_BUFFER_NEXT_PRESENT)
        {
          dpdk_validate_rte_mbuf (vm, b[0], 1);
@@ -385,10 +382,7 @@ VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
        }
 
       if (PREDICT_FALSE ((xd->flags & DPDK_DEVICE_FLAG_TX_OFFLOAD) &&
-                        (or_flags &
-                         (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM
-                          | VNET_BUFFER_F_OFFLOAD_IP_CKSUM
-                          | VNET_BUFFER_F_OFFLOAD_UDP_CKSUM))))
+                        (or_flags & VNET_BUFFER_F_OFFLOAD)))
        {
          dpdk_buffer_tx_offload (xd, b[0], mb[0]);
          dpdk_buffer_tx_offload (xd, b[1], mb[1]);
@@ -411,7 +405,6 @@ VNET_DEVICE_CLASS_TX_FN (dpdk_device_class) (vlib_main_t * vm,
     {
       b[0] = vlib_buffer_from_rte_mbuf (mb[0]);
       all_or_flags |= b[0]->flags;
-      VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b[0]);
 
       dpdk_validate_rte_mbuf (vm, b[0], 1);
       dpdk_buffer_tx_offload (xd, b[0], mb[0]);
@@ -477,11 +470,15 @@ dpdk_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
   if (is_up)
     {
       if ((xd->flags & DPDK_DEVICE_FLAG_ADMIN_UP) == 0)
-       dpdk_device_start (xd);
-      xd->flags |= DPDK_DEVICE_FLAG_ADMIN_UP;
-      f64 now = vlib_time_now (dm->vlib_main);
-      dpdk_update_counters (xd, now);
-      dpdk_update_link_state (xd, now);
+       {
+         dpdk_device_start (xd);
+         if (vec_len (xd->errors))
+           return clib_error_create ("Interface start failed");
+         xd->flags |= DPDK_DEVICE_FLAG_ADMIN_UP;
+         f64 now = vlib_time_now (dm->vlib_main);
+         dpdk_update_counters (xd, now);
+         dpdk_update_link_state (xd, now);
+       }
     }
   else
     {
@@ -539,8 +536,8 @@ dpdk_subif_add_del_function (vnet_main_t * vnm,
   if ((xd->flags & DPDK_DEVICE_FLAG_PMD) == 0)
     goto done;
 
-  /* currently we program VLANS only for IXGBE VF and I40E VF */
-  if ((xd->pmd != VNET_DPDK_PMD_IXGBEVF) && (xd->pmd != VNET_DPDK_PMD_I40EVF))
+  /* currently we program VLANS only for IXGBE VF */
+  if (xd->pmd != VNET_DPDK_PMD_IXGBEVF)
     goto done;
 
   if (t->sub.eth.flags.no_tags == 1)
@@ -584,6 +581,151 @@ done:
   return err;
 }
 
+static clib_error_t *
+dpdk_interface_set_rss_queues (struct vnet_main_t *vnm,
+                              struct vnet_hw_interface_t *hi,
+                              clib_bitmap_t * bitmap)
+{
+  dpdk_main_t *xm = &dpdk_main;
+  u32 hw_if_index = hi->hw_if_index;
+  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
+  dpdk_device_t *xd = vec_elt_at_index (xm->devices, hw->dev_instance);
+  clib_error_t *err = 0;
+  struct rte_eth_rss_reta_entry64 *reta_conf = NULL;
+  struct rte_eth_dev_info dev_info;
+  u16 *reta = NULL;
+  u16 *valid_queue = NULL;
+  u16 valid_queue_count = 0;
+  uint32_t i, j;
+  uint32_t ret;
+
+  rte_eth_dev_info_get (xd->port_id, &dev_info);
+
+  /* parameter check */
+  if (clib_bitmap_count_set_bits (bitmap) == 0)
+    {
+      err = clib_error_return (0, "must assign at least one valid rss queue");
+      goto done;
+    }
+
+  if (clib_bitmap_count_set_bits (bitmap) > dev_info.nb_rx_queues)
+    {
+      err = clib_error_return (0, "too many rss queues");
+      goto done;
+    }
+
+  /* new RETA */
+  reta = clib_mem_alloc (dev_info.reta_size * sizeof (*reta));
+  if (reta == NULL)
+    {
+      err = clib_error_return (0, "clib_mem_alloc failed");
+      goto done;
+    }
+
+  clib_memset (reta, 0, dev_info.reta_size * sizeof (*reta));
+
+  valid_queue_count = 0;
+  /* *INDENT-OFF* */
+  clib_bitmap_foreach (i, bitmap)  {
+    if (i >= dev_info.nb_rx_queues)
+      {
+        err = clib_error_return (0, "illegal queue number");
+        goto done;
+      }
+    reta[valid_queue_count++] = i;
+  }
+  /* *INDENT-ON* */
+
+  /* check valid_queue_count not zero, make coverity happy */
+  if (valid_queue_count == 0)
+    {
+      err = clib_error_return (0, "must assign at least one valid rss queue");
+      goto done;
+    }
+
+  valid_queue = reta;
+  for (i = valid_queue_count, j = 0; i < dev_info.reta_size; i++, j++)
+    {
+      j = j % valid_queue_count;
+      reta[i] = valid_queue[j];
+    }
+
+  /* update reta table */
+  reta_conf =
+    (struct rte_eth_rss_reta_entry64 *) clib_mem_alloc (dev_info.reta_size /
+                                                       RTE_RETA_GROUP_SIZE *
+                                                       sizeof (*reta_conf));
+  if (reta_conf == NULL)
+    {
+      err = clib_error_return (0, "clib_mem_alloc failed");
+      goto done;
+    }
+
+  clib_memset (reta_conf, 0,
+              dev_info.reta_size / RTE_RETA_GROUP_SIZE *
+              sizeof (*reta_conf));
+
+  for (i = 0; i < dev_info.reta_size; i++)
+    {
+      uint32_t reta_id = i / RTE_RETA_GROUP_SIZE;
+      uint32_t reta_pos = i % RTE_RETA_GROUP_SIZE;
+
+      reta_conf[reta_id].mask = UINT64_MAX;
+      reta_conf[reta_id].reta[reta_pos] = reta[i];
+    }
+
+  ret =
+    rte_eth_dev_rss_reta_update (xd->port_id, reta_conf, dev_info.reta_size);
+  if (ret)
+    {
+      err = clib_error_return (0, "rte_eth_dev_rss_reta_update err %d", ret);
+      goto done;
+    }
+
+done:
+  if (reta)
+    clib_mem_free (reta);
+  if (reta_conf)
+    clib_mem_free (reta_conf);
+
+  return err;
+}
+
+static clib_error_t *
+dpdk_interface_rx_mode_change (vnet_main_t *vnm, u32 hw_if_index, u32 qid,
+                              vnet_hw_if_rx_mode mode)
+{
+  dpdk_main_t *xm = &dpdk_main;
+  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
+  dpdk_device_t *xd = vec_elt_at_index (xm->devices, hw->dev_instance);
+  clib_file_main_t *fm = &file_main;
+  dpdk_rx_queue_t *rxq;
+  clib_file_t *f;
+  int rv = 0;
+  if (!(xd->flags & DPDK_DEVICE_FLAG_INT_SUPPORTED))
+    return clib_error_return (0, "unsupported op (is the interface up?)", rv);
+  if (mode == VNET_HW_IF_RX_MODE_POLLING &&
+      !(xd->flags & DPDK_DEVICE_FLAG_INT_UNMASKABLE))
+    rv = rte_eth_dev_rx_intr_disable (xd->port_id, qid);
+  else if (mode == VNET_HW_IF_RX_MODE_POLLING)
+    {
+      rxq = vec_elt_at_index (xd->rx_queues, qid);
+      f = pool_elt_at_index (fm->file_pool, rxq->clib_file_index);
+      fm->file_update (f, UNIX_FILE_UPDATE_DELETE);
+    }
+  else if (!(xd->flags & DPDK_DEVICE_FLAG_INT_UNMASKABLE))
+    rv = rte_eth_dev_rx_intr_enable (xd->port_id, qid);
+  else
+    {
+      rxq = vec_elt_at_index (xd->rx_queues, qid);
+      f = pool_elt_at_index (fm->file_pool, rxq->clib_file_index);
+      fm->file_update (f, UNIX_FILE_UPDATE_ADD);
+    }
+  if (rv)
+    return clib_error_return (0, "dpdk_interface_rx_mode_change err %d", rv);
+  return 0;
+}
+
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS (dpdk_device_class) = {
   .name = "dpdk",
@@ -600,6 +742,8 @@ VNET_DEVICE_CLASS (dpdk_device_class) = {
   .mac_addr_add_del_function = dpdk_add_del_mac_address,
   .format_flow = format_dpdk_flow,
   .flow_ops_function = dpdk_flow_ops_fn,
+  .set_rss_queues_function = dpdk_interface_set_rss_queues,
+  .rx_mode_change_function = dpdk_interface_rx_mode_change,
 };
 /* *INDENT-ON* */