diff options
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/Makefile.am | 4 | ||||
-rw-r--r-- | src/plugins/marvell.am | 47 | ||||
-rw-r--r-- | src/plugins/marvell/README.md | 55 | ||||
-rw-r--r-- | src/plugins/marvell/plugin.c | 35 | ||||
-rw-r--r-- | src/plugins/marvell/pp2/cli.c | 131 | ||||
-rw-r--r-- | src/plugins/marvell/pp2/format.c | 201 | ||||
-rw-r--r-- | src/plugins/marvell/pp2/input.c | 394 | ||||
-rw-r--r-- | src/plugins/marvell/pp2/output.c | 121 | ||||
-rw-r--r-- | src/plugins/marvell/pp2/pp2.c | 408 | ||||
-rw-r--r-- | src/plugins/marvell/pp2/pp2.h | 139 |
10 files changed, 1535 insertions, 0 deletions
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 03aef227cfb..5e54d0e504a 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -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 index 00000000000..2204c7935c8 --- /dev/null +++ b/src/plugins/marvell.am @@ -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 index 00000000000..786c4c8241e --- /dev/null +++ b/src/plugins/marvell/README.md @@ -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 index 00000000000..6fe6ebe22fd --- /dev/null +++ b/src/plugins/marvell/plugin.c @@ -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 index 00000000000..3264cc8638b --- /dev/null +++ b/src/plugins/marvell/pp2/cli.c @@ -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 index 00000000000..96b95edccb8 --- /dev/null +++ b/src/plugins/marvell/pp2/format.c @@ -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 index 00000000000..6329e3b2220 --- /dev/null +++ b/src/plugins/marvell/pp2/input.c @@ -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 index 00000000000..64529945f18 --- /dev/null +++ b/src/plugins/marvell/pp2/output.c @@ -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 index 00000000000..2699e346fa2 --- /dev/null +++ b/src/plugins/marvell/pp2/pp2.c @@ -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 index 00000000000..76db0a09749 --- /dev/null +++ b/src/plugins/marvell/pp2/pp2.h @@ -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: + */ |