Marvell device plugin 07/9907/11
authorDamjan Marion <damjan.marion@gmail.com>
Tue, 26 Dec 2017 13:40:13 +0000 (13:40 +0000)
committerDamjan Marion <dmarion.lists@gmail.com>
Mon, 15 Jan 2018 16:36:09 +0000 (16:36 +0000)
Depends in musdk 17.10 [1], tested on MACCHIATObin [2] board.
currently only coves PP2.

musdk must be compiled with:
./configure --enable-bpool-dma=64

[1] https://github.com/MarvellEmbeddedProcessors/musdk-marvell
[2] http://macchiatobin.net

Change-Id: I0247a6b860b0a067c54190eae0bd3fe08de234ad
Signed-off-by: Damjan Marion <damarion@cisco.com>
src/configure.ac
src/plugins/Makefile.am
src/plugins/marvell.am [new file with mode: 0644]
src/plugins/marvell/README.md [new file with mode: 0644]
src/plugins/marvell/plugin.c [new file with mode: 0644]
src/plugins/marvell/pp2/cli.c [new file with mode: 0644]
src/plugins/marvell/pp2/format.c [new file with mode: 0644]
src/plugins/marvell/pp2/input.c [new file with mode: 0644]
src/plugins/marvell/pp2/output.c [new file with mode: 0644]
src/plugins/marvell/pp2/pp2.c [new file with mode: 0644]
src/plugins/marvell/pp2/pp2.h [new file with mode: 0644]

index d2fd581..bfd5937 100644 (file)
@@ -217,6 +217,7 @@ PLUGIN_ENABLED(ioam)
 PLUGIN_ENABLED(ixge)
 PLUGIN_ENABLED(kubeproxy)
 PLUGIN_ENABLED(lb)
+PLUGIN_ENABLED(marvell)
 PLUGIN_ENABLED(memif)
 PLUGIN_ENABLED(pppoe)
 PLUGIN_ENABLED(sixrd)
@@ -291,6 +292,17 @@ AM_COND_IF([ENABLE_G2],
   PKG_CHECK_MODULES(g2, gtk+-2.0)
 ])
 
