#include "sfc_flow.h"
#include "sfc_dp.h"
#include "sfc_dp_rx.h"
+#include "sfc_repr.h"
#include "sfc_sw_stats.h"
#define SFC_XSTAT_ID_INVALID_VAL UINT64_MAX
.pool_ops_supported = sfc_pool_ops_supported,
};
+struct sfc_ethdev_init_data {
+ uint16_t nb_representors;
+};
+
/**
* Duplicate a string in potentially shared memory required for
* multi-process support.
}
static int
-sfc_parse_switch_mode(struct sfc_adapter *sa)
+sfc_parse_switch_mode(struct sfc_adapter *sa, bool has_representors)
{
const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
const char *switch_mode = NULL;
if (switch_mode == NULL) {
sa->switchdev = encp->enc_mae_supported &&
- !encp->enc_datapath_cap_evb;
+ (!encp->enc_datapath_cap_evb ||
+ has_representors);
} else if (strcasecmp(switch_mode, SFC_KVARG_SWITCH_MODE_LEGACY) == 0) {
sa->switchdev = false;
} else if (strcasecmp(switch_mode,
}
static int
-sfc_eth_dev_init(struct rte_eth_dev *dev)
+sfc_eth_dev_init(struct rte_eth_dev *dev, void *init_params)
{
struct sfc_adapter_shared *sas = sfc_adapter_shared_by_eth_dev(dev);
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+ struct sfc_ethdev_init_data *init_data = init_params;
uint32_t logtype_main;
struct sfc_adapter *sa;
int rc;
* Selecting a default switch mode requires the NIC to be probed and
* to have its capabilities filled in.
*/
- rc = sfc_parse_switch_mode(sa);
+ rc = sfc_parse_switch_mode(sa, init_data->nb_representors > 0);
if (rc != 0)
goto fail_switch_mode;
{ .vendor_id = 0 /* sentinel */ }
};
+static int
+sfc_parse_rte_devargs(const char *args, struct rte_eth_devargs *devargs)
+{
+ struct rte_eth_devargs eth_da = { .nb_representor_ports = 0 };
+ int rc;
+
+ if (args != NULL) {
+ rc = rte_eth_devargs_parse(args, ð_da);
+ if (rc != 0) {
+ SFC_GENERIC_LOG(ERR,
+ "Failed to parse generic devargs '%s'",
+ args);
+ return rc;
+ }
+ }
+
+ *devargs = eth_da;
+
+ return 0;
+}
+
+static int
+sfc_eth_dev_create(struct rte_pci_device *pci_dev,
+ struct sfc_ethdev_init_data *init_data,
+ struct rte_eth_dev **devp)
+{
+ struct rte_eth_dev *dev;
+ int rc;
+
+ rc = rte_eth_dev_create(&pci_dev->device, pci_dev->device.name,
+ sizeof(struct sfc_adapter_shared),
+ eth_dev_pci_specific_init, pci_dev,
+ sfc_eth_dev_init, init_data);
+ if (rc != 0) {
+ SFC_GENERIC_LOG(ERR, "Failed to create sfc ethdev '%s'",
+ pci_dev->device.name);
+ return rc;
+ }
+
+ dev = rte_eth_dev_allocated(pci_dev->device.name);
+ if (dev == NULL) {
+ SFC_GENERIC_LOG(ERR, "Failed to find allocated sfc ethdev '%s'",
+ pci_dev->device.name);
+ return -ENODEV;
+ }
+
+ *devp = dev;
+
+ return 0;
+}
+
+static int
+sfc_eth_dev_create_representors(struct rte_eth_dev *dev,
+ const struct rte_eth_devargs *eth_da)
+{
+ struct sfc_adapter *sa;
+ unsigned int i;
+ int rc;
+
+ if (eth_da->nb_representor_ports == 0)
+ return 0;
+
+ sa = sfc_adapter_by_eth_dev(dev);
+
+ if (!sa->switchdev) {
+ sfc_err(sa, "cannot create representors in non-switchdev mode");
+ return -EINVAL;
+ }
+
+ if (!sfc_repr_available(sfc_sa2shared(sa))) {
+ sfc_err(sa, "cannot create representors: unsupported");
+
+ return -ENOTSUP;
+ }
+
+ for (i = 0; i < eth_da->nb_representor_ports; ++i) {
+ const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
+ efx_mport_sel_t mport_sel;
+
+ rc = efx_mae_mport_by_pcie_function(encp->enc_pf,
+ eth_da->representor_ports[i], &mport_sel);
+ if (rc != 0) {
+ sfc_err(sa,
+ "failed to get representor %u m-port: %s - ignore",
+ eth_da->representor_ports[i],
+ rte_strerror(-rc));
+ continue;
+ }
+
+ rc = sfc_repr_create(dev, eth_da->representor_ports[i],
+ sa->mae.switch_domain_id, &mport_sel);
+ if (rc != 0) {
+ sfc_err(sa, "cannot create representor %u: %s - ignore",
+ eth_da->representor_ports[i],
+ rte_strerror(-rc));
+ }
+ }
+
+ return 0;
+}
+
static int sfc_eth_dev_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
struct rte_pci_device *pci_dev)
{
- return rte_eth_dev_pci_generic_probe(pci_dev,
- sizeof(struct sfc_adapter_shared), sfc_eth_dev_init);
+ struct sfc_ethdev_init_data init_data;
+ struct rte_eth_devargs eth_da;
+ struct rte_eth_dev *dev;
+ int rc;
+
+ if (pci_dev->device.devargs != NULL) {
+ rc = sfc_parse_rte_devargs(pci_dev->device.devargs->args,
+ ð_da);
+ if (rc != 0)
+ return rc;
+ } else {
+ memset(ð_da, 0, sizeof(eth_da));
+ }
+
+ init_data.nb_representors = eth_da.nb_representor_ports;
+
+ if (eth_da.nb_representor_ports > 0 &&
+ rte_eal_process_type() != RTE_PROC_PRIMARY) {
+ SFC_GENERIC_LOG(ERR,
+ "Create representors from secondary process not supported, dev '%s'",
+ pci_dev->device.name);
+ return -ENOTSUP;
+ }
+
+ rc = sfc_eth_dev_create(pci_dev, &init_data, &dev);
+ if (rc != 0)
+ return rc;
+
+ rc = sfc_eth_dev_create_representors(dev, ð_da);
+ if (rc != 0) {
+ (void)rte_eth_dev_destroy(dev, sfc_eth_dev_uninit);
+ return rc;
+ }
+
+ return 0;
}
static int sfc_eth_dev_pci_remove(struct rte_pci_device *pci_dev)
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright(c) 2019-2021 Xilinx, Inc.
+ * Copyright(c) 2019 Solarflare Communications Inc.
+ *
+ * This software was jointly developed between OKTET Labs (under contract
+ * for Solarflare) and Solarflare Communications, Inc.
+ */
+
+#include <stdint.h>
+
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+#include <ethdev_driver.h>
+
+#include "efx.h"
+
+#include "sfc_log.h"
+#include "sfc_debug.h"
+#include "sfc_repr.h"
+#include "sfc_ethdev_state.h"
+#include "sfc_switch.h"
+
+/** Multi-process shared representor private data */
+struct sfc_repr_shared {
+ uint16_t pf_port_id;
+ uint16_t repr_id;
+ uint16_t switch_domain_id;
+ uint16_t switch_port_id;
+};
+
+/** Primary process representor private data */
+struct sfc_repr {
+ /**
+ * PMD setup and configuration is not thread safe. Since it is not
+ * performance sensitive, it is better to guarantee thread-safety
+ * and add device level lock. Adapter control operations which
+ * change its state should acquire the lock.
+ */
+ rte_spinlock_t lock;
+ enum sfc_ethdev_state state;
+};
+
+#define sfcr_err(sr, ...) \
+ do { \
+ const struct sfc_repr *_sr = (sr); \
+ \
+ (void)_sr; \
+ SFC_GENERIC_LOG(ERR, __VA_ARGS__); \
+ } while (0)
+
+#define sfcr_info(sr, ...) \
+ do { \
+ const struct sfc_repr *_sr = (sr); \
+ \
+ (void)_sr; \
+ SFC_GENERIC_LOG(INFO, \
+ RTE_FMT("%s() " \
+ RTE_FMT_HEAD(__VA_ARGS__ ,), \
+ __func__, \
+ RTE_FMT_TAIL(__VA_ARGS__ ,))); \
+ } while (0)
+
+static inline struct sfc_repr_shared *
+sfc_repr_shared_by_eth_dev(struct rte_eth_dev *eth_dev)
+{
+ struct sfc_repr_shared *srs = eth_dev->data->dev_private;
+
+ return srs;
+}
+
+static inline struct sfc_repr *
+sfc_repr_by_eth_dev(struct rte_eth_dev *eth_dev)
+{
+ struct sfc_repr *sr = eth_dev->process_private;
+
+ return sr;
+}
+
+/*
+ * Add wrapper functions to acquire/release lock to be able to remove or
+ * change the lock in one place.
+ */
+
+static inline void
+sfc_repr_lock_init(struct sfc_repr *sr)
+{
+ rte_spinlock_init(&sr->lock);
+}
+
+#if defined(RTE_LIBRTE_SFC_EFX_DEBUG) || defined(RTE_ENABLE_ASSERT)
+
+static inline int
+sfc_repr_lock_is_locked(struct sfc_repr *sr)
+{
+ return rte_spinlock_is_locked(&sr->lock);
+}
+
+#endif
+
+static inline void
+sfc_repr_lock(struct sfc_repr *sr)
+{
+ rte_spinlock_lock(&sr->lock);
+}
+
+static inline void
+sfc_repr_unlock(struct sfc_repr *sr)
+{
+ rte_spinlock_unlock(&sr->lock);
+}
+
+static inline void
+sfc_repr_lock_fini(__rte_unused struct sfc_repr *sr)
+{
+ /* Just for symmetry of the API */
+}
+
+static int
+sfc_repr_check_conf(struct sfc_repr *sr, uint16_t nb_rx_queues,
+ const struct rte_eth_conf *conf)
+{
+ const struct rte_eth_rss_conf *rss_conf;
+ int ret = 0;
+
+ sfcr_info(sr, "entry");
+
+ if (conf->link_speeds != 0) {
+ sfcr_err(sr, "specific link speeds not supported");
+ ret = -EINVAL;
+ }
+
+ switch (conf->rxmode.mq_mode) {
+ case ETH_MQ_RX_RSS:
+ if (nb_rx_queues != 1) {
+ sfcr_err(sr, "Rx RSS is not supported with %u queues",
+ nb_rx_queues);
+ ret = -EINVAL;
+ break;
+ }
+
+ rss_conf = &conf->rx_adv_conf.rss_conf;
+ if (rss_conf->rss_key != NULL || rss_conf->rss_key_len != 0 ||
+ rss_conf->rss_hf != 0) {
+ sfcr_err(sr, "Rx RSS configuration is not supported");
+ ret = -EINVAL;
+ }
+ break;
+ case ETH_MQ_RX_NONE:
+ break;
+ default:
+ sfcr_err(sr, "Rx mode MQ modes other than RSS not supported");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (conf->txmode.mq_mode != ETH_MQ_TX_NONE) {
+ sfcr_err(sr, "Tx mode MQ modes not supported");
+ ret = -EINVAL;
+ }
+
+ if (conf->lpbk_mode != 0) {
+ sfcr_err(sr, "loopback not supported");
+ ret = -EINVAL;
+ }
+
+ if (conf->dcb_capability_en != 0) {
+ sfcr_err(sr, "priority-based flow control not supported");
+ ret = -EINVAL;
+ }
+
+ if (conf->fdir_conf.mode != RTE_FDIR_MODE_NONE) {
+ sfcr_err(sr, "Flow Director not supported");
+ ret = -EINVAL;
+ }
+
+ if (conf->intr_conf.lsc != 0) {
+ sfcr_err(sr, "link status change interrupt not supported");
+ ret = -EINVAL;
+ }
+
+ if (conf->intr_conf.rxq != 0) {
+ sfcr_err(sr, "receive queue interrupt not supported");
+ ret = -EINVAL;
+ }
+
+ if (conf->intr_conf.rmv != 0) {
+ sfcr_err(sr, "remove interrupt not supported");
+ ret = -EINVAL;
+ }
+
+ sfcr_info(sr, "done %d", ret);
+
+ return ret;
+}
+
+
+static int
+sfc_repr_configure(struct sfc_repr *sr, uint16_t nb_rx_queues,
+ const struct rte_eth_conf *conf)
+{
+ int ret;
+
+ sfcr_info(sr, "entry");
+
+ SFC_ASSERT(sfc_repr_lock_is_locked(sr));
+
+ ret = sfc_repr_check_conf(sr, nb_rx_queues, conf);
+ if (ret != 0)
+ goto fail_check_conf;
+
+ sr->state = SFC_ETHDEV_CONFIGURED;
+
+ sfcr_info(sr, "done");
+
+ return 0;
+
+fail_check_conf:
+ sfcr_info(sr, "failed %s", rte_strerror(-ret));
+ return ret;
+}
+
+static int
+sfc_repr_dev_configure(struct rte_eth_dev *dev)
+{
+ struct sfc_repr *sr = sfc_repr_by_eth_dev(dev);
+ struct rte_eth_dev_data *dev_data = dev->data;
+ int ret;
+
+ sfcr_info(sr, "entry n_rxq=%u n_txq=%u",
+ dev_data->nb_rx_queues, dev_data->nb_tx_queues);
+
+ sfc_repr_lock(sr);
+ switch (sr->state) {
+ case SFC_ETHDEV_CONFIGURED:
+ /* FALLTHROUGH */
+ case SFC_ETHDEV_INITIALIZED:
+ ret = sfc_repr_configure(sr, dev_data->nb_rx_queues,
+ &dev_data->dev_conf);
+ break;
+ default:
+ sfcr_err(sr, "unexpected adapter state %u to configure",
+ sr->state);
+ ret = -EINVAL;
+ break;
+ }
+ sfc_repr_unlock(sr);
+
+ sfcr_info(sr, "done %s", rte_strerror(-ret));
+
+ return ret;
+}
+
+static int
+sfc_repr_dev_infos_get(struct rte_eth_dev *dev,
+ struct rte_eth_dev_info *dev_info)
+{
+ struct sfc_repr_shared *srs = sfc_repr_shared_by_eth_dev(dev);
+
+ dev_info->device = dev->device;
+
+ dev_info->max_rx_queues = SFC_REPR_RXQ_MAX;
+ dev_info->max_tx_queues = SFC_REPR_TXQ_MAX;
+ dev_info->default_rxconf.rx_drop_en = 1;
+ dev_info->switch_info.domain_id = srs->switch_domain_id;
+ dev_info->switch_info.port_id = srs->switch_port_id;
+
+ return 0;
+}
+
+static void
+sfc_repr_close(struct sfc_repr *sr)
+{
+ SFC_ASSERT(sfc_repr_lock_is_locked(sr));
+
+ SFC_ASSERT(sr->state == SFC_ETHDEV_CONFIGURED);
+ sr->state = SFC_ETHDEV_CLOSING;
+
+ /* Put representor close actions here */
+
+ sr->state = SFC_ETHDEV_INITIALIZED;
+}
+
+static int
+sfc_repr_dev_close(struct rte_eth_dev *dev)
+{
+ struct sfc_repr *sr = sfc_repr_by_eth_dev(dev);
+
+ sfcr_info(sr, "entry");
+
+ sfc_repr_lock(sr);
+ switch (sr->state) {
+ case SFC_ETHDEV_CONFIGURED:
+ sfc_repr_close(sr);
+ SFC_ASSERT(sr->state == SFC_ETHDEV_INITIALIZED);
+ /* FALLTHROUGH */
+ case SFC_ETHDEV_INITIALIZED:
+ break;
+ default:
+ sfcr_err(sr, "unexpected adapter state %u on close", sr->state);
+ break;
+ }
+
+ /*
+ * Cleanup all resources.
+ * Rollback primary process sfc_repr_eth_dev_init() below.
+ */
+
+ dev->dev_ops = NULL;
+
+ sfc_repr_unlock(sr);
+ sfc_repr_lock_fini(sr);
+
+ sfcr_info(sr, "done");
+
+ free(sr);
+
+ return 0;
+}
+
+static const struct eth_dev_ops sfc_repr_dev_ops = {
+ .dev_configure = sfc_repr_dev_configure,
+ .dev_close = sfc_repr_dev_close,
+ .dev_infos_get = sfc_repr_dev_infos_get,
+};
+
+
+struct sfc_repr_init_data {
+ uint16_t pf_port_id;
+ uint16_t repr_id;
+ uint16_t switch_domain_id;
+ efx_mport_sel_t mport_sel;
+};
+
+static int
+sfc_repr_assign_mae_switch_port(uint16_t switch_domain_id,
+ const struct sfc_mae_switch_port_request *req,
+ uint16_t *switch_port_id)
+{
+ int rc;
+
+ rc = sfc_mae_assign_switch_port(switch_domain_id, req, switch_port_id);
+
+ SFC_ASSERT(rc >= 0);
+ return -rc;
+}
+
+static int
+sfc_repr_eth_dev_init(struct rte_eth_dev *dev, void *init_params)
+{
+ const struct sfc_repr_init_data *repr_data = init_params;
+ struct sfc_repr_shared *srs = sfc_repr_shared_by_eth_dev(dev);
+ struct sfc_mae_switch_port_request switch_port_request;
+ efx_mport_sel_t ethdev_mport_sel;
+ struct sfc_repr *sr;
+ int ret;
+
+ /*
+ * Currently there is no mport we can use for representor's
+ * ethdev. Use an invalid one for now. This way representors
+ * can be instantiated.
+ */
+ efx_mae_mport_invalid(ðdev_mport_sel);
+
+ memset(&switch_port_request, 0, sizeof(switch_port_request));
+ switch_port_request.type = SFC_MAE_SWITCH_PORT_REPRESENTOR;
+ switch_port_request.ethdev_mportp = ðdev_mport_sel;
+ switch_port_request.entity_mportp = &repr_data->mport_sel;
+ switch_port_request.ethdev_port_id = dev->data->port_id;
+
+ ret = sfc_repr_assign_mae_switch_port(repr_data->switch_domain_id,
+ &switch_port_request,
+ &srs->switch_port_id);
+ if (ret != 0) {
+ SFC_GENERIC_LOG(ERR,
+ "%s() failed to assign MAE switch port (domain id %u)",
+ __func__, repr_data->switch_domain_id);
+ goto fail_mae_assign_switch_port;
+ }
+
+ /*
+ * Allocate process private data from heap, since it should not
+ * be located in shared memory allocated using rte_malloc() API.
+ */
+ sr = calloc(1, sizeof(*sr));
+ if (sr == NULL) {
+ ret = -ENOMEM;
+ goto fail_alloc_sr;
+ }
+
+ sfc_repr_lock_init(sr);
+ sfc_repr_lock(sr);
+
+ dev->process_private = sr;
+
+ srs->pf_port_id = repr_data->pf_port_id;
+ srs->repr_id = repr_data->repr_id;
+ srs->switch_domain_id = repr_data->switch_domain_id;
+
+ dev->data->dev_flags |= RTE_ETH_DEV_REPRESENTOR;
+ dev->data->representor_id = srs->repr_id;
+ dev->data->backer_port_id = srs->pf_port_id;
+
+ dev->data->mac_addrs = rte_zmalloc("sfcr", RTE_ETHER_ADDR_LEN, 0);
+ if (dev->data->mac_addrs == NULL) {
+ ret = -ENOMEM;
+ goto fail_mac_addrs;
+ }
+
+ dev->dev_ops = &sfc_repr_dev_ops;
+
+ sr->state = SFC_ETHDEV_INITIALIZED;
+ sfc_repr_unlock(sr);
+
+ return 0;
+
+fail_mac_addrs:
+ sfc_repr_unlock(sr);
+ free(sr);
+
+fail_alloc_sr:
+fail_mae_assign_switch_port:
+ SFC_GENERIC_LOG(ERR, "%s() failed: %s", __func__, rte_strerror(-ret));
+ return ret;
+}
+
+int
+sfc_repr_create(struct rte_eth_dev *parent, uint16_t representor_id,
+ uint16_t switch_domain_id, const efx_mport_sel_t *mport_sel)
+{
+ struct sfc_repr_init_data repr_data;
+ char name[RTE_ETH_NAME_MAX_LEN];
+ int ret;
+
+ if (snprintf(name, sizeof(name), "net_%s_representor_%u",
+ parent->device->name, representor_id) >=
+ (int)sizeof(name)) {
+ SFC_GENERIC_LOG(ERR, "%s() failed name too long", __func__);
+ return -ENAMETOOLONG;
+ }
+
+ memset(&repr_data, 0, sizeof(repr_data));
+ repr_data.pf_port_id = parent->data->port_id;
+ repr_data.repr_id = representor_id;
+ repr_data.switch_domain_id = switch_domain_id;
+ repr_data.mport_sel = *mport_sel;
+
+ ret = rte_eth_dev_create(parent->device, name,
+ sizeof(struct sfc_repr_shared),
+ NULL, NULL,
+ sfc_repr_eth_dev_init, &repr_data);
+ if (ret != 0)
+ SFC_GENERIC_LOG(ERR, "%s() failed to create device", __func__);
+
+ SFC_GENERIC_LOG(INFO, "%s() done: %s", __func__, rte_strerror(-ret));
+
+ return ret;
+}