/* SPDX-License-Identifier: Apache-2.0 * Copyright (c) 2023 Cisco Systems, Inc. */ #include #include #include #include #include #include #include #include #include #include struct roc_model oct_model; VLIB_REGISTER_LOG_CLASS (oct_log, static) = { .class_name = "octeon", .subclass_name = "init", }; #define _(f, n, s, d) \ { .name = #n, .desc = d, .severity = VL_COUNTER_SEVERITY_##s }, vlib_error_desc_t oct_tx_node_counters[] = { foreach_oct_tx_node_counter }; #undef _ vnet_dev_node_t oct_rx_node = { .format_trace = format_oct_rx_trace, }; vnet_dev_node_t oct_tx_node = { .format_trace = format_oct_tx_trace, .error_counters = oct_tx_node_counters, .n_error_counters = ARRAY_LEN (oct_tx_node_counters), }; static struct { u16 device_id; oct_device_type_t type; char *description; } oct_dev_types[] = { #define _(id, device_type, desc) \ { \ .device_id = (id), .type = OCT_DEVICE_TYPE_##device_type, \ .description = (desc) \ } _ (0xa063, RVU_PF, "Marvell Octeon Resource Virtualization Unit PF"), _ (0xa064, RVU_VF, "Marvell Octeon Resource Virtualization Unit VF"), _ (0xa0f8, LBK_VF, "Marvell Octeon Loopback Unit VF"), _ (0xa0f7, SDP_VF, "Marvell Octeon System DPI Packet Interface Unit VF"), _ (0xa0f3, CPT_VF, "Marvell Octeon Cryptographic Accelerator Unit VF"), #undef _ }; static u8 * oct_probe (vlib_main_t *vm, vnet_dev_bus_index_t bus_index, void *dev_info) { vnet_dev_bus_pci_device_info_t *di = dev_info; if (di->vendor_id != 0x177d) /* Cavium */ return 0; FOREACH_ARRAY_ELT (dt, oct_dev_types) { if (dt->device_id == di->device_id) return format (0, "%s", dt->description); } return 0; } vnet_dev_rv_t cnx_return_roc_err (vnet_dev_t *dev, int rrv, char *fmt, ...) { va_list va; va_start (va, fmt); u8 *s = va_format (0, fmt, &va); va_end (va); log_err (dev, "%v: %s [%d]", s, roc_error_msg_get (rrv), rrv); vec_free (s); return VNET_DEV_ERR_UNSUPPORTED_DEVICE; } static vnet_dev_rv_t oct_alloc (vlib_main_t *vm, vnet_dev_t *dev) { oct_device_t *cd = vnet_dev_get_data (dev); cd->nix = clib_mem_alloc_aligned (sizeof (struct roc_nix), CLIB_CACHE_LINE_BYTES); return VNET_DEV_OK; } static vnet_dev_rv_t oct_init_nix (vlib_main_t *vm, vnet_dev_t *dev) { oct_device_t *cd = vnet_dev_get_data (dev); u8 mac_addr[6]; int rrv; oct_port_t oct_port = {}; *cd->nix = (struct roc_nix){ .reta_sz = ROC_NIX_RSS_RETA_SZ_256, .max_sqb_count = 512, .pci_dev = &cd->plt_pci_dev, .hw_vlan_ins = true, }; if ((rrv = roc_nix_dev_init (cd->nix))) return cnx_return_roc_err (dev, rrv, "roc_nix_dev_init"); if ((rrv = roc_nix_npc_mac_addr_get (cd->nix, mac_addr))) return cnx_return_roc_err (dev, rrv, "roc_nix_npc_mac_addr_get"); vnet_dev_port_add_args_t port_add_args = { .port = { .attr = { .type = VNET_DEV_PORT_TYPE_ETHERNET, .max_rx_queues = 64, .max_tx_queues = 64, .max_supported_rx_frame_size = roc_nix_max_pkt_len (cd->nix), .caps = { .rss = 1, }, .rx_offloads = { .ip4_cksum = 1, }, .tx_offloads = { .ip4_cksum = 1, }, }, .ops = { .init = oct_port_init, .deinit = oct_port_deinit, .start = oct_port_start, .stop = oct_port_stop, .config_change = oct_port_cfg_change, .config_change_validate = oct_port_cfg_change_validate, .format_status = format_oct_port_status, .format_flow = format_oct_port_flow, .clear_counters = oct_port_clear_counters, }, .data_size = sizeof (oct_port_t), .initial_data = &oct_port, }, .rx_node = &oct_rx_node, .tx_node = &oct_tx_node, .rx_queue = { .config = { .data_size = sizeof (oct_rxq_t), .default_size = 1024, .multiplier = 32, .min_size = 256, .max_size = 16384, }, .ops = { .alloc = oct_rx_queue_alloc, .free = oct_rx_queue_free, .format_info = format_oct_rxq_info, .clear_counters = oct_rxq_clear_counters, }, }, .tx_queue = { .config = { .data_size = sizeof (oct_txq_t), .default_size = 1024, .multiplier = 32, .min_size = 256, .max_size = 16384, }, .ops = { .alloc = oct_tx_queue_alloc, .free = oct_tx_queue_free, .format_info = format_oct_txq_info, .clear_counters = oct_txq_clear_counters, }, }, }; vnet_dev_set_hw_addr_eth_mac (&port_add_args.port.attr.hw_addr, mac_addr); log_info (dev, "MAC address is %U", format_ethernet_address, mac_addr); return vnet_dev_port_add (vm, dev, 0, &port_add_args); } static vnet_dev_rv_t oct_init_cpt (vlib_main_t *vm, vnet_dev_t *dev) { oct_device_t *cd = vnet_dev_get_data (dev); int rrv; struct roc_cpt cpt = { .pci_dev = &cd->plt_pci_dev, }; if ((rrv = roc_cpt_dev_init (&cpt))) return cnx_return_roc_err (dev, rrv, "roc_cpt_dev_init"); return VNET_DEV_OK; } static vnet_dev_rv_t oct_init (vlib_main_t *vm, vnet_dev_t *dev) { oct_device_t *cd = vnet_dev_get_data (dev); vlib_pci_config_hdr_t pci_hdr; vnet_dev_rv_t rv; rv = vnet_dev_pci_read_config_header (vm, dev, &pci_hdr); if (rv != VNET_DEV_OK) return rv; if (pci_hdr.vendor_id != 0x177d) return VNET_DEV_ERR_UNSUPPORTED_DEVICE; FOREACH_ARRAY_ELT (dt, oct_dev_types) { if (dt->device_id == pci_hdr.device_id) cd->type = dt->type; } if (cd->type == OCT_DEVICE_TYPE_UNKNOWN) return rv; rv = VNET_DEV_ERR_UNSUPPORTED_DEVICE; cd->plt_pci_dev = (struct plt_pci_device){ .id.vendor_id = pci_hdr.vendor_id, .id.device_id = pci_hdr.device_id, .id.class_id = pci_hdr.class << 16 | pci_hdr.subclass, .pci_handle = vnet_dev_get_pci_handle (dev), }; foreach_int (i, 2, 4) { rv = vnet_dev_pci_map_region (vm, dev, i, &cd->plt_pci_dev.mem_resource[i].addr); if (rv != VNET_DEV_OK) return rv; } strncpy ((char *) cd->plt_pci_dev.name, dev->device_id, sizeof (cd->plt_pci_dev.name) - 1); switch (cd->type) { case OCT_DEVICE_TYPE_RVU_PF: case OCT_DEVICE_TYPE_RVU_VF: case OCT_DEVICE_TYPE_LBK_VF: case OCT_DEVICE_TYPE_SDP_VF: return oct_init_nix (vm, dev); case OCT_DEVICE_TYPE_CPT_VF: return oct_init_cpt (vm, dev); default: return VNET_DEV_ERR_UNSUPPORTED_DEVICE; } return 0; } static void oct_deinit (vlib_main_t *vm, vnet_dev_t *dev) { oct_device_t *cd = vnet_dev_get_data (dev); if (cd->nix_initialized) roc_nix_dev_fini (cd->nix); } static void oct_free (vlib_main_t *vm, vnet_dev_t *dev) { oct_device_t *cd = vnet_dev_get_data (dev); if (cd->nix_initialized) roc_nix_dev_fini (cd->nix); } VNET_DEV_REGISTER_DRIVER (octeon) = { .name = "octeon", .bus = "pci", .device_data_sz = sizeof (oct_device_t), .ops = { .alloc = oct_alloc, .init = oct_init, .deinit = oct_deinit, .free = oct_free, .probe = oct_probe, }, }; static clib_error_t * oct_plugin_init (vlib_main_t *vm) { int rv; extern oct_plt_init_param_t oct_plt_init_param; rv = oct_plt_init (&oct_plt_init_param); if (rv) return clib_error_return (0, "oct_plt_init failed"); rv = roc_model_init (&oct_model); if (rv) return clib_error_return (0, "roc_model_init failed"); return 0; } VLIB_INIT_FUNCTION (oct_plugin_init); VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, .description = "dev_octeon", };