+AM_COND_IF([ENABLE_MARVELL_PLUGIN],
+[
+  AC_CHECK_LIB( [musdk], [pp2_init],
+    [],
+    [
+      AC_MSG_WARN([Marvell MUSDK library with PP2 support not found. Plugin disabled.])
+      enable_marvell_plugin=no
+      AM_CONDITIONAL(ENABLE_MARVELL_PLUGIN, false)
+    ])
+])
+
 # If cross-compiling, we need external vppapigen and we cannot continue without it
 # For native builds, we just set dependency on vpppaigen binary in top_builddir
 AM_COND_IF([CROSSCOMPILE],
index 03aef22..5e54d0e 100644 (file)
@@ -67,6 +67,10 @@ if ENABLE_LB_PLUGIN
 include lb.am
 endif
 
+if ENABLE_MARVELL_PLUGIN
+include marvell.am
+endif
+
 if ENABLE_MEMIF_PLUGIN
 include memif.am
 endif
diff --git a/src/plugins/marvell.am b/src/plugins/marvell.am
new file mode 100644 (file)
index 0000000..2204c79
--- /dev/null
@@ -0,0 +1,47 @@
+# Copyright (c) 2018 Cisco Systems, Inc.
+# 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.
+
+vppplugins_LTLIBRARIES += marvell_plugin.la
+
+marvell_plugin_la_SOURCES = \
+       marvell/plugin.c                        \
+       marvell/pp2/cli.c                       \
+       marvell/pp2/format.c                    \
+       marvell/pp2/input.c                     \
+       marvell/pp2/output.c                    \
+       marvell/pp2/pp2.c
+
+marvell_plugin_la_LDFLAGS = $(AM_LDFLAGS) -Wl,-l:libmusdk.a
+
+noinst_HEADERS += marvell/pp2/pp2.h
+
+#AUTOMAKE_OPTIONS = foreign subdir-objects
+
+#AM_CFLAGS = -Wall
+#AM_LDFLAGS = -module -shared -avoid-version
+
+#vpppluginsdir = ${libdir}/vpp_plugins
+
+#vppplugins_LTLIBRARIES = mrvl_device_plugin.la
+
+
+#mrvl_device_plugin_la_LDFLAGS = $(AM_LDFLAGS) -Wl,-lmusdk
+#-Wl,--whole-archive,-l:libdpdk.a,--no-whole-archive
+
+# Remove *.la files
+#install-data-hook:
+#      @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES))
+
+#CLEANFILES = $(BUILT_SOURCES)
+
+# vi:syntax=automake
diff --git a/src/plugins/marvell/README.md b/src/plugins/marvell/README.md
new file mode 100644 (file)
index 0000000..786c4c8
--- /dev/null
@@ -0,0 +1,55 @@
+# Marvel device plugin for VPP   
+
+##Overview
+This plugins provides native device support for Marvell PP2 network device, by use of Marvel Usermode SDK ([MUSDK][1]).
+Code is developed and tested on [MACCHIATObin][2] board.
+
+##Prerequisites
+Plugins depends on installed MUSDK and Marvell provided linux [kernel][3] with MUSDK provided kernel patches (see `patches/linux` in musdk repo and relevant documentation.
+Kernel version used: **4.4.52 armada-17.10.1**
+MUSDK version used: **armada-17.10.3**
+MUSDK must be compiled with `--enable-bpool-dma=64` parameter.
+Following kernel modules from MUSDK must be loaded for plugin to work:
+* `musdk_uio.ko`
+* `mv_pp_uio.ko`
+
+## Usage
+### Interface Cration
+Interfaces are dynamically created with following CLI:
+```
+create interface marvell pp2 name eth0
+set interface state mv-ppio0/0 up
+```
+
+Where `eth0` is linux interface name  and `mv-ppioX/Y` is VPP interface name where X is PP2 device ID and Y is PPIO ID
+Interface needs to be assigned to MUSDK in FDT configuration and linux interface state must be up.
+
+### Interface Deletion
+Interface can be deleted with following CLI:
+```
+delete interface marvell pp2 <interface name>
+```
+
+
+### Interface Statistics
+Interface statistics can be displayed with `sh hardware-interface mv-ppio0/0`
+command.
+
+### Interaction with DPDK plugin
+This plugin doesn't have any dependency on DPDK or DPDK plugin but it can
+work with DPDK plugin enabled or disabled. It is observed that performace is
+better around 30% when DPDK plugin is disabled, as DPDK plugin registers 
+own buffer manager, which needs to deal with additional metadata in each packet.
+
+DPKD plugin can be disabled by adding following config to the startup.conf.
+
+```
+plugins {
+  dpdk_plugin.so { disable }
+}
+```
+
+
+[1]: https://github.com/MarvellEmbeddedProcessors/musdk-marvell
+[2]: http://macchiatobin.net
+[3]: https://github.com/MarvellEmbeddedProcessors/linux-marvell
diff --git a/src/plugins/marvell/plugin.c b/src/plugins/marvell/plugin.c
new file mode 100644 (file)
index 0000000..6fe6ebe
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+
+/* *INDENT-OFF* */
+VLIB_PLUGIN_REGISTER () = {
+  .version = VPP_BUILD_VER,
+  .description = "Marvell Device Plugin",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/marvell/pp2/cli.c b/src/plugins/marvell/pp2/cli.c
new file mode 100644 (file)
index 0000000..3264cc8
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+#include <stdint.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vnet/ethernet/ethernet.h>
+
+#include <marvell/pp2/pp2.h>
+
+static clib_error_t *
+mrvl_pp2_create_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  mrvl_pp2_create_if_args_t args = { 0 };
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "name %s", &args.name))
+       ;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  unformat_free (line_input);
+
+
+  mrvl_pp2_create_if (&args);
+
+  vec_free (args.name);
+
+  return args.error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (mrvl_pp2_create_command, static) = {
+  .path = "create interface marvell pp2",
+  .short_help = "create interface marvell pp2 [name <ifname>]",
+  .function = mrvl_pp2_create_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+mrvl_pp2_delete_command_fn (vlib_main_t * vm, unformat_input_t * input,
+                           vlib_cli_command_t * cmd)
+{
+  unformat_input_t _line_input, *line_input = &_line_input;
+  u32 sw_if_index = ~0;
+  vnet_hw_interface_t *hw;
+  mrvl_pp2_main_t *mm = &mrvl_pp2_main;
+  mrvl_pp2_if_t *dif;
+  vnet_main_t *vnm = vnet_get_main ();
+
+  /* Get a line of input. */
+  if (!unformat_user (input, unformat_line_input, line_input))
+    return 0;
+
+  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (line_input, "sw_if_index %d", &sw_if_index))
+       ;
+      else if (unformat (line_input, "%U", unformat_vnet_sw_interface,
+                        vnm, &sw_if_index))
+       ;
+      else
+       return clib_error_return (0, "unknown input `%U'",
+                                 format_unformat_error, input);
+    }
+  unformat_free (line_input);
+
+  if (sw_if_index == ~0)
+    return clib_error_return (0,
+                             "please specify interface name or sw_if_index");
+
+  hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+  if (hw == NULL || mrvl_pp2_device_class.index != hw->dev_class_index)
+    return clib_error_return (0, "not a Marvell PP2 interface");
+
+  dif = pool_elt_at_index (mm->interfaces, hw->dev_instance);
+
+  mrvl_pp2_delete_if (dif);
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (mrvl_pp2_delete_command, static) = {
+  .path = "delete interface marvell pp2",
+  .short_help = "delete interface marvell pp2 "
+    "{<interface> | sw_if_index <sw_idx>}",
+  .function = mrvl_pp2_delete_command_fn,
+};
+/* *INDENT-ON* */
+
+clib_error_t *
+mrvl_pp2_cli_init (vlib_main_t * vm)
+{
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (mrvl_pp2_cli_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/marvell/pp2/format.c b/src/plugins/marvell/pp2/format.c
new file mode 100644 (file)
index 0000000..96b95ed
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vppinfra/linux/syscall.h>
+#include <vnet/plugin/plugin.h>
+#include <marvell/pp2/pp2.h>
+
+static inline u32
+mrvl_get_u32_bits (void *start, int offset, int first, int last)
+{
+  u32 value = *(u32 *) (((u8 *) start) + offset);
+  if ((last == 0) && (first == 31))
+    return value;
+  value >>= last;
+  value &= (1 << (first - last + 1)) - 1;
+  return value;
+}
+
+u8 *
+format_mrvl_pp2_interface_name (u8 * s, va_list * args)
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  u32 dev_instance = va_arg (*args, u32);
+  mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, dev_instance);
+  return format (s, "mv-ppio%d/%d", ppif->ppio->pp2_id, ppif->ppio->port_id);
+}
+
+#define foreach_ppio_statistics_entry \
+  _(rx_packets) \
+  _(rx_fullq_dropped) \
+  _(rx_bm_dropped) \
+  _(rx_early_dropped) \
+  _(rx_fifo_dropped) \
+  _(rx_cls_dropped) \
+  _(tx_packets)
+
+#define foreach_ppio_inq_statistics_entry \
+  _(enq_desc) \
+  _(drop_early) \
+  _(drop_fullq) \
+  _(drop_bm)
+
+#define foreach_ppio_outq_statistics_entry \
+  _(enq_desc) \
+  _(enq_dec_to_ddr) \
+  _(enq_buf_to_ddr) \
+  _(deq_desc)
+
+u8 *
+format_mrvl_pp2_interface (u8 * s, va_list * args)
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  u32 dev_instance = va_arg (*args, u32);
+  u32 indent = format_get_indent (s);
+  mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, dev_instance);
+  struct pp2_ppio_statistics stat;
+  int i;
+  u8 *s2 = 0;
+
+  pp2_ppio_get_statistics (ppif->ppio, &stat, 0);
+
+#define _(c) if (stat.c) \
+  s2 = format (s2, "\n%U%-25U%16Ld", \
+             format_white_space, indent + 2, \
+             format_c_identifier, #c, stat.c);
+  foreach_ppio_statistics_entry;
+
+  if (vec_len (s2))
+    s = format (s, "Interface statistics:%v", s2);
+  vec_reset_length (s2);
+
+  vec_foreach_index (i, ppif->inqs)
+  {
+    struct pp2_ppio_inq_statistics stat = { 0 };
+    pp2_ppio_inq_get_statistics (ppif->ppio, 0, i, &stat, 0);
+
+    foreach_ppio_inq_statistics_entry;
+
+    if (vec_len (s2))
+      s = format (s, "\n%UInput queue %u statistics:%v",
+                 format_white_space, indent, i, s2);
+    vec_reset_length (s2);
+  }
+  vec_foreach_index (i, ppif->outqs)
+  {
+    struct pp2_ppio_outq_statistics stat = { 0 };
+
+    pp2_ppio_outq_get_statistics (ppif->ppio, i, &stat, 0);
+
+    foreach_ppio_outq_statistics_entry;
+
+    if (vec_len (s2))
+      s = format (s, "\n%UOutput queue %u statistics:%v",
+                 format_white_space, indent, i, s2);
+    vec_reset_length (s2);
+  }
+#undef _
+  vec_free (s2);
+  return s;
+}
+
+#define foreach_pp2_rx_desc_field \
+  _(0x00,  6,  0, l3_offset) \
+  _(0x00, 12,  8, ip_hdlen) \
+  _(0x00, 14, 13, ec) \
+  _(0x00, 15, 15, es) \
+  _(0x00, 19, 16, pool_id) \
+  _(0x00, 21, 21, hwf_sync) \
+  _(0x00, 22, 22, l4_chk_ok) \
+  _(0x00, 23, 23, ip_frg) \
+  _(0x00, 24, 24, ipv4_hdr_err) \
+  _(0x00, 27, 25, l4_info) \
+  _(0x00, 30, 28, l3_info) \
+  _(0x00, 31, 31, buf_header) \
+  _(0x04,  5,  0, lookup_id) \
+  _(0x04,  8,  6, cpu_code) \
+  _(0x04,  9,  9, pppoe) \
+  _(0x04, 11, 10, l3_cast_info) \
+  _(0x04, 13, 12, l2_cast_info) \
+  _(0x04, 15, 14, vlan_info) \
+  _(0x04, 31, 16, byte_count) \
+  _(0x08, 11,  0, gem_port_id) \
+  _(0x08, 13, 12, color) \
+  _(0x08, 14, 14, gop_sop_u) \
+  _(0x08, 15, 15, key_hash_enable) \
+  _(0x08, 31, 16, l4chk) \
+  _(0x0c, 31,  0, timestamp) \
+  _(0x10, 31,  0, buf_phys_ptr_lo) \
+  _(0x14,  7,  0, buf_phys_ptr_hi) \
+  _(0x14, 31,  8, key_hash) \
+  _(0x18, 31,  0, buf_virt_ptr_lo) \
+  _(0x1c,  7,  0, buf_virt_ptr_hi) \
+  _(0x1c, 14,  8, buf_qset_no) \
+  _(0x1c, 15, 15, buf_type) \
+  _(0x1c, 21, 16, mod_dscp) \
+  _(0x1c, 24, 22, mod_pri) \
+  _(0x1c, 25, 25, mdscp) \
+  _(0x1c, 26, 26, mpri) \
+  _(0x1c, 27, 27, mgpid) \
+  _(0x1c, 31, 29, port_num)
+
+u8 *
+format_mrvl_pp2_input_trace (u8 * s, va_list * args)
+{
+  vlib_main_t *vm = va_arg (*args, vlib_main_t *);
+  vlib_node_t *node = va_arg (*args, vlib_node_t *);
+  mrvl_pp2_input_trace_t *t = va_arg (*args, mrvl_pp2_input_trace_t *);
+  vnet_main_t *vnm = vnet_get_main ();
+  vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, t->hw_if_index);
+  u32 indent = format_get_indent (s);
+  struct pp2_ppio_desc *d = &t->desc;
+  u32 r32;
+
+  s = format (s, "pp2: %v (%d) next-node %U",
+             hi->name, t->hw_if_index, format_vlib_next_node_name, vm,
+             node->index, t->next_index);
+  s = format (s, "\n%U", format_white_space, indent + 2);
+
+#define _(a, b, c, n) \
+  r32 = mrvl_get_u32_bits (d, a, b, c);                                \
+  if (r32 > 9)                                                 \
+    s = format (s, "%s %u (0x%x)", #n, r32, r32);              \
+  else                                                         \
+    s = format (s, "%s %u", #n,r32);                           \
+  if (format_get_indent (s) > 72)                              \
+    s = format (s, "\n%U", format_white_space, indent + 2);    \
+  else s = format (s, " ");
+
+  foreach_pp2_rx_desc_field;
+#undef _
+  return s;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
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:
+ */
diff --git a/src/plugins/marvell/pp2/output.c b/src/plugins/marvell/pp2/output.c
new file mode 100644 (file)
index 0000000..6452994
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+
+#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>
+
+uword
+mrvl_pp2_interface_tx (vlib_main_t * vm,
+                      vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  vnet_interface_output_runtime_t *rd = (void *) node->runtime_data;
+  mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, rd->dev_instance);
+  u32 thread_index = vlib_get_thread_index ();
+  mrvl_pp2_per_thread_data_t *ptd =
+    vec_elt_at_index (ppm->per_thread_data, thread_index);
+  u8 qid = thread_index;
+  mrvl_pp2_outq_t *outq = vec_elt_at_index (ppif->outqs, qid);
+  u32 *buffers = vlib_frame_args (frame);
+  u16 n_desc = frame->n_vectors, n_left = n_desc, n_sent = n_desc, n_done;
+  struct pp2_ppio_desc *d;
+  u16 mask = outq->size - 1;
+
+  if (PREDICT_FALSE (pp2_ppio_get_num_outq_done (ppif->ppio, ptd->hif, qid,
+                                                &n_done)))
+    {
+      n_done = 0;
+      vlib_error_count (vm, node->node_index,
+                       MRVL_PP2_TX_ERROR_PPIO_GET_NUM_OUTQ_DONE, 1);
+    }
+
+  if (n_done)
+    {
+      u16 n_free = clib_min (n_done, outq->size - (outq->tail & mask));
+      vlib_buffer_free (vm, outq->buffers + (outq->tail & mask), n_free);
+      if (PREDICT_FALSE (n_free < n_done))
+       vlib_buffer_free (vm, outq->buffers, n_done - n_free);
+      outq->tail += n_done;
+    }
+
+  vec_validate_aligned (ptd->descs, n_left, CLIB_CACHE_LINE_BYTES);
+  d = ptd->descs;
+  while (n_left)
+    {
+      u32 bi0 = buffers[0];
+      vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
+      u64 paddr = vlib_get_buffer_data_physical_address (vm, bi0);
+
+      pp2_ppio_outq_desc_reset (d);
+      pp2_ppio_outq_desc_set_phys_addr (d, paddr + b0->current_data);
+      pp2_ppio_outq_desc_set_pkt_offset (d, 0);
+      pp2_ppio_outq_desc_set_pkt_len (d, b0->current_length);
+      d++;
+      buffers++;
+      n_left--;
+    }
+
+  if (pp2_ppio_send (ppif->ppio, ptd->hif, qid, ptd->descs, &n_sent))
+    {
+      n_sent = 0;
+      vlib_error_count (vm, node->node_index, MRVL_PP2_TX_ERROR_PPIO_SEND, 1);
+    }
+
+  /* free unsent buffers */
+  if (PREDICT_FALSE (n_sent != n_desc))
+    {
+      vlib_buffer_free (vm, vlib_frame_args (frame) + n_sent,
+                       frame->n_vectors - n_sent);
+      vlib_error_count (vm, node->node_index, MRVL_PP2_TX_ERROR_NO_FREE_SLOTS,
+                       frame->n_vectors - n_sent);
+    }
+
+  /* store buffer index for each enqueued packet into the ring
+     so we can know what to free after packet is sent */
+  if (n_sent)
+    {
+      u16 slot = outq->head & mask;
+      buffers = vlib_frame_args (frame);
+      u16 n_copy = clib_min (outq->size - slot, n_sent);
+
+      clib_memcpy (outq->buffers + slot, buffers, n_copy * sizeof (u32));
+      if (PREDICT_FALSE (n_copy < n_sent))
+       clib_memcpy (outq->buffers, buffers + n_copy,
+                    (n_sent - n_copy) * sizeof (u32));
+
+      outq->head += n_sent;
+    }
+
+  return n_sent;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/marvell/pp2/pp2.c b/src/plugins/marvell/pp2/pp2.c
new file mode 100644 (file)
index 0000000..2699e34
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ *------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <vlib/vlib.h>
+#include <vlib/unix/unix.h>
+#include <vppinfra/linux/syscall.h>
+#include <vnet/plugin/plugin.h>
+#include <marvell/pp2/pp2.h>
+
+/* size of DMA memory used by musdk (not used for buffers) */
+#define MV_SYS_DMA_MEM_SZ              (2 << 20)
+/* number of HIFs reserved (first X) */
+#define NUM_HIFS_RSVD                  4
+/* number of buffer pools reserved (first X) */
+#define NUM_BPOOLS_RSVD                        7
+
+mrvl_pp2_main_t mrvl_pp2_main;
+extern vnet_device_class_t ppa2_device_class;
+
+static void
+mrvl_pp2_main_deinit ()
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  int i;
+  vec_foreach_index (i, ppm->per_thread_data)
+  {
+    mrvl_pp2_per_thread_data_t *ptd = vec_elt_at_index (ppm->per_thread_data,
+                                                       i);
+    if (ptd->hif)
+      pp2_hif_deinit (ptd->hif);
+    vec_free (ptd->descs);
+  }
+  vec_free (ppm->per_thread_data);
+  pp2_deinit ();
+  mv_sys_dma_mem_destroy ();
+}
+
+static clib_error_t *
+mrvl_pp2_main_init ()
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  clib_error_t *err = 0;
+  struct pp2_init_params init_params = { 0 };
+  int i, rv;
+  u8 *s = 0;
+
+  rv = mv_sys_dma_mem_init (MV_SYS_DMA_MEM_SZ);
+  if (rv)
+    return clib_error_return (0, "mv_sys_dma_mem_init failed, rv = %u", rv);
+
+  init_params.hif_reserved_map = ((1 << NUM_HIFS_RSVD) - 1);
+  init_params.bm_pool_reserved_map = ((1 << NUM_BPOOLS_RSVD) - 1);
+  rv = pp2_init (&init_params);
+  if (rv)
+    {
+      err = clib_error_return (0, "mrvl_pp2_init failed, rv = %u", rv);
+      goto done;
+    }
+
+  vec_validate_aligned (ppm->per_thread_data, tm->n_vlib_mains - 1,
+                       CLIB_CACHE_LINE_BYTES);
+
+  vec_foreach_index (i, ppm->per_thread_data)
+  {
+    mrvl_pp2_per_thread_data_t *ptd = vec_elt_at_index (ppm->per_thread_data,
+                                                       i);
+    struct pp2_hif_params hif_params = { 0 };
+    vec_reset_length (s);
+    s = format (s, "hif-%d%c", NUM_HIFS_RSVD + i, 0);
+    hif_params.match = (char *) s;
+    hif_params.out_size = 2048;        /* FIXME */
+    if (pp2_hif_init (&hif_params, &ptd->hif))
+      {
+       err = clib_error_return (0, "hif '%s' init failed", s);
+       goto done;
+      }
+  }
+
+done:
+  if (err)
+    mrvl_pp2_main_deinit ();
+  vec_free (s);
+  return err;
+}
+
+static u32
+mrvl_pp2_eth_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi,
+                         u32 flags)
+{
+  /* nothing for now */
+  return 0;
+}
+
+void
+mrvl_pp2_delete_if (mrvl_pp2_if_t * ppif)
+{
+  vlib_main_t *vm = vlib_get_main ();
+  vnet_main_t *vnm = vnet_get_main ();
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  mrvl_pp2_outq_t *outq;
+  mrvl_pp2_inq_t *inq;
+  int i;
+
+  if (ppif->hw_if_index != ~0)
+    {
+      vec_foreach_index (i, ppif->inqs)
+       vnet_hw_interface_unassign_rx_thread (vnm, ppif->hw_if_index, i);
+      ethernet_delete_interface (vnm, ppif->hw_if_index);
+    }
+
+  if (ppif->ppio)
+    {
+      pp2_ppio_disable (ppif->ppio);
+      pp2_ppio_deinit (ppif->ppio);
+    }
+
+  /* *INDENT-OFF* */
+  /* free buffers hanging in the tx ring */
+  vec_foreach (outq, ppif->outqs)
+    {
+      while (outq->tail < outq->head)
+       {
+         u16 slot = outq->tail & (outq->size - 1);
+         vlib_buffer_free (vm, outq->buffers + slot, 1);
+         outq->tail++;
+       }
+      vec_free (outq->buffers);
+    }
+  vec_free (ppif->outqs);
+
+  /* free buffers hangin in the rx buffer pool */
+  vec_foreach (inq, ppif->inqs)
+    if (inq->bpool)
+      {
+       u32 n_bufs = 0;
+       pp2_bpool_get_num_buffs (inq->bpool, &n_bufs);
+       while (n_bufs--)
+         {
+           struct pp2_buff_inf binf;
+           if (pp2_bpool_get_buff
+               (ppm->per_thread_data[0].hif, inq->bpool, &binf) == 0)
+             vlib_buffer_free (vm, &binf.cookie, 1);
+         }
+       pp2_bpool_deinit (inq->bpool);
+      }
+  vec_free (ppif->inqs);
+  /* *INDENT-ON* */
+
+
+  pool_put (ppm->interfaces, ppif);
+
+  if (pool_elts (ppm->interfaces) == 0)
+    mrvl_pp2_main_deinit ();
+}
+
+void
+mrvl_pp2_create_if (mrvl_pp2_create_if_args_t * args)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  struct pp2_bpool_params bpool_params = { 0 };
+  struct pp2_ppio_params ppio_params = { 0 };
+  struct pp2_ppio_inq_params inq_params = { 0 };
+  vnet_sw_interface_t *sw;
+  mrvl_pp2_if_t *ppif = 0;
+  u8 pp2_id, port_id, *s = 0;
+  eth_addr_t mac_addr;
+  u8 n_outqs, n_inqs = 1;
+  int i;
+
+  if (tm->n_vlib_mains > PP2_PPIO_MAX_NUM_OUTQS)
+    {
+      args->rv = VNET_API_ERROR_INIT_FAILED;
+      args->error = clib_error_return (0, "number of threads (main + workers)"
+                                      " is bigger than number of output "
+                                      "queues (%u)", PP2_PPIO_MAX_NUM_OUTQS);
+      return;
+    }
+  n_outqs = tm->n_vlib_mains;
+
+  /* defaults */
+  args->tx_q_sz = args->tx_q_sz ? args->tx_q_sz : 2048;
+  args->rx_q_sz = args->rx_q_sz ? args->rx_q_sz : 2048;
+
+  if (vec_len (ppm->per_thread_data) == 0)
+    {
+      if ((args->error = mrvl_pp2_main_init ()) != 0)
+       {
+         args->rv = VNET_API_ERROR_INIT_FAILED;
+         return;
+       }
+    }
+
+  pool_get (ppm->interfaces, ppif);
+  memset (ppif, 0, sizeof (*ppif));
+  ppif->dev_instance = ppif - ppm->interfaces;
+  ppif->hw_if_index = ~0;
+  vec_validate_aligned (ppif->inqs, n_inqs - 1, CLIB_CACHE_LINE_BYTES);
+  vec_validate_aligned (ppif->outqs, n_outqs - 1, CLIB_CACHE_LINE_BYTES);
+
+  for (i = 0; i < n_inqs; i++)
+    {
+      mrvl_pp2_inq_t *inq = vec_elt_at_index (ppif->inqs, i);
+      inq->size = args->rx_q_sz;
+    }
+  for (i = 0; i < n_outqs; i++)
+    {
+      mrvl_pp2_outq_t *outq = vec_elt_at_index (ppif->outqs, i);
+      outq->size = args->tx_q_sz;
+      vec_validate_aligned (outq->buffers, outq->size, CLIB_CACHE_LINE_BYTES);
+    }
+
+  if (pp2_netdev_get_ppio_info ((char *) args->name, &pp2_id, &port_id))
+    {
+      args->rv = VNET_API_ERROR_INVALID_INTERFACE;
+      args->error = clib_error_return (0, "Invalid interface '%s'",
+                                      args->name);
+      goto error;
+    }
+
+  /* FIXME bpool bit select per pp */
+  s = format (s, "pool-%d:%d%c", pp2_id, pp2_id + 8, 0);
+  bpool_params.match = (char *) s;
+  bpool_params.buff_len = VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES;
+  /* FIXME +64 ? */
+  if (pp2_bpool_init (&bpool_params, &ppif->inqs[0].bpool))
+    {
+      args->rv = VNET_API_ERROR_INIT_FAILED;
+      args->error = clib_error_return (0, "bpool '%s' init failed", s);
+      goto error;
+    }
+  vec_reset_length (s);
+
+  s = format (s, "ppio-%d:%d%c", pp2_id, port_id, 0);
+  ppio_params.match = (char *) s;
+  ppio_params.type = PP2_PPIO_T_NIC;
+  inq_params.size = 2048;
+  ppio_params.inqs_params.num_tcs = 1;
+  ppio_params.inqs_params.tcs_params[0].pkt_offset = 0;
+  ppio_params.inqs_params.tcs_params[0].num_in_qs = n_inqs;
+  ppio_params.inqs_params.tcs_params[0].inqs_params = &inq_params;
+  ppio_params.inqs_params.tcs_params[0].pools[0] = ppif->inqs[0].bpool;
+  ppio_params.outqs_params.num_outqs = n_outqs;
+  for (i = 0; i < n_outqs; i++)
+    {
+      ppio_params.outqs_params.outqs_params[i].weight = 1;
+      ppio_params.outqs_params.outqs_params[i].size = args->tx_q_sz;
+    }
+  if (pp2_ppio_init (&ppio_params, &ppif->ppio))
+    {
+      args->rv = VNET_API_ERROR_INIT_FAILED;
+      args->error = clib_error_return (0, "ppio '%s' init failed", s);
+      goto error;
+    }
+  vec_reset_length (s);
+
+  if (pp2_ppio_get_mac_addr (ppif->ppio, mac_addr))
+    {
+      args->rv = VNET_API_ERROR_INIT_FAILED;
+      args->error =
+       clib_error_return (0, "%s: pp2_ppio_get_mac_addr failed", s);
+      goto error;
+    }
+
+  args->error = ethernet_register_interface (vnm, mrvl_pp2_device_class.index,
+                                            ppif->dev_instance,
+                                            mac_addr,
+                                            &ppif->hw_if_index,
+                                            mrvl_pp2_eth_flag_change);
+  if (args->error)
+    {
+      args->rv = VNET_API_ERROR_INVALID_REGISTRATION;
+      goto error;
+    }
+
+  sw = vnet_get_hw_sw_interface (vnm, ppif->hw_if_index);
+  ppif->sw_if_index = sw->sw_if_index;
+  ppif->per_interface_next_index = ~0;
+  vnet_hw_interface_set_input_node (vnm, ppif->hw_if_index,
+                                   mrvl_pp2_input_node.index);
+  vnet_hw_interface_assign_rx_thread (vnm, ppif->hw_if_index, 0, ~0);
+  vnet_hw_interface_set_rx_mode (vnm, ppif->hw_if_index, 0,
+                                VNET_HW_INTERFACE_RX_MODE_POLLING);
+  vnet_hw_interface_set_flags (vnm, ppif->hw_if_index,
+                              VNET_HW_INTERFACE_FLAG_LINK_UP);
+  goto done;
+
+error:
+  mrvl_pp2_delete_if (ppif);
+done:
+  vec_free (s);
+}
+
+static clib_error_t *
+mrvl_pp2_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index,
+                                 u32 flags)
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
+  mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, hw->dev_instance);
+  static clib_error_t *error = 0;
+  int is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0;
+  int rv;
+
+  if (is_up)
+    rv = pp2_ppio_enable (ppif->ppio);
+  else
+    rv = pp2_ppio_disable (ppif->ppio);
+
+  if (rv)
+    return clib_error_return (0, "failed to %s interface",
+                             is_up ? "enable" : "disable");
+
+  if (is_up)
+    ppif->flags |= MRVL_PP2_IF_F_ADMIN_UP;
+  else
+    ppif->flags &= ~MRVL_PP2_IF_F_ADMIN_UP;
+
+  return error;
+}
+
+static void
+mrvl_pp2_clear_interface_counters (u32 instance)
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, instance);
+  struct pp2_ppio_statistics stats;
+
+  pp2_ppio_get_statistics (ppif->ppio, &stats, 1);
+}
+
+static void
+mrvl_pp2_set_interface_next_node (vnet_main_t * vnm, u32 hw_if_index,
+                                 u32 node_index)
+{
+  mrvl_pp2_main_t *ppm = &mrvl_pp2_main;
+  vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index);
+  mrvl_pp2_if_t *ppif = pool_elt_at_index (ppm->interfaces, hw->dev_instance);
+
+  /* Shut off redirection */
+  if (node_index == ~0)
+    {
+      ppif->per_interface_next_index = node_index;
+      return;
+    }
+
+  ppif->per_interface_next_index =
+    vlib_node_add_next (vlib_get_main (), mrvl_pp2_input_node.index,
+                       node_index);
+}
+
+static char *mrvl_pp2_tx_func_error_strings[] = {
+#define _(n,s) s,
+  foreach_mrvl_pp2_tx_func_error
+#undef _
+};
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (mrvl_pp2_device_class,) =
+{
+  .name = "Marvell PPv2 interface",
+  .format_device_name = format_mrvl_pp2_interface_name,
+  .format_device = format_mrvl_pp2_interface,
+  .tx_function = mrvl_pp2_interface_tx,
+  .tx_function_n_errors = MRVL_PP2_TX_N_ERROR,
+  .tx_function_error_strings = mrvl_pp2_tx_func_error_strings,
+  .admin_up_down_function = mrvl_pp2_interface_admin_up_down,
+  .clear_counters = mrvl_pp2_clear_interface_counters,
+  .rx_redirect_to_node = mrvl_pp2_set_interface_next_node,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+mrvl_pp2_init (vlib_main_t * vm)
+{
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (mrvl_pp2_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/marvell/pp2/pp2.h b/src/plugins/marvell/pp2/pp2.h
new file mode 100644 (file)
index 0000000..76db0a0
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ *------------------------------------------------------------------
+ * 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 MVCONF_DBG_LEVEL 0
+#define MVCONF_PP2_BPOOL_COOKIE_SIZE 32
+#define MVCONF_PP2_BPOOL_DMA_ADDR_SIZE 64
+#define MVCONF_DMA_PHYS_ADDR_T_SIZE 64
+#define MVCONF_SYS_DMA_UIO
+#define MVCONF_TYPES_PUBLIC
+#define MVCONF_DMA_PHYS_ADDR_T_PUBLIC
+
+#include "mv_std.h"
+#include "env/mv_sys_dma.h"
+#include "drivers/mv_pp2.h"
+#include <drivers/mv_pp2_bpool.h>
+#include <drivers/mv_pp2_ppio.h>
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  u16 size;
+  struct pp2_bpool *bpool;
+} mrvl_pp2_inq_t;
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  u16 size;
+  u32 *buffers;
+  u16 head;
+  u16 tail;
+} mrvl_pp2_outq_t;
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  u32 flags;
+#define MRVL_PP2_IF_F_ADMIN_UP (1 << 0)
+  struct pp2_ppio *ppio;
+  u32 per_interface_next_index;
+
+  mrvl_pp2_inq_t *inqs;
+  mrvl_pp2_outq_t *outqs;
+
+  u32 dev_instance;
+  u32 sw_if_index;
+  u32 hw_if_index;
+} mrvl_pp2_if_t;
+
+#define MRVL_PP2_BUFF_BATCH_SZ 64
+
+typedef struct
+{
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+  struct pp2_hif *hif;
+  struct pp2_ppio_desc *descs;
+  struct buff_release_entry bre[MRVL_PP2_BUFF_BATCH_SZ];
+  u32 buffers[VLIB_FRAME_SIZE];
+} mrvl_pp2_per_thread_data_t;
+
+typedef struct
+{
+  mrvl_pp2_if_t *interfaces;
+  mrvl_pp2_per_thread_data_t *per_thread_data;
+} mrvl_pp2_main_t;
+
+extern vnet_device_class_t mrvl_pp2_device_class;
+extern mrvl_pp2_main_t mrvl_pp2_main;
+
+typedef struct
+{
+  u8 *name;
+  u16 rx_q_sz;
+  u16 tx_q_sz;
+
+  /* return */
+  int rv;
+  clib_error_t *error;
+} mrvl_pp2_create_if_args_t;
+
+void mrvl_pp2_create_if (mrvl_pp2_create_if_args_t * args);
+void mrvl_pp2_delete_if (mrvl_pp2_if_t * dfif);
+
+/* output.c */
+
+#define foreach_mrvl_pp2_tx_func_error \
+  _(NO_FREE_SLOTS, "no free tx slots")                 \
+  _(PPIO_SEND, "pp2_ppio_send errors")                 \
+  _(PPIO_GET_NUM_OUTQ_DONE, "pp2_ppio_get_num_outq_done errors")
+
+typedef enum
+{
+#define _(f,s) MRVL_PP2_TX_ERROR_##f,
+  foreach_mrvl_pp2_tx_func_error
+#undef _
+    MRVL_PP2_TX_N_ERROR,
+} mrvl_pp2_tx_func_error_t;
+
+uword mrvl_pp2_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
+                            vlib_frame_t * frame);
+
+/* input.c */
+
+typedef struct
+{
+  u32 next_index;
+  u32 hw_if_index;
+  struct pp2_ppio_desc desc;
+} mrvl_pp2_input_trace_t;
+
+extern vlib_node_registration_t mrvl_pp2_input_node;
+
+/* format.c */
+format_function_t format_mrvl_pp2_input_trace;
+format_function_t format_mrvl_pp2_interface;
+format_function_t format_mrvl_pp2_interface_name;
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */