From patchwork Tue Nov 29 16:19:03 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Rybchenko X-Patchwork-Id: 17328 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [IPv6:::1]) by dpdk.org (Postfix) with ESMTP id 1F6B5FAED; Tue, 29 Nov 2016 17:23:08 +0100 (CET) Received: from nbfkord-smmo02.seg.att.com (nbfkord-smmo02.seg.att.com [209.65.160.78]) by dpdk.org (Postfix) with ESMTP id A61285585 for ; Tue, 29 Nov 2016 17:20:57 +0100 (CET) Received: from unknown [12.187.104.26] (EHLO nbfkord-smmo02.seg.att.com) by nbfkord-smmo02.seg.att.com(mxl_mta-7.2.4-7) with ESMTP id aeaad385.2b92a8c6d940.1895572.00-2466.4144482.nbfkord-smmo02.seg.att.com (envelope-from ); Tue, 29 Nov 2016 16:20:58 +0000 (UTC) X-MXL-Hash: 583daaea051a8cbf-46bba44969286dd75ae45f7427cbda3237337b11 Received: from unknown [12.187.104.26] by nbfkord-smmo02.seg.att.com(mxl_mta-7.2.4-7) with SMTP id adaad385.0.1895549.00-2399.4144403.nbfkord-smmo02.seg.att.com (envelope-from ); Tue, 29 Nov 2016 16:20:43 +0000 (UTC) X-MXL-Hash: 583daadb37490cbe-62d9a69bfebed2d38efe81ba67c74a66e8cd9a52 Received: from ocex03.SolarFlarecom.com (10.20.40.36) by ocex03.SolarFlarecom.com (10.20.40.36) with Microsoft SMTP Server (TLS) id 15.0.1044.25; Tue, 29 Nov 2016 08:20:25 -0800 Received: from opal.uk.solarflarecom.com (10.17.10.1) by ocex03.SolarFlarecom.com (10.20.40.36) with Microsoft SMTP Server (TLS) id 15.0.1044.25 via Frontend Transport; Tue, 29 Nov 2016 08:20:25 -0800 Received: from uklogin.uk.solarflarecom.com (uklogin.uk.solarflarecom.com [10.17.10.10]) by opal.uk.solarflarecom.com (8.13.8/8.13.8) with ESMTP id uATGKO26029980; Tue, 29 Nov 2016 16:20:24 GMT Received: from uklogin.uk.solarflarecom.com (localhost.localdomain [127.0.0.1]) by uklogin.uk.solarflarecom.com (8.13.8/8.13.8) with ESMTP id uATGKM1d021233; Tue, 29 Nov 2016 16:20:24 GMT From: Andrew Rybchenko To: CC: Date: Tue, 29 Nov 2016 16:19:03 +0000 Message-ID: <1480436367-20749-32-git-send-email-arybchenko@solarflare.com> X-Mailer: git-send-email 1.8.2.3 In-Reply-To: <1480436367-20749-1-git-send-email-arybchenko@solarflare.com> References: <1479740470-6723-1-git-send-email-arybchenko@solarflare.com> <1480436367-20749-1-git-send-email-arybchenko@solarflare.com> MIME-Version: 1.0 X-AnalysisOut: [v=2.1 cv=UI/baXry c=1 sm=1 tr=0 a=8BlWFWvVlq5taO8ncb8nKg==] X-AnalysisOut: [:17 a=L24OOQBejmoA:10 a=zRKbQ67AAAAA:8 a=Y0nAatnirh7pzCmIB] X-AnalysisOut: [FsA:9 a=PA03WX8tBzeizutn5_OT:22] X-Spam: [F=0.2000000000; CM=0.500; S=0.200(2015072901)] X-MAIL-FROM: X-SOURCE-IP: [12.187.104.26] Subject: [dpdk-dev] [PATCH v2 31/55] net/sfc: implement driver operation to init device on attach X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: patches and discussions about DPDK List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" 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. Reviewed-by: Andy Moreton Signed-off-by: Andrew Rybchenko --- drivers/net/sfc/Makefile | 2 + drivers/net/sfc/sfc.c | 227 +++++++++++++++++++++++++++++++++++++++++++ drivers/net/sfc/sfc.h | 100 +++++++++++++++++++ drivers/net/sfc/sfc_debug.h | 12 +++ drivers/net/sfc/sfc_ethdev.c | 52 +++++++++- drivers/net/sfc/sfc_mcdi.c | 198 +++++++++++++++++++++++++++++++++++++ 6 files changed, 590 insertions(+), 1 deletion(-) create mode 100644 drivers/net/sfc/sfc.c create mode 100644 drivers/net/sfc/sfc_mcdi.c diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile index 80d35f6..ea8cb0c 100644 --- a/drivers/net/sfc/Makefile +++ b/drivers/net/sfc/Makefile @@ -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 index 0000000..2a17d26 --- /dev/null +++ b/drivers/net/sfc/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 + +#include + +#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/sfc.h b/drivers/net/sfc/sfc.h index 8cd5b47..b9a0b0b 100644 --- a/drivers/net/sfc/sfc.h +++ b/drivers/net/sfc/sfc.h @@ -34,18 +34,118 @@ #include #include +#include + +#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_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 diff --git a/drivers/net/sfc/sfc_debug.h b/drivers/net/sfc/sfc_debug.h index f18ac7d..e2494c1 100644 --- a/drivers/net/sfc/sfc_debug.h +++ b/drivers/net/sfc/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/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c index 39ae965..381f90c 100644 --- a/drivers/net/sfc/sfc_ethdev.c +++ b/drivers/net/sfc/sfc_ethdev.c @@ -31,6 +31,8 @@ #include #include +#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 = dev->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 index 0000000..9ba28e1 --- /dev/null +++ b/drivers/net/sfc/sfc_mcdi.c @@ -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 + +#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); +}