Marvell device plugin
[vpp.git] / src / plugins / marvell / pp2 / input.c
diff --git a/src/plugins/marvell/pp2/input.c b/src/plugins/marvell/pp2/input.c
new file mode 100644 (file)
index 0000000..6329e3b
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 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.
+ *------------------------------------------------------------------
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/devices/devices.h>
+
+#include <marvell/pp2/pp2.h>
+
+#define foreach_mrvl_pp2_input_error \
+  _(PPIO_RECV, "pp2_ppio_recv error") \
+  _(BPOOL_GET_NUM_BUFFS, "pp2_bpool_get_num_buffs error") \
+  _(BPOOL_PUT_BUFFS, "pp2_bpool_put_buffs error") \
+  _(BUFFER_ALLOC, "buffer alloc error") \
+  _(MAC_CE, "MAC error (CRC error)") \
+  _(MAC_OR, "overrun error") \
+  _(MAC_RSVD, "unknown MAC error") \
+  _(MAC_RE, "resource error") \
+  _(IP_HDR, "ip4 header error")
+
+typedef enum
+{
+#define _(f,s) MRVL_PP2_INPUT_ERROR_##f,
+  foreach_mrvl_pp2_input_error
+#undef _
+    MRVL_PP2_INPUT_N_ERROR,
+} mrvl_pp2_input_error_t;
+
+static __clib_unused char *mrvl_pp2_input_error_strings[] = {
+#define _(n,s) s,
+  foreach_mrvl_pp2_input_error
+#undef _
+};
+
+static_always_inline void
+mrvl_pp2_input_trace (vlib_main_t * vm, vlib_node_runtime_t * node, u32 next0,
+                     vlib_buffer_t * b0, uword * n_trace,
+                     mrvl_pp2_if_t * ppif, struct pp2_ppio_desc *d)
+{
+  mrvl_pp2_input_trace_t *tr;
+  vlib_trace_buffer (vm, node, next0, b0,
+                    /* follow_chain */ 0);
+  vlib_set_trace_count (vm, node, --(*n_trace));
+  tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+  tr->next_index = next0;
+  tr->hw_if_index = ppif->hw_if_index;
+  clib_memcpy (&tr->desc, d, sizeof (struct pp2_ppio_desc));
+}
+
+static_always_inline u16
+mrvl_pp2_set_buf_data_len_flags (vlib_buffer_t * b, struct pp2_ppio_desc *d,
+                                u32 add_flags)
+{
+  u16 len;
+  len = pp2_ppio_inq_desc_get_pkt_len (d);
+  b->total_length_not_including_first_buffer = 0;
+  b->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | add_flags;
+
+  if (add_flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
+    vnet_buffer (b)->l2_hdr_offset = 2;
+
+  if (add_flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID)
+    {
+      u16 offset = DM_RXD_GET_L3_OFF (d);
+      vnet_buffer (b)->l3_hdr_offset = offset;
+      b->current_data = offset;
+      b->current_length = len - offset + 2;
+    }
+  else
+    {
+      b->current_data = 2;
+      b->current_length = len;
+    }
+
+  if (add_flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID)
+    vnet_buffer (b)->l4_hdr_offset = vnet_buffer (b)->l3_hdr_offset +
+      DM_RXD_GET_IPHDR_LEN (d) * 4;
+
+  return len;
+}
+
+static_always_inline u16
+mrvl_pp2_next_from_desc (vlib_node_runtime_t * node, struct pp2_ppio_desc * d,
+                        vlib_buffer_t * b, u32 * next)
+{
+  u8 l3_info;
+  /* ES bit set means MAC error  - drop and count */
+  if (PREDICT_FALSE (DM_RXD_GET_ES (d)))
+    {
+      *next = VNET_DEVICE_INPUT_NEXT_DROP;
+      u8 ec = DM_RXD_GET_EC (d);
+      if (ec == 0)
+       b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_CE];
+      else if (ec == 1)
+       b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_OR];
+      else if (ec == 2)
+       b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_RSVD];
+      else if (ec == 3)
+       b->error = node->errors[MRVL_PP2_INPUT_ERROR_MAC_RE];
+      return mrvl_pp2_set_buf_data_len_flags (b, d, 0);
+    }
+  l3_info = DM_RXD_GET_L3_PRS_INFO (d);
+
+  /* ipv4 packet can be value 1, 2 or 3 */
+  if (PREDICT_TRUE ((l3_info - 1) < 3))
+    {
+      if (PREDICT_FALSE (DM_RXD_GET_L3_IP4_HDR_ERR (d) != 0))
+       {
+         *next = VNET_DEVICE_INPUT_NEXT_DROP;
+         b->error = node->errors[MRVL_PP2_INPUT_ERROR_IP_HDR];
+         return mrvl_pp2_set_buf_data_len_flags (b, d, 0);
+       }
+      *next = VNET_DEVICE_INPUT_NEXT_IP4_NCS_INPUT;
+      return mrvl_pp2_set_buf_data_len_flags
+       (b, d,
+        VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
+        VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+        VNET_BUFFER_F_L4_HDR_OFFSET_VALID | VNET_BUFFER_F_IS_IP4);
+    }
+
+  /* ipv4 packet can be value 4 or 5 */
+  if (PREDICT_TRUE ((l3_info - 4) < 2))
+    {
+      *next = VNET_DEVICE_INPUT_NEXT_IP6_INPUT;
+      return mrvl_pp2_set_buf_data_len_flags
+       (b, d,
+        VNET_BUFFER_F_L2_HDR_OFFSET_VALID |
+        VNET_BUFFER_F_L3_HDR_OFFSET_VALID |
+        VNET_BUFFER_F_L4_HDR_OFFSET_VALID | VNET_BUFFER_F_IS_IP6);
+    }
+
+  *next = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
+  return mrvl_pp2_set_buf_data_len_flags (b, d,
+                                         VNET_BUFFER_F_L2_HDR_OFFSET_VALID);
+}
+
+static_always_inline uword
+mrvl_pp2_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
+                             vlib_frame_t * frame, mrvl_pp2_if_t * ppif,
+                             u16 qid)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  u32 thread_index = vlib_get_thread_index ();
+  mrvl_pp2_inq_t *inq = vec_elt_at_index (ppif->inqs, qid);
+  uword n_trace = vlib_get_trace_count (vm, node);
+  mrvl_pp2_per_thread_data_t *ptd =
+    vec_elt_at_index (ppm->per_thread_data, thread_index);
+  u32 next_index = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT;
+  u32 sw_if_index[VLIB_N_RX_TX];
+  u32 n_rx_packets = 0;
+  u32 n_rx_bytes = 0;
+  u32 *to_next = 0;
+  struct pp2_ppio_desc *d;
+  u16 n_desc = VLIB_FRAME_SIZE;
+  u32 n_bufs;
+  u32 *buffers;
+  int i;
+
+  vec_validate_aligned (ptd->descs, n_desc, CLIB_CACHE_LINE_BYTES);
+  if (PREDICT_FALSE (pp2_ppio_recv (ppif->ppio, 0, qid, ptd->descs, &n_desc)))
+    {
+      vlib_error_count (vm, node->node_index, MRVL_PP2_INPUT_ERROR_PPIO_RECV,
+                       1);
+      n_desc = 0;
+    }
+  n_rx_packets = n_desc;
+
+  for (i = 0; i < n_desc; i++)
+    ptd->buffers[i] = pp2_ppio_inq_desc_get_cookie (&ptd->descs[i]);
+
+  d = ptd->descs;
+  buffers = ptd->buffers;
+  sw_if_index[VLIB_RX] = ppif->sw_if_index;
+  sw_if_index[VLIB_TX] = (u32) ~ 0;
+  while (n_desc)
+    {
+      u32 n_left_to_next;
+      vlib_buffer_t *b0, *b1;
+      u32 bi0, bi1;
+      u32 next0, next1;
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+      while (n_desc >= 4 && n_left_to_next >= 2)
+       {
+         /* prefetch */
+         bi0 = buffers[0];
+         bi1 = buffers[1];
+         to_next[0] = bi0;
+         to_next[1] = bi1;
+         b0 = vlib_get_buffer (vm, bi0);
+         b1 = vlib_get_buffer (vm, bi1);
+
+         if (PREDICT_TRUE (ppif->per_interface_next_index == ~0))
+           {
+             n_rx_bytes += mrvl_pp2_next_from_desc (node, d, b0, &next0);
+             n_rx_bytes += mrvl_pp2_next_from_desc (node, d + 1, b1, &next1);
+             vnet_feature_start_device_input_x2 (ppif->sw_if_index, &next0,
+                                                 &next1, b0, b1);
+           }
+         else
+           {
+             n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b0, d, 0);
+             n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b1, d + 1, 0);
+             next0 = next1 = ppif->per_interface_next_index;
+           }
+
+         clib_memcpy (vnet_buffer (b0)->sw_if_index, sw_if_index,
+                      sizeof (sw_if_index));
+         clib_memcpy (vnet_buffer (b1)->sw_if_index, sw_if_index,
+                      sizeof (sw_if_index));
+
+         VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+         VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b1);
+
+         if (PREDICT_FALSE (n_trace > 0))
+           {
+             mrvl_pp2_input_trace (vm, node, next0, b0, &n_trace, ppif, d);
+             if (n_trace > 0)
+               mrvl_pp2_input_trace (vm, node, next1, b1, &n_trace, ppif,
+                                     d + 1);
+           }
+
+         to_next += 2;
+         n_left_to_next -= 2;
+         d += 2;
+         buffers += 2;
+         n_desc -= 2;
+
+         /* enqueue */
+         vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, bi1, next0,
+                                          next1);
+
+       }
+      while (n_desc && n_left_to_next)
+       {
+         u32 bi0 = buffers[0];
+         to_next[0] = bi0;
+         b0 = vlib_get_buffer (vm, bi0);
+
+         if (PREDICT_TRUE (ppif->per_interface_next_index == ~0))
+           {
+             n_rx_bytes += mrvl_pp2_next_from_desc (node, d, b0, &next0);
+             vnet_feature_start_device_input_x1 (ppif->sw_if_index, &next0,
+                                                 b0);
+           }
+         else
+           {
+             n_rx_bytes += mrvl_pp2_set_buf_data_len_flags (b0, d, 0);
+             next0 = ppif->per_interface_next_index;
+           }
+
+         clib_memcpy (vnet_buffer (b0)->sw_if_index, sw_if_index,
+                      sizeof (sw_if_index));
+
+         VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+
+         if (PREDICT_FALSE (n_trace > 0))
+           mrvl_pp2_input_trace (vm, node, next0, b0, &n_trace, ppif, d);
+
+         to_next += 1;
+         n_left_to_next--;
+         d++;
+         buffers++;
+         n_desc--;
+
+         /* enqueue */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+  vlib_increment_combined_counter (vnm->
+                                  interface_main.combined_sw_if_counters +
+                                  VNET_INTERFACE_COUNTER_RX, thread_index,
+                                  ppif->hw_if_index, n_rx_packets,
+                                  n_rx_bytes);
+
+  if (PREDICT_FALSE (pp2_bpool_get_num_buffs (inq->bpool, &n_bufs)))
+    {
+      vlib_error_count (vm, node->node_index,
+                       MRVL_PP2_INPUT_ERROR_BPOOL_GET_NUM_BUFFS, 1);
+      goto done;
+    }
+
+  n_bufs = inq->size - n_bufs;
+  while (n_bufs >= MRVL_PP2_BUFF_BATCH_SZ)
+    {
+      u16 n_alloc, i;
+      struct buff_release_entry *e = ptd->bre;
+      u32 *buffers = ptd->buffers;
+
+      n_alloc = vlib_buffer_alloc (vm, ptd->buffers, MRVL_PP2_BUFF_BATCH_SZ);
+      i = n_alloc;
+
+      if (PREDICT_FALSE (n_alloc == 0))
+       {
+         vlib_error_count (vm, node->node_index,
+                           MRVL_PP2_INPUT_ERROR_BUFFER_ALLOC, 1);
+         goto done;
+       }
+
+      while (i--)
+       {
+         u32 bi = buffers[0];
+         e->buff.addr = vlib_get_buffer_data_physical_address (vm, bi) - 64;
+         e->buff.cookie = bi;
+         e->bpool = inq->bpool;
+         e++;
+         buffers++;
+       }
+
+      i = n_alloc;
+      if (PREDICT_FALSE (pp2_bpool_put_buffs (ptd->hif, ptd->bre, &i)))
+       {
+         vlib_error_count (vm, node->node_index,
+                           MRVL_PP2_INPUT_ERROR_BPOOL_PUT_BUFFS, 1);
+         vlib_buffer_free (vm, ptd->buffers, n_alloc);
+         goto done;
+       }
+
+      if (PREDICT_FALSE (i != n_alloc))
+       vlib_buffer_free (vm, ptd->buffers + i, n_alloc - i);
+
+      n_bufs -= i;
+    }
+
+done:
+  return n_rx_packets;
+}
+
+uword
+mrvl_pp2_input_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
+                  vlib_frame_t * frame)
+{
+  u32 n_rx = 0;
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  vnet_device_input_runtime_t *rt = (void *) node->runtime_data;
+  vnet_device_and_queue_t *dq;
+
+  foreach_device_and_queue (dq, rt->devices_and_queues)
+  {
+    mrvl_pp2_if_t *ppif;
+    ppif = vec_elt_at_index (ppm->interfaces, dq->dev_instance);
+    if (ppif->flags & MRVL_PP2_IF_F_ADMIN_UP)
+      n_rx += mrvl_pp2_device_input_inline (vm, node, frame, ppif,
+                                           dq->queue_id);
+  }
+  return n_rx;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (mrvl_pp2_input_node) = {
+  .function = mrvl_pp2_input_fn,
+  .name = "mrvl-pp2-input",
+  .sibling_of = "device-input",
+  .format_trace = format_mrvl_pp2_input_trace,
+  .type = VLIB_NODE_TYPE_INPUT,
+  .state = VLIB_NODE_STATE_POLLING,
+  .n_errors = MRVL_PP2_INPUT_N_ERROR,
+  .error_strings = mrvl_pp2_input_error_strings,
+};
+
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */