diff options
Diffstat (limited to 'src/plugins/dev_armada/pp2')
-rw-r--r-- | src/plugins/dev_armada/pp2/format.c | 176 | ||||
-rw-r--r-- | src/plugins/dev_armada/pp2/init.c | 343 | ||||
-rw-r--r-- | src/plugins/dev_armada/pp2/port.c | 280 | ||||
-rw-r--r-- | src/plugins/dev_armada/pp2/pp2.h | 144 | ||||
-rw-r--r-- | src/plugins/dev_armada/pp2/queue.c | 48 | ||||
-rw-r--r-- | src/plugins/dev_armada/pp2/rx.c | 158 | ||||
-rw-r--r-- | src/plugins/dev_armada/pp2/tx.c | 83 |
7 files changed, 1232 insertions, 0 deletions
diff --git a/src/plugins/dev_armada/pp2/format.c b/src/plugins/dev_armada/pp2/format.c new file mode 100644 index 00000000000..37d482b5ce8 --- /dev/null +++ b/src/plugins/dev_armada/pp2/format.c @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <dev_armada/musdk.h> +#include <dev_armada/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_pp2_ppio_link_info (u8 *s, va_list *args) +{ + struct pp2_ppio_link_info *li = va_arg (*args, struct pp2_ppio_link_info *); + + char *port_duplex[] = { + [MV_NET_LINK_DUPLEX_HALF] = "half", + [MV_NET_LINK_DUPLEX_FULL] = "full", + }; + + u32 port_speeds[] = { + [MV_NET_LINK_SPEED_10] = 10, [MV_NET_LINK_SPEED_100] = 100, + [MV_NET_LINK_SPEED_1000] = 1000, [MV_NET_LINK_SPEED_2500] = 2500, + [MV_NET_LINK_SPEED_10000] = 10000, + }; + + char *port_phy_modes[] = { + [MV_NET_PHY_MODE_NONE] = "NONE", + [MV_NET_PHY_MODE_MII] = "MII", + [MV_NET_PHY_MODE_GMII] = "GMII", + [MV_NET_PHY_MODE_SGMII] = "SGMII", + [MV_NET_PHY_MODE_TBI] = "TBI", + [MV_NET_PHY_MODE_REVMII] = "REVMII", + [MV_NET_PHY_MODE_RMII] = "RMII", + [MV_NET_PHY_MODE_RGMII] = "RGMII", + [MV_NET_PHY_MODE_RGMII_ID] = "RGMII_ID", + [MV_NET_PHY_MODE_RGMII_RXID] = "RGMII_RXID", + [MV_NET_PHY_MODE_RGMII_TXID] = "RGMII_TXID", + [MV_NET_PHY_MODE_RTBI] = "RTBI", + [MV_NET_PHY_MODE_SMII] = "SMII", + [MV_NET_PHY_MODE_XGMII] = "XGMII", + [MV_NET_PHY_MODE_MOCA] = "MOCA", + [MV_NET_PHY_MODE_QSGMII] = "QSGMII", + [MV_NET_PHY_MODE_XAUI] = "XAUI", + [MV_NET_PHY_MODE_RXAUI] = "RXAUI", + [MV_NET_PHY_MODE_KR] = "KR", + }; + + s = + format (s, "duplex %s speed %d up %d phy_mode %s", port_duplex[li->duplex], + port_speeds[li->speed], li->up, port_phy_modes[li->phy_mode]); + + return s; +} + +u8 * +format_mvpp2_port_status (u8 *s, va_list *args) +{ + vnet_dev_format_args_t __clib_unused *a = + va_arg (*args, vnet_dev_format_args_t *); + vnet_dev_port_t *port = va_arg (*args, vnet_dev_port_t *); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + struct pp2_ppio_link_info li = {}; + + if (mp->ppio == 0 || pp2_ppio_get_link_info (mp->ppio, &li)) + return format (s, "link info not available"); + + return format (s, "%U", format_pp2_ppio_link_info, &li); +} + +u8 * +format_mvpp2_dev_info (u8 *s, va_list *args) +{ + vnet_dev_format_args_t __clib_unused *a = + va_arg (*args, vnet_dev_format_args_t *); + vnet_dev_t *dev = va_arg (*args, vnet_dev_t *); + mvpp2_device_t *md = vnet_dev_get_data (dev); + + format (s, "pp_id is %u", md->pp_id); + 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_mvpp2_rx_desc (u8 *s, va_list *args) + +{ + struct pp2_ppio_desc *d = va_arg (*args, struct pp2_ppio_desc *); + u32 indent = format_get_indent (s); + u32 r32; + +#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; + return s; +} + +u8 * +format_mvpp2_rx_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 *); + mvpp2_rx_trace_t *t = va_arg (*args, mvpp2_rx_trace_t *); + vnet_main_t *vnm = vnet_get_main (); + u32 hw_if_index = t->rxq->port->intf.hw_if_index; + vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); + u32 indent = format_get_indent (s); + struct pp2_ppio_desc *d = &t->desc; + + s = format (s, "pp2: %v (%d) next-node %U", hi->name, hw_if_index, + format_vlib_next_node_name, vm, node->index, t->rxq->next_index); + s = format (s, "\n%U%U", format_white_space, indent + 2, + format_mvpp2_rx_desc, d); + + return s; +} diff --git a/src/plugins/dev_armada/pp2/init.c b/src/plugins/dev_armada/pp2/init.c new file mode 100644 index 00000000000..38ff32d8f53 --- /dev/null +++ b/src/plugins/dev_armada/pp2/init.c @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/plugin/plugin.h> +#include <vpp/app/version.h> + +#include <linux/if.h> +#include <sys/ioctl.h> + +#define MV_SYS_DMA_MEM_SZ (2 << 20) + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "init", +}; + +static int num_pp2_in_use = 0; +static int dma_mem_initialized = 0; +static int global_pp2_initialized = 0; + +#define _(f, n, s, d) \ + { .name = #n, .desc = d, .severity = VL_COUNTER_SEVERITY_##s }, + +vlib_error_desc_t mvpp2_rx_node_counters[] = { foreach_mvpp2_rx_node_counter }; +vlib_error_desc_t mvpp2_tx_node_counters[] = { foreach_mvpp2_tx_node_counter }; +#undef _ + +vnet_dev_node_t mvpp2_rx_node = { + .error_counters = mvpp2_rx_node_counters, + .n_error_counters = ARRAY_LEN (mvpp2_rx_node_counters), + .format_trace = format_mvpp2_rx_trace, +}; + +vnet_dev_node_t mvpp2_tx_node = { + .error_counters = mvpp2_tx_node_counters, + .n_error_counters = ARRAY_LEN (mvpp2_tx_node_counters), +}; + +static u8 * +mvpp2_probe (vlib_main_t *vm, vnet_dev_bus_index_t bus_index, void *dev_info) +{ + vnet_dev_bus_platform_device_info_t *di = dev_info; + + if (clib_dt_node_is_compatible (di->node, "marvell,armada-7k-pp22")) + return format (0, "Marvell Armada Packet Processor v2.2"); + return 0; +} +static void +mvpp2_global_deinit (vlib_main_t *vm, vnet_dev_t *dev) +{ + mvpp2_device_t *md = vnet_dev_get_data (dev); + log_debug (dev, ""); + if (--num_pp2_in_use == 0) + { + if (global_pp2_initialized) + { + for (u32 i = 0; i < ARRAY_LEN (md->thread); i++) + if (md->thread[i].bpool) + { + pp2_bpool_deinit (md->thread[i].bpool); + md->thread[i].bpool = 0; + } + for (u32 i = 0; i < ARRAY_LEN (md->hif); i++) + if (md->hif[i]) + { + pp2_hif_deinit (md->hif[i]); + md->hif[i] = 0; + } + + pp2_deinit (); + global_pp2_initialized = 0; + } + if (dma_mem_initialized) + { + mv_sys_dma_mem_destroy (); + log_debug (0, "mv_sys_dma_mem_destroy()"); + dma_mem_initialized = 0; + } + } +} + +static void +mvpp2_deinit (vlib_main_t *vm, vnet_dev_t *dev) +{ + log_debug (dev, ""); + mvpp2_global_deinit (vm, dev); +} + +static vnet_dev_rv_t +mvpp2_global_init (vlib_main_t *vm, vnet_dev_t *dev) +{ + mvpp2_device_t *md = vnet_dev_get_data (dev); + vnet_dev_rv_t rv = VNET_DEV_OK; + int mrv; + u16 free_hifs, free_bpools; + u16 n_threads = vlib_get_n_threads (); + + struct pp2_init_params init_params = { + .hif_reserved_map = 0xf, + .bm_pool_reserved_map = 0x7, + }; + + if (num_pp2_in_use++) + return rv; + + mrv = mv_sys_dma_mem_init (MV_SYS_DMA_MEM_SZ); + if (mrv < 0) + { + log_err (0, "mv_sys_dma_mem_init failed, err %d", mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + + dma_mem_initialized = 1; + log_debug (0, "mv_sys_dma_mem_init(%u) ok", MV_SYS_DMA_MEM_SZ); + + if ((mrv = pp2_init (&init_params))) + { + log_err (dev, "pp2_init failed, err %d", mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + + log_debug (dev, "pp2_init() ok"); + + free_hifs = pow2_mask (MVPP2_NUM_HIFS) ^ init_params.hif_reserved_map; + free_bpools = + pow2_mask (MVPP2_NUM_BPOOLS) ^ init_params.bm_pool_reserved_map; + + if (n_threads > count_set_bits (free_hifs)) + { + log_err (dev, "no enough HIFs (needed %u available %u)", n_threads, + count_set_bits (free_hifs)); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + + for (u32 i = 0; i < n_threads; i++) + { + char match[16]; + u8 index; + struct pp2_hif_params hif_params = { + .match = match, + .out_size = 2048, + }; + struct pp2_bpool_params bpool_params = { + .match = match, + .buff_len = vlib_buffer_get_default_data_size (vm), + }; + + index = get_lowest_set_bit_index (free_hifs); + free_hifs ^= 1 << index; + snprintf (match, sizeof (match), "hif-%u", index); + + mrv = pp2_hif_init (&hif_params, md->hif + i); + if (mrv < 0) + { + log_err (dev, "pp2_hif_init failed for hif %u thread %u, err %d", + index, i, mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + log_debug (dev, "pp2_hif_init(hif %u, thread %u) ok", index, i); + + index = get_lowest_set_bit_index (free_bpools); + free_bpools ^= 1 << index; + snprintf (match, sizeof (match), "pool-%u:%u", md->pp_id, index); + + mrv = pp2_bpool_init (&bpool_params, &md->thread[i].bpool); + if (mrv < 0) + { + log_err (dev, "pp2_bpool_init failed for bpool %u thread %u, err %d", + index, i, mrv); + rv = VNET_DEV_ERR_INIT_FAILED; + goto done; + } + log_debug (dev, "pp2_bpool_init(bpool %u, thread %u) pool-%u:%u ok", + index, i, md->thread[i].bpool->pp2_id, + md->thread[i].bpool->id); + for (u32 j = 0; j < ARRAY_LEN (md->thread[0].bre); j++) + md->thread[i].bre[j].bpool = md->thread[i].bpool; + } + +done: + return rv; +} + +static vnet_dev_rv_t +mvpp2_init (vlib_main_t *vm, vnet_dev_t *dev) +{ + mvpp2_device_t *md = vnet_dev_get_data (dev); + vnet_dev_rv_t rv = VNET_DEV_OK; + vnet_dev_bus_platform_device_data_t *dd = vnet_dev_get_bus_data (dev); + clib_dt_node_t *sc; + int pp_id = -1; + + if (!clib_dt_node_is_compatible (dd->node, "marvell,armada-7k-pp22")) + return VNET_DEV_ERR_NOT_SUPPORTED; + + sc = clib_dt_dereference_node (dd->node, "marvell,system-controller"); + + if (sc && vec_len (sc->path) > strlen ("/cpX/")) + { + if (strncmp ((char *) sc->path, "/cp0/", 4) == 0) + pp_id = 0; + else if (strncmp ((char *) sc->path, "/cp1/", 4) == 0) + pp_id = 1; + } + + if (pp_id < 0) + return VNET_DEV_ERR_UNKNOWN_DEVICE; + + if ((mvpp2_global_init (vm, dev)) != VNET_DEV_OK) + return rv; + + md->pp_id = pp_id; + + vec_foreach_pointer (cn, dd->node->child_nodes) + { + clib_dt_property_t *p; + char netdev_name[IFNAMSIZ]; + struct ifreq s = {}; + u8 ppio_id; + int fd, srv; + + p = clib_dt_get_node_property_by_name (cn, "port-id"); + + if (!clib_dt_proprerty_is_u32 (p)) + continue; + + ppio_id = clib_dt_proprerty_get_u32 (p); + log_debug (dev, "found port with ppio id %u", ppio_id); + + if (pp2_ppio_available (md->pp_id, ppio_id) == 0) + continue; + + if (pp2_netdev_get_ifname (md->pp_id, ppio_id, netdev_name) < 0) + { + log_warn (dev, "failed to get ifname, skipping port %u ", ppio_id); + continue; + } + + srv = -1; + if ((fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP)) >= 0) + { + strcpy (s.ifr_name, netdev_name); + srv = ioctl (fd, SIOCGIFHWADDR, &s); + close (fd); + } + + if (srv < 0) + { + log_warn (dev, "unable to get hw address, skipping port %u", + ppio_id); + continue; + } + + log_debug (dev, "adding ppio %u (netdev name %s, hwaddr %U)", ppio_id, + netdev_name, format_ethernet_address, s.ifr_addr.sa_data); + + mvpp2_port_t mvpp2_port = { + .ppio_id = ppio_id, + }; + + vnet_dev_port_add_args_t port_add_args = { + .port = { + .attr = { + .type = VNET_DEV_PORT_TYPE_ETHERNET, + .max_rx_queues = PP2_PPIO_MAX_NUM_INQS, + .max_tx_queues = PP2_PPIO_MAX_NUM_OUTQS, + .max_supported_rx_frame_size = 9216, + }, + .ops = { + .init = mvpp2_port_init, + .deinit = mvpp2_port_deinit, + .start = mvpp2_port_start, + .stop = mvpp2_port_stop, + .config_change = mvpp2_port_cfg_change, + .config_change_validate = mvpp2_port_cfg_change_validate, + .format_status = format_mvpp2_port_status, + }, + .data_size = sizeof (mvpp2_port_t), + .initial_data = &mvpp2_port, + }, + .rx_node = &mvpp2_rx_node, + .tx_node = &mvpp2_tx_node, + .rx_queue = { + .config = { + .data_size = sizeof (mvpp2_rxq_t), + .default_size = 512, + .multiplier = 32, + .min_size = 32, + .max_size = 4096, + .size_is_power_of_two = 1, + }, + }, + .tx_queue = { + .config = { + .data_size = sizeof (mvpp2_txq_t), + .default_size = 512, + .multiplier = 32, + .min_size = 32, + .max_size = 4096, + .size_is_power_of_two = 1, + }, + .ops = { + .alloc = mvpp2_txq_alloc, + .free = mvpp2_txq_free, + }, + }, + }; + + vnet_dev_set_hw_addr_eth_mac (&port_add_args.port.attr.hw_addr, + (u8 *) s.ifr_addr.sa_data); + + vnet_dev_port_add (vm, dev, ppio_id, &port_add_args); + } + + if (rv != VNET_DEV_OK) + mvpp2_deinit (vm, dev); + return rv; +} + +VNET_DEV_REGISTER_DRIVER (pp2) = { + .name = "mvpp2", + .bus = PLATFORM_BUS_NAME, + .device_data_sz = sizeof (mvpp2_device_t), + .ops = { + .init = mvpp2_init, + .deinit = mvpp2_deinit, + .probe = mvpp2_probe, + .format_info = format_mvpp2_dev_info, + }, +}; diff --git a/src/plugins/dev_armada/pp2/port.c b/src/plugins/dev_armada/pp2/port.c new file mode 100644 index 00000000000..8e785e5e0e4 --- /dev/null +++ b/src/plugins/dev_armada/pp2/port.c @@ -0,0 +1,280 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "pp2-port", +}; + +vnet_dev_rv_t +mvpp2_port_init (vlib_main_t *vm, vnet_dev_port_t *port) +{ + vnet_dev_t *dev = port->dev; + mvpp2_device_t *md = vnet_dev_get_data (dev); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vnet_dev_rv_t rv = VNET_DEV_OK; + struct pp2_ppio_link_info li; + char match[16]; + int mrv; + + log_debug (port->dev, ""); + + snprintf (match, sizeof (match), "ppio-%d:%d", md->pp_id, port->port_id); + + struct pp2_ppio_params ppio_params = { + .match = match, + .type = PP2_PPIO_T_NIC, + .eth_start_hdr = mp->is_dsa ? PP2_PPIO_HDR_ETH_DSA : PP2_PPIO_HDR_ETH, + .inqs_params = { + .num_tcs = 1, + .tcs_params[0] = { + .pkt_offset = 0, + .num_in_qs = 1, + .inqs_params = &(struct pp2_ppio_inq_params) { .size = 512 }, + .pools[0][0] = md->thread[0].bpool, + }, + }, + }; + + foreach_vnet_dev_port_rx_queue (q, port) + { + struct pp2_ppio_outqs_params *oqs = &ppio_params.outqs_params; + oqs->outqs_params[0].weight = 1; + oqs->outqs_params[0].size = q->size; + oqs->num_outqs++; + } + + mrv = pp2_ppio_init (&ppio_params, &mp->ppio); + if (mrv) + { + rv = VNET_DEV_ERR_INIT_FAILED; + log_err (dev, "port %u ppio '%s' init failed, rv %d", port->port_id, + match, mrv); + goto done; + } + log_debug (dev, "port %u ppio '%s' init ok", port->port_id, match); + + mrv = pp2_ppio_get_link_info (mp->ppio, &li); + if (mrv) + { + rv = VNET_DEV_ERR_INIT_FAILED; + log_err (dev, "failed to get link info for port %u, rv %d", + port->port_id, mrv); + goto done; + } + + log_debug (dev, "port %u %U", port->port_id, format_pp2_ppio_link_info, &li); + +done: + if (rv != VNET_DEV_OK) + mvpp2_port_stop (vm, port); + return rv; +} + +void +mvpp2_port_deinit (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + + log_debug (port->dev, ""); + + if (mp->ppio) + { + pp2_ppio_deinit (mp->ppio); + mp->ppio = 0; + } +} + +void +mvpp2_port_poll (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vnet_dev_t *dev = port->dev; + vnet_dev_port_state_changes_t changes = {}; + struct pp2_ppio_link_info li; + int mrv; + + mrv = pp2_ppio_get_link_info (mp->ppio, &li); + + if (mrv) + { + log_debug (dev, "pp2_ppio_get_link_info: failed, rv %d", mrv); + return; + } + + if (mp->last_link_info.up != li.up) + { + changes.change.link_state = 1; + changes.link_state = li.up != 0; + log_debug (dev, "link state changed to %u", changes.link_state); + } + + if (mp->last_link_info.duplex != li.duplex) + { + changes.change.link_duplex = 1; + changes.full_duplex = li.duplex != 0; + log_debug (dev, "link full duplex changed to %u", changes.full_duplex); + } + + if (mp->last_link_info.speed != li.speed) + { + u32 speeds[] = { + [MV_NET_LINK_SPEED_AN] = 0, + [MV_NET_LINK_SPEED_10] = 10000, + [MV_NET_LINK_SPEED_100] = 100000, + [MV_NET_LINK_SPEED_1000] = 1000000, + [MV_NET_LINK_SPEED_2500] = 2500000, + [MV_NET_LINK_SPEED_10000] = 10000000, + }; + + if (li.speed < ARRAY_LEN (speeds)) + { + changes.change.link_speed = 1; + changes.link_speed = speeds[li.speed]; + log_debug (dev, "link speed changed to %u", changes.link_speed); + } + } + + if (changes.change.any == 0) + return; + + mp->last_link_info = li; + + vnet_dev_port_state_change (vm, port, changes); +} + +vnet_dev_rv_t +mvpp2_port_start (vlib_main_t *vm, vnet_dev_port_t *port) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + int mrv; + + log_debug (port->dev, ""); + + mrv = pp2_ppio_enable (mp->ppio); + if (mrv) + { + log_err (port->dev, "pp2_ppio_enable() failed, rv %d", mrv); + return VNET_DEV_ERR_NOT_READY; + } + + mp->is_enabled = 1; + + vnet_dev_poll_port_add (vm, port, 0.5, mvpp2_port_poll); + + return VNET_DEV_OK; +} + +void +mvpp2_port_stop (vlib_main_t *vm, vnet_dev_port_t *port) +{ + int rv; + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + + log_debug (port->dev, ""); + + if (mp->is_enabled) + { + vnet_dev_poll_port_remove (vm, port, mvpp2_port_poll); + + rv = pp2_ppio_disable (mp->ppio); + if (rv) + log_err (port->dev, "pp2_ppio_disable() failed, rv %d", rv); + + vnet_dev_port_state_change (vm, port, + (vnet_dev_port_state_changes_t){ + .change.link_state = 1, + .change.link_speed = 1, + .link_speed = 0, + .link_state = 0, + }); + mp->is_enabled = 0; + } +} + +vnet_dev_rv_t +mvpp2_port_cfg_change_validate (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_cfg_change_req_t *req) +{ + vnet_dev_rv_t rv = VNET_DEV_OK; + + switch (req->type) + { + case VNET_DEV_PORT_CFG_PROMISC_MODE: + case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR: + case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR: + break; + + default: + rv = VNET_DEV_ERR_NOT_SUPPORTED; + }; + + return rv; +} + +vnet_dev_rv_t +mvpp2_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port, + vnet_dev_port_cfg_change_req_t *req) +{ + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vnet_dev_rv_t rv = VNET_DEV_OK; + eth_addr_t addr; + int mrv; + + switch (req->type) + { + + case VNET_DEV_PORT_CFG_PROMISC_MODE: + mrv = pp2_ppio_set_promisc (mp->ppio, req->promisc); + if (mrv) + { + log_err (port->dev, "pp2_ppio_set_promisc: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_set_promisc: promisc %u", + req->promisc); + break; + + case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR: + clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr)); + mrv = pp2_ppio_add_mac_addr (mp->ppio, addr); + if (mrv) + { + log_err (port->dev, "pp2_ppio_add_mac_addr: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_add_mac_addr: %U added", + format_ethernet_address, &addr); + break; + + case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR: + clib_memcpy (&addr, req->addr.eth_mac, sizeof (addr)); + mrv = pp2_ppio_remove_mac_addr (mp->ppio, addr); + if (mrv) + { + log_err (port->dev, "pp2_ppio_remove_mac_addr: failed, rv %d", mrv); + rv = VNET_DEV_ERR_INTERNAL; + } + else + log_debug (port->dev, "pp2_ppio_remove_mac_addr: %U added", + format_ethernet_address, &addr); + break; + + default: + return VNET_DEV_ERR_NOT_SUPPORTED; + }; + + return rv; +} diff --git a/src/plugins/dev_armada/pp2/pp2.h b/src/plugins/dev_armada/pp2/pp2.h new file mode 100644 index 00000000000..6b12dc737a7 --- /dev/null +++ b/src/plugins/dev_armada/pp2/pp2.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#ifndef _PP2_H_ +#define _PP2_H_ + +#include <vppinfra/clib.h> +#include <vppinfra/error_bootstrap.h> +#include <vppinfra/format.h> +#include <vnet/vnet.h> +#include <vnet/dev/dev.h> + +#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> + +#define MVPP2_NUM_HIFS 9 +#define MVPP2_NUM_BPOOLS 16 +#define MVPP2_MAX_THREADS 4 +#define MRVL_PP2_BUFF_BATCH_SZ 32 + +typedef struct +{ + u8 pp_id; + struct pp2_hif *hif[MVPP2_NUM_HIFS]; + struct + { + struct pp2_bpool *bpool; + struct buff_release_entry bre[MRVL_PP2_BUFF_BATCH_SZ]; + } thread[MVPP2_NUM_BPOOLS]; + +} mvpp2_device_t; + +typedef struct +{ + u8 is_enabled : 1; + u8 is_dsa : 1; + struct pp2_ppio *ppio; + u8 ppio_id; + struct pp2_ppio_link_info last_link_info; +} mvpp2_port_t; + +typedef struct +{ + u16 next; + u16 n_enq; + u32 *buffers; +} mvpp2_txq_t; + +typedef struct +{ +} mvpp2_rxq_t; + +typedef struct +{ + struct pp2_ppio_desc desc; + vnet_dev_rx_queue_t *rxq; +} mvpp2_rx_trace_t; + +/* format.c */ +format_function_t format_pp2_ppio_link_info; +format_function_t format_mvpp2_port_status; +format_function_t format_mvpp2_dev_info; +format_function_t format_mvpp2_rx_trace; +format_function_t format_mvpp2_rx_desc; + +/* port.c */ +vnet_dev_port_op_t mvpp2_port_init; +vnet_dev_port_op_no_rv_t mvpp2_port_deinit; +vnet_dev_port_op_t mvpp2_port_start; +vnet_dev_port_op_no_rv_t mvpp2_port_stop; +vnet_dev_rv_t mvpp2_port_cfg_change (vlib_main_t *, vnet_dev_port_t *, + vnet_dev_port_cfg_change_req_t *); +vnet_dev_rv_t +mvpp2_port_cfg_change_validate (vlib_main_t *, vnet_dev_port_t *, + vnet_dev_port_cfg_change_req_t *); + +/* queue.c */ +vnet_dev_tx_queue_op_t mvpp2_txq_alloc; +vnet_dev_tx_queue_op_no_rv_t mvpp2_txq_free; + +/* inline funcs */ + +#define log_debug(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_DEBUG, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), \ + clib_string_skip_prefix (__func__, "mvpp2_"), ##__VA_ARGS__) +#define log_info(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_INFO, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), 0, ##__VA_ARGS__) +#define log_notice(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_NOTICE, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), 0, ##__VA_ARGS__) +#define log_warn(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_WARNING, mvpp2_log.class, "%U" f, \ + format_vnet_dev_log, (dev), 0, ##__VA_ARGS__) +#define log_err(dev, f, ...) \ + vlib_log (VLIB_LOG_LEVEL_ERR, mvpp2_log.class, "%U" f, format_vnet_dev_log, \ + (dev), 0, ##__VA_ARGS__) + +#define foreach_mvpp2_tx_node_counter \ + _ (NO_FREE_SLOTS, no_free_slots, ERROR, "no free tx slots") \ + _ (PPIO_SEND, ppio_semd, ERROR, "pp2_ppio_send errors") \ + _ (PPIO_GET_NUM_OUTQ_DONE, ppio_get_num_outq_done, ERROR, \ + "pp2_ppio_get_num_outq_done errors") + +typedef enum +{ +#define _(f, n, s, d) MVPP2_TX_NODE_CTR_##f, + foreach_mvpp2_tx_node_counter +#undef _ +} mvpp2_tx_node_counter_t; + +#define foreach_mvpp2_rx_node_counter \ + _ (PPIO_RECV, ppio_recv, ERROR, "pp2_ppio_recv error") \ + _ (BPOOL_GET_NUM_BUFFS, bpool_get_num_bufs, ERROR, \ + "pp2_bpool_get_num_buffs error") \ + _ (BPOOL_PUT_BUFFS, bpool_put_buffs, ERROR, "pp2_bpool_put_buffs error") \ + _ (BUFFER_ALLOC, buffer_alloc, ERROR, "buffer alloc error") \ + _ (MAC_CE, mac_ce, ERROR, "MAC error (CRC error)") \ + _ (MAC_OR, mac_or, ERROR, "overrun error") \ + _ (MAC_RSVD, mac_rsvd, ERROR, "unknown MAC error") \ + _ (MAC_RE, mac_re, ERROR, "resource error") \ + _ (IP_HDR, ip_hdr, ERROR, "ip4 header error") + +typedef enum +{ +#define _(f, n, s, d) MVPP2_RX_NODE_CTR_##f, + foreach_mvpp2_rx_node_counter +#undef _ +} mvpp2_rx_node_counter_t; + +#endif /* _PP2_H_ */ diff --git a/src/plugins/dev_armada/pp2/queue.c b/src/plugins/dev_armada/pp2/queue.c new file mode 100644 index 00000000000..05015414816 --- /dev/null +++ b/src/plugins/dev_armada/pp2/queue.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2023 Cisco Systems, Inc. + */ + +#include <vnet/vnet.h> +#include <vnet/ethernet/ethernet.h> +#include <vnet/dev/dev.h> +#include <vnet/dev/counters.h> +#include <vnet/dev/bus/platform.h> +#include <vppinfra/ring.h> +#include <dev_armada/musdk.h> +#include <dev_armada/pp2/pp2.h> + +VLIB_REGISTER_LOG_CLASS (mvpp2_log, static) = { + .class_name = "armada", + .subclass_name = "pp2-queue", +}; + +vnet_dev_rv_t +mvpp2_txq_alloc (vlib_main_t *vm, vnet_dev_tx_queue_t *txq) +{ + vnet_dev_rv_t rv = VNET_DEV_OK; + mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq); + log_debug (txq->port->dev, ""); + + ASSERT (mtq->buffers == 0); + if (mtq->buffers == 0) + { + u32 sz = sizeof (u32) * txq->size; + mtq->buffers = clib_mem_alloc_aligned (sz, CLIB_CACHE_LINE_BYTES); + clib_memset (mtq->buffers, 0, sz); + } + + return rv; +} + +void +mvpp2_txq_free (vlib_main_t *vm, vnet_dev_tx_queue_t *txq) +{ + mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq); + + log_debug (txq->port->dev, ""); + if (mtq->buffers) + { + clib_mem_free (mtq->buffers); + mtq->buffers = 0; + } +} diff --git a/src/plugins/dev_armada/pp2/rx.c b/src/plugins/dev_armada/pp2/rx.c new file mode 100644 index 00000000000..81101ef9313 --- /dev/null +++ b/src/plugins/dev_armada/pp2/rx.c @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vnet/dev/dev.h> +#include <vnet/ethernet/ethernet.h> + +#include <dev_armada/pp2/pp2.h> + +static_always_inline void +mvpp2_rx_trace (vlib_main_t *vm, vlib_node_runtime_t *node, + vnet_dev_rx_queue_t *rxq, vlib_buffer_t *b0, uword *n_trace, + struct pp2_ppio_desc *d) +{ + if (PREDICT_TRUE (vlib_trace_buffer (vm, node, rxq->next_index, b0, + /* follow_chain */ 0))) + { + mvpp2_rx_trace_t *tr; + vlib_set_trace_count (vm, node, --(*n_trace)); + tr = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->desc = *d; + tr->rxq = rxq; + } +} + +static_always_inline uword +mrvl_pp2_rx_inline (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, vnet_dev_rx_queue_t *rxq) +{ + vnet_main_t *vnm = vnet_get_main (); + vnet_dev_port_t *port = rxq->port; + vnet_dev_t *dev = port->dev; + mvpp2_device_t *md = vnet_dev_get_data (dev); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + vlib_buffer_template_t bt = rxq->buffer_template; + u32 thread_index = vm->thread_index; + uword n_trace = vlib_get_trace_count (vm, node); + u32 next_index = rxq->next_index; + u32 n_rx_packets = 0, n_rx_bytes = 0; + struct pp2_hif *hif = md->hif[thread_index]; + struct pp2_ppio_desc descs[VLIB_FRAME_SIZE], *d; + struct pp2_bpool *bpool = md->thread[thread_index].bpool; + struct buff_release_entry *bre = md->thread[thread_index].bre; + u16 n_desc = VLIB_FRAME_SIZE; + u32 buffers[VLIB_FRAME_SIZE]; + u32 n_bufs, *bi, i; + vlib_buffer_t *b0, *b1; + + if (PREDICT_FALSE ( + pp2_ppio_recv (mp->ppio, 0, rxq->queue_id, descs, &n_desc))) + { + vlib_error_count (vm, node->node_index, MVPP2_RX_NODE_CTR_PPIO_RECV, 1); + n_desc = 0; + } + + n_rx_packets = n_desc; + + for (i = 0; i < n_desc; i++) + buffers[i] = pp2_ppio_inq_desc_get_cookie (descs + i); + + bt.current_data = 2; + + for (d = descs, bi = buffers; n_desc >= 4; d += 2, bi += 2, n_desc -= 2) + { + /* prefetch */ + b0 = vlib_get_buffer (vm, bi[0]); + b1 = vlib_get_buffer (vm, bi[1]); + b0->template = bt; + b1->template = bt; + + n_rx_bytes += b0->current_length = pp2_ppio_inq_desc_get_pkt_len (d); + n_rx_bytes += b1->current_length = pp2_ppio_inq_desc_get_pkt_len (d + 1); + + if (PREDICT_FALSE (n_trace > 0)) + { + mvpp2_rx_trace (vm, node, rxq, b0, &n_trace, d); + if (n_trace > 0) + mvpp2_rx_trace (vm, node, rxq, b1, &n_trace, d + 1); + } + } + + for (; n_desc; d++, bi++, n_desc--) + { + b0 = vlib_get_buffer (vm, bi[0]); + b0->template = bt; + + n_rx_bytes += b0->current_length = pp2_ppio_inq_desc_get_pkt_len (d); + + if (PREDICT_FALSE (n_trace > 0)) + mvpp2_rx_trace (vm, node, rxq, b0, &n_trace, d); + } + + vlib_buffer_enqueue_to_single_next (vm, node, buffers, next_index, + n_rx_packets); + + vlib_increment_combined_counter ( + vnm->interface_main.combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, port->intf.sw_if_index, n_rx_packets, n_rx_bytes); + + if (PREDICT_FALSE (pp2_bpool_get_num_buffs (bpool, &n_bufs))) + { + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_BPOOL_GET_NUM_BUFFS, 1); + goto done; + } + + n_bufs = rxq->size - n_bufs; + while (n_bufs >= MRVL_PP2_BUFF_BATCH_SZ) + { + u16 n_alloc, i; + struct buff_release_entry *e = bre; + + n_alloc = vlib_buffer_alloc (vm, buffers, MRVL_PP2_BUFF_BATCH_SZ); + i = n_alloc; + + if (PREDICT_FALSE (n_alloc == 0)) + { + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_BUFFER_ALLOC, 1); + goto done; + } + + for (bi = buffers; i--; e++, bi++) + { + + vlib_buffer_t *b = vlib_get_buffer (vm, bi[0]); + e->buff.addr = vlib_buffer_get_pa (vm, b) - 64; + e->buff.cookie = bi[0]; + } + + i = n_alloc; + if (PREDICT_FALSE (pp2_bpool_put_buffs (hif, bre, &i))) + { + vlib_error_count (vm, node->node_index, + MVPP2_RX_NODE_CTR_BPOOL_PUT_BUFFS, 1); + vlib_buffer_free (vm, buffers, n_alloc); + goto done; + } + + if (PREDICT_FALSE (i != n_alloc)) + vlib_buffer_free (vm, buffers + i, n_alloc - i); + + n_bufs -= i; + } + +done: + return n_rx_packets; +} + +VNET_DEV_NODE_FN (mvpp2_rx_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + u32 n_rx = 0; + foreach_vnet_dev_rx_queue_runtime (rxq, node) + n_rx += mrvl_pp2_rx_inline (vm, node, frame, rxq); + return n_rx; +} diff --git a/src/plugins/dev_armada/pp2/tx.c b/src/plugins/dev_armada/pp2/tx.c new file mode 100644 index 00000000000..1e6675c9746 --- /dev/null +++ b/src/plugins/dev_armada/pp2/tx.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright (c) 2024 Cisco Systems, Inc. + */ + +#include <vlib/vlib.h> +#include <vnet/dev/dev.h> +#include <vnet/ethernet/ethernet.h> + +#include <dev_armada/pp2/pp2.h> + +VNET_DEV_NODE_FN (mvpp2_tx_node) +(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame) +{ + vnet_dev_tx_node_runtime_t *rt = vnet_dev_get_tx_node_runtime (node); + vnet_dev_tx_queue_t *txq = rt->tx_queue; + vnet_dev_port_t *port = txq->port; + vnet_dev_t *dev = port->dev; + mvpp2_txq_t *mtq = vnet_dev_get_tx_queue_data (txq); + mvpp2_port_t *mp = vnet_dev_get_port_data (port); + mvpp2_device_t *md = vnet_dev_get_data (dev); + u8 qid = txq->queue_id; + u32 *buffers = vlib_frame_vector_args (frame); + u32 n_vectors = frame->n_vectors, n_left; + u16 n_sent; + struct pp2_ppio *ppio = mp->ppio; + struct pp2_hif *hif = md->hif[vm->thread_index]; + struct pp2_ppio_desc descs[VLIB_FRAME_SIZE], *d = descs; + u16 sz = txq->size; + u16 mask = sz - 1; + + if (mtq->n_enq) + { + u16 n_done = 0; + if (PREDICT_FALSE (pp2_ppio_get_num_outq_done (ppio, hif, qid, &n_done))) + vlib_error_count (vm, node->node_index, + MVPP2_TX_NODE_CTR_PPIO_GET_NUM_OUTQ_DONE, 1); + + if (n_done) + { + vlib_buffer_free_from_ring ( + vm, mtq->buffers, (mtq->next - mtq->n_enq) & mask, sz, n_done); + mtq->n_enq -= n_done; + } + } + + n_sent = clib_min (n_vectors, sz - mtq->n_enq); + + for (d = descs, n_left = n_sent; n_left; d++, buffers++, n_left--) + { + vlib_buffer_t *b0 = vlib_get_buffer (vm, buffers[0]); + u64 paddr = vlib_buffer_get_pa (vm, b0); + + 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); + } + + buffers = vlib_frame_vector_args (frame); + + if (pp2_ppio_send (ppio, hif, qid, descs, &n_sent)) + { + n_sent = 0; + vlib_error_count (vm, node->node_index, MVPP2_TX_NODE_CTR_PPIO_SEND, 1); + } + else if (n_sent) + { + vlib_buffer_copy_indices_to_ring (mtq->buffers, buffers, + mtq->next & mask, sz, n_sent); + mtq->next += n_sent; + mtq->n_enq += n_sent; + } + + /* free unsent buffers */ + if (PREDICT_FALSE (n_sent != n_vectors)) + { + vlib_buffer_free (vm, buffers + n_sent, n_vectors - n_sent); + vlib_error_count (vm, node->node_index, MVPP2_TX_NODE_CTR_NO_FREE_SLOTS, + n_vectors - n_sent); + } + + return n_sent; +} |