From patchwork Fri Dec 2 07:44:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Rybchenko X-Patchwork-Id: 17395 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 9874CFA45; Fri, 2 Dec 2016 08:45:40 +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 3B86BFA34 for ; Fri, 2 Dec 2016 08:45:10 +0100 (CET) Received: from unknown [12.187.104.26] by nbfkord-smmo02.seg.att.com(mxl_mta-7.2.4-7) with SMTP id 48621485.0.106138.00-2388.245929.nbfkord-smmo02.seg.att.com (envelope-from ); Fri, 02 Dec 2016 07:45:10 +0000 (UTC) X-MXL-Hash: 584126862343e4f4-76de2538c07705af98055f63f40b09601401133c 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; Thu, 1 Dec 2016 23:45:01 -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; Thu, 1 Dec 2016 23:45:01 -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 uB27j0wx005862 for ; Fri, 2 Dec 2016 07:45:00 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 uB27ixwh026628 for ; Fri, 2 Dec 2016 07:45:00 GMT From: Andrew Rybchenko To: Date: Fri, 2 Dec 2016 07:44:26 +0000 Message-ID: <1480664691-26561-7-git-send-email-arybchenko@solarflare.com> X-Mailer: git-send-email 1.8.2.3 In-Reply-To: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com> References: <1480664691-26561-1-git-send-email-arybchenko@solarflare.com> MIME-Version: 1.0 X-AnalysisOut: [v=2.1 cv=NIXTjhOg c=1 sm=1 tr=0 a=8BlWFWvVlq5taO8ncb8nKg==] X-AnalysisOut: [:17 a=n5n_aSjo0skA:10 a=zRKbQ67AAAAA:8 a=eM4qgqThgr03olCwd] X-AnalysisOut: [GcA:9 a=D-aPRzS5uglZuY-u:21 a=P-ZFwieez7x7BluZ:21 a=PA03WX] X-AnalysisOut: [8tBzeizutn5_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 06/31] net/sfc: support link status change interrupt X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Reviewed-by: Andrew Lee Reviewed-by: Robert Stonehouse Signed-off-by: Andrew Rybchenko --- doc/guides/nics/features/sfc_efx.ini | 1 + doc/guides/nics/sfc_efx.rst | 4 +- drivers/net/sfc/sfc.c | 4 +- drivers/net/sfc/sfc.h | 4 + drivers/net/sfc/sfc_ethdev.c | 1 + drivers/net/sfc/sfc_ev.c | 30 +++++- drivers/net/sfc/sfc_intr.c | 204 +++++++++++++++++++++++++++++++++++ 7 files changed, 242 insertions(+), 6 deletions(-) diff --git a/doc/guides/nics/features/sfc_efx.ini b/doc/guides/nics/features/sfc_efx.ini index 25472f8..693d35e 100644 --- a/doc/guides/nics/features/sfc_efx.ini +++ b/doc/guides/nics/features/sfc_efx.ini @@ -5,6 +5,7 @@ ; [Features] Link status = Y +Link status event = Y Flow control = Y L3 checksum offload = P L4 checksum offload = P diff --git a/doc/guides/nics/sfc_efx.rst b/doc/guides/nics/sfc_efx.rst index 1cfed6a..94eedd1 100644 --- a/doc/guides/nics/sfc_efx.rst +++ b/doc/guides/nics/sfc_efx.rst @@ -44,7 +44,7 @@ SFC EFX PMD has support for: - Multiple transmit and receive queues -- Link state information +- Link state information including link status change interrupt - IPv4/IPv6 TCP/UDP transmit checksum offload @@ -58,8 +58,6 @@ Non-supported Features The features not yet supported include: -- Link status change interrupt - - Receive queue interupts - Priority-based flow control diff --git a/drivers/net/sfc/sfc.c b/drivers/net/sfc/sfc.c index ef9e0d4..36044a0 100644 --- a/drivers/net/sfc/sfc.c +++ b/drivers/net/sfc/sfc.c @@ -116,7 +116,9 @@ sfc_check_conf(struct sfc_adapter *sa) rc = EINVAL; } - if (conf->intr_conf.lsc != 0) { + if ((conf->intr_conf.lsc != 0) && + (sa->intr.type != EFX_INTR_LINE) && + (sa->intr.type != EFX_INTR_MESSAGE)) { sfc_err(sa, "Link status change interrupt not supported"); rc = EINVAL; } diff --git a/drivers/net/sfc/sfc.h b/drivers/net/sfc/sfc.h index 1189283..257622f 100644 --- a/drivers/net/sfc/sfc.h +++ b/drivers/net/sfc/sfc.h @@ -112,6 +112,8 @@ struct sfc_mcdi { struct sfc_intr { efx_intr_type_t type; + rte_intr_callback_fn handler; + boolean_t lsc_intr; }; struct sfc_evq_info; @@ -119,6 +121,8 @@ struct sfc_rxq_info; struct sfc_txq_info; struct sfc_port { + unsigned int lsc_seq; + unsigned int flow_ctrl; boolean_t flow_ctrl_autoneg; size_t pdu; diff --git a/drivers/net/sfc/sfc_ethdev.c b/drivers/net/sfc/sfc_ethdev.c index eff648b..8c46500 100644 --- a/drivers/net/sfc/sfc_ethdev.c +++ b/drivers/net/sfc/sfc_ethdev.c @@ -682,6 +682,7 @@ static struct eth_driver sfc_efx_pmd = { .pci_drv = { .id_table = pci_id_sfc_efx_map, .drv_flags = + RTE_PCI_DRV_INTR_LSC | RTE_PCI_DRV_NEED_MAPPING, .probe = rte_eth_dev_pci_probe, .remove = rte_eth_dev_pci_remove, diff --git a/drivers/net/sfc/sfc_ev.c b/drivers/net/sfc/sfc_ev.c index 34c1127..c8a2d23 100644 --- a/drivers/net/sfc/sfc_ev.c +++ b/drivers/net/sfc/sfc_ev.c @@ -286,11 +286,25 @@ sfc_ev_link_change(void *arg, efx_link_mode_t link_mode) struct sfc_adapter *sa = evq->sa; struct rte_eth_link *dev_link = &sa->eth_dev->data->dev_link; struct rte_eth_link new_link; + uint64_t new_link_u64; + uint64_t old_link_u64; EFX_STATIC_ASSERT(sizeof(*dev_link) == sizeof(rte_atomic64_t)); sfc_port_link_mode_to_info(link_mode, &new_link); - rte_atomic64_set((rte_atomic64_t *)dev_link, *(uint64_t *)&new_link); + + new_link_u64 = *(uint64_t *)&new_link; + do { + old_link_u64 = rte_atomic64_read((rte_atomic64_t *)dev_link); + if (old_link_u64 == new_link_u64) + break; + + if (rte_atomic64_cmpset((volatile uint64_t *)dev_link, + old_link_u64, new_link_u64)) { + evq->sa->port.lsc_seq++; + break; + } + } while (B_TRUE); return B_FALSE; } @@ -481,6 +495,12 @@ sfc_ev_start(struct sfc_adapter *sa) if (rc != 0) goto fail_mgmt_evq_start; + if (sa->intr.lsc_intr) { + rc = sfc_ev_qprime(sa->evq_info[sa->mgmt_evq_index].evq); + if (rc != 0) + goto fail_evq0_prime; + } + rte_spinlock_unlock(&sa->mgmt_evq_lock); /* @@ -498,6 +518,9 @@ sfc_ev_start(struct sfc_adapter *sa) return 0; +fail_evq0_prime: + sfc_ev_qstop(sa, 0); + fail_mgmt_evq_start: rte_spinlock_unlock(&sa->mgmt_evq_lock); efx_ev_fini(sa->nic); @@ -599,7 +622,10 @@ sfc_ev_qinit_info(struct sfc_adapter *sa, unsigned int sw_index) SFC_ASSERT(rte_is_power_of_2(max_entries)); evq_info->max_entries = max_entries; - evq_info->flags = sa->evq_flags | EFX_EVQ_FLAGS_NOTIFY_DISABLED; + evq_info->flags = sa->evq_flags | + ((sa->intr.lsc_intr && sw_index == sa->mgmt_evq_index) ? + EFX_EVQ_FLAGS_NOTIFY_INTERRUPT : + EFX_EVQ_FLAGS_NOTIFY_DISABLED); return 0; } diff --git a/drivers/net/sfc/sfc_intr.c b/drivers/net/sfc/sfc_intr.c index 1b7dcdd..e0b1693 100644 --- a/drivers/net/sfc/sfc_intr.c +++ b/drivers/net/sfc/sfc_intr.c @@ -27,10 +27,130 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * At the momemt of writing DPDK v16.07 has notion of two types of + * interrupts: LSC (link status change) and RXQ (receive indication). + * It allows to register interrupt callback for entire device which is + * not intended to be used for receive indication (i.e. link status + * change indication only). The handler has no information which HW + * interrupt has triggered it, so we don't know which event queue should + * be polled/reprimed (except qmask in the case of legacy line interrupt). + */ + +#include +#include + #include "efx.h" #include "sfc.h" #include "sfc_log.h" +#include "sfc_ev.h" + +static void +sfc_intr_handle_mgmt_evq(struct sfc_adapter *sa) +{ + struct sfc_evq *evq; + + rte_spinlock_lock(&sa->mgmt_evq_lock); + + evq = sa->evq_info[sa->mgmt_evq_index].evq; + + if (evq->init_state != SFC_EVQ_STARTED) { + sfc_log_init(sa, "interrupt on stopped EVQ %u", evq->evq_index); + } else { + sfc_ev_qpoll(evq); + + if (sfc_ev_qprime(evq) != 0) + sfc_err(sa, "cannot prime EVQ %u", evq->evq_index); + } + + rte_spinlock_unlock(&sa->mgmt_evq_lock); +} + +static void +sfc_intr_line_handler(struct rte_intr_handle *intr_handle, void *cb_arg) +{ + struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg; + efx_nic_t *enp = sa->nic; + boolean_t fatal; + uint32_t qmask; + unsigned int lsc_seq = sa->port.lsc_seq; + + sfc_log_init(sa, "entry"); + + if (sa->state != SFC_ADAPTER_STARTED && + sa->state != SFC_ADAPTER_STARTING && + sa->state != SFC_ADAPTER_STOPPING) { + sfc_log_init(sa, + "interrupt on stopped adapter, don't reenable"); + goto exit; + } + + efx_intr_status_line(enp, &fatal, &qmask); + if (fatal) { + (void)efx_intr_disable(enp); + (void)efx_intr_fatal(enp); + sfc_err(sa, "fatal, interrupts disabled"); + goto exit; + } + + if (qmask & (1 << sa->mgmt_evq_index)) + sfc_intr_handle_mgmt_evq(sa); + + if (rte_intr_enable(intr_handle) != 0) + sfc_err(sa, "cannot reenable interrupts"); + + sfc_log_init(sa, "done"); + +exit: + if (lsc_seq != sa->port.lsc_seq) { + sfc_info(sa, "link status change event: link %s", + sa->eth_dev->data->dev_link.link_status ? + "UP" : "DOWN"); + _rte_eth_dev_callback_process(sa->eth_dev, + RTE_ETH_EVENT_INTR_LSC, NULL); + } +} + +static void +sfc_intr_message_handler(struct rte_intr_handle *intr_handle, void *cb_arg) +{ + struct sfc_adapter *sa = (struct sfc_adapter *)cb_arg; + efx_nic_t *enp = sa->nic; + boolean_t fatal; + unsigned int lsc_seq = sa->port.lsc_seq; + + sfc_log_init(sa, "entry"); + + if (sa->state != SFC_ADAPTER_STARTED && + sa->state != SFC_ADAPTER_STARTING && + sa->state != SFC_ADAPTER_STOPPING) { + sfc_log_init(sa, "adapter not-started, don't reenable"); + goto exit; + } + + efx_intr_status_message(enp, sa->mgmt_evq_index, &fatal); + if (fatal) { + (void)efx_intr_disable(enp); + (void)efx_intr_fatal(enp); + sfc_err(sa, "fatal, interrupts disabled"); + goto exit; + } + + sfc_intr_handle_mgmt_evq(sa); + + if (rte_intr_enable(intr_handle) != 0) + sfc_err(sa, "cannot reenable interrupts"); + + sfc_log_init(sa, "done"); + +exit: + if (lsc_seq != sa->port.lsc_seq) { + sfc_info(sa, "link status change event"); + _rte_eth_dev_callback_process(sa->eth_dev, + RTE_ETH_EVENT_INTR_LSC, NULL); + } +} int sfc_intr_start(struct sfc_adapter *sa) @@ -54,11 +174,49 @@ sfc_intr_start(struct sfc_adapter *sa) intr_handle = &sa->eth_dev->pci_dev->intr_handle; + if (intr->handler != NULL) { + sfc_log_init(sa, "rte_intr_callback_register"); + rc = rte_intr_callback_register(intr_handle, intr->handler, + (void *)sa); + if (rc != 0) { + sfc_err(sa, + "cannot register interrupt handler (rc=%d)", + rc); + /* + * Convert error code from negative returned by RTE API + * to positive used in the driver. + */ + rc = -rc; + goto fail_rte_intr_cb_reg; + } + + sfc_log_init(sa, "rte_intr_enable"); + rc = rte_intr_enable(intr_handle); + if (rc != 0) { + sfc_err(sa, "cannot enable interrupts (rc=%d)", rc); + /* + * Convert error code from negative returned by RTE API + * to positive used in the driver. + */ + rc = -rc; + goto fail_rte_intr_enable; + } + + sfc_log_init(sa, "efx_intr_enable"); + efx_intr_enable(sa->nic); + } + sfc_log_init(sa, "done type=%u max_intr=%d nb_efd=%u vec=%p", intr_handle->type, intr_handle->max_intr, intr_handle->nb_efd, intr_handle->intr_vec); return 0; +fail_rte_intr_enable: + rte_intr_callback_unregister(intr_handle, intr->handler, (void *)sa); + +fail_rte_intr_cb_reg: + efx_intr_fini(sa->nic); + fail_intr_init: sfc_log_init(sa, "failed %d", rc); return rc; @@ -67,8 +225,29 @@ sfc_intr_start(struct sfc_adapter *sa) void sfc_intr_stop(struct sfc_adapter *sa) { + struct sfc_intr *intr = &sa->intr; + sfc_log_init(sa, "entry"); + if (intr->handler != NULL) { + struct rte_intr_handle *intr_handle; + int rc; + + efx_intr_disable(sa->nic); + + intr_handle = &sa->eth_dev->pci_dev->intr_handle; + if (rte_intr_disable(intr_handle) != 0) + sfc_err(sa, "cannot disable interrupts"); + + while ((rc = rte_intr_callback_unregister(intr_handle, + intr->handler, (void *)sa)) == -EAGAIN) + ; + if (rc != 1) + sfc_err(sa, + "cannot unregister interrupt handler %d", + rc); + } + efx_intr_fini(sa->nic); sfc_log_init(sa, "done"); @@ -77,8 +256,33 @@ sfc_intr_stop(struct sfc_adapter *sa) int sfc_intr_init(struct sfc_adapter *sa) { + struct sfc_intr *intr = &sa->intr; + sfc_log_init(sa, "entry"); + intr->handler = NULL; + intr->lsc_intr = (sa->eth_dev->data->dev_conf.intr_conf.lsc != 0); + if (!intr->lsc_intr) { + sfc_info(sa, "LSC tracking using interrupts is disabled"); + goto done; + } + + switch (intr->type) { + case EFX_INTR_MESSAGE: + intr->handler = sfc_intr_message_handler; + break; + case EFX_INTR_LINE: + intr->handler = sfc_intr_line_handler; + break; + case EFX_INTR_INVALID: + sfc_warn(sa, "interrupts are not supported"); + break; + default: + sfc_panic(sa, "unexpected EFX interrupt type %u\n", intr->type); + break; + } + +done: sfc_log_init(sa, "done"); return 0; }