Mainly focus on files that use BSD 2-Clause license, however the tool I was using misidentified many licenses so this was mostly a manual - error prone - task. The Software Package Data Exchange (SPDX) group provides a specification to make it easier for automated tools to detect and summarize well known opensource licenses. We are gradually adopting the specification, noting that the tags are considered only advisory and do not, in any way, superceed or replace the license texts.
1476 lines
34 KiB
C
1476 lines
34 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2007-2016 Solarflare Communications Inc.
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* The views and conclusions contained in the software and documentation are
|
|
* those of the authors and should not be interpreted as representing official
|
|
* policies, either expressed or implied, of the FreeBSD Project.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "efx.h"
|
|
#include "efx_impl.h"
|
|
#if EFSYS_OPT_MON_MCDI
|
|
#include "mcdi_mon.h"
|
|
#endif
|
|
|
|
#if EFSYS_OPT_QSTATS
|
|
#define EFX_EV_QSTAT_INCR(_eep, _stat) \
|
|
do { \
|
|
(_eep)->ee_stat[_stat]++; \
|
|
_NOTE(CONSTANTCONDITION) \
|
|
} while (B_FALSE)
|
|
#else
|
|
#define EFX_EV_QSTAT_INCR(_eep, _stat)
|
|
#endif
|
|
|
|
#define EFX_EV_PRESENT(_qword) \
|
|
(EFX_QWORD_FIELD((_qword), EFX_DWORD_0) != 0xffffffff && \
|
|
EFX_QWORD_FIELD((_qword), EFX_DWORD_1) != 0xffffffff)
|
|
|
|
|
|
|
|
#if EFSYS_OPT_SIENA
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_init(
|
|
__in efx_nic_t *enp);
|
|
|
|
static void
|
|
siena_ev_fini(
|
|
__in efx_nic_t *enp);
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_qcreate(
|
|
__in efx_nic_t *enp,
|
|
__in unsigned int index,
|
|
__in efsys_mem_t *esmp,
|
|
__in size_t n,
|
|
__in uint32_t id,
|
|
__in uint32_t us,
|
|
__in uint32_t flags,
|
|
__in efx_evq_t *eep);
|
|
|
|
static void
|
|
siena_ev_qdestroy(
|
|
__in efx_evq_t *eep);
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_qprime(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int count);
|
|
|
|
static void
|
|
siena_ev_qpost(
|
|
__in efx_evq_t *eep,
|
|
__in uint16_t data);
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_qmoderate(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int us);
|
|
|
|
#if EFSYS_OPT_QSTATS
|
|
static void
|
|
siena_ev_qstats_update(
|
|
__in efx_evq_t *eep,
|
|
__inout_ecount(EV_NQSTATS) efsys_stat_t *stat);
|
|
|
|
#endif
|
|
|
|
#endif /* EFSYS_OPT_SIENA */
|
|
|
|
#if EFSYS_OPT_SIENA
|
|
static const efx_ev_ops_t __efx_ev_siena_ops = {
|
|
siena_ev_init, /* eevo_init */
|
|
siena_ev_fini, /* eevo_fini */
|
|
siena_ev_qcreate, /* eevo_qcreate */
|
|
siena_ev_qdestroy, /* eevo_qdestroy */
|
|
siena_ev_qprime, /* eevo_qprime */
|
|
siena_ev_qpost, /* eevo_qpost */
|
|
siena_ev_qmoderate, /* eevo_qmoderate */
|
|
#if EFSYS_OPT_QSTATS
|
|
siena_ev_qstats_update, /* eevo_qstats_update */
|
|
#endif
|
|
};
|
|
#endif /* EFSYS_OPT_SIENA */
|
|
|
|
#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
|
|
static const efx_ev_ops_t __efx_ev_ef10_ops = {
|
|
ef10_ev_init, /* eevo_init */
|
|
ef10_ev_fini, /* eevo_fini */
|
|
ef10_ev_qcreate, /* eevo_qcreate */
|
|
ef10_ev_qdestroy, /* eevo_qdestroy */
|
|
ef10_ev_qprime, /* eevo_qprime */
|
|
ef10_ev_qpost, /* eevo_qpost */
|
|
ef10_ev_qmoderate, /* eevo_qmoderate */
|
|
#if EFSYS_OPT_QSTATS
|
|
ef10_ev_qstats_update, /* eevo_qstats_update */
|
|
#endif
|
|
};
|
|
#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
|
|
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_ev_init(
|
|
__in efx_nic_t *enp)
|
|
{
|
|
const efx_ev_ops_t *eevop;
|
|
efx_rc_t rc;
|
|
|
|
EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
|
|
EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR);
|
|
|
|
if (enp->en_mod_flags & EFX_MOD_EV) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
switch (enp->en_family) {
|
|
#if EFSYS_OPT_SIENA
|
|
case EFX_FAMILY_SIENA:
|
|
eevop = &__efx_ev_siena_ops;
|
|
break;
|
|
#endif /* EFSYS_OPT_SIENA */
|
|
|
|
#if EFSYS_OPT_HUNTINGTON
|
|
case EFX_FAMILY_HUNTINGTON:
|
|
eevop = &__efx_ev_ef10_ops;
|
|
break;
|
|
#endif /* EFSYS_OPT_HUNTINGTON */
|
|
|
|
#if EFSYS_OPT_MEDFORD
|
|
case EFX_FAMILY_MEDFORD:
|
|
eevop = &__efx_ev_ef10_ops;
|
|
break;
|
|
#endif /* EFSYS_OPT_MEDFORD */
|
|
|
|
default:
|
|
EFSYS_ASSERT(0);
|
|
rc = ENOTSUP;
|
|
goto fail1;
|
|
}
|
|
|
|
EFSYS_ASSERT3U(enp->en_ev_qcount, ==, 0);
|
|
|
|
if ((rc = eevop->eevo_init(enp)) != 0)
|
|
goto fail2;
|
|
|
|
enp->en_eevop = eevop;
|
|
enp->en_mod_flags |= EFX_MOD_EV;
|
|
return (0);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
enp->en_eevop = NULL;
|
|
enp->en_mod_flags &= ~EFX_MOD_EV;
|
|
return (rc);
|
|
}
|
|
|
|
void
|
|
efx_ev_fini(
|
|
__in efx_nic_t *enp)
|
|
{
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
|
|
EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
|
|
EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_INTR);
|
|
EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_EV);
|
|
EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX));
|
|
EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX));
|
|
EFSYS_ASSERT3U(enp->en_ev_qcount, ==, 0);
|
|
|
|
eevop->eevo_fini(enp);
|
|
|
|
enp->en_eevop = NULL;
|
|
enp->en_mod_flags &= ~EFX_MOD_EV;
|
|
}
|
|
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_ev_qcreate(
|
|
__in efx_nic_t *enp,
|
|
__in unsigned int index,
|
|
__in efsys_mem_t *esmp,
|
|
__in size_t n,
|
|
__in uint32_t id,
|
|
__in uint32_t us,
|
|
__in uint32_t flags,
|
|
__deref_out efx_evq_t **eepp)
|
|
{
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
|
|
efx_evq_t *eep;
|
|
efx_rc_t rc;
|
|
|
|
EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
|
|
EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_EV);
|
|
|
|
EFSYS_ASSERT3U(enp->en_ev_qcount + 1, <, encp->enc_evq_limit);
|
|
|
|
switch (flags & EFX_EVQ_FLAGS_NOTIFY_MASK) {
|
|
case EFX_EVQ_FLAGS_NOTIFY_INTERRUPT:
|
|
break;
|
|
case EFX_EVQ_FLAGS_NOTIFY_DISABLED:
|
|
if (us != 0) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
break;
|
|
default:
|
|
rc = EINVAL;
|
|
goto fail2;
|
|
}
|
|
|
|
/* Allocate an EVQ object */
|
|
EFSYS_KMEM_ALLOC(enp->en_esip, sizeof (efx_evq_t), eep);
|
|
if (eep == NULL) {
|
|
rc = ENOMEM;
|
|
goto fail3;
|
|
}
|
|
|
|
eep->ee_magic = EFX_EVQ_MAGIC;
|
|
eep->ee_enp = enp;
|
|
eep->ee_index = index;
|
|
eep->ee_mask = n - 1;
|
|
eep->ee_flags = flags;
|
|
eep->ee_esmp = esmp;
|
|
|
|
/*
|
|
* Set outputs before the queue is created because interrupts may be
|
|
* raised for events immediately after the queue is created, before the
|
|
* function call below returns. See bug58606.
|
|
*
|
|
* The eepp pointer passed in by the client must therefore point to data
|
|
* shared with the client's event processing context.
|
|
*/
|
|
enp->en_ev_qcount++;
|
|
*eepp = eep;
|
|
|
|
if ((rc = eevop->eevo_qcreate(enp, index, esmp, n, id, us, flags,
|
|
eep)) != 0)
|
|
goto fail4;
|
|
|
|
return (0);
|
|
|
|
fail4:
|
|
EFSYS_PROBE(fail4);
|
|
|
|
*eepp = NULL;
|
|
enp->en_ev_qcount--;
|
|
EFSYS_KMEM_FREE(enp->en_esip, sizeof (efx_evq_t), eep);
|
|
fail3:
|
|
EFSYS_PROBE(fail3);
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
return (rc);
|
|
}
|
|
|
|
void
|
|
efx_ev_qdestroy(
|
|
__in efx_evq_t *eep)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
EFSYS_ASSERT(enp->en_ev_qcount != 0);
|
|
--enp->en_ev_qcount;
|
|
|
|
eevop->eevo_qdestroy(eep);
|
|
|
|
/* Free the EVQ object */
|
|
EFSYS_KMEM_FREE(enp->en_esip, sizeof (efx_evq_t), eep);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_ev_qprime(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int count)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
efx_rc_t rc;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
if (!(enp->en_mod_flags & EFX_MOD_INTR)) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
if ((rc = eevop->eevo_qprime(eep, count)) != 0)
|
|
goto fail2;
|
|
|
|
return (0);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
return (rc);
|
|
}
|
|
|
|
__checkReturn boolean_t
|
|
efx_ev_qpending(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int count)
|
|
{
|
|
size_t offset;
|
|
efx_qword_t qword;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
offset = (count & eep->ee_mask) * sizeof (efx_qword_t);
|
|
EFSYS_MEM_READQ(eep->ee_esmp, offset, &qword);
|
|
|
|
return (EFX_EV_PRESENT(qword));
|
|
}
|
|
|
|
#if EFSYS_OPT_EV_PREFETCH
|
|
|
|
void
|
|
efx_ev_qprefetch(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int count)
|
|
{
|
|
unsigned int offset;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
offset = (count & eep->ee_mask) * sizeof (efx_qword_t);
|
|
EFSYS_MEM_PREFETCH(eep->ee_esmp, offset);
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_EV_PREFETCH */
|
|
|
|
#define EFX_EV_BATCH 8
|
|
|
|
void
|
|
efx_ev_qpoll(
|
|
__in efx_evq_t *eep,
|
|
__inout unsigned int *countp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
efx_qword_t ev[EFX_EV_BATCH];
|
|
unsigned int batch;
|
|
unsigned int total;
|
|
unsigned int count;
|
|
unsigned int index;
|
|
size_t offset;
|
|
|
|
/* Ensure events codes match for EF10 (Huntington/Medford) and Siena */
|
|
EFX_STATIC_ASSERT(ESF_DZ_EV_CODE_LBN == FSF_AZ_EV_CODE_LBN);
|
|
EFX_STATIC_ASSERT(ESF_DZ_EV_CODE_WIDTH == FSF_AZ_EV_CODE_WIDTH);
|
|
|
|
EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_RX_EV == FSE_AZ_EV_CODE_RX_EV);
|
|
EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_TX_EV == FSE_AZ_EV_CODE_TX_EV);
|
|
EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_DRIVER_EV == FSE_AZ_EV_CODE_DRIVER_EV);
|
|
EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_DRV_GEN_EV ==
|
|
FSE_AZ_EV_CODE_DRV_GEN_EV);
|
|
#if EFSYS_OPT_MCDI
|
|
EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_MCDI_EV ==
|
|
FSE_AZ_EV_CODE_MCDI_EVRESPONSE);
|
|
#endif
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
EFSYS_ASSERT(countp != NULL);
|
|
EFSYS_ASSERT(eecp != NULL);
|
|
|
|
count = *countp;
|
|
do {
|
|
/* Read up until the end of the batch period */
|
|
batch = EFX_EV_BATCH - (count & (EFX_EV_BATCH - 1));
|
|
offset = (count & eep->ee_mask) * sizeof (efx_qword_t);
|
|
for (total = 0; total < batch; ++total) {
|
|
EFSYS_MEM_READQ(eep->ee_esmp, offset, &(ev[total]));
|
|
|
|
if (!EFX_EV_PRESENT(ev[total]))
|
|
break;
|
|
|
|
EFSYS_PROBE3(event, unsigned int, eep->ee_index,
|
|
uint32_t, EFX_QWORD_FIELD(ev[total], EFX_DWORD_1),
|
|
uint32_t, EFX_QWORD_FIELD(ev[total], EFX_DWORD_0));
|
|
|
|
offset += sizeof (efx_qword_t);
|
|
}
|
|
|
|
#if EFSYS_OPT_EV_PREFETCH && (EFSYS_OPT_EV_PREFETCH_PERIOD > 1)
|
|
/*
|
|
* Prefetch the next batch when we get within PREFETCH_PERIOD
|
|
* of a completed batch. If the batch is smaller, then prefetch
|
|
* immediately.
|
|
*/
|
|
if (total == batch && total < EFSYS_OPT_EV_PREFETCH_PERIOD)
|
|
EFSYS_MEM_PREFETCH(eep->ee_esmp, offset);
|
|
#endif /* EFSYS_OPT_EV_PREFETCH */
|
|
|
|
/* Process the batch of events */
|
|
for (index = 0; index < total; ++index) {
|
|
boolean_t should_abort;
|
|
uint32_t code;
|
|
|
|
#if EFSYS_OPT_EV_PREFETCH
|
|
/* Prefetch if we've now reached the batch period */
|
|
if (total == batch &&
|
|
index + EFSYS_OPT_EV_PREFETCH_PERIOD == total) {
|
|
offset = (count + batch) & eep->ee_mask;
|
|
offset *= sizeof (efx_qword_t);
|
|
|
|
EFSYS_MEM_PREFETCH(eep->ee_esmp, offset);
|
|
}
|
|
#endif /* EFSYS_OPT_EV_PREFETCH */
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_ALL);
|
|
|
|
code = EFX_QWORD_FIELD(ev[index], FSF_AZ_EV_CODE);
|
|
switch (code) {
|
|
case FSE_AZ_EV_CODE_RX_EV:
|
|
should_abort = eep->ee_rx(eep,
|
|
&(ev[index]), eecp, arg);
|
|
break;
|
|
case FSE_AZ_EV_CODE_TX_EV:
|
|
should_abort = eep->ee_tx(eep,
|
|
&(ev[index]), eecp, arg);
|
|
break;
|
|
case FSE_AZ_EV_CODE_DRIVER_EV:
|
|
should_abort = eep->ee_driver(eep,
|
|
&(ev[index]), eecp, arg);
|
|
break;
|
|
case FSE_AZ_EV_CODE_DRV_GEN_EV:
|
|
should_abort = eep->ee_drv_gen(eep,
|
|
&(ev[index]), eecp, arg);
|
|
break;
|
|
#if EFSYS_OPT_MCDI
|
|
case FSE_AZ_EV_CODE_MCDI_EVRESPONSE:
|
|
should_abort = eep->ee_mcdi(eep,
|
|
&(ev[index]), eecp, arg);
|
|
break;
|
|
#endif
|
|
case FSE_AZ_EV_CODE_GLOBAL_EV:
|
|
if (eep->ee_global) {
|
|
should_abort = eep->ee_global(eep,
|
|
&(ev[index]), eecp, arg);
|
|
break;
|
|
}
|
|
/* else fallthrough */
|
|
default:
|
|
EFSYS_PROBE3(bad_event,
|
|
unsigned int, eep->ee_index,
|
|
uint32_t,
|
|
EFX_QWORD_FIELD(ev[index], EFX_DWORD_1),
|
|
uint32_t,
|
|
EFX_QWORD_FIELD(ev[index], EFX_DWORD_0));
|
|
|
|
EFSYS_ASSERT(eecp->eec_exception != NULL);
|
|
(void) eecp->eec_exception(arg,
|
|
EFX_EXCEPTION_EV_ERROR, code);
|
|
should_abort = B_TRUE;
|
|
}
|
|
if (should_abort) {
|
|
/* Ignore subsequent events */
|
|
total = index + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that the hardware has most likely moved onto dma'ing
|
|
* into the next cache line, clear the processed events. Take
|
|
* care to only clear out events that we've processed
|
|
*/
|
|
EFX_SET_QWORD(ev[0]);
|
|
offset = (count & eep->ee_mask) * sizeof (efx_qword_t);
|
|
for (index = 0; index < total; ++index) {
|
|
EFSYS_MEM_WRITEQ(eep->ee_esmp, offset, &(ev[0]));
|
|
offset += sizeof (efx_qword_t);
|
|
}
|
|
|
|
count += total;
|
|
|
|
} while (total == batch);
|
|
|
|
*countp = count;
|
|
}
|
|
|
|
void
|
|
efx_ev_qpost(
|
|
__in efx_evq_t *eep,
|
|
__in uint16_t data)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
EFSYS_ASSERT(eevop != NULL &&
|
|
eevop->eevo_qpost != NULL);
|
|
|
|
eevop->eevo_qpost(eep, data);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_ev_usecs_to_ticks(
|
|
__in efx_nic_t *enp,
|
|
__in unsigned int us,
|
|
__out unsigned int *ticksp)
|
|
{
|
|
efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
|
|
unsigned int ticks;
|
|
|
|
/* Convert microseconds to a timer tick count */
|
|
if (us == 0)
|
|
ticks = 0;
|
|
else if (us * 1000 < encp->enc_evq_timer_quantum_ns)
|
|
ticks = 1; /* Never round down to zero */
|
|
else
|
|
ticks = us * 1000 / encp->enc_evq_timer_quantum_ns;
|
|
|
|
*ticksp = ticks;
|
|
return (0);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_ev_qmoderate(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int us)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
efx_rc_t rc;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
if ((eep->ee_flags & EFX_EVQ_FLAGS_NOTIFY_MASK) ==
|
|
EFX_EVQ_FLAGS_NOTIFY_DISABLED) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
if ((rc = eevop->eevo_qmoderate(eep, us)) != 0)
|
|
goto fail2;
|
|
|
|
return (0);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
return (rc);
|
|
}
|
|
|
|
#if EFSYS_OPT_QSTATS
|
|
void
|
|
efx_ev_qstats_update(
|
|
__in efx_evq_t *eep,
|
|
__inout_ecount(EV_NQSTATS) efsys_stat_t *stat)
|
|
|
|
{ efx_nic_t *enp = eep->ee_enp;
|
|
const efx_ev_ops_t *eevop = enp->en_eevop;
|
|
|
|
EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
|
|
|
|
eevop->eevo_qstats_update(eep, stat);
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_QSTATS */
|
|
|
|
#if EFSYS_OPT_SIENA
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_init(
|
|
__in efx_nic_t *enp)
|
|
{
|
|
efx_oword_t oword;
|
|
|
|
/*
|
|
* Program the event queue for receive and transmit queue
|
|
* flush events.
|
|
*/
|
|
EFX_BAR_READO(enp, FR_AZ_DP_CTRL_REG, &oword);
|
|
EFX_SET_OWORD_FIELD(oword, FRF_AZ_FLS_EVQ_ID, 0);
|
|
EFX_BAR_WRITEO(enp, FR_AZ_DP_CTRL_REG, &oword);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_rx_not_ok(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in uint32_t label,
|
|
__in uint32_t id,
|
|
__inout uint16_t *flagsp)
|
|
{
|
|
boolean_t ignore = B_FALSE;
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_TOBE_DISC) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_TOBE_DISC);
|
|
EFSYS_PROBE(tobe_disc);
|
|
/*
|
|
* Assume this is a unicast address mismatch, unless below
|
|
* we find either FSF_AZ_RX_EV_ETH_CRC_ERR or
|
|
* EV_RX_PAUSE_FRM_ERR is set.
|
|
*/
|
|
(*flagsp) |= EFX_ADDR_MISMATCH;
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_FRM_TRUNC) != 0) {
|
|
EFSYS_PROBE2(frm_trunc, uint32_t, label, uint32_t, id);
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_FRM_TRUNC);
|
|
(*flagsp) |= EFX_DISCARD;
|
|
|
|
#if EFSYS_OPT_RX_SCATTER
|
|
/*
|
|
* Lookout for payload queue ran dry errors and ignore them.
|
|
*
|
|
* Sadly for the header/data split cases, the descriptor
|
|
* pointer in this event refers to the header queue and
|
|
* therefore cannot be easily detected as duplicate.
|
|
* So we drop these and rely on the receive processing seeing
|
|
* a subsequent packet with FSF_AZ_RX_EV_SOP set to discard
|
|
* the partially received packet.
|
|
*/
|
|
if ((EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_SOP) == 0) &&
|
|
(EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_JUMBO_CONT) == 0) &&
|
|
(EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_BYTE_CNT) == 0))
|
|
ignore = B_TRUE;
|
|
#endif /* EFSYS_OPT_RX_SCATTER */
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_ETH_CRC_ERR) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_ETH_CRC_ERR);
|
|
EFSYS_PROBE(crc_err);
|
|
(*flagsp) &= ~EFX_ADDR_MISMATCH;
|
|
(*flagsp) |= EFX_DISCARD;
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_PAUSE_FRM_ERR) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_PAUSE_FRM_ERR);
|
|
EFSYS_PROBE(pause_frm_err);
|
|
(*flagsp) &= ~EFX_ADDR_MISMATCH;
|
|
(*flagsp) |= EFX_DISCARD;
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_BUF_OWNER_ID_ERR) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_BUF_OWNER_ID_ERR);
|
|
EFSYS_PROBE(owner_id_err);
|
|
(*flagsp) |= EFX_DISCARD;
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_IP_HDR_CHKSUM_ERR) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_IPV4_HDR_CHKSUM_ERR);
|
|
EFSYS_PROBE(ipv4_err);
|
|
(*flagsp) &= ~EFX_CKSUM_IPV4;
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_TCP_UDP_CHKSUM_ERR) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_TCP_UDP_CHKSUM_ERR);
|
|
EFSYS_PROBE(udp_chk_err);
|
|
(*flagsp) &= ~EFX_CKSUM_TCPUDP;
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_IP_FRAG_ERR) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_IP_FRAG_ERR);
|
|
|
|
/*
|
|
* If IP is fragmented FSF_AZ_RX_EV_IP_FRAG_ERR is set. This
|
|
* causes FSF_AZ_RX_EV_PKT_OK to be clear. This is not an error
|
|
* condition.
|
|
*/
|
|
(*flagsp) &= ~(EFX_PKT_TCP | EFX_PKT_UDP | EFX_CKSUM_TCPUDP);
|
|
}
|
|
|
|
return (ignore);
|
|
}
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_rx(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
uint32_t id;
|
|
uint32_t size;
|
|
uint32_t label;
|
|
boolean_t ok;
|
|
#if EFSYS_OPT_RX_SCATTER
|
|
boolean_t sop;
|
|
boolean_t jumbo_cont;
|
|
#endif /* EFSYS_OPT_RX_SCATTER */
|
|
uint32_t hdr_type;
|
|
boolean_t is_v6;
|
|
uint16_t flags;
|
|
boolean_t ignore;
|
|
boolean_t should_abort;
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX);
|
|
|
|
/* Basic packet information */
|
|
id = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_DESC_PTR);
|
|
size = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_BYTE_CNT);
|
|
label = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_Q_LABEL);
|
|
ok = (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_PKT_OK) != 0);
|
|
|
|
#if EFSYS_OPT_RX_SCATTER
|
|
sop = (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_SOP) != 0);
|
|
jumbo_cont = (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_JUMBO_CONT) != 0);
|
|
#endif /* EFSYS_OPT_RX_SCATTER */
|
|
|
|
hdr_type = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_HDR_TYPE);
|
|
|
|
is_v6 = (EFX_QWORD_FIELD(*eqp, FSF_CZ_RX_EV_IPV6_PKT) != 0);
|
|
|
|
/*
|
|
* If packet is marked as OK and packet type is TCP/IP or
|
|
* UDP/IP or other IP, then we can rely on the hardware checksums.
|
|
*/
|
|
switch (hdr_type) {
|
|
case FSE_AZ_RX_EV_HDR_TYPE_IPV4V6_TCP:
|
|
flags = EFX_PKT_TCP | EFX_CKSUM_TCPUDP;
|
|
if (is_v6) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_TCP_IPV6);
|
|
flags |= EFX_PKT_IPV6;
|
|
} else {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_TCP_IPV4);
|
|
flags |= EFX_PKT_IPV4 | EFX_CKSUM_IPV4;
|
|
}
|
|
break;
|
|
|
|
case FSE_AZ_RX_EV_HDR_TYPE_IPV4V6_UDP:
|
|
flags = EFX_PKT_UDP | EFX_CKSUM_TCPUDP;
|
|
if (is_v6) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_UDP_IPV6);
|
|
flags |= EFX_PKT_IPV6;
|
|
} else {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_UDP_IPV4);
|
|
flags |= EFX_PKT_IPV4 | EFX_CKSUM_IPV4;
|
|
}
|
|
break;
|
|
|
|
case FSE_AZ_RX_EV_HDR_TYPE_IPV4V6_OTHER:
|
|
if (is_v6) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_OTHER_IPV6);
|
|
flags = EFX_PKT_IPV6;
|
|
} else {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_OTHER_IPV4);
|
|
flags = EFX_PKT_IPV4 | EFX_CKSUM_IPV4;
|
|
}
|
|
break;
|
|
|
|
case FSE_AZ_RX_EV_HDR_TYPE_OTHER:
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_NON_IP);
|
|
flags = 0;
|
|
break;
|
|
|
|
default:
|
|
EFSYS_ASSERT(B_FALSE);
|
|
flags = 0;
|
|
break;
|
|
}
|
|
|
|
#if EFSYS_OPT_RX_SCATTER
|
|
/* Report scatter and header/lookahead split buffer flags */
|
|
if (sop)
|
|
flags |= EFX_PKT_START;
|
|
if (jumbo_cont)
|
|
flags |= EFX_PKT_CONT;
|
|
#endif /* EFSYS_OPT_RX_SCATTER */
|
|
|
|
/* Detect errors included in the FSF_AZ_RX_EV_PKT_OK indication */
|
|
if (!ok) {
|
|
ignore = siena_ev_rx_not_ok(eep, eqp, label, id, &flags);
|
|
if (ignore) {
|
|
EFSYS_PROBE4(rx_complete, uint32_t, label, uint32_t, id,
|
|
uint32_t, size, uint16_t, flags);
|
|
|
|
return (B_FALSE);
|
|
}
|
|
}
|
|
|
|
/* If we're not discarding the packet then it is ok */
|
|
if (~flags & EFX_DISCARD)
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_OK);
|
|
|
|
/* Detect multicast packets that didn't match the filter */
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_MCAST_PKT) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_MCAST_PKT);
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_MCAST_HASH_MATCH) != 0) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_RX_MCAST_HASH_MATCH);
|
|
} else {
|
|
EFSYS_PROBE(mcast_mismatch);
|
|
flags |= EFX_ADDR_MISMATCH;
|
|
}
|
|
} else {
|
|
flags |= EFX_PKT_UNICAST;
|
|
}
|
|
|
|
/*
|
|
* The packet parser in Siena can abort parsing packets under
|
|
* certain error conditions, setting the PKT_NOT_PARSED bit
|
|
* (which clears PKT_OK). If this is set, then don't trust
|
|
* the PKT_TYPE field.
|
|
*/
|
|
if (!ok) {
|
|
uint32_t parse_err;
|
|
|
|
parse_err = EFX_QWORD_FIELD(*eqp, FSF_CZ_RX_EV_PKT_NOT_PARSED);
|
|
if (parse_err != 0)
|
|
flags |= EFX_CHECK_VLAN;
|
|
}
|
|
|
|
if (~flags & EFX_CHECK_VLAN) {
|
|
uint32_t pkt_type;
|
|
|
|
pkt_type = EFX_QWORD_FIELD(*eqp, FSF_AZ_RX_EV_PKT_TYPE);
|
|
if (pkt_type >= FSE_AZ_RX_EV_PKT_TYPE_VLAN)
|
|
flags |= EFX_PKT_VLAN_TAGGED;
|
|
}
|
|
|
|
EFSYS_PROBE4(rx_complete, uint32_t, label, uint32_t, id,
|
|
uint32_t, size, uint16_t, flags);
|
|
|
|
EFSYS_ASSERT(eecp->eec_rx != NULL);
|
|
should_abort = eecp->eec_rx(arg, label, id, size, flags);
|
|
|
|
return (should_abort);
|
|
}
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_tx(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
uint32_t id;
|
|
uint32_t label;
|
|
boolean_t should_abort;
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_TX);
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_COMP) != 0 &&
|
|
EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_ERR) == 0 &&
|
|
EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_TOO_BIG) == 0 &&
|
|
EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_WQ_FF_FULL) == 0) {
|
|
|
|
id = EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_DESC_PTR);
|
|
label = EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_Q_LABEL);
|
|
|
|
EFSYS_PROBE2(tx_complete, uint32_t, label, uint32_t, id);
|
|
|
|
EFSYS_ASSERT(eecp->eec_tx != NULL);
|
|
should_abort = eecp->eec_tx(arg, label, id);
|
|
|
|
return (should_abort);
|
|
}
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_COMP) != 0)
|
|
EFSYS_PROBE3(bad_event, unsigned int, eep->ee_index,
|
|
uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_1),
|
|
uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_0));
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_ERR) != 0)
|
|
EFX_EV_QSTAT_INCR(eep, EV_TX_PKT_ERR);
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_PKT_TOO_BIG) != 0)
|
|
EFX_EV_QSTAT_INCR(eep, EV_TX_PKT_TOO_BIG);
|
|
|
|
if (EFX_QWORD_FIELD(*eqp, FSF_AZ_TX_EV_WQ_FF_FULL) != 0)
|
|
EFX_EV_QSTAT_INCR(eep, EV_TX_WQ_FF_FULL);
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_TX_UNEXPECTED);
|
|
return (B_FALSE);
|
|
}
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_global(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
_NOTE(ARGUNUSED(eqp, eecp, arg))
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_GLOBAL);
|
|
|
|
return (B_FALSE);
|
|
}
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_driver(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
boolean_t should_abort;
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER);
|
|
should_abort = B_FALSE;
|
|
|
|
switch (EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBCODE)) {
|
|
case FSE_AZ_TX_DESCQ_FLS_DONE_EV: {
|
|
uint32_t txq_index;
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER_TX_DESCQ_FLS_DONE);
|
|
|
|
txq_index = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
|
|
|
|
EFSYS_PROBE1(tx_descq_fls_done, uint32_t, txq_index);
|
|
|
|
EFSYS_ASSERT(eecp->eec_txq_flush_done != NULL);
|
|
should_abort = eecp->eec_txq_flush_done(arg, txq_index);
|
|
|
|
break;
|
|
}
|
|
case FSE_AZ_RX_DESCQ_FLS_DONE_EV: {
|
|
uint32_t rxq_index;
|
|
uint32_t failed;
|
|
|
|
rxq_index = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_RX_DESCQ_ID);
|
|
failed = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_RX_FLUSH_FAIL);
|
|
|
|
EFSYS_ASSERT(eecp->eec_rxq_flush_done != NULL);
|
|
EFSYS_ASSERT(eecp->eec_rxq_flush_failed != NULL);
|
|
|
|
if (failed) {
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER_RX_DESCQ_FLS_FAILED);
|
|
|
|
EFSYS_PROBE1(rx_descq_fls_failed, uint32_t, rxq_index);
|
|
|
|
should_abort = eecp->eec_rxq_flush_failed(arg,
|
|
rxq_index);
|
|
} else {
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER_RX_DESCQ_FLS_DONE);
|
|
|
|
EFSYS_PROBE1(rx_descq_fls_done, uint32_t, rxq_index);
|
|
|
|
should_abort = eecp->eec_rxq_flush_done(arg, rxq_index);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case FSE_AZ_EVQ_INIT_DONE_EV:
|
|
EFSYS_ASSERT(eecp->eec_initialized != NULL);
|
|
should_abort = eecp->eec_initialized(arg);
|
|
|
|
break;
|
|
|
|
case FSE_AZ_EVQ_NOT_EN_EV:
|
|
EFSYS_PROBE(evq_not_en);
|
|
break;
|
|
|
|
case FSE_AZ_SRM_UPD_DONE_EV: {
|
|
uint32_t code;
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER_SRM_UPD_DONE);
|
|
|
|
code = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
|
|
|
|
EFSYS_ASSERT(eecp->eec_sram != NULL);
|
|
should_abort = eecp->eec_sram(arg, code);
|
|
|
|
break;
|
|
}
|
|
case FSE_AZ_WAKE_UP_EV: {
|
|
uint32_t id;
|
|
|
|
id = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
|
|
|
|
EFSYS_ASSERT(eecp->eec_wake_up != NULL);
|
|
should_abort = eecp->eec_wake_up(arg, id);
|
|
|
|
break;
|
|
}
|
|
case FSE_AZ_TX_PKT_NON_TCP_UDP:
|
|
EFSYS_PROBE(tx_pkt_non_tcp_udp);
|
|
break;
|
|
|
|
case FSE_AZ_TIMER_EV: {
|
|
uint32_t id;
|
|
|
|
id = EFX_QWORD_FIELD(*eqp, FSF_AZ_DRIVER_EV_SUBDATA);
|
|
|
|
EFSYS_ASSERT(eecp->eec_timer != NULL);
|
|
should_abort = eecp->eec_timer(arg, id);
|
|
|
|
break;
|
|
}
|
|
case FSE_AZ_RX_DSC_ERROR_EV:
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER_RX_DSC_ERROR);
|
|
|
|
EFSYS_PROBE(rx_dsc_error);
|
|
|
|
EFSYS_ASSERT(eecp->eec_exception != NULL);
|
|
should_abort = eecp->eec_exception(arg,
|
|
EFX_EXCEPTION_RX_DSC_ERROR, 0);
|
|
|
|
break;
|
|
|
|
case FSE_AZ_TX_DSC_ERROR_EV:
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRIVER_TX_DSC_ERROR);
|
|
|
|
EFSYS_PROBE(tx_dsc_error);
|
|
|
|
EFSYS_ASSERT(eecp->eec_exception != NULL);
|
|
should_abort = eecp->eec_exception(arg,
|
|
EFX_EXCEPTION_TX_DSC_ERROR, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (should_abort);
|
|
}
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_drv_gen(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
uint32_t data;
|
|
boolean_t should_abort;
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_DRV_GEN);
|
|
|
|
data = EFX_QWORD_FIELD(*eqp, FSF_AZ_EV_DATA_DW0);
|
|
if (data >= ((uint32_t)1 << 16)) {
|
|
EFSYS_PROBE3(bad_event, unsigned int, eep->ee_index,
|
|
uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_1),
|
|
uint32_t, EFX_QWORD_FIELD(*eqp, EFX_DWORD_0));
|
|
return (B_TRUE);
|
|
}
|
|
|
|
EFSYS_ASSERT(eecp->eec_software != NULL);
|
|
should_abort = eecp->eec_software(arg, (uint16_t)data);
|
|
|
|
return (should_abort);
|
|
}
|
|
|
|
#if EFSYS_OPT_MCDI
|
|
|
|
static __checkReturn boolean_t
|
|
siena_ev_mcdi(
|
|
__in efx_evq_t *eep,
|
|
__in efx_qword_t *eqp,
|
|
__in const efx_ev_callbacks_t *eecp,
|
|
__in_opt void *arg)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
unsigned int code;
|
|
boolean_t should_abort = B_FALSE;
|
|
|
|
EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
|
|
|
|
if (enp->en_family != EFX_FAMILY_SIENA)
|
|
goto out;
|
|
|
|
EFSYS_ASSERT(eecp->eec_link_change != NULL);
|
|
EFSYS_ASSERT(eecp->eec_exception != NULL);
|
|
#if EFSYS_OPT_MON_STATS
|
|
EFSYS_ASSERT(eecp->eec_monitor != NULL);
|
|
#endif
|
|
|
|
EFX_EV_QSTAT_INCR(eep, EV_MCDI_RESPONSE);
|
|
|
|
code = EFX_QWORD_FIELD(*eqp, MCDI_EVENT_CODE);
|
|
switch (code) {
|
|
case MCDI_EVENT_CODE_BADSSERT:
|
|
efx_mcdi_ev_death(enp, EINTR);
|
|
break;
|
|
|
|
case MCDI_EVENT_CODE_CMDDONE:
|
|
efx_mcdi_ev_cpl(enp,
|
|
MCDI_EV_FIELD(eqp, CMDDONE_SEQ),
|
|
MCDI_EV_FIELD(eqp, CMDDONE_DATALEN),
|
|
MCDI_EV_FIELD(eqp, CMDDONE_ERRNO));
|
|
break;
|
|
|
|
case MCDI_EVENT_CODE_LINKCHANGE: {
|
|
efx_link_mode_t link_mode;
|
|
|
|
siena_phy_link_ev(enp, eqp, &link_mode);
|
|
should_abort = eecp->eec_link_change(arg, link_mode);
|
|
break;
|
|
}
|
|
case MCDI_EVENT_CODE_SENSOREVT: {
|
|
#if EFSYS_OPT_MON_STATS
|
|
efx_mon_stat_t id;
|
|
efx_mon_stat_value_t value;
|
|
efx_rc_t rc;
|
|
|
|
if ((rc = mcdi_mon_ev(enp, eqp, &id, &value)) == 0)
|
|
should_abort = eecp->eec_monitor(arg, id, value);
|
|
else if (rc == ENOTSUP) {
|
|
should_abort = eecp->eec_exception(arg,
|
|
EFX_EXCEPTION_UNKNOWN_SENSOREVT,
|
|
MCDI_EV_FIELD(eqp, DATA));
|
|
} else
|
|
EFSYS_ASSERT(rc == ENODEV); /* Wrong port */
|
|
#else
|
|
should_abort = B_FALSE;
|
|
#endif
|
|
break;
|
|
}
|
|
case MCDI_EVENT_CODE_SCHEDERR:
|
|
/* Informational only */
|
|
break;
|
|
|
|
case MCDI_EVENT_CODE_REBOOT:
|
|
efx_mcdi_ev_death(enp, EIO);
|
|
break;
|
|
|
|
case MCDI_EVENT_CODE_MAC_STATS_DMA:
|
|
#if EFSYS_OPT_MAC_STATS
|
|
if (eecp->eec_mac_stats != NULL) {
|
|
eecp->eec_mac_stats(arg,
|
|
MCDI_EV_FIELD(eqp, MAC_STATS_DMA_GENERATION));
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case MCDI_EVENT_CODE_FWALERT: {
|
|
uint32_t reason = MCDI_EV_FIELD(eqp, FWALERT_REASON);
|
|
|
|
if (reason == MCDI_EVENT_FWALERT_REASON_SRAM_ACCESS)
|
|
should_abort = eecp->eec_exception(arg,
|
|
EFX_EXCEPTION_FWALERT_SRAM,
|
|
MCDI_EV_FIELD(eqp, FWALERT_DATA));
|
|
else
|
|
should_abort = eecp->eec_exception(arg,
|
|
EFX_EXCEPTION_UNKNOWN_FWALERT,
|
|
MCDI_EV_FIELD(eqp, DATA));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
EFSYS_PROBE1(mc_pcol_error, int, code);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
return (should_abort);
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_MCDI */
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_qprime(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int count)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
uint32_t rptr;
|
|
efx_dword_t dword;
|
|
|
|
rptr = count & eep->ee_mask;
|
|
|
|
EFX_POPULATE_DWORD_1(dword, FRF_AZ_EVQ_RPTR, rptr);
|
|
|
|
EFX_BAR_TBL_WRITED(enp, FR_AZ_EVQ_RPTR_REG, eep->ee_index,
|
|
&dword, B_FALSE);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
siena_ev_qpost(
|
|
__in efx_evq_t *eep,
|
|
__in uint16_t data)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
efx_qword_t ev;
|
|
efx_oword_t oword;
|
|
|
|
EFX_POPULATE_QWORD_2(ev, FSF_AZ_EV_CODE, FSE_AZ_EV_CODE_DRV_GEN_EV,
|
|
FSF_AZ_EV_DATA_DW0, (uint32_t)data);
|
|
|
|
EFX_POPULATE_OWORD_3(oword, FRF_AZ_DRV_EV_QID, eep->ee_index,
|
|
EFX_DWORD_0, EFX_QWORD_FIELD(ev, EFX_DWORD_0),
|
|
EFX_DWORD_1, EFX_QWORD_FIELD(ev, EFX_DWORD_1));
|
|
|
|
EFX_BAR_WRITEO(enp, FR_AZ_DRV_EV_REG, &oword);
|
|
}
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_qmoderate(
|
|
__in efx_evq_t *eep,
|
|
__in unsigned int us)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
|
|
unsigned int locked;
|
|
efx_dword_t dword;
|
|
efx_rc_t rc;
|
|
|
|
if (us > encp->enc_evq_timer_max_us) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
/* If the value is zero then disable the timer */
|
|
if (us == 0) {
|
|
EFX_POPULATE_DWORD_2(dword,
|
|
FRF_CZ_TC_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS,
|
|
FRF_CZ_TC_TIMER_VAL, 0);
|
|
} else {
|
|
unsigned int ticks;
|
|
|
|
if ((rc = efx_ev_usecs_to_ticks(enp, us, &ticks)) != 0)
|
|
goto fail2;
|
|
|
|
EFSYS_ASSERT(ticks > 0);
|
|
EFX_POPULATE_DWORD_2(dword,
|
|
FRF_CZ_TC_TIMER_MODE, FFE_CZ_TIMER_MODE_INT_HLDOFF,
|
|
FRF_CZ_TC_TIMER_VAL, ticks - 1);
|
|
}
|
|
|
|
locked = (eep->ee_index == 0) ? 1 : 0;
|
|
|
|
EFX_BAR_TBL_WRITED(enp, FR_BZ_TIMER_COMMAND_REGP0,
|
|
eep->ee_index, &dword, locked);
|
|
|
|
return (0);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
static __checkReturn efx_rc_t
|
|
siena_ev_qcreate(
|
|
__in efx_nic_t *enp,
|
|
__in unsigned int index,
|
|
__in efsys_mem_t *esmp,
|
|
__in size_t n,
|
|
__in uint32_t id,
|
|
__in uint32_t us,
|
|
__in uint32_t flags,
|
|
__in efx_evq_t *eep)
|
|
{
|
|
efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
|
|
uint32_t size;
|
|
efx_oword_t oword;
|
|
efx_rc_t rc;
|
|
boolean_t notify_mode;
|
|
|
|
_NOTE(ARGUNUSED(esmp))
|
|
|
|
EFX_STATIC_ASSERT(ISP2(EFX_EVQ_MAXNEVS));
|
|
EFX_STATIC_ASSERT(ISP2(EFX_EVQ_MINNEVS));
|
|
|
|
if (!ISP2(n) || (n < EFX_EVQ_MINNEVS) || (n > EFX_EVQ_MAXNEVS)) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
if (index >= encp->enc_evq_limit) {
|
|
rc = EINVAL;
|
|
goto fail2;
|
|
}
|
|
#if EFSYS_OPT_RX_SCALE
|
|
if (enp->en_intr.ei_type == EFX_INTR_LINE &&
|
|
index >= EFX_MAXRSS_LEGACY) {
|
|
rc = EINVAL;
|
|
goto fail3;
|
|
}
|
|
#endif
|
|
for (size = 0; (1 << size) <= (EFX_EVQ_MAXNEVS / EFX_EVQ_MINNEVS);
|
|
size++)
|
|
if ((1 << size) == (int)(n / EFX_EVQ_MINNEVS))
|
|
break;
|
|
if (id + (1 << size) >= encp->enc_buftbl_limit) {
|
|
rc = EINVAL;
|
|
goto fail4;
|
|
}
|
|
|
|
/* Set up the handler table */
|
|
eep->ee_rx = siena_ev_rx;
|
|
eep->ee_tx = siena_ev_tx;
|
|
eep->ee_driver = siena_ev_driver;
|
|
eep->ee_global = siena_ev_global;
|
|
eep->ee_drv_gen = siena_ev_drv_gen;
|
|
#if EFSYS_OPT_MCDI
|
|
eep->ee_mcdi = siena_ev_mcdi;
|
|
#endif /* EFSYS_OPT_MCDI */
|
|
|
|
notify_mode = ((flags & EFX_EVQ_FLAGS_NOTIFY_MASK) !=
|
|
EFX_EVQ_FLAGS_NOTIFY_INTERRUPT);
|
|
|
|
/* Set up the new event queue */
|
|
EFX_POPULATE_OWORD_3(oword, FRF_CZ_TIMER_Q_EN, 1,
|
|
FRF_CZ_HOST_NOTIFY_MODE, notify_mode,
|
|
FRF_CZ_TIMER_MODE, FFE_CZ_TIMER_MODE_DIS);
|
|
EFX_BAR_TBL_WRITEO(enp, FR_AZ_TIMER_TBL, index, &oword, B_TRUE);
|
|
|
|
EFX_POPULATE_OWORD_3(oword, FRF_AZ_EVQ_EN, 1, FRF_AZ_EVQ_SIZE, size,
|
|
FRF_AZ_EVQ_BUF_BASE_ID, id);
|
|
|
|
EFX_BAR_TBL_WRITEO(enp, FR_AZ_EVQ_PTR_TBL, index, &oword, B_TRUE);
|
|
|
|
/* Set initial interrupt moderation */
|
|
siena_ev_qmoderate(eep, us);
|
|
|
|
return (0);
|
|
|
|
fail4:
|
|
EFSYS_PROBE(fail4);
|
|
#if EFSYS_OPT_RX_SCALE
|
|
fail3:
|
|
EFSYS_PROBE(fail3);
|
|
#endif
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_SIENA */
|
|
|
|
#if EFSYS_OPT_QSTATS
|
|
#if EFSYS_OPT_NAMES
|
|
/* START MKCONFIG GENERATED EfxEventQueueStatNamesBlock c0f3bc5083b40532 */
|
|
static const char * const __efx_ev_qstat_name[] = {
|
|
"all",
|
|
"rx",
|
|
"rx_ok",
|
|
"rx_frm_trunc",
|
|
"rx_tobe_disc",
|
|
"rx_pause_frm_err",
|
|
"rx_buf_owner_id_err",
|
|
"rx_ipv4_hdr_chksum_err",
|
|
"rx_tcp_udp_chksum_err",
|
|
"rx_eth_crc_err",
|
|
"rx_ip_frag_err",
|
|
"rx_mcast_pkt",
|
|
"rx_mcast_hash_match",
|
|
"rx_tcp_ipv4",
|
|
"rx_tcp_ipv6",
|
|
"rx_udp_ipv4",
|
|
"rx_udp_ipv6",
|
|
"rx_other_ipv4",
|
|
"rx_other_ipv6",
|
|
"rx_non_ip",
|
|
"rx_batch",
|
|
"tx",
|
|
"tx_wq_ff_full",
|
|
"tx_pkt_err",
|
|
"tx_pkt_too_big",
|
|
"tx_unexpected",
|
|
"global",
|
|
"global_mnt",
|
|
"driver",
|
|
"driver_srm_upd_done",
|
|
"driver_tx_descq_fls_done",
|
|
"driver_rx_descq_fls_done",
|
|
"driver_rx_descq_fls_failed",
|
|
"driver_rx_dsc_error",
|
|
"driver_tx_dsc_error",
|
|
"drv_gen",
|
|
"mcdi_response",
|
|
};
|
|
/* END MKCONFIG GENERATED EfxEventQueueStatNamesBlock */
|
|
|
|
const char *
|
|
efx_ev_qstat_name(
|
|
__in efx_nic_t *enp,
|
|
__in unsigned int id)
|
|
{
|
|
EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
|
|
EFSYS_ASSERT3U(id, <, EV_NQSTATS);
|
|
|
|
return (__efx_ev_qstat_name[id]);
|
|
}
|
|
#endif /* EFSYS_OPT_NAMES */
|
|
#endif /* EFSYS_OPT_QSTATS */
|
|
|
|
#if EFSYS_OPT_SIENA
|
|
|
|
#if EFSYS_OPT_QSTATS
|
|
static void
|
|
siena_ev_qstats_update(
|
|
__in efx_evq_t *eep,
|
|
__inout_ecount(EV_NQSTATS) efsys_stat_t *stat)
|
|
{
|
|
unsigned int id;
|
|
|
|
for (id = 0; id < EV_NQSTATS; id++) {
|
|
efsys_stat_t *essp = &stat[id];
|
|
|
|
EFSYS_STAT_INCR(essp, eep->ee_stat[id]);
|
|
eep->ee_stat[id] = 0;
|
|
}
|
|
}
|
|
#endif /* EFSYS_OPT_QSTATS */
|
|
|
|
static void
|
|
siena_ev_qdestroy(
|
|
__in efx_evq_t *eep)
|
|
{
|
|
efx_nic_t *enp = eep->ee_enp;
|
|
efx_oword_t oword;
|
|
|
|
/* Purge event queue */
|
|
EFX_ZERO_OWORD(oword);
|
|
|
|
EFX_BAR_TBL_WRITEO(enp, FR_AZ_EVQ_PTR_TBL,
|
|
eep->ee_index, &oword, B_TRUE);
|
|
|
|
EFX_ZERO_OWORD(oword);
|
|
EFX_BAR_TBL_WRITEO(enp, FR_AZ_TIMER_TBL, eep->ee_index, &oword, B_TRUE);
|
|
}
|
|
|
|
static void
|
|
siena_ev_fini(
|
|
__in efx_nic_t *enp)
|
|
{
|
|
_NOTE(ARGUNUSED(enp))
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_SIENA */
|