From df7f8e8cffcc43531f7daeda44d436b60e538141 Mon Sep 17 00:00:00 2001 From: Steven Luong Date: Sun, 18 Mar 2018 08:01:27 -0700 Subject: [PATCH] vmxnet3 device driver Implemented vmxnet3 deivice driver for VMWare ESXi. Tested with Ubuntu 18.04 connected to ESXi 6.0 Ubuntu-18.04 (VPP) --- ESXi-6.0 Change-Id: I85fbc86f2d8532b017bc4271612d17e24e498e4d Signed-off-by: Steven Luong --- src/plugins/vmxnet3/CMakeLists.txt | 37 ++ src/plugins/vmxnet3/README.md | 59 +++ src/plugins/vmxnet3/cli.c | 371 ++++++++++++++++++ src/plugins/vmxnet3/format.c | 143 +++++++ src/plugins/vmxnet3/input.c | 295 +++++++++++++++ src/plugins/vmxnet3/output.c | 209 +++++++++++ src/plugins/vmxnet3/plugin.c | 35 ++ src/plugins/vmxnet3/vmxnet3.api | 125 +++++++ src/plugins/vmxnet3/vmxnet3.c | 612 ++++++++++++++++++++++++++++++ src/plugins/vmxnet3/vmxnet3.h | 639 ++++++++++++++++++++++++++++++++ src/plugins/vmxnet3/vmxnet3_all_api_h.h | 26 ++ src/plugins/vmxnet3/vmxnet3_api.c | 252 +++++++++++++ src/plugins/vmxnet3/vmxnet3_msg_enum.h | 39 ++ src/plugins/vmxnet3/vmxnet3_test.c | 331 +++++++++++++++++ 14 files changed, 3173 insertions(+) create mode 100644 src/plugins/vmxnet3/CMakeLists.txt create mode 100644 src/plugins/vmxnet3/README.md create mode 100644 src/plugins/vmxnet3/cli.c create mode 100644 src/plugins/vmxnet3/format.c create mode 100644 src/plugins/vmxnet3/input.c create mode 100644 src/plugins/vmxnet3/output.c create mode 100644 src/plugins/vmxnet3/plugin.c create mode 100644 src/plugins/vmxnet3/vmxnet3.api create mode 100644 src/plugins/vmxnet3/vmxnet3.c create mode 100644 src/plugins/vmxnet3/vmxnet3.h create mode 100644 src/plugins/vmxnet3/vmxnet3_all_api_h.h create mode 100644 src/plugins/vmxnet3/vmxnet3_api.c create mode 100644 src/plugins/vmxnet3/vmxnet3_msg_enum.h create mode 100644 src/plugins/vmxnet3/vmxnet3_test.c diff --git a/src/plugins/vmxnet3/CMakeLists.txt b/src/plugins/vmxnet3/CMakeLists.txt new file mode 100644 index 00000000000..a81d8d59387 --- /dev/null +++ b/src/plugins/vmxnet3/CMakeLists.txt @@ -0,0 +1,37 @@ +# 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. + +add_vpp_plugin(vmxnet3 + SOURCES + cli.c + format.c + input.c + output.c + plugin.c + vmxnet3.c + vmxnet3_api.c + + MULTIARCH_SOURCES + input.c + output.c + + API_FILES + vmxnet3.api + + API_TEST_SOURCES + vmxnet3_test.c + + INSTALL_HEADERS + vmxnet3_all_api_h.h + vmxnet3_msg_enum.h +) diff --git a/src/plugins/vmxnet3/README.md b/src/plugins/vmxnet3/README.md new file mode 100644 index 00000000000..a49671325c9 --- /dev/null +++ b/src/plugins/vmxnet3/README.md @@ -0,0 +1,59 @@ +# VMWARE vmxnet3 device driver plugin + +##Overview +This plugin provides native PCI driver support for VMWare vmxnet3. + +##Prerequisites + * This code is tested with vfio-pci driver installed with Ubuntu 18.04 which +has kernel version 4.15.0-33-generic. + + * This code is tested with ESXi vSwitch version 6.0, release build 3620759. + + * Driver requires MSI-X interrupt support, which is not supported by +uio_pci_generic driver, so vfio-pci needs to be used. On systems without IOMMU +vfio driver can still be used with recent kernels which support no-iommu mode. + +##Known issues + +* NUMA support +* TSO +* VLAN filter + +## Usage +### System setup + +1. load VFIO driver +``` +sudo modprobe vfio-pci +``` + +2. (systems without IOMMU only) enable unsafe NOIOMMU mode +``` +echo Y | sudo tee /sys/module/vfio/parameters/enable_unsafe_noiommu_mode +``` + +3. Bind interface to vfio-pci +``` +dpdk-devbind.py --bind vfio-pci 0b:00.0 +``` + +### Interface Creation +Interface can be dynamically created with following CLI: +``` +create interface vmxnet3 0000:0b:00.0 +set int state vmxnet3-0/b/0/0 up +``` + +### Interface Deletion +Interface can be deleted with following CLI: +``` +delete interface vmxnet3 +``` + +### Interface Statistics +Interface statistics can be displayed with `show hardware-interface ` +command. + +### Show Interface CLI +Interface and ring information can be obtained with +`show vmxnet3 [if-name] [desc]` diff --git a/src/plugins/vmxnet3/cli.c b/src/plugins/vmxnet3/cli.c new file mode 100644 index 00000000000..3e3ab9d74f7 --- /dev/null +++ b/src/plugins/vmxnet3/cli.c @@ -0,0 +1,371 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +static clib_error_t * +vmxnet3_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; + vmxnet3_create_if_args_t args = { 0 }; + u32 tmp; + + /* 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, "%U", unformat_vlib_pci_addr, &args.addr)) + ; + else if (unformat (line_input, "elog")) + args.enable_elog = 1; + else if (unformat (line_input, "rx-queue-size %u", &tmp)) + args.rxq_size = tmp; + else if (unformat (line_input, "tx-queue-size %u", &tmp)) + args.txq_size = tmp; + else + return clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + } + unformat_free (line_input); + + + vmxnet3_create_if (vm, &args); + + return args.error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vmxnet3_create_command, static) = { + .path = "create interface vmxnet3", + .short_help = "create interface vmxnet3 " + "[rx-queue-size ] [tx-queue-size ]", + .function = vmxnet3_create_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +vmxnet3_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; + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_device_t *vd; + 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 || vmxnet3_device_class.index != hw->dev_class_index) + return clib_error_return (0, "not a vmxnet3 interface"); + + vd = pool_elt_at_index (vmxm->devices, hw->dev_instance); + + vmxnet3_delete_if (vm, vd); + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vmxnet3_delete_command, static) = { + .path = "delete interface vmxnet3", + .short_help = "delete interface vmxnet3 " + "{ | sw_if_index }", + .function = vmxnet3_delete_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +vmxnet3_test_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; + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_device_t *vd; + vnet_main_t *vnm = vnet_get_main (); + int enable_elog = 0, disable_elog = 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, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (line_input, "elog-on")) + enable_elog = 1; + else if (unformat (line_input, "elog-off")) + disable_elog = 1; + 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 || vmxnet3_device_class.index != hw->dev_class_index) + return clib_error_return (0, "not a vmxnet3 interface"); + + vd = pool_elt_at_index (vmxm->devices, hw->dev_instance); + + if (enable_elog) + vd->flags |= VMXNET3_DEVICE_F_ELOG; + + if (disable_elog) + vd->flags &= ~VMXNET3_DEVICE_F_ELOG; + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (vmxnet3_test_command, static) = { + .path = "test vmxnet3", + .short_help = "test vmxnet3 | sw_if_index [irq] " + "[elog-on] [elog-off]", + .function = vmxnet3_test_command_fn, +}; +/* *INDENT-ON* */ + +static void +show_vmxnet3 (vlib_main_t * vm, u32 * hw_if_indices, u8 show_descr) +{ + u32 i, desc_idx; + vmxnet3_device_t *vd; + vnet_main_t *vnm = &vnet_main; + vmxnet3_main_t *vmxm = &vmxnet3_main; + vnet_hw_interface_t *hi; + vmxnet3_rxq_t *rxq; + vmxnet3_rx_desc *rxd; + vmxnet3_rx_comp *rx_comp; + vmxnet3_txq_t *txq; + vmxnet3_tx_desc *txd; + vmxnet3_tx_comp *tx_comp; + u16 qid; + + if (!hw_if_indices) + return; + + for (i = 0; i < vec_len (hw_if_indices); i++) + { + hi = vnet_get_hw_interface (vnm, hw_if_indices[i]); + vd = vec_elt_at_index (vmxm->devices, hi->dev_instance); + vlib_cli_output (vm, "Interface: %s (ifindex %d)", + hi->name, hw_if_indices[i]); + vlib_cli_output (vm, " Version: %u", vd->version); + vlib_cli_output (vm, " PCI Address: %U", format_vlib_pci_addr, + &vd->pci_addr); + vlib_cli_output (vm, " Mac Address: %U", format_ethernet_address, + vd->mac_addr); + vlib_cli_output (vm, " hw if index: %u", vd->hw_if_index); + vlib_cli_output (vm, " Device instance: %u", vd->dev_instance); + vlib_cli_output (vm, " Number of interrupts: %u", vd->num_intrs); + + vec_foreach_index (qid, vd->rxqs) + { + rxq = vec_elt_at_index (vd->rxqs, qid); + u16 rid; + + vlib_cli_output (vm, " Queue %u (RX)", qid); + vlib_cli_output (vm, " RX completion next index %u", + rxq->rx_comp_ring.next); + for (rid = 0; rid < VMXNET3_RX_RING_SIZE; rid++) + { + vmxnet3_rx_ring *ring = &rxq->rx_ring[rid]; + + vlib_cli_output (vm, + " ring %u size %u fill %u " + "consume %u produce %u", rid, + rxq->size, ring->fill, ring->consume, + ring->produce); + if (show_descr) + { + vlib_cli_output (vm, "RX descriptors table"); + vlib_cli_output (vm, " %5s %18s %10s", + "slot", "address", "flags"); + for (desc_idx = 0; desc_idx < rxq->size; desc_idx++) + { + rxd = &rxq->rx_desc[rid][desc_idx]; + vlib_cli_output (vm, " %5u 0x%016llx 0x%08x", + desc_idx, rxd->address, rxd->flags); + } + vlib_cli_output (vm, "RX completion descriptors table"); + vlib_cli_output (vm, " %5s %10s %10s %10s %10s", + "slot", "index", "rss", "len", "flags"); + for (desc_idx = 0; desc_idx < rxq->size; desc_idx++) + { + rx_comp = &rxq->rx_comp[desc_idx]; + vlib_cli_output (vm, " %5u 0x%08x %10u %10u 0x%08x", + desc_idx, rx_comp->index, rx_comp->rss, + rx_comp->len, rx_comp->flags); + } + } + } + } + + vec_foreach_index (qid, vd->rxqs) + { + txq = vec_elt_at_index (vd->txqs, 0); + vlib_cli_output (vm, " Queue %u (TX)", qid); + vlib_cli_output (vm, " TX completion next index %u", + txq->tx_comp_ring.next); + vlib_cli_output (vm, " size %u consume %u produce %u", + txq->size, txq->tx_ring.consume, + txq->tx_ring.produce); + if (show_descr) + { + vlib_cli_output (vm, "TX descriptors table"); + vlib_cli_output (vm, " %5s %18s %10s %10s", + "slot", "address", "flags0", "flags1"); + for (desc_idx = 0; desc_idx < txq->size; desc_idx++) + { + txd = &txq->tx_desc[desc_idx]; + vlib_cli_output (vm, " %5u 0x%016llx 0x%08x 0x%08x", + desc_idx, txd->address, txd->flags[0], + txd->flags[1]); + } + vlib_cli_output (vm, "TX completion descriptors table"); + vlib_cli_output (vm, " %5s %10s %10s", + "slot", "index", "flags"); + for (desc_idx = 0; desc_idx < txq->size; desc_idx++) + { + tx_comp = &txq->tx_comp[desc_idx]; + vlib_cli_output (vm, " %5u 0x%08x 0x%08x", + desc_idx, tx_comp->index, tx_comp->flags); + } + } + } + } +} + +static clib_error_t * +show_vmxnet3_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vnet_main_t *vnm = &vnet_main; + vmxnet3_device_t *vd; + clib_error_t *error = 0; + u32 hw_if_index, *hw_if_indices = 0; + vnet_hw_interface_t *hi; + u8 show_descr = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat + (input, "%U", unformat_vnet_hw_interface, vnm, &hw_if_index)) + { + hi = vnet_get_hw_interface (vnm, hw_if_index); + if (vmxnet3_device_class.index != hi->dev_class_index) + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + goto done; + } + vec_add1 (hw_if_indices, hw_if_index); + } + else if (unformat (input, "descriptors") || unformat (input, "desc")) + show_descr = 1; + else + { + error = clib_error_return (0, "unknown input `%U'", + format_unformat_error, input); + goto done; + } + } + + if (vec_len (hw_if_indices) == 0) + { + pool_foreach (vd, vmxm->devices, + vec_add1 (hw_if_indices, vd->hw_if_index); + ); + } + + show_vmxnet3 (vm, hw_if_indices, show_descr); + +done: + vec_free (hw_if_indices); + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_vmxnet3_command, static) = { + .path = "show vmxnet3", + .short_help = "show vmxnet3 []", + .function = show_vmxnet3_fn, +}; +/* *INDENT-ON* */ + +clib_error_t * +vmxnet3_cli_init (vlib_main_t * vm) +{ + /* initialize binary API */ + vmxnet3_plugin_api_hookup (vm); + + return 0; +} + +VLIB_INIT_FUNCTION (vmxnet3_cli_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/format.c b/src/plugins/vmxnet3/format.c new file mode 100644 index 00000000000..8ee812ecc06 --- /dev/null +++ b/src/plugins/vmxnet3/format.c @@ -0,0 +1,143 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include + +#include + +u8 * +format_vmxnet3_device_name (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_device_t *vd = vec_elt_at_index (vmxm->devices, i); + vlib_pci_addr_t *addr = vlib_pci_get_addr (vd->pci_dev_handle); + + s = format (s, "vmxnet3-%x/%x/%x/%x", + addr->domain, addr->bus, addr->slot, addr->function); + return s; +} + +u8 * +format_vmxnet3_device_flags (u8 * s, va_list * args) +{ + vmxnet3_device_t *vd = va_arg (*args, vmxnet3_device_t *); + u8 *t = 0; + +#define _(a, b, c) if (vd->flags & (1 << a)) \ + t = format (t, "%s%s", t ? " ":"", c); + foreach_vmxnet3_device_flags +#undef _ + s = format (s, "%v", t); + vec_free (t); + return s; +} + +u8 * +format_vmxnet3_device (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_device_t *vd = vec_elt_at_index (vmxm->devices, i); + u32 indent = format_get_indent (s); + vmxnet3_queues *q = &vd->dma->queues; + vmxnet3_rxq_t *rxq = vec_elt_at_index (vd->rxqs, 0); + vmxnet3_txq_t *txq = vec_elt_at_index (vd->txqs, 0); + + s = format (s, "flags: %U", format_vmxnet3_device_flags, vd); + s = format (s, "\n%Urx queues %u, rx desc %u, tx queues %u, tx desc %u", + format_white_space, indent, + vd->num_rx_queues, rxq->size, vd->num_tx_queues, txq->size); + if (vd->error) + s = format (s, "\n%Uerror %U", format_white_space, indent, + format_clib_error, vd->error); + + vmxnet3_reg_write (vd, 1, VMXNET3_REG_CMD, VMXNET3_CMD_GET_STATS); + + s = format (s, "\n%UTX:", format_white_space, indent); + s = format (s, "\n%U TSO packets %llu", + format_white_space, indent, q->tx.stats.tso_pkts); + s = format (s, "\n%U TSO bytes %llu", + format_white_space, indent, q->tx.stats.tso_bytes); + s = format (s, "\n%U ucast packets %llu", + format_white_space, indent, q->tx.stats.ucast_pkts); + s = format (s, "\n%U ucast bytes %llu", + format_white_space, indent, q->tx.stats.ucast_bytes); + s = format (s, "\n%U mcast packets %llu", + format_white_space, indent, q->tx.stats.mcast_pkts); + s = format (s, "\n%U mcast bytes %llu", + format_white_space, indent, q->tx.stats.mcast_bytes); + s = format (s, "\n%U bcast packets %llu", + format_white_space, indent, q->tx.stats.bcast_pkts); + s = format (s, "\n%U bcast bytes %llu", + format_white_space, indent, q->tx.stats.bcast_bytes); + s = format (s, "\n%U Errors packets %llu", + format_white_space, indent, q->tx.stats.error_pkts); + s = format (s, "\n%U Discard packets %llu", + format_white_space, indent, q->tx.stats.discard_pkts); + + s = format (s, "\n%URX:", format_white_space, indent); + s = format (s, "\n%U LRO packets %llu", + format_white_space, indent, q->rx.stats.lro_pkts); + s = format (s, "\n%U LRO bytes %llu", + format_white_space, indent, q->rx.stats.lro_bytes); + s = format (s, "\n%U ucast packets %llu", + format_white_space, indent, q->rx.stats.ucast_pkts); + s = format (s, "\n%U ucast bytes %llu", + format_white_space, indent, q->rx.stats.ucast_bytes); + s = format (s, "\n%U mcast packets %llu", + format_white_space, indent, q->rx.stats.mcast_pkts); + s = format (s, "\n%U mcast bytes %llu", + format_white_space, indent, q->rx.stats.mcast_bytes); + s = format (s, "\n%U bcast packets %llu", + format_white_space, indent, q->rx.stats.bcast_pkts); + s = format (s, "\n%U bcast bytes %llu", + format_white_space, indent, q->rx.stats.bcast_bytes); + s = format (s, "\n%U No Bufs %llu", + format_white_space, indent, q->rx.stats.nobuf_pkts); + s = format (s, "\n%U Error packets %llu", + format_white_space, indent, q->rx.stats.error_pkts); + return s; +} + +u8 * +format_vmxnet3_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 *); + vmxnet3_input_trace_t *t = va_arg (*args, vmxnet3_input_trace_t *); + vnet_main_t *vnm = vnet_get_main (); + vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, t->hw_if_index); + + s = format (s, "vmxnet3: %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 buffer %U", format_vlib_buffer, &t->buffer); + + return s; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/input.c b/src/plugins/vmxnet3/input.c new file mode 100644 index 00000000000..a5a5d2f43b8 --- /dev/null +++ b/src/plugins/vmxnet3/input.c @@ -0,0 +1,295 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include +#include + +#include + +#define foreach_vmxnet3_input_error \ + _(BUFFER_ALLOC, "buffer alloc error") \ + _(RX_PACKET, "Rx packet error") \ + _(NO_BUFFER, "Rx no buffer error") + +typedef enum +{ +#define _(f,s) VMXNET3_INPUT_ERROR_##f, + foreach_vmxnet3_input_error +#undef _ + VMXNET3_INPUT_N_ERROR, +} vmxnet3_input_error_t; + +static __clib_unused char *vmxnet3_input_error_strings[] = { +#define _(n,s) s, + foreach_vmxnet3_input_error +#undef _ +}; + +static_always_inline u16 +vmxnet3_find_rid (vmxnet3_device_t * vd, vmxnet3_rx_comp * rx_comp) +{ + u32 rid; + + // rid is bits 16-25 (10 bits number) + rid = rx_comp->index & (0xffffffff >> 6); + rid >>= 16; + if ((rid >= vd->num_rx_queues) && (rid < (vd->num_rx_queues << 1))) + return 1; + else + return 0; +} + +static_always_inline void +vmxnet3_rx_comp_ring_advance_next (vmxnet3_rxq_t * rxq) +{ + vmxnet3_rx_comp_ring *comp_ring = &rxq->rx_comp_ring; + + comp_ring->next++; + if (PREDICT_FALSE (comp_ring->next == rxq->size)) + { + comp_ring->next = 0; + comp_ring->gen ^= VMXNET3_RXCF_GEN; + } +} + +static_always_inline uword +vmxnet3_device_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame, vmxnet3_device_t * vd, + u16 qid) +{ + vnet_main_t *vnm = vnet_get_main (); + uword n_trace = vlib_get_trace_count (vm, node); + u32 n_rx_packets = 0, n_rx_bytes = 0; + vmxnet3_rx_comp *rx_comp; + u32 comp_idx; + u32 desc_idx; + vmxnet3_rxq_t *rxq; + u32 thread_index = vm->thread_index; + u32 buffer_indices[VLIB_FRAME_SIZE], *bi; + u16 nexts[VLIB_FRAME_SIZE], *next; + vmxnet3_rx_ring *ring; + vmxnet3_rx_comp_ring *comp_ring; + u16 rid; + vlib_buffer_t *prev_b0 = 0, *hb = 0; + + rxq = vec_elt_at_index (vd->rxqs, qid); + comp_ring = &rxq->rx_comp_ring; + bi = buffer_indices; + next = nexts; + while (comp_ring->gen == + (rxq->rx_comp[comp_ring->next].flags & VMXNET3_RXCF_GEN)) + { + vlib_buffer_t *b0; + + comp_idx = comp_ring->next; + rx_comp = &rxq->rx_comp[comp_idx]; + + rid = vmxnet3_find_rid (vd, rx_comp); + ring = &rxq->rx_ring[rid]; + + if (PREDICT_TRUE (ring->fill >= 1)) + ring->fill--; + else + { + vlib_error_count (vm, node->node_index, + VMXNET3_INPUT_ERROR_NO_BUFFER, 1); + break; + } + + vmxnet3_rx_comp_ring_advance_next (rxq); + desc_idx = rx_comp->index & VMXNET3_RXC_INDEX; + ring->consume = desc_idx; + + bi[0] = ring->bufs[desc_idx]; + ring->bufs[desc_idx] = ~0; + + b0 = vlib_get_buffer (vm, bi[0]); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = vd->sw_if_index; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + b0->current_length = rx_comp->len & VMXNET3_RXCL_LEN_MASK; + b0->current_data = 0; + b0->total_length_not_including_first_buffer = 0; + b0->next_buffer = 0; + b0->flags = 0; + b0->error = 0; + ASSERT (b0->current_length != 0); + + if (rx_comp->index & VMXNET3_RXCI_SOP) + { + /* start segment */ + hb = b0; + if (!(rx_comp->index & VMXNET3_RXCI_EOP)) + { + hb->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID; + b0->flags |= VLIB_BUFFER_NEXT_PRESENT; + prev_b0 = b0; + } + else + { + /* + * Both start and end of packet is set. It is a complete packet + */ + prev_b0 = 0; + } + + } + else if (rx_comp->index & VMXNET3_RXCI_EOP) + { + /* end of segment */ + if (prev_b0) + { + prev_b0->next_buffer = bi[0]; + prev_b0->flags |= VLIB_BUFFER_NEXT_PRESENT; + hb->total_length_not_including_first_buffer += + b0->current_length; + prev_b0 = 0; // Get next packet + } + else + { + /* EOP without SOP, error */ + hb = 0; + bi++; + vlib_error_count (vm, node->node_index, + VMXNET3_INPUT_ERROR_RX_PACKET, 1); + vlib_buffer_free_one (vm, bi[0]); + continue; + } + } + else if (prev_b0) // !sop && !eop + { + /* mid chain */ + b0->flags |= VLIB_BUFFER_NEXT_PRESENT; + prev_b0->next_buffer = bi[0]; + prev_b0 = b0; + hb->total_length_not_including_first_buffer += b0->current_length; + } + else + { + ASSERT (0); + } + + bi++; + n_rx_bytes += b0->current_length; + + if (!prev_b0) + { + next[0] = VNET_DEVICE_INPUT_NEXT_ETHERNET_INPUT; + n_rx_packets++; + next++; + hb = 0; + } + } + + if (PREDICT_FALSE ((n_trace = vlib_get_trace_count (vm, node)))) + { + u32 n_left = n_rx_packets; + + bi = buffer_indices; + next = nexts; + while (n_trace && n_left) + { + vlib_buffer_t *b; + vmxnet3_input_trace_t *tr; + + b = vlib_get_buffer (vm, bi[0]); + vlib_trace_buffer (vm, node, next[0], b, /* follow_chain */ 0); + tr = vlib_add_trace (vm, node, b, sizeof (*tr)); + tr->next_index = next[0]; + tr->hw_if_index = vd->hw_if_index; + tr->buffer = *b; + + n_trace--; + n_left--; + bi++; + next++; + } + vlib_set_trace_count (vm, node, n_trace); + } + + if (PREDICT_TRUE (n_rx_packets)) + { + clib_error_t *error; + + vlib_buffer_enqueue_to_next (vm, node, buffer_indices, nexts, + n_rx_packets); + vlib_increment_combined_counter + (vnm->interface_main.combined_sw_if_counters + + VNET_INTERFACE_COUNTER_RX, thread_index, + vd->hw_if_index, n_rx_packets, n_rx_bytes); + + error = vmxnet3_rxq_refill_ring0 (vm, vd, rxq); + if (PREDICT_FALSE (error != 0)) + { + vlib_error_count (vm, node->node_index, + VMXNET3_INPUT_ERROR_BUFFER_ALLOC, 1); + } + error = vmxnet3_rxq_refill_ring1 (vm, vd, rxq); + if (PREDICT_FALSE (error != 0)) + { + vlib_error_count (vm, node->node_index, + VMXNET3_INPUT_ERROR_BUFFER_ALLOC, 1); + } + } + + return n_rx_packets; +} + +VLIB_NODE_FN (vmxnet3_input_node) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_rx = 0; + vmxnet3_main_t *vmxm = &vmxnet3_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) + { + vmxnet3_device_t *vd; + vd = vec_elt_at_index (vmxm->devices, dq->dev_instance); + if ((vd->flags & VMXNET3_DEVICE_F_ADMIN_UP) == 0) + continue; + n_rx += vmxnet3_device_input_inline (vm, node, frame, vd, dq->queue_id); + } + return n_rx; +} + +#ifndef CLIB_MARCH_VARIANT +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (vmxnet3_input_node) = { + .name = "vmxnet3-input", + .sibling_of = "device-input", + .format_trace = format_vmxnet3_input_trace, + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_DISABLED, + .n_errors = VMXNET3_INPUT_N_ERROR, + .error_strings = vmxnet3_input_error_strings, +}; +#endif + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/output.c b/src/plugins/vmxnet3/output.c new file mode 100644 index 00000000000..2ca1cc9f107 --- /dev/null +++ b/src/plugins/vmxnet3/output.c @@ -0,0 +1,209 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include +#include + +#include + +static_always_inline void +vmxnet3_tx_comp_ring_advance_next (vmxnet3_txq_t * txq) +{ + vmxnet3_tx_comp_ring *comp_ring = &txq->tx_comp_ring; + + comp_ring->next++; + if (PREDICT_FALSE (comp_ring->next == txq->size)) + { + comp_ring->next = 0; + comp_ring->gen ^= VMXNET3_TXCF_GEN; + } +} + +static_always_inline void +vmxnet3_tx_ring_advance_produce (vmxnet3_txq_t * txq) +{ + txq->tx_ring.produce++; + if (PREDICT_FALSE (txq->tx_ring.produce == txq->size)) + { + txq->tx_ring.produce = 0; + txq->tx_ring.gen ^= VMXNET3_TXF_GEN; + } +} + +static_always_inline void +vmxnet3_tx_ring_advance_consume (vmxnet3_txq_t * txq) +{ + txq->tx_ring.consume++; + txq->tx_ring.consume &= txq->size - 1; +} + +static_always_inline void +vmxnet3_txq_release (vlib_main_t * vm, vmxnet3_device_t * vd, + vmxnet3_txq_t * txq) +{ + vmxnet3_tx_comp *tx_comp; + u32 bi0; + vmxnet3_tx_comp_ring *comp_ring; + u16 eop_idx, desc_idx; + + comp_ring = &txq->tx_comp_ring; + tx_comp = &txq->tx_comp[comp_ring->next]; + + while ((tx_comp->flags & VMXNET3_TXCF_GEN) == comp_ring->gen) + { + eop_idx = tx_comp->index & VMXNET3_TXC_INDEX; + do + { + desc_idx = txq->tx_ring.consume; + bi0 = txq->tx_ring.bufs[desc_idx]; + txq->tx_ring.bufs[desc_idx] = ~0; + vlib_buffer_free_no_next (vm, &bi0, 1); + vmxnet3_tx_ring_advance_consume (txq); + } + while (desc_idx != eop_idx); + + vmxnet3_tx_comp_ring_advance_next (txq); + tx_comp = &txq->tx_comp[comp_ring->next]; + } +} + +static_always_inline u16 +vmxnet3_tx_ring_space_left (vmxnet3_txq_t * txq) +{ + u16 count; + + count = (txq->tx_ring.consume - txq->tx_ring.produce - 1); + /* Wrapped? */ + if (txq->tx_ring.produce >= txq->tx_ring.consume) + count += txq->size; + return count; +} + +VNET_DEVICE_CLASS_TX_FN (vmxnet3_device_class) (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vnet_interface_output_runtime_t *rd = (void *) node->runtime_data; + vmxnet3_device_t *vd = pool_elt_at_index (vmxm->devices, rd->dev_instance); + u32 *buffers = vlib_frame_args (frame); + u32 bi0; + vlib_buffer_t *b0; + vmxnet3_tx_desc *txd; + u32 desc_idx, generation, first_idx; + u16 space_left; + u16 n_left = frame->n_vectors; + vmxnet3_txq_t *txq; + u32 thread_index = vm->thread_index; + u16 qid = thread_index; + u16 n_retry = 5; + + txq = vec_elt_at_index (vd->txqs, qid % vd->num_tx_queues); + + clib_spinlock_lock_if_init (&txq->lock); + +retry: + vmxnet3_txq_release (vm, vd, txq); + + while (n_left) + { + bi0 = buffers[0]; + txd = 0; + + /* + * Toggle the generation bit for SOP fragment to avoid device starts + * reading incomplete packet + */ + generation = txq->tx_ring.gen ^ VMXNET3_TXF_GEN; + first_idx = txq->tx_ring.produce; + while (1) + { + b0 = vlib_get_buffer (vm, bi0); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + + space_left = vmxnet3_tx_ring_space_left (txq); + if (PREDICT_FALSE (space_left == 0)) + { + break; + } + + desc_idx = txq->tx_ring.produce; + + vmxnet3_tx_ring_advance_produce (txq); + txq->tx_ring.bufs[desc_idx] = bi0; + + txd = &txq->tx_desc[desc_idx]; + txd->address = + vlib_get_buffer_data_physical_address (vm, + bi0) + b0->current_data; + + txd->flags[0] = generation | b0->current_length; + + generation = txq->tx_ring.gen; + if (b0->flags & VLIB_BUFFER_NEXT_PRESENT) + { + txd->flags[1] = 0; + bi0 = b0->next_buffer; + } + else + break; + } + + if (PREDICT_TRUE (txd != 0)) + { + txd->flags[1] = VMXNET3_TXF_CQ | VMXNET3_TXF_EOP; + asm volatile ("":::"memory"); + /* + * Now toggle back the generation bit for the first segment. + * Device can start reading the packet + */ + txq->tx_desc[first_idx].flags[0] ^= VMXNET3_TXF_GEN; + vmxnet3_reg_write (vd, 0, VMXNET3_REG_TXPROD, txq->tx_ring.produce); + } + + if (PREDICT_FALSE (space_left == 0)) + { + break; + } + + buffers++; + n_left--; + } + + if (PREDICT_FALSE (n_left)) + { + if (PREDICT_TRUE (n_retry--)) + goto retry; + vlib_buffer_free (vm, buffers, n_left); + vlib_error_count (vm, node->node_index, VMXNET3_TX_ERROR_NO_FREE_SLOTS, + n_left); + } + clib_spinlock_unlock_if_init (&txq->lock); + + return (frame->n_vectors - n_left); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/plugin.c b/src/plugins/vmxnet3/plugin.c new file mode 100644 index 00000000000..8c7112d6064 --- /dev/null +++ b/src/plugins/vmxnet3/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 +#include +#include + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "VMWare Vmxnet3 Device Plugin", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3.api b/src/plugins/vmxnet3/vmxnet3.api new file mode 100644 index 00000000000..68beac030c1 --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3.api @@ -0,0 +1,125 @@ +/* + *------------------------------------------------------------------ + * 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. + *------------------------------------------------------------------ + */ + +option version = "1.0.0"; + +/** \brief + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param pci_addr - pci address as unsigned 32bit integer: + 0-15 domain, 16-23 bus, 24-28 slot, 29-31 function + ddddddddddddddddbbbbbbbbsssssfff + @param enable_elog - turn on elog (optional - default is off) + @param rxq_size - receive queue size (optional - default is 1024) + @param txq_size - transmit queue size (optional - default is 1024) +*/ + +define vmxnet3_create +{ + u32 client_index; + u32 context; + + u32 pci_addr; + i32 enable_elog; + u16 rxq_size; + u16 txq_size; +}; + +/** \brief + @param context - sender context, to match reply w/ request + @param retval - return value for request + @param sw_if_index - software index for the new vmxnet3 interface +*/ + +define vmxnet3_create_reply +{ + u32 context; + i32 retval; + u32 sw_if_index; +}; + +/** \brief + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface index +*/ + +autoreply define vmxnet3_delete +{ + u32 client_index; + u32 context; + + u32 sw_if_index; +}; + +/** \brief Memory interface details structure + @param context - sender context, to match reply w/ request (memif_dump) + @param sw_if_index - index of the interface + @param if_name - name of the interface + @param hw_addr - interface MAC address + @param id - id associated with the interface + @param role - role of the interface in the connection (master/slave) + @param mode - interface mode + @param socket_id - id of the socket filename used by this interface + to establish new connections + @param ring_size - the number of entries of RX/TX rings + @param buffer_size - size of the buffer allocated for each ring entry + @param admin_up_down - interface administrative status + @param link_up_down - interface link status + +*/ +define vmxnet3_details +{ + u32 context; + + u32 sw_if_index; + u8 if_name[64]; + u8 hw_addr[6]; + u32 pci_addr; + u8 version; + + u16 rx_qid; + u16 rx_qsize; + u16 rx_fill[2]; + u16 rx_next; + u16 rx_produce[2]; + u16 rx_consume[2]; + + u16 tx_qid; + u16 tx_qsize; + u16 tx_next; + u16 tx_produce; + u16 tx_consume; + + u8 admin_up_down; +}; + +/** \brief Dump all vmxnet3 interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define vmxnet3_dump +{ + u32 client_index; + u32 context; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3.c b/src/plugins/vmxnet3/vmxnet3.c new file mode 100644 index 00000000000..74ef12619d2 --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3.c @@ -0,0 +1,612 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07b0 + +vmxnet3_main_t vmxnet3_main; + +static pci_device_id_t vmxnet3_pci_device_ids[] = { + { + .vendor_id = PCI_VENDOR_ID_VMWARE, + .device_id = PCI_DEVICE_ID_VMWARE_VMXNET3}, + {0}, +}; + +static clib_error_t * +vmxnet3_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, + u32 flags) +{ + vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_device_t *vd = vec_elt_at_index (vmxm->devices, hi->dev_instance); + uword is_up = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) != 0; + + if (vd->flags & VMXNET3_DEVICE_F_ERROR) + return clib_error_return (0, "device is in error state"); + + if (is_up) + { + vnet_hw_interface_set_flags (vnm, vd->hw_if_index, + VNET_HW_INTERFACE_FLAG_LINK_UP); + vd->flags |= VMXNET3_DEVICE_F_ADMIN_UP; + } + else + { + vnet_hw_interface_set_flags (vnm, vd->hw_if_index, 0); + vd->flags &= ~VMXNET3_DEVICE_F_ADMIN_UP; + } + return 0; +} + +static clib_error_t * +vmxnet3_interface_rx_mode_change (vnet_main_t * vnm, u32 hw_if_index, u32 qid, + vnet_hw_interface_rx_mode mode) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, hw_if_index); + vmxnet3_device_t *vd = pool_elt_at_index (vmxm->devices, hw->dev_instance); + vmxnet3_rxq_t *rxq = vec_elt_at_index (vd->rxqs, qid); + + if (mode == VNET_HW_INTERFACE_RX_MODE_POLLING) + rxq->int_mode = 0; + else + rxq->int_mode = 1; + + return 0; +} + +static char *vmxnet3_tx_func_error_strings[] = { +#define _(n,s) s, + foreach_vmxnet3_tx_func_error +#undef _ +}; + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (vmxnet3_device_class,) = +{ + .name = "VMXNET3 interface", + .format_device = format_vmxnet3_device, + .format_device_name = format_vmxnet3_device_name, + .admin_up_down_function = vmxnet3_interface_admin_up_down, + .rx_mode_change_function = vmxnet3_interface_rx_mode_change, + .tx_function_n_errors = VMXNET3_TX_N_ERROR, + .tx_function_error_strings = vmxnet3_tx_func_error_strings, +}; +/* *INDENT-ON* */ + +static u32 +vmxnet3_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hw, u32 flags) +{ + return 0; +} + +static void +vmxnet3_write_mac (vmxnet3_device_t * vd) +{ + u32 val; + + memcpy (&val, vd->mac_addr, 4); + vmxnet3_reg_write (vd, 1, VMXNET3_REG_MACL, val); + + val = 0; + memcpy (&val, vd->mac_addr + 4, 2); + vmxnet3_reg_write (vd, 1, VMXNET3_REG_MACH, val); +} + +static clib_error_t * +vmxnet3_provision_driver_shared (vlib_main_t * vm, vmxnet3_device_t * vd) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_shared *shared; + vmxnet3_queues *q; + u64 shared_dma; + clib_error_t *error; + u16 qid = 0, rid; + vmxnet3_rxq_t *rxq = vec_elt_at_index (vd->rxqs, qid); + vmxnet3_txq_t *txq = vec_elt_at_index (vd->txqs, qid); + + vd->dma = vlib_physmem_alloc_aligned (vm, vmxm->physmem_region, &error, + sizeof (*vd->dma), 512); + if (error) + return error; + + memset (vd->dma, 0, sizeof (*vd->dma)); + + q = &vd->dma->queues; + q->tx.cfg.desc_address = vmxnet3_dma_addr (vm, vd, txq->tx_desc); + q->tx.cfg.comp_address = vmxnet3_dma_addr (vm, vd, txq->tx_comp); + q->tx.cfg.num_desc = txq->size; + q->tx.cfg.num_comp = txq->size; + for (rid = 0; rid < VMXNET3_RX_RING_SIZE; rid++) + { + q->rx.cfg.desc_address[rid] = vmxnet3_dma_addr (vm, vd, + rxq->rx_desc[rid]); + q->rx.cfg.num_desc[rid] = rxq->size; + } + q->rx.cfg.comp_address = vmxnet3_dma_addr (vm, vd, rxq->rx_comp); + q->rx.cfg.num_comp = rxq->size; + + shared = &vd->dma->shared; + shared->magic = VMXNET3_SHARED_MAGIC; + shared->misc.version = VMXNET3_VERSION_MAGIC; + if (sizeof (void *) == 4) + shared->misc.guest_info = VMXNET3_GOS_BITS_32; + else + shared->misc.guest_info = VMXNET3_GOS_BITS_64; + shared->misc.guest_info |= VMXNET3_GOS_TYPE_LINUX; + shared->misc.version_support = VMXNET3_VERSION_SELECT; + shared->misc.upt_version_support = VMXNET3_UPT_VERSION_SELECT; + shared->misc.queue_desc_address = vmxnet3_dma_addr (vm, vd, q); + shared->misc.queue_desc_len = sizeof (*q); + shared->misc.mtu = VMXNET3_MTU; + shared->misc.num_tx_queues = vd->num_tx_queues; + shared->misc.num_rx_queues = vd->num_rx_queues; + shared->interrupt.num_intrs = vd->num_intrs; + shared->interrupt.control = VMXNET3_IC_DISABLE_ALL; + shared->rx_filter.mode = VMXNET3_RXMODE_UCAST | VMXNET3_RXMODE_BCAST | + VMXNET3_RXMODE_ALL_MULTI; + shared_dma = vmxnet3_dma_addr (vm, vd, shared); + + vmxnet3_reg_write (vd, 1, VMXNET3_REG_DSAL, shared_dma); + vmxnet3_reg_write (vd, 1, VMXNET3_REG_DSAH, shared_dma >> 32); + + return 0; +} + +static inline void +vmxnet3_enable_interrupt (vmxnet3_device_t * vd) +{ + int i; + vmxnet3_shared *shared = &vd->dma->shared; + + shared->interrupt.control &= ~VMXNET3_IC_DISABLE_ALL; + for (i = 0; i < vd->num_intrs; i++) + vmxnet3_reg_write (vd, 0, VMXNET3_REG_IMR + i * 8, 0); +} + +static inline void +vmxnet3_disable_interrupt (vmxnet3_device_t * vd) +{ + int i; + vmxnet3_shared *shared = &vd->dma->shared; + + shared->interrupt.control |= VMXNET3_IC_DISABLE_ALL; + for (i = 0; i < vd->num_intrs; i++) + vmxnet3_reg_write (vd, 0, VMXNET3_REG_IMR + i * 8, 1); +} + +static clib_error_t * +vmxnet3_rxq_init (vlib_main_t * vm, vmxnet3_device_t * vd, u16 qid, u16 qsz) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_rxq_t *rxq; + clib_error_t *error; + u16 rid; + + vec_validate_aligned (vd->rxqs, qid, CLIB_CACHE_LINE_BYTES); + rxq = vec_elt_at_index (vd->rxqs, qid); + memset (rxq, 0, sizeof (*rxq)); + rxq->size = qsz; + for (rid = 0; rid < VMXNET3_RX_RING_SIZE; rid++) + { + rxq->rx_desc[rid] = + vlib_physmem_alloc_aligned (vm, vmxm->physmem_region, + &error, qsz * sizeof (*rxq->rx_desc[rid]), + 512); + if (error) + return error; + memset (rxq->rx_desc[rid], 0, qsz * sizeof (*rxq->rx_desc[rid])); + } + rxq->rx_comp = vlib_physmem_alloc_aligned (vm, vmxm->physmem_region, &error, + qsz * sizeof (*rxq->rx_comp), + 512); + if (error) + return error; + memset (rxq->rx_comp, 0, qsz * sizeof (*rxq->rx_comp)); + for (rid = 0; rid < VMXNET3_RX_RING_SIZE; rid++) + { + vmxnet3_rx_ring *ring; + + ring = &rxq->rx_ring[rid]; + ring->gen = VMXNET3_RXF_GEN; + vec_validate_aligned (ring->bufs, rxq->size, CLIB_CACHE_LINE_BYTES); + } + rxq->rx_comp_ring.gen = VMXNET3_RXCF_GEN; + + return 0; +} + +static clib_error_t * +vmxnet3_txq_init (vlib_main_t * vm, vmxnet3_device_t * vd, u16 qid, u16 qsz) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_txq_t *txq; + clib_error_t *error; + + if (qid >= vd->num_tx_queues) + { + qid = qid % vd->num_tx_queues; + txq = vec_elt_at_index (vd->txqs, qid); + if (txq->lock == 0) + clib_spinlock_init (&txq->lock); + vd->flags |= VMXNET3_DEVICE_F_SHARED_TXQ_LOCK; + } + + vec_validate_aligned (vd->txqs, qid, CLIB_CACHE_LINE_BYTES); + txq = vec_elt_at_index (vd->txqs, qid); + memset (txq, 0, sizeof (*txq)); + txq->size = qsz; + txq->tx_desc = vlib_physmem_alloc_aligned (vm, vmxm->physmem_region, &error, + qsz * sizeof (*txq->tx_desc), + 512); + if (error) + return error; + memset (txq->tx_desc, 0, qsz * sizeof (*txq->tx_desc)); + txq->tx_comp = vlib_physmem_alloc_aligned (vm, vmxm->physmem_region, &error, + qsz * sizeof (*txq->tx_comp), + 512); + if (error) + return error; + memset (txq->tx_comp, 0, qsz * sizeof (*txq->tx_comp)); + vec_validate_aligned (txq->tx_ring.bufs, txq->size, CLIB_CACHE_LINE_BYTES); + txq->tx_ring.gen = VMXNET3_TXF_GEN; + txq->tx_comp_ring.gen = VMXNET3_TXCF_GEN; + + return 0; +} + +static clib_error_t * +vmxnet3_device_init (vlib_main_t * vm, vmxnet3_device_t * vd, + vmxnet3_create_if_args_t * args) +{ + clib_error_t *error = 0; + u32 ret, i; + vmxnet3_main_t *vmxm = &vmxnet3_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + vd->num_tx_queues = 1; + vd->num_rx_queues = 1; + vd->num_intrs = 1; + + /* Quiesce the device */ + vmxnet3_reg_write (vd, 1, VMXNET3_REG_CMD, VMXNET3_CMD_QUIESCE_DEV); + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_CMD); + if (ret != 0) + { + error = clib_error_return (0, "error on quisecing device rc (%u)", ret); + return error; + } + + /* Reset the device */ + vmxnet3_reg_write (vd, 1, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV); + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_CMD); + if (ret != 0) + { + error = clib_error_return (0, "error on resetting device rc (%u)", ret); + return error; + } + + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_VRRS); + vd->version = count_leading_zeros (ret); + vd->version = uword_bits - vd->version; + + if (vd->version == 0 || vd->version > 3) + { + error = clib_error_return (0, "unsupported hardware version %u", + vd->version); + return error; + } + + vmxnet3_reg_write (vd, 1, VMXNET3_REG_VRRS, 1 << (vd->version - 1)); + + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_UVRS); + if (ret & 1) + vmxnet3_reg_write (vd, 1, VMXNET3_REG_UVRS, 1); + else + { + error = clib_error_return (0, "unsupported upt version %u", ret); + return error; + } + + /* Get the mac address */ + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_MACL); + clib_memcpy (vd->mac_addr, &ret, 4); + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_MACH); + clib_memcpy (vd->mac_addr + 4, &ret, 2); + + if (vmxm->physmem_region_alloc == 0) + { + u32 flags = VLIB_PHYSMEM_F_INIT_MHEAP | VLIB_PHYSMEM_F_HUGETLB; + error = + vlib_physmem_region_alloc (vm, "vmxnet3 descriptors", 4 << 20, 0, + flags, &vmxm->physmem_region); + if (error) + return error; + vmxm->physmem_region_alloc = 1; + } + + error = vmxnet3_rxq_init (vm, vd, 0, args->rxq_size); + if (error) + return error; + + for (i = 0; i < tm->n_vlib_mains; i++) + { + error = vmxnet3_txq_init (vm, vd, i, args->txq_size); + if (error) + return error; + } + + error = vmxnet3_provision_driver_shared (vm, vd); + if (error) + return error; + + vmxnet3_write_mac (vd); + + /* Activate device */ + vmxnet3_reg_write (vd, 1, VMXNET3_REG_CMD, VMXNET3_CMD_ACTIVATE_DEV); + ret = vmxnet3_reg_read (vd, 1, VMXNET3_REG_CMD); + if (ret != 0) + { + error = + clib_error_return (0, "error on activating device rc (%u)", ret); + return error; + } + + /* Disable interrupts */ + vmxnet3_disable_interrupt (vd); + + vec_foreach_index (i, vd->rxqs) + { + vmxnet3_rxq_t *rxq = vec_elt_at_index (vd->rxqs, i); + + vmxnet3_rxq_refill_ring0 (vm, vd, rxq); + vmxnet3_rxq_refill_ring1 (vm, vd, rxq); + } + vd->flags |= VMXNET3_DEVICE_F_INITIALIZED; + + vmxnet3_enable_interrupt (vd); + + return error; +} + +static void +vmxnet3_irq_handler (vlib_pci_dev_handle_t h, u16 line) +{ + vnet_main_t *vnm = vnet_get_main (); + vmxnet3_main_t *vmxm = &vmxnet3_main; + uword pd = vlib_pci_get_private_data (h); + vmxnet3_device_t *vd = pool_elt_at_index (vmxm->devices, pd); + u16 qid = line; + + if (vec_len (vd->rxqs) > qid && vd->rxqs[qid].int_mode != 0) + vnet_device_input_set_interrupt_pending (vnm, vd->hw_if_index, qid); +} + +static u8 +vmxnet3_queue_size_valid (u16 qsz) +{ + if (qsz < 64 || qsz > 4096) + return 0; + if ((qsz % 64) != 0) + return 0; + return 1; +} + +void +vmxnet3_create_if (vlib_main_t * vm, vmxnet3_create_if_args_t * args) +{ + vnet_main_t *vnm = vnet_get_main (); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vmxnet3_device_t *vd; + vlib_pci_dev_handle_t h; + clib_error_t *error = 0; + + if (args->rxq_size == 0) + args->rxq_size = VMXNET3_NUM_RX_DESC; + if (args->txq_size == 0) + args->txq_size = VMXNET3_NUM_TX_DESC; + + if (!vmxnet3_queue_size_valid (args->rxq_size) || + !vmxnet3_queue_size_valid (args->txq_size)) + { + args->rv = VNET_API_ERROR_INVALID_VALUE; + args->error = + clib_error_return (error, + "queue size must be <= 4096, >= 64, " + "and multiples of 64"); + return; + } + + /* *INDENT-OFF* */ + pool_foreach (vd, vmxm->devices, ({ + if (vd->pci_addr.as_u32 == args->addr.as_u32) + { + args->rv = VNET_API_ERROR_INVALID_VALUE; + args->error = + clib_error_return (error, "PCI address in use"); + return; + } + })); + /* *INDENT-ON* */ + + pool_get (vmxm->devices, vd); + vd->dev_instance = vd - vmxm->devices; + vd->per_interface_next_index = ~0; + vd->pci_addr = args->addr; + + if (args->enable_elog) + vd->flags |= VMXNET3_DEVICE_F_ELOG; + + if ((error = + vlib_pci_device_open (&args->addr, vmxnet3_pci_device_ids, &h))) + { + pool_put (vmxm->devices, vd); + args->rv = VNET_API_ERROR_INVALID_INTERFACE; + args->error = + clib_error_return (error, "pci-addr %U", format_vlib_pci_addr, + &args->addr); + return; + } + vd->pci_dev_handle = h; + + vlib_pci_set_private_data (h, vd->dev_instance); + + if ((error = vlib_pci_bus_master_enable (h))) + goto error; + + if ((error = vlib_pci_map_region (h, 0, (void **) &vd->bar[0]))) + goto error; + + if ((error = vlib_pci_map_region (h, 1, (void **) &vd->bar[1]))) + goto error; + + if ((error = vlib_pci_register_msix_handler (h, 0, 1, + &vmxnet3_irq_handler))) + goto error; + + if ((error = vlib_pci_enable_msix_irq (h, 0, 1))) + goto error; + + if ((error = vlib_pci_intr_enable (h))) + goto error; + + if ((error = vmxnet3_device_init (vm, vd, args))) + goto error; + + /* create interface */ + error = ethernet_register_interface (vnm, vmxnet3_device_class.index, + vd->dev_instance, vd->mac_addr, + &vd->hw_if_index, vmxnet3_flag_change); + + if (error) + goto error; + + vnet_sw_interface_t *sw = vnet_get_hw_sw_interface (vnm, vd->hw_if_index); + vd->sw_if_index = sw->sw_if_index; + args->sw_if_index = sw->sw_if_index; + + vnet_hw_interface_t *hw = vnet_get_hw_interface (vnm, vd->hw_if_index); + hw->flags |= VNET_HW_INTERFACE_FLAG_SUPPORTS_INT_MODE; + vnet_hw_interface_set_input_node (vnm, vd->hw_if_index, + vmxnet3_input_node.index); + vnet_hw_interface_assign_rx_thread (vnm, vd->hw_if_index, 0, ~0); + return; + +error: + vmxnet3_delete_if (vm, vd); + args->rv = VNET_API_ERROR_INVALID_INTERFACE; + args->error = error; +} + +void +vmxnet3_delete_if (vlib_main_t * vm, vmxnet3_device_t * vd) +{ + vnet_main_t *vnm = vnet_get_main (); + vmxnet3_main_t *vmxm = &vmxnet3_main; + u32 i, bi; + u16 desc_idx; + + /* Quiesce the device */ + vmxnet3_reg_write (vd, 1, VMXNET3_REG_CMD, VMXNET3_CMD_QUIESCE_DEV); + + /* Reset the device */ + vmxnet3_reg_write (vd, 1, VMXNET3_REG_CMD, VMXNET3_CMD_RESET_DEV); + + if (vd->hw_if_index) + { + vnet_hw_interface_set_flags (vnm, vd->hw_if_index, 0); + vnet_hw_interface_unassign_rx_thread (vnm, vd->hw_if_index, 0); + ethernet_delete_interface (vnm, vd->hw_if_index); + } + + vlib_pci_device_close (vd->pci_dev_handle); + + /* *INDENT-OFF* */ + vec_foreach_index (i, vd->rxqs) + { + vmxnet3_rxq_t *rxq = vec_elt_at_index (vd->rxqs, i); + u16 mask = rxq->size - 1; + u16 rid; + + for (rid = 0; rid < VMXNET3_RX_RING_SIZE; rid++) + { + vmxnet3_rx_ring *ring; + + ring = &rxq->rx_ring[rid]; + desc_idx = ring->consume; + while (ring->fill) + { + desc_idx &= mask; + bi = ring->bufs[desc_idx]; + vlib_buffer_free_no_next (vm, &bi, 1); + ring->fill--; + desc_idx++; + } + vec_free (ring->bufs); + vlib_physmem_free (vm, vmxm->physmem_region, rxq->rx_desc[rid]); + } + vlib_physmem_free (vm, vmxm->physmem_region, rxq->rx_comp); + } + /* *INDENT-ON* */ + vec_free (vd->rxqs); + + /* *INDENT-OFF* */ + vec_foreach_index (i, vd->txqs) + { + vmxnet3_txq_t *txq = vec_elt_at_index (vd->txqs, i); + u16 mask = txq->size - 1; + u16 end_idx; + + desc_idx = txq->tx_ring.consume; + end_idx = txq->tx_ring.produce; + while (desc_idx != end_idx) + { + bi = txq->tx_ring.bufs[desc_idx]; + vlib_buffer_free_no_next (vm, &bi, 1); + desc_idx++; + desc_idx &= mask; + } + clib_spinlock_free (&txq->lock); + vec_free (txq->tx_ring.bufs); + vlib_physmem_free (vm, vmxm->physmem_region, txq->tx_desc); + vlib_physmem_free (vm, vmxm->physmem_region, txq->tx_comp); + } + /* *INDENT-ON* */ + vec_free (vd->txqs); + + vlib_physmem_free (vm, vmxm->physmem_region, vd->dma); + + clib_error_free (vd->error); + memset (vd, 0, sizeof (*vd)); + pool_put (vmxm->devices, vd); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3.h b/src/plugins/vmxnet3/vmxnet3.h new file mode 100644 index 00000000000..13799407d1e --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3.h @@ -0,0 +1,639 @@ +/* + * 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. + */ + +#ifndef __included_vmnet_vmnet_h__ +#define __included_vmnet_vmnet_h__ + +#define foreach_vmxnet3_tx_func_error \ + _(ERROR_PACKETS, "error packets") \ + _(NO_FREE_SLOTS, "no free tx slots") + +typedef enum +{ +#define _(f,s) VMXNET3_TX_ERROR_##f, + foreach_vmxnet3_tx_func_error +#undef _ + VMXNET3_TX_N_ERROR, +} vmxnet3_tx_func_error_t; + +#define foreach_vmxnet3_rxmode_flags \ + _(0, UCAST, "unicast") \ + _(1, MCAST, "multicast") \ + _(2, BCAST, "broadcast") \ + _(3, ALL_MULTI, "all multicast") \ + _(4, PROMISC, "promiscuous") + +enum +{ +#define _(a, b, c) VMXNET3_RXMODE_##b = (1 << a), + foreach_vmxnet3_rxmode_flags +#undef _ +}; + +/* BAR 0 */ +#define VMXNET3_REG_IMR 0x0000 /* Interrupt Mask Register */ +#define VMXNET3_REG_TXPROD 0x0600 /* Tx Producer Index */ +#define VMXNET3_REG_RXPROD 0x0800 /* Rx Producer Index for ring 1 */ +#define VMXNET3_REG_RXPROD2 0x0A00 /* Rx Producer Index for ring 2 */ + + +/* BAR 1 */ +#define VMXNET3_REG_VRRS 0x0000 /* VMXNET3 Revision Report Selection */ +#define VMXNET3_REG_UVRS 0x0008 /* UPT Version Report Selection */ +#define VMXNET3_REG_DSAL 0x0010 /* Driver Shared Address Low */ +#define VMXNET3_REG_DSAH 0x0018 /* Driver Shared Address High */ +#define VMXNET3_REG_CMD 0x0020 /* Command */ +#define VMXNET3_REG_MACL 0x0028 /* MAC Address Low */ +#define VMXNET3_REG_MACH 0x0030 /* MAC Address High */ +#define VMXNET3_REG_ICR 0x0038 /* Interrupt Cause Register */ +#define VMXNET3_REG_ECR 0x0040 /* Event Cause Register */ + +#define VMXNET3_VLAN_LEN 4 +#define VMXNET3_FCS_LEN 4 +#define VMXNET3_MTU (1514 + VMXNET3_VLAN_LEN + VMXNET3_FCS_LEN) + +#define VMXNET3_RXF_BTYPE (1 << 14) /* rx body buffer type */ +#define VMXNET3_RXF_GEN (1 << 31) /* rx generation */ +#define VMXNET3_RXCF_GEN (1 << 31) /* rx completion generation */ +#define VMXNET3_RXC_INDEX (0xFFF) /* rx completion index mask */ + +#define VMXNET3_TXF_GEN (1 << 14) /* tx generation */ +#define VMXNET3_TXF_EOP (1 << 12) /* tx end of packet */ +#define VMXNET3_TXF_CQ (1 << 13) /* tx completion request */ +#define VMXNET3_TXCF_GEN (1 << 31) /* tx completion generation */ +#define VMXNET3_TXC_INDEX (0xFFF) /* tx completion index mask */ + +#define VMXNET3_RX_RING_SIZE 2 +#define VMXNET3_INPUT_REFILL_THRESHOLD 32 +#define VMXNET3_NUM_TX_DESC 1024 +#define VMXNET3_NUM_TX_COMP VMXNET3_NUM_TX_DESC +#define VMXNET3_NUM_RX_DESC 1024 +#define VMXNET3_NUM_RX_COMP VMXNET3_NUM_RX_DESC + +#define VMXNET3_VERSION_MAGIC 0x69505845 +#define VMXNET3_SHARED_MAGIC 0xbabefee1 +#define VMXNET3_VERSION_SELECT 1 +#define VMXNET3_UPT_VERSION_SELECT 1 +#define VMXNET3_MAX_INTRS 25 +#define VMXNET3_IC_DISABLE_ALL 0x1 + +#define VMXNET3_GOS_BITS_32 (1 << 0) +#define VMXNET3_GOS_BITS_64 (2 << 0) +#define VMXNET3_GOS_TYPE_LINUX (1 << 2) +#define VMXNET3_RXCL_LEN_MASK (0x3FFF) // 14 bits +#define VMXNET3_RXCL_ERROR (1 << 14) +#define VMXNET3_RXCI_EOP (1 << 14) +#define VMXNET3_RXCI_SOP (1 << 15) + +#define foreach_vmxnet3_device_flags \ + _(0, INITIALIZED, "initialized") \ + _(1, ERROR, "error") \ + _(2, ADMIN_UP, "admin-up") \ + _(3, IOVA, "iova") \ + _(4, LINK_UP, "link-up") \ + _(5, SHARED_TXQ_LOCK, "shared-txq-lock") \ + _(6, ELOG, "elog") + +enum +{ +#define _(a, b, c) VMXNET3_DEVICE_F_##b = (1 << a), + foreach_vmxnet3_device_flags +#undef _ +}; + +#define foreach_vmxnet3_set_cmds \ + _(0, ACTIVATE_DEV, "activate device") \ + _(1, QUIESCE_DEV, "quiesce device") \ + _(2, RESET_DEV, "reset device") \ + _(3, UPDATE_RX_MODE, "update rx mode") \ + _(4, UPDATE_MAC_FILTERS, "update mac filters") \ + _(5, UPDATE_VLAN_FILTERS, "update vlan filters") \ + _(6, UPDATE_RSSIDT, "update rss idt") \ + _(7, UPDATE_IML, "update iml") \ + _(8, UPDATE_PMCFG, "update pm cfg") \ + _(9, UPDATE_FEATURE, "update feature") \ + _(10, STOP_EMULATION, "stop emulation") \ + _(11, LOAD_PLUGIN, "load plugin") \ + _(12, ACTIVATE_VF, "activate vf") \ + _(13, RESERVED3, "reserved 3") \ + _(14, RESERVED4, "reservced 4") \ + _(15, REGISTER_MEMREGS, "register mem regs") + +enum +{ +#define _(a, b, c) VMXNET3_CMD_##b = (a + 0xCAFE0000), + foreach_vmxnet3_set_cmds +#undef _ +}; + +#define foreach_vmxnet3_get_cmds \ + _(0, GET_QUEUE_STATUS, "get queue status") \ + _(1, GET_STATS, "get stats") \ + _(2, GET_LINK, "get link") \ + _(3, GET_PERM_MAC_LO, "get perm mac lo") \ + _(4, GET_PERM_MAC_HI, "get perm mac hi") \ + _(5, GET_DID_LO, "get did lo") \ + _(6, GET_DID_HI, "get did hi") \ + _(7, GET_DEV_EXTRA_INFO, "get dev extra info") \ + _(8, GET_CONF_INTR, "get conf intr") \ + _(9, GET_ADAPTIVE_RING_INFO, "get adaptive ring info") \ + _(10, GET_TXDATA_DESC_SIZE, "gte txdata desc size") \ + _(11, RESERVED5, "reserved5") + +enum +{ +#define _(a, b, c) VMXNET3_CMD_##b = (a + 0xF00D0000), + foreach_vmxnet3_get_cmds +#undef _ +}; + +typedef CLIB_PACKED (struct + { + u32 version; u32 guest_info; u32 version_support; + u32 upt_version_support; u64 upt_features; + u64 driver_data_address; u64 queue_desc_address; + u32 driver_data_len; u32 queue_desc_len; + u32 mtu; + u16 max_num_rx_sg; u8 num_tx_queues; u8 num_rx_queues; + u32 pad[4]; + }) vmxnet3_misc_config; + +typedef CLIB_PACKED (struct + { + u8 mask_mode; + u8 num_intrs; + u8 event_intr_index; + u8 moderation_level[VMXNET3_MAX_INTRS]; u32 control; + u32 pad[2]; + }) vmxnet3_interrupt_config; + +typedef CLIB_PACKED (struct + { + u32 mode; + u16 multicast_len; + u16 pad; u64 multicast_address; u8 vlan_filter[512]; + }) vmxnet3_rx_filter_config; + +typedef CLIB_PACKED (struct + { + u32 version; u32 length; + u64 address; + }) vmxnet3_variable_config; + +typedef CLIB_PACKED (struct + { + u32 magic; + u32 pad; + vmxnet3_misc_config misc; + vmxnet3_interrupt_config interrupt; + vmxnet3_rx_filter_config rx_filter; + vmxnet3_variable_config rss; + vmxnet3_variable_config pattern; + vmxnet3_variable_config plugin; u32 ecr; + u32 pad1[5]; + }) vmxnet3_shared; + +typedef CLIB_PACKED (struct + { + u8 stopped; + u8 pad[3]; + u32 error; + }) vmxnet3_queue_status; + +typedef CLIB_PACKED (struct + { + u32 num_deferred; u32 threshold; + u64 pad; + }) vmxnet3_tx_queue_control; + +typedef CLIB_PACKED (struct + { + u64 desc_address; + u64 data_address; + u64 comp_address; u64 driver_data_address; u64 pad; + u32 num_desc; + u32 num_data; + u32 num_comp; u32 driver_data_len; u8 intr_index; + u8 pad1[7]; + }) vmxnet3_tx_queue_config; + +typedef CLIB_PACKED (struct + { + u64 tso_pkts; + u64 tso_bytes; + u64 ucast_pkts; u64 ucast_bytes; u64 mcast_pkts; + u64 mcast_bytes; + u64 bcast_pkts; u64 bcast_bytes; u64 error_pkts; + u64 discard_pkts; + }) vmxnet3_tx_stats; + +typedef CLIB_PACKED (struct + { + vmxnet3_tx_queue_control ctrl; + vmxnet3_tx_queue_config cfg; + vmxnet3_queue_status status; vmxnet3_tx_stats stats; + u8 pad[88]; + }) vmxnet3_tx_queue; + +typedef CLIB_PACKED (struct + { + u8 update_prod; u8 pad[7]; + u64 pad1; + }) vmxnet3_rx_queue_control; + +typedef CLIB_PACKED (struct + { + u64 desc_address[2]; + u64 comp_address; u64 driver_data_address; u64 pad; + u32 num_desc[2]; + u32 num_comp; u32 driver_data_len; u8 intr_index; + u8 pad1[7]; + }) vmxnet3_rx_queue_config; + +typedef CLIB_PACKED (struct + { + u64 lro_pkts; + u64 lro_bytes; + u64 ucast_pkts; u64 ucast_bytes; u64 mcast_pkts; + u64 mcast_bytes; + u64 bcast_pkts; u64 bcast_bytes; u64 nobuf_pkts; + u64 error_pkts; + }) vmxnet3_rx_stats; + +typedef CLIB_PACKED (struct + { + vmxnet3_rx_queue_control ctrl; + vmxnet3_rx_queue_config cfg; + vmxnet3_queue_status status; vmxnet3_rx_stats stats; + u8 pad[88]; + }) vmxnet3_rx_queue; + +typedef CLIB_PACKED (struct + { + vmxnet3_tx_queue tx; vmxnet3_rx_queue rx; + }) vmxnet3_queues; + +/* + * flags: + * buffer length -- bits 0-13 + * buffer type -- bit 14 + * descriptor type -- bit 15 + * reserved -- bits 16-30 + * generation -- bit 31 + */ +typedef CLIB_PACKED (struct + { + u64 address; + u32 flags; + u32 pad; + }) vmxnet3_rx_desc; + +/* + * index: + * RX desc index -- bits 0-11 + * ext1 -- bits 12-13 + * end of packet -- bit 14 + * start of packet -- bit 15 + * ring ID -- bits 16-25 + * RSS hash type -- bits 26-29 + * checksum not calculated -- bit 30 + * ext2 -- bit 31 + * + * rss: RSS hash value + * + * len: + * data length -- bits 0-13 + * error -- bit 14 + * tag is stripped -- bit 15 + * tag stripped -- bits 16-31 + * + * flags: + * checksum -- bits 0 - 15 + * tcp/udp checksum correct-- bit 16 + * udp packet -- bit 17 + * tcp packet -- bit 18 + * ip checksum correct -- bit 19 + * ipv6 -- bit 20 + * ipv4 -- bit 21 + * ip fragment -- bit 22 + * frame crc correct -- bit 23 + * completion type -- bits 24-30 + * generation -- bit 31 + */ +typedef CLIB_PACKED (struct + { + u32 index; u32 rss; + u32 len; + u32 flags; + }) vmxnet3_rx_comp; + +/* + * index: + * TX desc index -- bits 0-11 + * ext1 -- bits 12-31 + * + * flags: + * reserved -- bits 0-23 + * completion type -- bits 24-30 + * generation -- bit 31 + */ +typedef CLIB_PACKED (struct + { + u32 index; + u32 pad[2]; + u32 flags; + }) vmxnet3_tx_comp; + +/* + * flags[0]: + * length -- bits 0-13 + * generation -- bit 14 + * reserved -- bit 15 + * descriptor type -- bit 16 + * ext1 -- bit 17 + * MSS, checksum offset -- bits 18-31 + * flags[1]: + * header length -- bits 0-9 + * offload mode -- bits 10-11 + * end of packet -- bit 12 + * completion request -- bit 13 + * ext2 -- bit 14 + * vlan tag insertion -- bit 15 + * tag to insert -- bits 16-31 + */ +typedef CLIB_PACKED (struct + { + u64 address; + u32 flags[2]; + }) vmxnet3_tx_desc; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u32 *bufs; + u32 gen; + u16 fill; + u16 rid; + u16 produce; + u16 consume; +} vmxnet3_rx_ring; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u64 next; + u32 gen; +} vmxnet3_rx_comp_ring; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u16 size; + u8 int_mode; + vmxnet3_rx_ring rx_ring[VMXNET3_RX_RING_SIZE]; + vmxnet3_rx_desc *rx_desc[VMXNET3_RX_RING_SIZE]; + vmxnet3_rx_comp *rx_comp; + vmxnet3_rx_comp_ring rx_comp_ring; +} vmxnet3_rxq_t; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u32 *bufs; + u32 gen; + u16 produce; + u16 consume; +} vmxnet3_tx_ring; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u64 next; + u32 gen; +} vmxnet3_tx_comp_ring; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u16 size; + clib_spinlock_t lock; + + vmxnet3_tx_desc *tx_desc; + vmxnet3_tx_comp *tx_comp; + vmxnet3_tx_ring tx_ring; + vmxnet3_tx_comp_ring tx_comp_ring; +} vmxnet3_txq_t; + +typedef CLIB_PACKED (struct + { + vmxnet3_queues queues; vmxnet3_shared shared; + }) vmxnet3_dma; + +typedef struct +{ + CLIB_CACHE_LINE_ALIGN_MARK (cacheline0); + u32 flags; + u32 per_interface_next_index; + + u32 dev_instance; + u32 sw_if_index; + u32 hw_if_index; + vlib_pci_dev_handle_t pci_dev_handle; + vlib_pci_addr_t pci_addr; + void *bar[2]; + + /* queues */ + vmxnet3_rxq_t *rxqs; + vmxnet3_txq_t *txqs; + + u16 num_tx_queues; + u16 num_rx_queues; + u16 num_intrs; + + u8 version; + u8 mac_addr[6]; + + /* error */ + clib_error_t *error; + + vmxnet3_dma *dma; + +} vmxnet3_device_t; + +typedef struct +{ + vmxnet3_device_t *devices; + vlib_physmem_region_index_t physmem_region; + u32 physmem_region_alloc; + u16 msg_id_base; +} vmxnet3_main_t; + +extern vmxnet3_main_t vmxnet3_main; + +typedef struct +{ + vlib_pci_addr_t addr; + u32 enable_elog; + u16 rxq_size; + u16 txq_size; + /* return */ + i32 rv; + u32 sw_if_index; + clib_error_t *error; +} vmxnet3_create_if_args_t; + +typedef struct +{ + u32 next_index; + u32 hw_if_index; + vlib_buffer_t buffer; +} vmxnet3_input_trace_t; + +void vmxnet3_create_if (vlib_main_t * vm, vmxnet3_create_if_args_t * args); +void vmxnet3_delete_if (vlib_main_t * vm, vmxnet3_device_t * ad); + +extern clib_error_t *vmxnet3_plugin_api_hookup (vlib_main_t * vm); +extern vlib_node_registration_t vmxnet3_input_node; +extern vnet_device_class_t vmxnet3_device_class; + +/* format.c */ +format_function_t format_vmxnet3_device; +format_function_t format_vmxnet3_device_name; +format_function_t format_vmxnet3_input_trace; + +static_always_inline void +vmxnet3_reg_write (vmxnet3_device_t * vd, u8 bar, u32 addr, u32 val) +{ + *(volatile u32 *) ((u8 *) vd->bar[bar] + addr) = val; +} + +static_always_inline u32 +vmxnet3_reg_read (vmxnet3_device_t * vd, u8 bar, u32 addr) +{ + return *(volatile u32 *) (vd->bar[bar] + addr); +} + +static_always_inline uword +vmxnet3_dma_addr (vlib_main_t * vm, vmxnet3_device_t * vd, void *p) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + + return (vd->flags & VMXNET3_DEVICE_F_IOVA) ? pointer_to_uword (p) : + vlib_physmem_virtual_to_physical (vm, vmxm->physmem_region, p); +} + +static_always_inline void +vmxnet3_rx_ring_advance_produce (vmxnet3_rxq_t * rxq, vmxnet3_rx_ring * ring) +{ + ring->produce++; + if (PREDICT_FALSE (ring->produce == rxq->size)) + { + ring->produce = 0; + ring->gen ^= VMXNET3_RXF_GEN; + } +} + +static_always_inline clib_error_t * +vmxnet3_rxq_refill_ring0 (vlib_main_t * vm, vmxnet3_device_t * vd, + vmxnet3_rxq_t * rxq) +{ + vmxnet3_rx_desc *rxd; + u16 n_refill, n_alloc; + vmxnet3_rx_ring *ring; + + ring = &rxq->rx_ring[0]; + n_refill = rxq->size - ring->fill; + + if (PREDICT_TRUE (n_refill <= VMXNET3_INPUT_REFILL_THRESHOLD)) + return 0; + + n_alloc = + vlib_buffer_alloc_to_ring (vm, ring->bufs, ring->produce, rxq->size, + n_refill); + if (PREDICT_FALSE (n_alloc != n_refill)) + { + if (n_alloc) + vlib_buffer_free_from_ring (vm, ring->bufs, ring->produce, rxq->size, + n_alloc); + return clib_error_return (0, "buffer alloc failed"); + } + + while (n_alloc) + { + rxd = &rxq->rx_desc[0][ring->produce]; + rxd->address = + vlib_get_buffer_data_physical_address (vm, ring->bufs[ring->produce]); + rxd->flags = ring->gen | VLIB_BUFFER_DATA_SIZE; + + vmxnet3_rx_ring_advance_produce (rxq, ring); + ring->fill++; + n_alloc--; + } + + vmxnet3_reg_write (vd, 0, VMXNET3_REG_RXPROD, ring->produce); + + return 0; +} + +static_always_inline clib_error_t * +vmxnet3_rxq_refill_ring1 (vlib_main_t * vm, vmxnet3_device_t * vd, + vmxnet3_rxq_t * rxq) +{ + vmxnet3_rx_desc *rxd; + u16 n_refill, n_alloc; + vmxnet3_rx_ring *ring; + + ring = &rxq->rx_ring[1]; + n_refill = rxq->size - ring->fill; + + if (PREDICT_TRUE (n_refill <= VMXNET3_INPUT_REFILL_THRESHOLD)) + return 0; + + n_alloc = + vlib_buffer_alloc_to_ring (vm, ring->bufs, ring->produce, rxq->size, + n_refill); + if (PREDICT_FALSE (n_alloc != n_refill)) + { + if (n_alloc) + vlib_buffer_free_from_ring (vm, ring->bufs, ring->produce, rxq->size, + n_alloc); + return clib_error_return (0, "buffer alloc failed"); + } + + while (n_alloc) + { + rxd = &rxq->rx_desc[1][ring->produce]; + rxd->address = + vlib_get_buffer_data_physical_address (vm, ring->bufs[ring->produce]); + rxd->flags = ring->gen | VLIB_BUFFER_DATA_SIZE | VMXNET3_RXF_BTYPE; + + vmxnet3_rx_ring_advance_produce (rxq, ring); + ring->fill++; + n_alloc--; + } + + vmxnet3_reg_write (vd, 0, VMXNET3_REG_RXPROD2, ring->produce); + + return 0; +} + +#endif /* __included_vmnet_vmnet_h__ */ +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3_all_api_h.h b/src/plugins/vmxnet3/vmxnet3_all_api_h.h new file mode 100644 index 00000000000..7dee01bf417 --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3_all_api_h.h @@ -0,0 +1,26 @@ +/* + *------------------------------------------------------------------ + * 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 + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3_api.c b/src/plugins/vmxnet3/vmxnet3_api.c new file mode 100644 index 00000000000..e0c2cfa6cf0 --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3_api.c @@ -0,0 +1,252 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include + +#include + +#include +#include + +/* define message IDs */ +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include +#undef vl_printfun + +/* get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#include + +#define foreach_vmxnet3_plugin_api_msg \ +_(VMXNET3_CREATE, vmxnet3_create) \ +_(VMXNET3_DELETE, vmxnet3_delete) \ +_(VMXNET3_DUMP, vmxnet3_dump) + +static void +vl_api_vmxnet3_create_t_handler (vl_api_vmxnet3_create_t * mp) +{ + vlib_main_t *vm = vlib_get_main (); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vl_api_vmxnet3_create_reply_t *rmp; + vmxnet3_create_if_args_t args; + int rv; + + memset (&args, 0, sizeof (vmxnet3_create_if_args_t)); + + args.enable_elog = ntohl (mp->enable_elog); + args.addr.as_u32 = ntohl (mp->pci_addr); + args.rxq_size = ntohs (mp->rxq_size); + args.txq_size = ntohs (mp->txq_size); + + vmxnet3_create_if (vm, &args); + rv = args.rv; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_VMXNET3_CREATE_REPLY + vmxm->msg_id_base, + ({ + rmp->sw_if_index = ntohl (args.sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void +vl_api_vmxnet3_delete_t_handler (vl_api_vmxnet3_delete_t * mp) +{ + vlib_main_t *vm = vlib_get_main (); + vnet_main_t *vnm = vnet_get_main (); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vl_api_vmxnet3_delete_reply_t *rmp; + vmxnet3_device_t *vd; + vnet_hw_interface_t *hw; + int rv = 0; + + hw = vnet_get_sup_hw_interface (vnm, htonl (mp->sw_if_index)); + if (hw == NULL || vmxnet3_device_class.index != hw->dev_class_index) + { + rv = VNET_API_ERROR_INVALID_INTERFACE; + goto reply; + } + + vd = pool_elt_at_index (vmxm->devices, hw->dev_instance); + + vmxnet3_delete_if (vm, vd); + +reply: + REPLY_MACRO (VL_API_VMXNET3_DELETE_REPLY + vmxm->msg_id_base); +} + +static void +send_vmxnet3_details (vl_api_registration_t * reg, vmxnet3_device_t * vd, + u16 rx_qid, vmxnet3_rxq_t * rxq, u16 tx_qid, + vmxnet3_txq_t * txq, vnet_sw_interface_t * swif, + u8 * interface_name, u32 context) +{ + vl_api_vmxnet3_details_t *mp; + vnet_main_t *vnm = vnet_get_main (); + vmxnet3_main_t *vmxm = &vmxnet3_main; + vnet_hw_interface_t *hwif; + vmxnet3_rx_ring *ring; + u16 rid; + + hwif = vnet_get_sup_hw_interface (vnm, swif->sw_if_index); + + mp = vl_msg_api_alloc (sizeof (*mp)); + memset (mp, 0, sizeof (*mp)); + + mp->_vl_msg_id = htons (VL_API_VMXNET3_DETAILS + vmxm->msg_id_base); + mp->context = context; + + mp->sw_if_index = htonl (swif->sw_if_index); + strncpy ((char *) mp->if_name, + (char *) interface_name, ARRAY_LEN (mp->if_name) - 1); + + if (hwif->hw_address) + memcpy (mp->hw_addr, hwif->hw_address, ARRAY_LEN (mp->hw_addr)); + + mp->version = vd->version; + mp->pci_addr = ntohl (vd->pci_addr.as_u32); + + mp->rx_qsize = htons (rxq->size); + mp->rx_next = htons (rxq->rx_comp_ring.next); + for (rid = 0; rid < VMXNET3_RX_RING_SIZE; rid++) + { + ring = &rxq->rx_ring[rid]; + mp->rx_fill[rid] = htons (ring->fill); + mp->rx_produce[rid] = htons (ring->produce); + mp->rx_consume[rid] = htons (ring->consume); + } + mp->tx_qsize = htons (txq->size); + mp->tx_next = htons (txq->tx_comp_ring.next); + mp->tx_produce = htons (txq->tx_ring.produce); + mp->tx_consume = htons (txq->tx_ring.consume); + + mp->admin_up_down = (swif->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? 1 : 0; + + vl_api_send_msg (reg, (u8 *) mp); +} + +/** + * @brief Message handler for vmxnet3_dump API. + * @param mp vl_api_vmxnet3_dump_t * mp the api message + */ +static void +vl_api_vmxnet3_dump_t_handler (vl_api_vmxnet3_dump_t * mp) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + vnet_main_t *vnm = vnet_get_main (); + vnet_sw_interface_t *swif; + vmxnet3_device_t *vd; + u8 *if_name = 0; + vl_api_registration_t *reg; + vmxnet3_rxq_t *rxq; + vmxnet3_txq_t *txq; + u16 qid = 0; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + /* *INDENT-OFF* */ + pool_foreach (vd, vmxm->devices, + ({ + swif = vnet_get_sw_interface (vnm, vd->sw_if_index); + if_name = format (if_name, "%U%c", format_vnet_sw_interface_name, vnm, + swif, 0); + rxq = vec_elt_at_index (vd->rxqs, qid); + txq = vec_elt_at_index (vd->txqs, qid); + send_vmxnet3_details (reg, vd, qid, rxq, qid, txq, swif, if_name, + mp->context); + _vec_len (if_name) = 0; + })); + /* *INDENT-ON* */ + + vec_free (if_name); +} + +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (vmxnet3_main_t * vmxm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + vmxm->msg_id_base); + foreach_vl_msg_name_crc_vmxnet3; +#undef _ +} + +/* set tup the API message handling tables */ +clib_error_t * +vmxnet3_plugin_api_hookup (vlib_main_t * vm) +{ + vmxnet3_main_t *vmxm = &vmxnet3_main; + api_main_t *am = &api_main; + u8 *name; + + /* construct the API name */ + name = format (0, "vmxnet3_%08x%c", api_version, 0); + + /* ask for a correctly-sized block of API message decode slots */ + vmxm->msg_id_base = vl_msg_api_get_msg_ids + ((char *) name, VL_MSG_FIRST_AVAILABLE); + +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + vmxm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vmxnet3_plugin_api_msg; +#undef _ + + /* set up the (msg_name, crc, message-id) table */ + setup_message_id_table (vmxm, am); + + vec_free (name); + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3_msg_enum.h b/src/plugins/vmxnet3/vmxnet3_msg_enum.h new file mode 100644 index 00000000000..3086fb3bfc4 --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3_msg_enum.h @@ -0,0 +1,39 @@ +/* + *------------------------------------------------------------------ + * 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. + *------------------------------------------------------------------ + */ + +#ifndef _VMXNET3_MSG_ENUM_H_ +#define _VMXNET3_MSG_ENUM_H_ + +#include + +#define vl_msg_id(n,h) n, +typedef enum +{ +#include + VL_MSG_FIRST_AVAILABLE, +} vl_msg_id_t; +#undef vl_msg_id + +#endif /* VMXNET3_MSG_ENUM_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/vmxnet3/vmxnet3_test.c b/src/plugins/vmxnet3/vmxnet3_test.c new file mode 100644 index 00000000000..b08f61b0bd9 --- /dev/null +++ b/src/plugins/vmxnet3/vmxnet3_test.c @@ -0,0 +1,331 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define __plugin_msg_base vmxnet3_test_main.msg_id_base +#include + +/* declare message IDs */ +#include + +/* Get CRC codes of the messages defined outside of this plugin */ +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +/* define message structures */ +#define vl_typedefs +#include +#include +#undef vl_typedefs + +/* declare message handlers for each api */ +#define vl_endianfun +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* get API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vp_api_version + +typedef struct +{ + /* API message ID base */ + u16 msg_id_base; + u32 ping_id; + vat_main_t *vat_main; +} vmxnet3_test_main_t; + +vmxnet3_test_main_t vmxnet3_test_main; + +#define foreach_standard_reply_retval_handler \ +_(vmxnet3_delete_reply) + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = vmxnet3_test_main.vat_main; \ + i32 retval = ntohl(mp->retval); \ + if (vam->async_mode) { \ + vam->async_errors += (retval < 0); \ + } else { \ + vam->retval = retval; \ + vam->result_ready = 1; \ + } \ + } +foreach_standard_reply_retval_handler; +#undef _ + +#define foreach_vpe_api_reply_msg \ +_(VMXNET3_CREATE_REPLY, vmxnet3_create_reply) \ +_(VMXNET3_DELETE_REPLY, vmxnet3_delete_reply) \ +_(VMXNET3_DETAILS, vmxnet3_details) + +/* vmxnet3 create API */ +static int +api_vmxnet3_create (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_vmxnet3_create_t *mp; + vmxnet3_create_if_args_t args; + int ret; + u32 x[4]; + + memset (&args, 0, sizeof (vmxnet3_create_if_args_t)); + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%x:%x:%x.%x", &x[0], &x[1], &x[2], &x[3])) + { + args.addr.domain = x[0]; + args.addr.bus = x[1]; + args.addr.slot = x[2]; + args.addr.function = x[3]; + } + else if (unformat (i, "elog")) + args.enable_elog = 1; + else if (unformat (i, "rx-queue-size %u", &args.rxq_size)) + ; + else if (unformat (i, "tx-queue-size %u", &args.txq_size)) + ; + else + { + clib_warning ("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + M (VMXNET3_CREATE, mp); + + mp->pci_addr = clib_host_to_net_u32 (args.addr.as_u32); + mp->enable_elog = clib_host_to_net_u16 (args.enable_elog); + mp->rxq_size = clib_host_to_net_u16 (args.rxq_size); + mp->txq_size = clib_host_to_net_u16 (args.txq_size); + + S (mp); + W (ret); + + return ret; +} + +/* vmxnet3-create reply handler */ +static void +vl_api_vmxnet3_create_reply_t_handler (vl_api_vmxnet3_create_reply_t * mp) +{ + vat_main_t *vam = vmxnet3_test_main.vat_main; + i32 retval = ntohl (mp->retval); + + if (retval == 0) + { + fformat (vam->ofp, "created vmxnet3 with sw_if_index %d\n", + ntohl (mp->sw_if_index)); + } + + vam->retval = retval; + vam->result_ready = 1; + vam->regenerate_interface_table = 1; +} + +/* vmxnet3 delete API */ +static int +api_vmxnet3_delete (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_vmxnet3_delete_t *mp; + u32 sw_if_index = 0; + u8 index_defined = 0; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "sw_if_index %u", &sw_if_index)) + index_defined = 1; + else + { + clib_warning ("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + if (!index_defined) + { + errmsg ("missing sw_if_index\n"); + return -99; + } + + M (VMXNET3_DELETE, mp); + + mp->sw_if_index = clib_host_to_net_u32 (sw_if_index); + + S (mp); + W (ret); + + return ret; +} + +static int +api_vmxnet3_dump (vat_main_t * vam) +{ + vmxnet3_test_main_t *vxm = &vmxnet3_test_main; + vl_api_vmxnet3_dump_t *mp; + vl_api_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for vmxnet3_dump"); + return -99; + } + + M (VMXNET3_DUMP, mp); + S (mp); + + /* Use a control ping for synchronization */ + mp_ping = vl_msg_api_alloc_as_if_client (sizeof (*mp_ping)); + mp_ping->_vl_msg_id = htons (vxm->ping_id); + mp_ping->client_index = vam->my_client_index; + + fformat (vam->ofp, "Sending ping id=%d\n", vxm->ping_id); + + vam->result_ready = 0; + S (mp_ping); + + W (ret); + return ret; +} + +static void +vl_api_vmxnet3_details_t_handler (vl_api_vmxnet3_details_t * mp) +{ + vat_main_t *vam = vmxnet3_test_main.vat_main; + u32 pci_addr = ntohl (mp->pci_addr); + + fformat (vam->ofp, "%s: sw_if_index %u mac %U\n" + " version: %u\n" + " PCI Address: %U\n" + " RX completion next index %u" + " RX Queue %u\n" + " ring 0 size %u fill %u consume %u produce %u\n" + " ring 1 size %u fill %u consume %u produce %u\n" + " TX completion next index %u" + " TX Queue %u\n" + " size %u consume %u produce %u\n" + " state %s\n", + mp->if_name, ntohl (mp->sw_if_index), format_ethernet_address, + mp->hw_addr, mp->version, + format_vlib_pci_addr, &pci_addr, + ntohs (mp->rx_next), + ntohs (mp->rx_qid), + ntohs (mp->rx_qsize), ntohs (mp->rx_fill[0]), + ntohs (mp->rx_consume[0]), + ntohs (mp->rx_produce[0]), + ntohs (mp->rx_qsize), ntohs (mp->rx_fill[1]), + ntohs (mp->rx_consume[1]), + ntohs (mp->rx_produce[1]), + ntohs (mp->tx_next), + ntohs (mp->tx_qid), + ntohs (mp->tx_qsize), ntohs (mp->tx_consume), + ntohs (mp->tx_produce), mp->admin_up_down ? "up" : "down"); +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#define foreach_vpe_api_msg \ +_(vmxnet3_create, " [rx-queue-size ] " \ + "[tx-queue-size ]") \ +_(vmxnet3_delete, "") \ +_(vmxnet3_dump, "") + +static void +vmxnet3_vat_api_hookup (vat_main_t * vam) +{ + vmxnet3_test_main_t *vxm __attribute__ ((unused)) = &vmxnet3_test_main; +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + vxm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vpe_api_reply_msg; +#undef _ + +#define _(n,h) \ + hash_set_mem (vam->function_by_name, #n, api_##n); + foreach_vpe_api_msg; +#undef _ + +#define _(n,h) hash_set_mem (vam->help_by_name, #n, h); + foreach_vpe_api_msg; +#undef _ +} + +clib_error_t * +vat_plugin_register (vat_main_t * vam) +{ + vmxnet3_test_main_t *vxm = &vmxnet3_test_main; + u8 *name; + + vxm->vat_main = vam; + + name = format (0, "vmxnet3_%08x%c", api_version, 0); + vxm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + /* Get the control ping ID */ +#define _(id,n,crc) \ + const char *id ## _CRC __attribute__ ((unused)) = #n "_" #crc; + foreach_vl_msg_name_crc_vpe; +#undef _ + vxm->ping_id = vl_msg_api_get_msg_index ((u8 *) (VL_API_CONTROL_PING_CRC)); + + if (vxm->msg_id_base != (u16) ~ 0) + vmxnet3_vat_api_hookup (vam); + + vec_free (name); + + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ -- 2.16.6