From 995a3bf49c1a868b2c33f4eecb1847ee3ab286da Mon Sep 17 00:00:00 2001 From: Andrew Rybchenko Date: Sun, 5 Jun 2016 06:37:54 +0000 Subject: [PATCH] sfxge(4): allow firmware to auto-configure event queues on Medford On Medford, licenses are required to enable RX and event cut through and to disable RX batching. To avoid the need for the driver to make decisions based on the licensing state, the MC_CMD_INIT_EVQ has been extended to allow us to leave the decision to the firmware. If the adapter is licensed for low-latency use, the firmware will choose the optimal settings for latency, otherwise it will use the best settings for throughput. For Huntington we still need to choose the settings ourselves. Submitted by: Mark Spender Sponsored by: Solarflare Communications, Inc. MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D6717 --- sys/dev/sfxge/common/ef10_ev.c | 163 +++++++++++++++++++++++++++++--- sys/dev/sfxge/common/ef10_nic.c | 7 ++ sys/dev/sfxge/common/efx.h | 1 + 3 files changed, 158 insertions(+), 13 deletions(-) diff --git a/sys/dev/sfxge/common/ef10_ev.c b/sys/dev/sfxge/common/ef10_ev.c index 0121fa75aa33..9887a37a3f51 100644 --- a/sys/dev/sfxge/common/ef10_ev.c +++ b/sys/dev/sfxge/common/ef10_ev.c @@ -139,7 +139,8 @@ efx_mcdi_init_evq( __in efsys_mem_t *esmp, __in size_t nevs, __in uint32_t irq, - __in uint32_t us) + __in uint32_t us, + __in boolean_t low_latency) { efx_mcdi_req_t req; uint8_t payload[ @@ -149,7 +150,7 @@ efx_mcdi_init_evq( uint64_t addr; int npages; int i; - int supports_rx_batching; + int ev_cut_through; efx_rc_t rc; npages = EFX_EVQ_NBUFS(nevs); @@ -170,21 +171,19 @@ efx_mcdi_init_evq( MCDI_IN_SET_DWORD(req, INIT_EVQ_IN_IRQ_NUM, irq); /* - * On Huntington RX and TX event batching can only be requested - * together (even if the datapath firmware doesn't actually support RX - * batching). - * Cut through is incompatible with RX batching and so enabling cut - * through disables RX batching (but it does not affect TX batching). + * On Huntington RX and TX event batching can only be requested together + * (even if the datapath firmware doesn't actually support RX + * batching). If event cut through is enabled no RX batching will occur. * - * So always enable RX and TX event batching, and enable cut through - * if RX event batching isn't supported (i.e. on low latency firmware). + * So always enable RX and TX event batching, and enable event cut + * through if we want low latency operation. */ - supports_rx_batching = enp->en_nic_cfg.enc_rx_batching_enabled ? 1 : 0; + ev_cut_through = low_latency ? 1 : 0; MCDI_IN_POPULATE_DWORD_6(req, INIT_EVQ_IN_FLAGS, INIT_EVQ_IN_FLAG_INTERRUPTING, 1, INIT_EVQ_IN_FLAG_RPTR_DOS, 0, INIT_EVQ_IN_FLAG_INT_ARMD, 0, - INIT_EVQ_IN_FLAG_CUT_THRU, !supports_rx_batching, + INIT_EVQ_IN_FLAG_CUT_THRU, ev_cut_through, INIT_EVQ_IN_FLAG_RX_MERGE, 1, INIT_EVQ_IN_FLAG_TX_MERGE, 1); @@ -250,6 +249,114 @@ fail1: return (rc); } + +static __checkReturn efx_rc_t +efx_mcdi_init_evq_v2( + __in efx_nic_t *enp, + __in unsigned int instance, + __in efsys_mem_t *esmp, + __in size_t nevs, + __in uint32_t irq, + __in uint32_t us) +{ + efx_mcdi_req_t req; + uint8_t payload[ + MAX(MC_CMD_INIT_EVQ_V2_IN_LEN(EFX_EVQ_NBUFS(EFX_EVQ_MAXNEVS)), + MC_CMD_INIT_EVQ_V2_OUT_LEN)]; + efx_qword_t *dma_addr; + uint64_t addr; + int npages; + int i; + efx_rc_t rc; + + npages = EFX_EVQ_NBUFS(nevs); + if (MC_CMD_INIT_EVQ_V2_IN_LEN(npages) > MC_CMD_INIT_EVQ_V2_IN_LENMAX) { + rc = EINVAL; + goto fail1; + } + + (void) memset(payload, 0, sizeof (payload)); + req.emr_cmd = MC_CMD_INIT_EVQ; + req.emr_in_buf = payload; + req.emr_in_length = MC_CMD_INIT_EVQ_V2_IN_LEN(npages); + req.emr_out_buf = payload; + req.emr_out_length = MC_CMD_INIT_EVQ_V2_OUT_LEN; + + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_SIZE, nevs); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_INSTANCE, instance); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_IRQ_NUM, irq); + + MCDI_IN_POPULATE_DWORD_4(req, INIT_EVQ_V2_IN_FLAGS, + INIT_EVQ_V2_IN_FLAG_INTERRUPTING, 1, + INIT_EVQ_V2_IN_FLAG_RPTR_DOS, 0, + INIT_EVQ_V2_IN_FLAG_INT_ARMD, 0, + INIT_EVQ_V2_IN_FLAG_TYPE, MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO); + + /* If the value is zero then disable the timer */ + if (us == 0) { + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_MODE, + MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_DIS); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_LOAD, 0); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_RELOAD, 0); + } else { + unsigned int ticks; + + if ((rc = efx_ev_usecs_to_ticks(enp, us, &ticks)) != 0) + goto fail2; + + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_MODE, + MC_CMD_INIT_EVQ_V2_IN_TMR_INT_HLDOFF); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_LOAD, ticks); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_RELOAD, ticks); + } + + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_COUNT_MODE, + MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_DIS); + MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_COUNT_THRSHLD, 0); + + dma_addr = MCDI_IN2(req, efx_qword_t, INIT_EVQ_V2_IN_DMA_ADDR); + addr = EFSYS_MEM_ADDR(esmp); + + for (i = 0; i < npages; i++) { + EFX_POPULATE_QWORD_2(*dma_addr, + EFX_DWORD_1, (uint32_t)(addr >> 32), + EFX_DWORD_0, (uint32_t)(addr & 0xffffffff)); + + dma_addr++; + addr += EFX_BUF_SIZE; + } + + efx_mcdi_execute(enp, &req); + + if (req.emr_rc != 0) { + rc = req.emr_rc; + goto fail3; + } + + if (req.emr_out_length_used < MC_CMD_INIT_EVQ_V2_OUT_LEN) { + rc = EMSGSIZE; + goto fail4; + } + + /* NOTE: ignore the returned IRQ param as firmware does not set it. */ + + EFSYS_PROBE1(mcdi_evq_flags, uint32_t, + MCDI_OUT_DWORD(req, INIT_EVQ_V2_OUT_FLAGS)); + + return (0); + +fail4: + EFSYS_PROBE(fail4); +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + static __checkReturn efx_rc_t efx_mcdi_fini_evq( __in efx_nic_t *enp, @@ -348,11 +455,41 @@ ef10_ev_qcreate( * Interrupts may be raised for events immediately after the queue is * created. See bug58606. */ - if ((rc = efx_mcdi_init_evq(enp, index, esmp, n, irq, us)) != 0) - goto fail4; + + if (encp->enc_init_evq_v2_supported) { + /* + * On Medford the low latency license is required to enable RX + * and event cut through and to disable RX batching. We let the + * firmware decide the settings to use. If the adapter has a low + * latency license, it will choose the best settings for low + * latency, otherwise it choose the best settings for + * throughput. + */ + rc = efx_mcdi_init_evq_v2(enp, index, esmp, n, irq, us); + if (rc != 0) + goto fail4; + } else { + /* + * On Huntington we need to specify the settings to use. We + * favour latency if the adapter is running low-latency firmware + * and throughput otherwise, and assume not support RX batching + * implies the adapter is running low-latency firmware. (This + * is how it's been done since Huntington GA. It doesn't make + * much sense with hindsight as the 'low-latency' firmware + * variant is also best for throughput, and does now support RX + * batching). + */ + boolean_t low_latency = encp->enc_rx_batching_enabled ? 0 : 1; + rc = efx_mcdi_init_evq(enp, index, esmp, n, irq, us, + low_latency); + if (rc != 0) + goto fail5; + } return (0); +fail5: + EFSYS_PROBE(fail5); fail4: EFSYS_PROBE(fail4); fail3: diff --git a/sys/dev/sfxge/common/ef10_nic.c b/sys/dev/sfxge/common/ef10_nic.c index 6d04bcd2d23e..3c4058840a70 100644 --- a/sys/dev/sfxge/common/ef10_nic.c +++ b/sys/dev/sfxge/common/ef10_nic.c @@ -1025,6 +1025,13 @@ ef10_get_datapath_caps( encp->enc_enhanced_set_mac_supported = CAP_FLAG(flags, SET_MAC_ENHANCED) ? B_TRUE : B_FALSE; + /* + * Check if firmware supports version 2 of MC_CMD_INIT_EVQ, which allows + * us to let the firmware choose the settings to use on an EVQ. + */ + encp->enc_init_evq_v2_supported = + CAP_FLAG2(flags2, INIT_EVQ_V2) ? B_TRUE : B_FALSE; + #undef CAP_FLAG #undef CAP_FLAG2 diff --git a/sys/dev/sfxge/common/efx.h b/sys/dev/sfxge/common/efx.h index cb8f01ea4457..7419aeb33674 100644 --- a/sys/dev/sfxge/common/efx.h +++ b/sys/dev/sfxge/common/efx.h @@ -1148,6 +1148,7 @@ typedef struct efx_nic_cfg_s { boolean_t enc_rx_disable_scatter_supported; boolean_t enc_allow_set_mac_with_installed_filters; boolean_t enc_enhanced_set_mac_supported; + boolean_t enc_init_evq_v2_supported; /* External port identifier */ uint8_t enc_external_port; uint32_t enc_mcdi_max_payload_length;