[11/38] net/sfc: add port representors infrastructure

Message ID 20210827065717.1838258-12-andrew.rybchenko@oktetlabs.ru (mailing list archive)
State Superseded, archived
Delegated to: Ferruh Yigit
Headers
Series net/sfc: support port representors |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Andrew Rybchenko Aug. 27, 2021, 6:56 a.m. UTC
  From: Igor Romanov <igor.romanov@oktetlabs.ru>

Provide minimal implementation for port representors that only can be
configured and can provide device information.

Signed-off-by: Igor Romanov <igor.romanov@oktetlabs.ru>
Signed-off-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
Reviewed-by: Ivan Malov <ivan.malov@oktetlabs.ru>
---
 doc/guides/nics/sfc_efx.rst  |  13 +-
 drivers/net/sfc/meson.build  |   1 +
 drivers/net/sfc/sfc_ethdev.c | 156 +++++++++++-
 drivers/net/sfc/sfc_kvargs.c |   1 +
 drivers/net/sfc/sfc_kvargs.h |   2 +
 drivers/net/sfc/sfc_repr.c   | 458 +++++++++++++++++++++++++++++++++++
 drivers/net/sfc/sfc_repr.h   |  36 +++
 drivers/net/sfc/sfc_switch.h |   5 +
 8 files changed, 663 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/sfc/sfc_repr.c
 create mode 100644 drivers/net/sfc/sfc_repr.h
  

Patch

diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst
index d66cb76dab..4719031508 100644
--- a/doc/guides/nics/sfc_efx.rst
+++ b/doc/guides/nics/sfc_efx.rst
@@ -74,6 +74,8 @@  SFC EFX PMD has support for:
 
 - SR-IOV PF
 
+- Port representors (see :ref: switch_representation)
+
 
 Non-supported Features
 ----------------------
@@ -382,7 +384,16 @@  boolean parameters value.
   software virtual switch (for example, Open vSwitch) makes the decision.
   Software virtual switch may install MAE rules to pass established traffic
   flows via hardware and offload software datapath as the result.
-  Default is legacy.
+  Default is legacy, unless representors are specified, in which case switchdev
+  is chosen.
+
+- ``representor`` parameter [list]
+
+  Instantiate port representor Ethernet devices for specified Virtual
+  Functions list.
+
+  It is a standard parameter whose format is described in
+  :ref:`ethernet_device_standard_device_arguments`.
 
 - ``rx_datapath`` [auto|efx|ef10|ef10_essb] (default **auto**)
 
diff --git a/drivers/net/sfc/meson.build b/drivers/net/sfc/meson.build
index 4fc2063f7a..98365e9e73 100644
--- a/drivers/net/sfc/meson.build
+++ b/drivers/net/sfc/meson.build
@@ -98,4 +98,5 @@  sources = files(
         'sfc_ef100_tx.c',
         'sfc_service.c',
         'sfc_repr_proxy.c',
+        'sfc_repr.c',
 )
diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c
index ff762bb90b..8308cbdfef 100644
--- a/drivers/net/sfc/sfc_ethdev.c
+++ b/drivers/net/sfc/sfc_ethdev.c
@@ -28,6 +28,7 @@ 
 #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
@@ -1908,6 +1909,10 @@  static const struct eth_dev_ops sfc_eth_dev_ops = {
 	.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.
@@ -2189,7 +2194,7 @@  sfc_register_dp(void)
 }
 
 static int
