The memcpy operation overwrites the device info fields set by ethdev API and leaves the device field undefined. Thus, replaces memcpy by only updating the required fields at the driver layer. error log: testpmd> show port info 1 **** Infos for port 1 **** MAC address: 00:00:00:00:00:00 Device name: net_softnic0 Segmentation fault. port_infos_display (port_id=1) at /dpdk/app/test-pmd/config.c:418 if (dev_info.device->devargs && dev_info.device->devargs->args) Fixes: cc6d421574fe ("net/softnic: add softnic PMD") Cc: stable@dpdk.org Signed-off-by: Jasvinder Singh <jasvinder.singh@intel.com> Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
608 lines
13 KiB
C
608 lines
13 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2017 Intel Corporation
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <rte_ethdev_driver.h>
|
|
#include <rte_ethdev_vdev.h>
|
|
#include <rte_malloc.h>
|
|
#include <rte_bus_vdev.h>
|
|
#include <rte_kvargs.h>
|
|
#include <rte_errno.h>
|
|
#include <rte_ring.h>
|
|
#include <rte_tm_driver.h>
|
|
#include <rte_mtr_driver.h>
|
|
|
|
#include "rte_eth_softnic.h"
|
|
#include "rte_eth_softnic_internals.h"
|
|
|
|
#define PMD_PARAM_FIRMWARE "firmware"
|
|
#define PMD_PARAM_CONN_PORT "conn_port"
|
|
#define PMD_PARAM_CPU_ID "cpu_id"
|
|
#define PMD_PARAM_TM_N_QUEUES "tm_n_queues"
|
|
#define PMD_PARAM_TM_QSIZE0 "tm_qsize0"
|
|
#define PMD_PARAM_TM_QSIZE1 "tm_qsize1"
|
|
#define PMD_PARAM_TM_QSIZE2 "tm_qsize2"
|
|
#define PMD_PARAM_TM_QSIZE3 "tm_qsize3"
|
|
|
|
static const char * const pmd_valid_args[] = {
|
|
PMD_PARAM_FIRMWARE,
|
|
PMD_PARAM_CONN_PORT,
|
|
PMD_PARAM_CPU_ID,
|
|
PMD_PARAM_TM_N_QUEUES,
|
|
PMD_PARAM_TM_QSIZE0,
|
|
PMD_PARAM_TM_QSIZE1,
|
|
PMD_PARAM_TM_QSIZE2,
|
|
PMD_PARAM_TM_QSIZE3,
|
|
NULL
|
|
};
|
|
|
|
static const char welcome[] =
|
|
"\n"
|
|
"Welcome to Soft NIC!\n"
|
|
"\n";
|
|
|
|
static const char prompt[] = "softnic> ";
|
|
|
|
static const struct softnic_conn_params conn_params_default = {
|
|
.welcome = welcome,
|
|
.prompt = prompt,
|
|
.addr = "0.0.0.0",
|
|
.port = 0,
|
|
.buf_size = 1024 * 1024,
|
|
.msg_in_len_max = 1024,
|
|
.msg_out_len_max = 1024 * 1024,
|
|
.msg_handle = softnic_cli_process,
|
|
.msg_handle_arg = NULL,
|
|
};
|
|
|
|
static int pmd_softnic_logtype;
|
|
|
|
#define PMD_LOG(level, fmt, args...) \
|
|
rte_log(RTE_LOG_ ## level, pmd_softnic_logtype, \
|
|
"%s(): " fmt "\n", __func__, ##args)
|
|
|
|
static void
|
|
pmd_dev_infos_get(struct rte_eth_dev *dev __rte_unused,
|
|
struct rte_eth_dev_info *dev_info)
|
|
{
|
|
dev_info->max_rx_pktlen = UINT32_MAX;
|
|
dev_info->max_rx_queues = UINT16_MAX;
|
|
dev_info->max_tx_queues = UINT16_MAX;
|
|
}
|
|
|
|
static int
|
|
pmd_dev_configure(struct rte_eth_dev *dev __rte_unused)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_rx_queue_setup(struct rte_eth_dev *dev,
|
|
uint16_t rx_queue_id,
|
|
uint16_t nb_rx_desc,
|
|
unsigned int socket_id __rte_unused,
|
|
const struct rte_eth_rxconf *rx_conf __rte_unused,
|
|
struct rte_mempool *mb_pool __rte_unused)
|
|
{
|
|
char name[NAME_SIZE];
|
|
struct pmd_internals *p = dev->data->dev_private;
|
|
struct softnic_swq *swq;
|
|
|
|
struct softnic_swq_params params = {
|
|
.size = nb_rx_desc,
|
|
};
|
|
|
|
snprintf(name, sizeof(name), "RXQ%u", rx_queue_id);
|
|
|
|
swq = softnic_swq_create(p,
|
|
name,
|
|
¶ms);
|
|
if (swq == NULL)
|
|
return -1;
|
|
|
|
dev->data->rx_queues[rx_queue_id] = swq->r;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_tx_queue_setup(struct rte_eth_dev *dev,
|
|
uint16_t tx_queue_id,
|
|
uint16_t nb_tx_desc,
|
|
unsigned int socket_id __rte_unused,
|
|
const struct rte_eth_txconf *tx_conf __rte_unused)
|
|
{
|
|
char name[NAME_SIZE];
|
|
struct pmd_internals *p = dev->data->dev_private;
|
|
struct softnic_swq *swq;
|
|
|
|
struct softnic_swq_params params = {
|
|
.size = nb_tx_desc,
|
|
};
|
|
|
|
snprintf(name, sizeof(name), "TXQ%u", tx_queue_id);
|
|
|
|
swq = softnic_swq_create(p,
|
|
name,
|
|
¶ms);
|
|
if (swq == NULL)
|
|
return -1;
|
|
|
|
dev->data->tx_queues[tx_queue_id] = swq->r;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_dev_start(struct rte_eth_dev *dev)
|
|
{
|
|
struct pmd_internals *p = dev->data->dev_private;
|
|
int status;
|
|
|
|
/* Firmware */
|
|
status = softnic_cli_script_process(p,
|
|
p->params.firmware,
|
|
conn_params_default.msg_in_len_max,
|
|
conn_params_default.msg_out_len_max);
|
|
if (status)
|
|
return status;
|
|
|
|
/* Link UP */
|
|
dev->data->dev_link.link_status = ETH_LINK_UP;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pmd_dev_stop(struct rte_eth_dev *dev)
|
|
{
|
|
struct pmd_internals *p = dev->data->dev_private;
|
|
|
|
/* Link DOWN */
|
|
dev->data->dev_link.link_status = ETH_LINK_DOWN;
|
|
|
|
/* Firmware */
|
|
softnic_pipeline_disable_all(p);
|
|
softnic_pipeline_free(p);
|
|
softnic_table_action_profile_free(p);
|
|
softnic_port_in_action_profile_free(p);
|
|
softnic_tap_free(p);
|
|
softnic_tmgr_free(p);
|
|
softnic_link_free(p);
|
|
softnic_softnic_swq_free_keep_rxq_txq(p);
|
|
softnic_mempool_free(p);
|
|
|
|
tm_hierarchy_free(p);
|
|
softnic_mtr_free(p);
|
|
}
|
|
|
|
static void
|
|
pmd_dev_close(struct rte_eth_dev *dev __rte_unused)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static int
|
|
pmd_link_update(struct rte_eth_dev *dev __rte_unused,
|
|
int wait_to_complete __rte_unused)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_filter_ctrl(struct rte_eth_dev *dev __rte_unused,
|
|
enum rte_filter_type filter_type,
|
|
enum rte_filter_op filter_op,
|
|
void *arg)
|
|
{
|
|
if (filter_type == RTE_ETH_FILTER_GENERIC &&
|
|
filter_op == RTE_ETH_FILTER_GET) {
|
|
*(const void **)arg = &pmd_flow_ops;
|
|
return 0;
|
|
}
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
static int
|
|
pmd_tm_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg)
|
|
{
|
|
*(const struct rte_tm_ops **)arg = &pmd_tm_ops;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_mtr_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg)
|
|
{
|
|
*(const struct rte_mtr_ops **)arg = &pmd_mtr_ops;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct eth_dev_ops pmd_ops = {
|
|
.dev_configure = pmd_dev_configure,
|
|
.dev_start = pmd_dev_start,
|
|
.dev_stop = pmd_dev_stop,
|
|
.dev_close = pmd_dev_close,
|
|
.link_update = pmd_link_update,
|
|
.dev_infos_get = pmd_dev_infos_get,
|
|
.rx_queue_setup = pmd_rx_queue_setup,
|
|
.tx_queue_setup = pmd_tx_queue_setup,
|
|
.filter_ctrl = pmd_filter_ctrl,
|
|
.tm_ops_get = pmd_tm_ops_get,
|
|
.mtr_ops_get = pmd_mtr_ops_get,
|
|
};
|
|
|
|
static uint16_t
|
|
pmd_rx_pkt_burst(void *rxq,
|
|
struct rte_mbuf **rx_pkts,
|
|
uint16_t nb_pkts)
|
|
{
|
|
return (uint16_t)rte_ring_sc_dequeue_burst(rxq,
|
|
(void **)rx_pkts,
|
|
nb_pkts,
|
|
NULL);
|
|
}
|
|
|
|
static uint16_t
|
|
pmd_tx_pkt_burst(void *txq,
|
|
struct rte_mbuf **tx_pkts,
|
|
uint16_t nb_pkts)
|
|
{
|
|
return (uint16_t)rte_ring_sp_enqueue_burst(txq,
|
|
(void **)tx_pkts,
|
|
nb_pkts,
|
|
NULL);
|
|
}
|
|
|
|
static void *
|
|
pmd_init(struct pmd_params *params)
|
|
{
|
|
struct pmd_internals *p;
|
|
int status;
|
|
|
|
p = rte_zmalloc_socket(params->name,
|
|
sizeof(struct pmd_internals),
|
|
0,
|
|
params->cpu_id);
|
|
if (p == NULL)
|
|
return NULL;
|
|
|
|
/* Params */
|
|
memcpy(&p->params, params, sizeof(p->params));
|
|
|
|
/* Resources */
|
|
tm_hierarchy_init(p);
|
|
softnic_mtr_init(p);
|
|
|
|
softnic_mempool_init(p);
|
|
softnic_swq_init(p);
|
|
softnic_link_init(p);
|
|
softnic_tmgr_init(p);
|
|
softnic_tap_init(p);
|
|
softnic_cryptodev_init(p);
|
|
softnic_port_in_action_profile_init(p);
|
|
softnic_table_action_profile_init(p);
|
|
softnic_pipeline_init(p);
|
|
|
|
status = softnic_thread_init(p);
|
|
if (status) {
|
|
rte_free(p);
|
|
return NULL;
|
|
}
|
|
|
|
if (params->conn_port) {
|
|
struct softnic_conn_params conn_params;
|
|
|
|
memcpy(&conn_params, &conn_params_default, sizeof(conn_params));
|
|
conn_params.port = p->params.conn_port;
|
|
conn_params.msg_handle_arg = p;
|
|
|
|
p->conn = softnic_conn_init(&conn_params);
|
|
if (p->conn == NULL) {
|
|
softnic_thread_free(p);
|
|
rte_free(p);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
static void
|
|
pmd_free(struct pmd_internals *p)
|
|
{
|
|
if (p == NULL)
|
|
return;
|
|
|
|
if (p->params.conn_port)
|
|
softnic_conn_free(p->conn);
|
|
|
|
softnic_thread_free(p);
|
|
softnic_pipeline_free(p);
|
|
softnic_table_action_profile_free(p);
|
|
softnic_port_in_action_profile_free(p);
|
|
softnic_tap_free(p);
|
|
softnic_tmgr_free(p);
|
|
softnic_link_free(p);
|
|
softnic_swq_free(p);
|
|
softnic_mempool_free(p);
|
|
|
|
tm_hierarchy_free(p);
|
|
softnic_mtr_free(p);
|
|
|
|
rte_free(p);
|
|
}
|
|
|
|
static struct ether_addr eth_addr = {
|
|
.addr_bytes = {0},
|
|
};
|
|
|
|
static int
|
|
pmd_ethdev_register(struct rte_vdev_device *vdev,
|
|
struct pmd_params *params,
|
|
void *dev_private)
|
|
{
|
|
struct rte_eth_dev *dev;
|
|
|
|
/* Ethdev entry allocation */
|
|
dev = rte_eth_dev_allocate(params->name);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
|
|
/* dev */
|
|
dev->rx_pkt_burst = pmd_rx_pkt_burst;
|
|
dev->tx_pkt_burst = pmd_tx_pkt_burst;
|
|
dev->tx_pkt_prepare = NULL;
|
|
dev->dev_ops = &pmd_ops;
|
|
dev->device = &vdev->device;
|
|
|
|
/* dev->data */
|
|
dev->data->dev_private = dev_private;
|
|
dev->data->dev_link.link_speed = ETH_SPEED_NUM_100G;
|
|
dev->data->dev_link.link_duplex = ETH_LINK_FULL_DUPLEX;
|
|
dev->data->dev_link.link_autoneg = ETH_LINK_FIXED;
|
|
dev->data->dev_link.link_status = ETH_LINK_DOWN;
|
|
dev->data->mac_addrs = ð_addr;
|
|
dev->data->promiscuous = 1;
|
|
dev->data->kdrv = RTE_KDRV_NONE;
|
|
dev->data->numa_node = params->cpu_id;
|
|
|
|
rte_eth_dev_probing_finish(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_string(const char *key __rte_unused, const char *value, void *extra_args)
|
|
{
|
|
if (!value || !extra_args)
|
|
return -EINVAL;
|
|
|
|
*(char **)extra_args = strdup(value);
|
|
|
|
if (!*(char **)extra_args)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_uint32(const char *key __rte_unused, const char *value, void *extra_args)
|
|
{
|
|
if (!value || !extra_args)
|
|
return -EINVAL;
|
|
|
|
*(uint32_t *)extra_args = strtoull(value, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_uint16(const char *key __rte_unused, const char *value, void *extra_args)
|
|
{
|
|
if (!value || !extra_args)
|
|
return -EINVAL;
|
|
|
|
*(uint16_t *)extra_args = strtoull(value, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_parse_args(struct pmd_params *p, const char *params)
|
|
{
|
|
struct rte_kvargs *kvlist;
|
|
int ret = 0;
|
|
|
|
kvlist = rte_kvargs_parse(params, pmd_valid_args);
|
|
if (kvlist == NULL)
|
|
return -EINVAL;
|
|
|
|
/* Set default values */
|
|
memset(p, 0, sizeof(*p));
|
|
p->firmware = SOFTNIC_FIRMWARE;
|
|
p->cpu_id = SOFTNIC_CPU_ID;
|
|
p->tm.n_queues = SOFTNIC_TM_N_QUEUES;
|
|
p->tm.qsize[0] = SOFTNIC_TM_QUEUE_SIZE;
|
|
p->tm.qsize[1] = SOFTNIC_TM_QUEUE_SIZE;
|
|
p->tm.qsize[2] = SOFTNIC_TM_QUEUE_SIZE;
|
|
p->tm.qsize[3] = SOFTNIC_TM_QUEUE_SIZE;
|
|
|
|
/* Firmware script (optional) */
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_FIRMWARE) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_FIRMWARE,
|
|
&get_string, &p->firmware);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
/* Connection listening port (optional) */
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_CONN_PORT) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_CONN_PORT,
|
|
&get_uint16, &p->conn_port);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
/* CPU ID (optional) */
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_CPU_ID) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_CPU_ID,
|
|
&get_uint32, &p->cpu_id);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
/* TM number of queues (optional) */
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_TM_N_QUEUES) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_TM_N_QUEUES,
|
|
&get_uint32, &p->tm.n_queues);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
/* TM queue size 0 .. 3 (optional) */
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_TM_QSIZE0) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_TM_QSIZE0,
|
|
&get_uint32, &p->tm.qsize[0]);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_TM_QSIZE1) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_TM_QSIZE1,
|
|
&get_uint32, &p->tm.qsize[1]);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_TM_QSIZE2) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_TM_QSIZE2,
|
|
&get_uint32, &p->tm.qsize[2]);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
if (rte_kvargs_count(kvlist, PMD_PARAM_TM_QSIZE3) == 1) {
|
|
ret = rte_kvargs_process(kvlist, PMD_PARAM_TM_QSIZE3,
|
|
&get_uint32, &p->tm.qsize[3]);
|
|
if (ret < 0)
|
|
goto out_free;
|
|
}
|
|
|
|
out_free:
|
|
rte_kvargs_free(kvlist);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
pmd_probe(struct rte_vdev_device *vdev)
|
|
{
|
|
struct pmd_params p;
|
|
const char *params;
|
|
int status = 0;
|
|
|
|
void *dev_private;
|
|
const char *name = rte_vdev_device_name(vdev);
|
|
|
|
PMD_LOG(INFO, "Probing device \"%s\"", name);
|
|
|
|
/* Parse input arguments */
|
|
params = rte_vdev_device_args(vdev);
|
|
if (!params)
|
|
return -EINVAL;
|
|
|
|
status = pmd_parse_args(&p, params);
|
|
if (status)
|
|
return status;
|
|
|
|
p.name = name;
|
|
|
|
/* Allocate and initialize soft ethdev private data */
|
|
dev_private = pmd_init(&p);
|
|
if (dev_private == NULL)
|
|
return -ENOMEM;
|
|
|
|
/* Register soft ethdev */
|
|
PMD_LOG(INFO, "Creating soft ethdev \"%s\"", p.name);
|
|
|
|
status = pmd_ethdev_register(vdev, &p, dev_private);
|
|
if (status) {
|
|
pmd_free(dev_private);
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pmd_remove(struct rte_vdev_device *vdev)
|
|
{
|
|
struct rte_eth_dev *dev = NULL;
|
|
|
|
if (!vdev)
|
|
return -EINVAL;
|
|
|
|
PMD_LOG(INFO, "Removing device \"%s\"", rte_vdev_device_name(vdev));
|
|
|
|
/* Find the ethdev entry */
|
|
dev = rte_eth_dev_allocated(rte_vdev_device_name(vdev));
|
|
if (dev == NULL)
|
|
return -ENODEV;
|
|
|
|
/* Free device data structures*/
|
|
pmd_free(dev->data->dev_private);
|
|
dev->data->dev_private = NULL; /* already freed */
|
|
dev->data->mac_addrs = NULL; /* statically allocated */
|
|
rte_eth_dev_release_port(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct rte_vdev_driver pmd_softnic_drv = {
|
|
.probe = pmd_probe,
|
|
.remove = pmd_remove,
|
|
};
|
|
|
|
RTE_PMD_REGISTER_VDEV(net_softnic, pmd_softnic_drv);
|
|
RTE_PMD_REGISTER_PARAM_STRING(net_softnic,
|
|
PMD_PARAM_FIRMWARE "=<string> "
|
|
PMD_PARAM_CONN_PORT "=<uint16> "
|
|
PMD_PARAM_CPU_ID "=<uint32> "
|
|
PMD_PARAM_TM_N_QUEUES "=<uint32> "
|
|
PMD_PARAM_TM_QSIZE0 "=<uint32> "
|
|
PMD_PARAM_TM_QSIZE1 "=<uint32> "
|
|
PMD_PARAM_TM_QSIZE2 "=<uint32> "
|
|
PMD_PARAM_TM_QSIZE3 "=<uint32>"
|
|
);
|
|
|
|
|
|
RTE_INIT(pmd_softnic_init_log)
|
|
{
|
|
pmd_softnic_logtype = rte_log_register("pmd.net.softnic");
|
|
if (pmd_softnic_logtype >= 0)
|
|
rte_log_set_level(pmd_softnic_logtype, RTE_LOG_NOTICE);
|
|
}
|
|
|
|
int
|
|
rte_pmd_softnic_manage(uint16_t port_id)
|
|
{
|
|
struct rte_eth_dev *dev = &rte_eth_devices[port_id];
|
|
struct pmd_internals *softnic;
|
|
|
|
#ifdef RTE_LIBRTE_ETHDEV_DEBUG
|
|
RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, 0);
|
|
#endif
|
|
|
|
softnic = dev->data->dev_private;
|
|
|
|
softnic_conn_poll_for_conn(softnic->conn);
|
|
|
|
softnic_conn_poll_for_msg(softnic->conn);
|
|
|
|
return 0;
|
|
}
|