[dpdk-dev] [PATCH 32/56] net/sfc: implement driver operation to init device on attach

Andrew Rybchenko arybchenko at solarflare.com
Mon Nov 21 16:00:46 CET 2016


The setup and configuration of the PMD is not performance sensitive,
but is not thread safe either. It is possible that the multiple
read/writes during PMD setup and configuration could be corrupted
in a multi-thread environment.  Since this is not performance
sensitive, the developer can choose to add their own layer to provide
thread-safe setup and configuration. It is expected that, in most
applications, the initial configuration of the network ports would be
done by a single thread at startup.

Reviewed-by: Andy Moreton <amoreton at solarflare.com>
Signed-off-by: Andrew Rybchenko <arybchenko at solarflare.com>
---
 drivers/net/sfc/efx/Makefile     |   2 +
 drivers/net/sfc/efx/sfc.c        | 227 +++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/efx/sfc.h        | 100 +++++++++++++++++
 drivers/net/sfc/efx/sfc_debug.h  |  12 +++
 drivers/net/sfc/efx/sfc_ethdev.c |  52 ++++++++-
 drivers/net/sfc/efx/sfc_mcdi.c   | 197 +++++++++++++++++++++++++++++++++
 6 files changed, 589 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/sfc/efx/sfc.c
 create mode 100644 drivers/net/sfc/efx/sfc_mcdi.c

diff --git a/drivers/net/sfc/efx/Makefile b/drivers/net/sfc/efx/Makefile
index de95ea8..eadb1ea 100644
--- a/drivers/net/sfc/efx/Makefile
+++ b/drivers/net/sfc/efx/Makefile
@@ -82,6 +82,8 @@ LIBABIVER := 1
 #
 SRCS-$(CONFIG_RTE_LIBRTE_SFC_EFX_PMD) += sfc_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_SFC_EFX_PMD) += sfc_kvargs.c
+SRCS-$(CONFIG_RTE_LIBRTE_SFC_EFX_PMD) += sfc.c
+SRCS-$(CONFIG_RTE_LIBRTE_SFC_EFX_PMD) += sfc_mcdi.c
 
 VPATH += $(SRCDIR)/base
 