-sfc_parse_switch_mode(struct sfc_adapter *sa)
+sfc_parse_switch_mode(struct sfc_adapter *sa, bool has_representors)
 {
 	const char *switch_mode = NULL;
 	int rc;
@@ -2201,9 +2206,9 @@  sfc_parse_switch_mode(struct sfc_adapter *sa)
 	if (rc != 0)
 		goto fail_kvargs;
 
-	/* Check representors when supported */
-	if (switch_mode == NULL ||
-	    strcasecmp(switch_mode, SFC_KVARG_SWITCH_MODE_LEGACY) == 0) {
+	if (switch_mode == NULL) {
+		sa->switchdev = has_representors;
+	} else if (strcasecmp(switch_mode, SFC_KVARG_SWITCH_MODE_LEGACY) == 0) {
 		sa->switchdev = false;
 	} else if (strcasecmp(switch_mode,
 			      SFC_KVARG_SWITCH_MODE_SWITCHDEV) == 0) {
@@ -2227,10 +2232,11 @@  sfc_parse_switch_mode(struct sfc_adapter *sa)
 }
 
 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;
@@ -2312,7 +2318,7 @@  sfc_eth_dev_init(struct rte_eth_dev *dev)
 	sfc_adapter_lock_init(sa);
 	sfc_adapter_lock(sa);
 
-	rc = sfc_parse_switch_mode(sa);
+	rc = sfc_parse_switch_mode(sa, init_data->nb_representors > 0);
 	if (rc != 0)
 		goto fail_switch_mode;
 
@@ -2402,11 +2408,145 @@  static const struct rte_pci_id pci_id_sfc_efx_map[] = {
 	{ .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, &eth_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,
+					   &eth_da);
+		if (rc != 0)
+			return rc;
+	} else {
+		memset(&eth_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, &eth_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)
diff --git a/drivers/net/sfc/sfc_kvargs.c b/drivers/net/sfc/sfc_kvargs.c
index cd16213637..783cb43ae6 100644
--- a/drivers/net/sfc/sfc_kvargs.c
+++ b/drivers/net/sfc/sfc_kvargs.c
@@ -23,6 +23,7 @@  sfc_kvargs_parse(struct sfc_adapter *sa)
 	struct rte_devargs *devargs = eth_dev->device->devargs;
 	const char **params = (const char *[]){
 		SFC_KVARG_SWITCH_MODE,
+		SFC_KVARG_REPRESENTOR,
 		SFC_KVARG_STATS_UPDATE_PERIOD_MS,
 		SFC_KVARG_PERF_PROFILE,
 		SFC_KVARG_RX_DATAPATH,
diff --git a/drivers/net/sfc/sfc_kvargs.h b/drivers/net/sfc/sfc_kvargs.h
index 8e34ec92a2..2226f2b3d9 100644
--- a/drivers/net/sfc/sfc_kvargs.h
+++ b/drivers/net/sfc/sfc_kvargs.h
@@ -26,6 +26,8 @@  extern "C" {
 	"[" SFC_KVARG_SWITCH_MODE_LEGACY "|" \
 	    SFC_KVARG_SWITCH_MODE_SWITCHDEV "]"
 
+#define SFC_KVARG_REPRESENTOR		"representor"
+
 #define SFC_KVARG_PERF_PROFILE		"perf_profile"
 
 #define SFC_KVARG_PERF_PROFILE_AUTO		"auto"
diff --git a/drivers/net/sfc/sfc_repr.c b/drivers/net/sfc/sfc_repr.c
new file mode 100644
index 0000000000..603a613ec6
--- /dev/null
+++ b/drivers/net/sfc/sfc_repr.c
@@ -0,0 +1,458 @@ 
+/* 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);
+}
+
+#ifdef RTE_LIBRTE_SFC_EFX_DEBUG
+
+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(&ethdev_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 = &ethdev_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->parent_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;
+}
diff --git a/drivers/net/sfc/sfc_repr.h b/drivers/net/sfc/sfc_repr.h
new file mode 100644
index 0000000000..1347206006
--- /dev/null
+++ b/drivers/net/sfc/sfc_repr.h
@@ -0,0 +1,36 @@ 
+/* 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.
+ */
+
+#ifndef _SFC_REPR_H
+#define _SFC_REPR_H
+
+#include <stdint.h>
+
+#include <rte_ethdev.h>
+
+#include "efx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Max count of the representor Rx queues */
+#define SFC_REPR_RXQ_MAX	1
+
+/** Max count of the representor Tx queues */
+#define SFC_REPR_TXQ_MAX	1
+
+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);
+
+#ifdef __cplusplus
+}
+#endif
+#endif  /* _SFC_REPR_H */
diff --git a/drivers/net/sfc/sfc_switch.h b/drivers/net/sfc/sfc_switch.h
index 84a02a61f8..a1a2ab9848 100644
--- a/drivers/net/sfc/sfc_switch.h
+++ b/drivers/net/sfc/sfc_switch.h
@@ -27,6 +27,11 @@  enum sfc_mae_switch_port_type {
 	 * and thus refers to its underlying PCIe function
 	 */
 	SFC_MAE_SWITCH_PORT_INDEPENDENT = 0,
+	/**
+	 * The switch port is operated by a representor RTE ethdev
+	 * and thus refers to the represented PCIe function
+	 */
+	SFC_MAE_SWITCH_PORT_REPRESENTOR,
 };
 
 struct sfc_mae_switch_port_request {