net/sfc: add init on attach
authorAndrew Rybchenko <arybchenko@solarflare.com>
Tue, 29 Nov 2016 16:19:03 +0000 (16:19 +0000)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 17 Jan 2017 18:39:26 +0000 (19:39 +0100)
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.

In the case of exception on the event queue, the event queue and
corresponding Rx/Tx queue should be restarted in the Rx/Tx queue
polling context. These operations require access to the device
control which should be serialized. The device level lock will do
the job.

Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
Reviewed-by: Andy Moreton <amoreton@solarflare.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
drivers/net/sfc/Makefile
drivers/net/sfc/sfc.c [new file with mode: 0644]
drivers/net/sfc/sfc.h
drivers/net/sfc/sfc_debug.h
drivers/net/sfc/sfc_ethdev.c
drivers/net/sfc/sfc_mcdi.c [new file with mode: 0644]

index 80d35f6..ea8cb0c 100644 (file)
@@ -80,6 +80,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/sfc.c b/drivers/net/sfc/sfc.c
new file mode 100644 (file)
index 0000000..6f86c07
--- /dev/null
@@ -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 = SFC_DEV_TO_PCI(eth_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 = SFC_DEV_TO_PCI(sa->eth_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;
+}
index d772260..7f4bdee 100644 (file)
@@ -34,6 +34,9 @@
 
 #include <rte_ethdev.h>
 #include <rte_kvargs.h>
+#include <rte_spinlock.h>
+
+#include "efx.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -42,13 +45,110 @@ extern "C" {
 #define SFC_DEV_TO_PCI(eth_dev) \
        RTE_DEV_TO_PCI((eth_dev)->device)
 
+/*
+ * +---------------+
+ * | 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_fini(__rte_unused 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
index f18ac7d..a541ffc 100644 (file)
 #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 = SFC_DEV_TO_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_ */
index ab4bd4d..37ba6b0 100644 (file)
@@ -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"
@@ -57,6 +59,8 @@ sfc_eth_dev_init(struct rte_eth_dev *dev)
        struct sfc_adapter *sa = dev->data->dev_private;
        struct rte_pci_device *pci_dev = SFC_DEV_TO_PCI(dev);
        int rc;
+       const efx_nic_cfg_t *encp;
+       const struct ether_addr *from;
 
        /* Required for logging */
        sa->eth_dev = dev;
@@ -75,11 +79,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_fini(sa);
+       rte_free(dev->data->mac_addrs);
+       dev->data->mac_addrs = NULL;
+
+fail_mac_addrs:
 fail_kvarg_debug_init:
        sfc_kvargs_cleanup(sa);
 
@@ -96,10 +132,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_fini(sa);
+
        sfc_log_init(sa, "done");
 
        /* Required for logging, so cleanup last */
@@ -108,13 +154,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/sfc_mcdi.c b/drivers/net/sfc/sfc_mcdi.c
new file mode 100644 (file)
index 0000000..9ba28e1
--- /dev/null
@@ -0,0 +1,198 @@
+/*-
+ * 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 __rte_unused;
+
+       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 __rte_unused;
+
+       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);
+}