diff --git a/drivers/net/sfc/efx/sfc.c b/drivers/net/sfc/efx/sfc.c
new file mode 100644
index 0000000..2a17d26
--- /dev/null
+++ b/drivers/net/sfc/efx/sfc.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * This software was jointly developed between OKTET Labs (under contract
+ * for Solarflare) and Solarflare Communications, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* sysconf() */
+#include <unistd.h>
+
+#include <rte_errno.h>
+
+#include "efx.h"
+
+#include "sfc.h"
+#include "sfc_log.h"
+
+
+int
+sfc_dma_alloc(const struct sfc_adapter *sa, const char *name, uint16_t id,
+	      size_t len, int socket_id, efsys_mem_t *esmp)
+{
+	const struct rte_memzone *mz;
+
+	sfc_log_init(sa, "name=%s id=%u len=%lu socket_id=%d",
+		     name, id, len, socket_id);
+
+	mz = rte_eth_dma_zone_reserve(sa->eth_dev, name, id, len,
+				      sysconf(_SC_PAGESIZE), socket_id);
+	if (mz == NULL) {
+		sfc_err(sa, "cannot reserve DMA zone for %s:%u %#x@%d: %s",
+			name, (unsigned int)id, (unsigned int)len, socket_id,
+			rte_strerror(rte_errno));
+		return ENOMEM;
+	}
+
+	esmp->esm_addr = rte_mem_phy2mch(mz->memseg_id, mz->phys_addr);
+	if (esmp->esm_addr == RTE_BAD_PHYS_ADDR) {
+		(void)rte_memzone_free(mz);
+		return EFAULT;
+	}
+
+	esmp->esm_mz = mz;
+	esmp->esm_base = mz->addr;
+
+	return 0;
+}
+
+void
+sfc_dma_free(const struct sfc_adapter *sa, efsys_mem_t *esmp)
+{
+	int rc;
+
+	sfc_log_init(sa, "name=%s", esmp->esm_mz->name);
+
+	rc = rte_memzone_free(esmp->esm_mz);
+	if (rc != 0)
+		sfc_err(sa, "rte_memzone_free(() failed: %d", rc);
+
+	memset(esmp, 0, sizeof(*esmp));
+}
+
+static int
+sfc_mem_bar_init(struct sfc_adapter *sa)
+{
+	struct rte_eth_dev *eth_dev = sa->eth_dev;
+	struct rte_pci_device *pci_dev = eth_dev->pci_dev;
+	efsys_bar_t *ebp = &sa->mem_bar;
+	unsigned int i;
+	struct rte_mem_resource *res;
+
+	for (i = 0; i < RTE_DIM(pci_dev->mem_resource); i++) {
+		res = &pci_dev->mem_resource[i];
+		if ((res->len != 0) && (res->phys_addr != 0)) {
+			/* Found first memory BAR */
+			SFC_BAR_LOCK_INIT(ebp, eth_dev->data->name);
+			ebp->esb_rid = i;
+			ebp->esb_dev = pci_dev;
+			ebp->esb_base = res->addr;
+			return 0;
+		}
+	}
+
+	return EFAULT;
+}
+
+static void
+sfc_mem_bar_fini(struct sfc_adapter *sa)
+{
+	efsys_bar_t *ebp = &sa->mem_bar;
+
+	SFC_BAR_LOCK_DESTROY(ebp);
+	memset(ebp, 0, sizeof(*ebp));
+}
+
+int
+sfc_attach(struct sfc_adapter *sa)
+{
+	struct rte_pci_device *pci_dev = sa->eth_dev->pci_dev;
+	efx_nic_t *enp;
+	int rc;
+
+	sfc_log_init(sa, "entry");
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	sa->socket_id = rte_socket_id();
+
+	sfc_log_init(sa, "init mem bar");
+	rc = sfc_mem_bar_init(sa);
+	if (rc != 0)
+		goto fail_mem_bar_init;
+
+	sfc_log_init(sa, "get family");
+	rc = efx_family(pci_dev->id.vendor_id, pci_dev->id.device_id,
+			&sa->family);
+	if (rc != 0)
+		goto fail_family;
+	sfc_log_init(sa, "family is %u", sa->family);
+
+	sfc_log_init(sa, "create nic");
+	rte_spinlock_init(&sa->nic_lock);
+	rc = efx_nic_create(sa->family, (efsys_identifier_t *)sa,
+			    &sa->mem_bar, &sa->nic_lock, &enp);
+	if (rc != 0)
+		goto fail_nic_create;
+	sa->nic = enp;
+
+	rc = sfc_mcdi_init(sa);
+	if (rc != 0)
+		goto fail_mcdi_init;
+
+	sfc_log_init(sa, "probe nic");
+	rc = efx_nic_probe(enp);
+	if (rc != 0)
+		goto fail_nic_probe;
+
+	efx_mcdi_new_epoch(enp);
+
+	sfc_log_init(sa, "reset nic");
+	rc = efx_nic_reset(enp);
+	if (rc != 0)
+		goto fail_nic_reset;
+
+	/* Initialize NIC to double-check hardware */
+	sfc_log_init(sa, "init nic");
+	rc = efx_nic_init(enp);
+	if (rc != 0)
+		goto fail_nic_init;
+
+	sfc_log_init(sa, "fini nic");
+	efx_nic_fini(enp);
+
+	sa->rxq_max = 1;
+	sa->txq_max = 1;
+
+	sa->state = SFC_ADAPTER_INITIALIZED;
+
+	sfc_log_init(sa, "done");
+	return 0;
+
+fail_nic_init:
+fail_nic_reset:
+	sfc_log_init(sa, "unprobe nic");
+	efx_nic_unprobe(enp);
+
+fail_nic_probe:
+	sfc_mcdi_fini(sa);
+
+fail_mcdi_init:
+	sfc_log_init(sa, "destroy nic");
+	sa->nic = NULL;
+	efx_nic_destroy(enp);
+
+fail_nic_create:
+fail_family:
+	sfc_mem_bar_fini(sa);
+
+fail_mem_bar_init:
+	sfc_log_init(sa, "failed %d", rc);
+	return rc;
+}
+
+void
+sfc_detach(struct sfc_adapter *sa)
+{
+	efx_nic_t *enp = sa->nic;
+
+	sfc_log_init(sa, "entry");
+
+	SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+	sfc_log_init(sa, "unprobe nic");
+	efx_nic_unprobe(enp);
+
+	sfc_mcdi_fini(sa);
+
+	sfc_log_init(sa, "destroy nic");
+	sa->nic = NULL;
+	efx_nic_destroy(enp);
+
+	sfc_mem_bar_fini(sa);
+
+	sa->state = SFC_ADAPTER_UNINITIALIZED;
+}
diff --git a/drivers/net/sfc/efx/sfc.h b/drivers/net/sfc/efx/sfc.h
index 16fd2bb..01d652d 100644
--- a/drivers/net/sfc/efx/sfc.h
+++ b/drivers/net/sfc/efx/sfc.h
@@ -34,18 +34,118 @@
 
 #include <rte_ethdev.h>
 #include <rte_kvargs.h>
+#include <rte_spinlock.h>
+
+#include "efx.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/*
+ * +---------------+
+ * | UNINITIALIZED |<-----------+
+ * +---------------+		|
+ *	|.eth_dev_init		|.eth_dev_uninit
+ *	V			|
+ * +---------------+------------+
+ * |  INITIALIZED  |
+ * +---------------+
+ */
+enum sfc_adapter_state {
+	SFC_ADAPTER_UNINITIALIZED = 0,
+	SFC_ADAPTER_INITIALIZED,
+
+	SFC_ADAPTER_NSTATES
+};
+
+enum sfc_mcdi_state {
+	SFC_MCDI_UNINITIALIZED = 0,
+	SFC_MCDI_INITIALIZED,
+	SFC_MCDI_BUSY,
+	SFC_MCDI_COMPLETED,
+
+	SFC_MCDI_NSTATES
+};
+
+struct sfc_mcdi {
+	rte_spinlock_t			lock;
+	efsys_mem_t			mem;
+	enum sfc_mcdi_state		state;
+	efx_mcdi_transport_t		transport;
+};
+
 /* Adapter private data */
 struct sfc_adapter {
+	/*
+	 * 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_adapter_state		state;
 	struct rte_eth_dev		*eth_dev;
 	struct rte_kvargs		*kvargs;
 	bool				debug_init;
+	int				socket_id;
+	efsys_bar_t			mem_bar;
+	efx_family_t			family;
+	efx_nic_t			*nic;
+	rte_spinlock_t			nic_lock;
+
+	struct sfc_mcdi			mcdi;
+
+	unsigned int			rxq_max;
+	unsigned int			txq_max;
 };
 
+/*
+ * Add wrapper functions to acquire/release lock to be able to remove or
+ * change the lock in one place.
+ */
+
+static inline void
+sfc_adapter_lock_init(struct sfc_adapter *sa)
+{
+	rte_spinlock_init(&sa->lock);
+}
+
+static inline int
+sfc_adapter_is_locked(struct sfc_adapter *sa)
+{
+	return rte_spinlock_is_locked(&sa->lock);
+}
+
+static inline void
+sfc_adapter_lock(struct sfc_adapter *sa)
+{
+	rte_spinlock_lock(&sa->lock);
+}
+
+static inline void
+sfc_adapter_unlock(struct sfc_adapter *sa)
+{
+	rte_spinlock_unlock(&sa->lock);
+}
+
+static inline void
+sfc_adapter_lock_destroy(struct sfc_adapter *sa)
+{
+	/* Just for symmetry of the API */
+}
+
+int sfc_dma_alloc(const struct sfc_adapter *sa, const char *name, uint16_t id,
+		  size_t len, int socket_id, efsys_mem_t *esmp);
+void sfc_dma_free(const struct sfc_adapter *sa, efsys_mem_t *esmp);
+
+int sfc_attach(struct sfc_adapter *sa);
+void sfc_detach(struct sfc_adapter *sa);
+
+int sfc_mcdi_init(struct sfc_adapter *sa);
+void sfc_mcdi_fini(struct sfc_adapter *sa);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/drivers/net/sfc/efx/sfc_debug.h b/drivers/net/sfc/efx/sfc_debug.h
index de3ec61..2c3988b 100644
--- a/drivers/net/sfc/efx/sfc_debug.h
+++ b/drivers/net/sfc/efx/sfc_debug.h
@@ -42,4 +42,16 @@
 #define	SFC_ASSERT(exp)			RTE_ASSERT(exp)
 #endif
 
+/* Log PMD message, automatically add prefix and \n */
+#define	sfc_panic(sa, fmt, args...) \
+	do {								\
+		const struct rte_eth_dev *_dev = (sa)->eth_dev;		\
+		const struct rte_pci_device *_pci_dev = _dev->pci_dev;	\
+									\
+		rte_panic("sfc " PCI_PRI_FMT " #%" PRIu8 ": " fmt "\n",	\
+			  _pci_dev->addr.domain, _pci_dev->addr.bus,	\
+			  _pci_dev->addr.devid, _pci_dev->addr.function,\
+			  _dev->data->port_id, ##args);			\
+	} while (0)
+
 #endif /* _SFC_DEBUG_H_ */
diff --git a/drivers/net/sfc/efx/sfc_ethdev.c b/drivers/net/sfc/efx/sfc_ethdev.c
index 0deff07..e5b609c 100644
--- a/drivers/net/sfc/efx/sfc_ethdev.c
+++ b/drivers/net/sfc/efx/sfc_ethdev.c
@@ -31,6 +31,8 @@
 #include <rte_ethdev.h>
 #include <rte_pci.h>
 
+#include "efx.h"
+
 #include "sfc.h"
 #include "sfc_debug.h"
 #include "sfc_log.h"
@@ -55,6 +57,8 @@ sfc_eth_dev_init(struct rte_eth_dev *dev)
 	struct sfc_adapter *sa = dev->data->dev_private;
 	struct rte_pci_device *pci_dev = dev->pci_dev;
 	int rc;
+	const efx_nic_cfg_t *encp;
+	const struct ether_addr *from;
 
 	/* Required for logging */
 	sa->eth_dev = dev;
@@ -73,11 +77,43 @@ sfc_eth_dev_init(struct rte_eth_dev *dev)
 
 	sfc_log_init(sa, "entry");
 
+	dev->data->mac_addrs = rte_zmalloc("sfc", ETHER_ADDR_LEN, 0);
+	if (dev->data->mac_addrs == NULL) {
+		rc = ENOMEM;
+		goto fail_mac_addrs;
+	}
+
+	sfc_adapter_lock_init(sa);
+	sfc_adapter_lock(sa);
+
+	sfc_log_init(sa, "attaching");
+	rc = sfc_attach(sa);
+	if (rc != 0)
+		goto fail_attach;
+
+	encp = efx_nic_cfg_get(sa->nic);
+
+	/*
+	 * The arguments are really reverse order in comparison to
+	 * Linux kernel. Copy from NIC config to Ethernet device data.
+	 */
+	from = (const struct ether_addr *)(encp->enc_mac_addr);
+	ether_addr_copy(from, &dev->data->mac_addrs[0]);
+
 	dev->dev_ops = &sfc_eth_dev_ops;
 
+	sfc_adapter_unlock(sa);
+
 	sfc_log_init(sa, "done");
 	return 0;
 
+fail_attach:
+	sfc_adapter_unlock(sa);
+	sfc_adapter_lock_destroy(sa);
+	rte_free(dev->data->mac_addrs);
+	dev->data->mac_addrs = NULL;
+
+fail_mac_addrs:
 fail_kvarg_debug_init:
 	sfc_kvargs_cleanup(sa);
 
@@ -94,10 +130,20 @@ sfc_eth_dev_uninit(struct rte_eth_dev *dev)
 
 	sfc_log_init(sa, "entry");
 
+	sfc_adapter_lock(sa);
+
+	sfc_detach(sa);
+
+	rte_free(dev->data->mac_addrs);
+	dev->data->mac_addrs = NULL;
+
 	dev->dev_ops = NULL;
 
 	sfc_kvargs_cleanup(sa);
 
+	sfc_adapter_unlock(sa);
+	sfc_adapter_lock_destroy(sa);
+
 	sfc_log_init(sa, "done");
 
 	/* Required for logging, so cleanup last */
@@ -106,13 +152,17 @@ sfc_eth_dev_uninit(struct rte_eth_dev *dev)
 }
 
 static const struct rte_pci_id pci_id_sfc_efx_map[] = {
+	{ RTE_PCI_DEVICE(EFX_PCI_VENID_SFC, EFX_PCI_DEVID_FARMINGDALE) },
+	{ RTE_PCI_DEVICE(EFX_PCI_VENID_SFC, EFX_PCI_DEVID_GREENPORT) },
+	{ RTE_PCI_DEVICE(EFX_PCI_VENID_SFC, EFX_PCI_DEVID_MEDFORD) },
 	{ .vendor_id = 0 /* sentinel */ }
 };
 
 static struct eth_driver sfc_efx_pmd = {
 	.pci_drv = {
 		.id_table = pci_id_sfc_efx_map,
-		.drv_flags = 0,
+		.drv_flags =
+			RTE_PCI_DRV_NEED_MAPPING,
 		.probe = rte_eth_dev_pci_probe,
 		.remove = rte_eth_dev_pci_remove,
 	},
diff --git a/drivers/net/sfc/efx/sfc_mcdi.c b/drivers/net/sfc/efx/sfc_mcdi.c
new file mode 100644
index 0000000..bf641d9
--- /dev/null
+++ b/drivers/net/sfc/efx/sfc_mcdi.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2016 Solarflare Communications Inc.
+ * All rights reserved.
+ *
+ * This software was jointly developed between OKTET Labs (under contract
+ * for Solarflare) and Solarflare Communications, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <rte_cycles.h>
+
+#include "efx.h"
+#include "efx_mcdi.h"
+#include "efx_regs_mcdi.h"
+
+#include "sfc.h"
+#include "sfc_log.h"
+
+#define	SFC_MCDI_POLL_INTERVAL_MIN_US	10		/* 10us in 1us units */
+#define	SFC_MCDI_POLL_INTERVAL_MAX_US	(US_PER_S / 10)	/* 100ms in 1us units */
+#define	SFC_MCDI_WATCHDOG_INTERVAL_US	(10 * US_PER_S)	/* 10s in 1us units */
+
+static void
+sfc_mcdi_timeout(struct sfc_adapter *sa)
+{
+	sfc_warn(sa, "MC TIMEOUT");
+
+	sfc_panic(sa, "MCDI timeout handling is not implemented\n");
+}
+
+static void
+sfc_mcdi_poll(struct sfc_adapter *sa)
+{
+	efx_nic_t *enp;
+	unsigned int delay_total;
+	unsigned int delay_us;
+	boolean_t aborted;
+
+	delay_total = 0;
+	delay_us = SFC_MCDI_POLL_INTERVAL_MIN_US;
+	enp = sa->nic;
+
+	do {
+		if (efx_mcdi_request_poll(enp))
+			return;
+
+		if (delay_total > SFC_MCDI_WATCHDOG_INTERVAL_US) {
+			aborted = efx_mcdi_request_abort(enp);
+			SFC_ASSERT(aborted);
+			sfc_mcdi_timeout(sa);
+			return;
+		}
+
+		rte_delay_us(delay_us);
+
+		delay_total += delay_us;
+
+		/* Exponentially back off the poll frequency */
+		RTE_BUILD_BUG_ON(SFC_MCDI_POLL_INTERVAL_MAX_US > UINT_MAX / 2);
+		delay_us *= 2;
+		if (delay_us > SFC_MCDI_POLL_INTERVAL_MAX_US)
+			delay_us = SFC_MCDI_POLL_INTERVAL_MAX_US;
+
+	} while (1);
+}
+
+static void
+sfc_mcdi_execute(void *arg, efx_mcdi_req_t *emrp)
+{
+	struct sfc_adapter *sa = (struct sfc_adapter *)arg;
+	struct sfc_mcdi *mcdi = &sa->mcdi;
+
+	rte_spinlock_lock(&mcdi->lock);
+
+	SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
+
+	efx_mcdi_request_start(sa->nic, emrp, B_FALSE);
+	sfc_mcdi_poll(sa);
+
+	rte_spinlock_unlock(&mcdi->lock);
+}
+
+static void
+sfc_mcdi_ev_cpl(void *arg)
+{
+	struct sfc_adapter *sa = (struct sfc_adapter *)arg;
+	struct sfc_mcdi *mcdi = &sa->mcdi;
+
+	SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
+
+	/* MCDI is polled, completions are not expected */
+	SFC_ASSERT(0);
+}
+
+static void
+sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme)
+{
+	struct sfc_adapter *sa = (struct sfc_adapter *)arg;
+
+	sfc_warn(sa, "MC %s",
+	    (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" :
+	    (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN");
+
+	sfc_panic(sa, "MCDI exceptions handling is not implemented\n");
+}
+
+int
+sfc_mcdi_init(struct sfc_adapter *sa)
+{
+	struct sfc_mcdi *mcdi;
+	size_t max_msg_size;
+	efx_mcdi_transport_t *emtp;
+	int rc;
+
+	sfc_log_init(sa, "entry");
+
+	mcdi = &sa->mcdi;
+
+	SFC_ASSERT(mcdi->state == SFC_MCDI_UNINITIALIZED);
+
+	rte_spinlock_init(&mcdi->lock);
+
+	mcdi->state = SFC_MCDI_INITIALIZED;
+
+	max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2;
+	rc = sfc_dma_alloc(sa, "mcdi", 0, max_msg_size, sa->socket_id,
+			   &mcdi->mem);
+	if (rc != 0)
+		goto fail_dma_alloc;
+
+	emtp = &mcdi->transport;
+	emtp->emt_context = sa;
+	emtp->emt_dma_mem = &mcdi->mem;
+	emtp->emt_execute = sfc_mcdi_execute;
+	emtp->emt_ev_cpl = sfc_mcdi_ev_cpl;
+	emtp->emt_exception = sfc_mcdi_exception;
+
+	sfc_log_init(sa, "init MCDI");
+	rc = efx_mcdi_init(sa->nic, emtp);
+	if (rc != 0)
+		goto fail_mcdi_init;
+
+	return 0;
+
+fail_mcdi_init:
+	memset(emtp, 0, sizeof(*emtp));
+	sfc_dma_free(sa, &mcdi->mem);
+
+fail_dma_alloc:
+	mcdi->state = SFC_MCDI_UNINITIALIZED;
+	return rc;
+}
+
+void
+sfc_mcdi_fini(struct sfc_adapter *sa)
+{
+	struct sfc_mcdi *mcdi;
+	efx_mcdi_transport_t *emtp;
+
+	sfc_log_init(sa, "entry");
+
+	mcdi = &sa->mcdi;
+	emtp = &mcdi->transport;
+
+	rte_spinlock_lock(&mcdi->lock);
+
+	SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED);
+	mcdi->state = SFC_MCDI_UNINITIALIZED;
+
+	sfc_log_init(sa, "fini MCDI");
+	efx_mcdi_fini(sa->nic);
+	memset(emtp, 0, sizeof(*emtp));
+
+	rte_spinlock_unlock(&mcdi->lock);
+
+	sfc_dma_free(sa, &mcdi->mem);
+}
-- 
2.5.5



More information about the dev mailing list