Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixl/if_ixlv.c
Show All 32 Lines | |||||
/*$FreeBSD$*/ | /*$FreeBSD$*/ | ||||
#include "ixl.h" | #include "ixl.h" | ||||
#include "ixlv.h" | #include "ixlv.h" | ||||
/********************************************************************* | /********************************************************************* | ||||
* Driver version | * Driver version | ||||
*********************************************************************/ | *********************************************************************/ | ||||
char ixlv_driver_version[] = "1.4.6-k"; | char ixlv_driver_version[] = "1.4.12-k"; | ||||
/********************************************************************* | /********************************************************************* | ||||
* PCI Device ID Table | * PCI Device ID Table | ||||
* | * | ||||
* Used by probe to select devices to load on | * Used by probe to select devices to load on | ||||
* Last field stores an index into ixlv_strings | * Last field stores an index into ixlv_strings | ||||
* Last entry must be all 0s | * Last entry must be all 0s | ||||
* | * | ||||
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static ixl_vendor_info_t ixlv_vendor_info_array[] = | static ixl_vendor_info_t ixlv_vendor_info_array[] = | ||||
{ | { | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF_HV, 0, 0, 0}, | |||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_A0_VF, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_A0_VF, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF_HV, 0, 0, 0}, | |||||
/* required last entry */ | /* required last entry */ | ||||
{0, 0, 0, 0, 0} | {0, 0, 0, 0, 0} | ||||
}; | }; | ||||
/********************************************************************* | /********************************************************************* | ||||
* Table of branding strings | * Table of branding strings | ||||
*********************************************************************/ | *********************************************************************/ | ||||
Show All 17 Lines | |||||
static int ixlv_init_taskqueue(struct ixlv_sc *); | static int ixlv_init_taskqueue(struct ixlv_sc *); | ||||
static int ixlv_setup_queues(struct ixlv_sc *); | static int ixlv_setup_queues(struct ixlv_sc *); | ||||
static void ixlv_config_rss(struct ixlv_sc *); | static void ixlv_config_rss(struct ixlv_sc *); | ||||
static void ixlv_stop(struct ixlv_sc *); | static void ixlv_stop(struct ixlv_sc *); | ||||
static void ixlv_add_multi(struct ixl_vsi *); | static void ixlv_add_multi(struct ixl_vsi *); | ||||
static void ixlv_del_multi(struct ixl_vsi *); | static void ixlv_del_multi(struct ixl_vsi *); | ||||
static void ixlv_free_queues(struct ixl_vsi *); | static void ixlv_free_queues(struct ixl_vsi *); | ||||
static int ixlv_setup_interface(device_t, struct ixlv_sc *); | static int ixlv_setup_interface(device_t, struct ixlv_sc *); | ||||
static int ixlv_teardown_adminq_msix(struct ixlv_sc *); | |||||
static int ixlv_media_change(struct ifnet *); | static int ixlv_media_change(struct ifnet *); | ||||
static void ixlv_media_status(struct ifnet *, struct ifmediareq *); | static void ixlv_media_status(struct ifnet *, struct ifmediareq *); | ||||
static void ixlv_local_timer(void *); | static void ixlv_local_timer(void *); | ||||
static int ixlv_add_mac_filter(struct ixlv_sc *, u8 *, u16); | static int ixlv_add_mac_filter(struct ixlv_sc *, u8 *, u16); | ||||
static int ixlv_del_mac_filter(struct ixlv_sc *sc, u8 *macaddr); | static int ixlv_del_mac_filter(struct ixlv_sc *sc, u8 *macaddr); | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
static SYSCTL_NODE(_hw, OID_AUTO, ixlv, CTLFLAG_RD, 0, | static SYSCTL_NODE(_hw, OID_AUTO, ixlv, CTLFLAG_RD, 0, | ||||
"IXLV driver parameters"); | "IXLV driver parameters"); | ||||
/* | /* | ||||
** Number of descriptors per ring: | ** Number of descriptors per ring: | ||||
** - TX and RX are the same size | ** - TX and RX are the same size | ||||
*/ | */ | ||||
static int ixlv_ringsz = DEFAULT_RING; | static int ixlv_ringsz = IXL_DEFAULT_RING; | ||||
TUNABLE_INT("hw.ixlv.ringsz", &ixlv_ringsz); | TUNABLE_INT("hw.ixlv.ringsz", &ixlv_ringsz); | ||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, ring_size, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixlv, OID_AUTO, ring_size, CTLFLAG_RDTUN, | ||||
&ixlv_ringsz, 0, "Descriptor Ring Size"); | &ixlv_ringsz, 0, "Descriptor Ring Size"); | ||||
/* Set to zero to auto calculate */ | /* Set to zero to auto calculate */ | ||||
int ixlv_max_queues = 0; | int ixlv_max_queues = 0; | ||||
TUNABLE_INT("hw.ixlv.max_queues", &ixlv_max_queues); | TUNABLE_INT("hw.ixlv.max_queues", &ixlv_max_queues); | ||||
SYSCTL_INT(_hw_ixlv, OID_AUTO, max_queues, CTLFLAG_RDTUN, | SYSCTL_INT(_hw_ixlv, OID_AUTO, max_queues, CTLFLAG_RDTUN, | ||||
▲ Show 20 Lines • Show All 298 Lines • ▼ Show 20 Lines | |||||
* return 0 on success, positive on failure | * return 0 on success, positive on failure | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static int | static int | ||||
ixlv_detach(device_t dev) | ixlv_detach(device_t dev) | ||||
{ | { | ||||
struct ixlv_sc *sc = device_get_softc(dev); | struct ixlv_sc *sc = device_get_softc(dev); | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct i40e_hw *hw = &sc->hw; | |||||
enum i40e_status_code status; | |||||
INIT_DBG_DEV(dev, "begin"); | INIT_DBG_DEV(dev, "begin"); | ||||
/* Make sure VLANS are not using driver */ | /* Make sure VLANS are not using driver */ | ||||
if (vsi->ifp->if_vlantrunk != NULL) { | if (vsi->ifp->if_vlantrunk != NULL) { | ||||
if_printf(vsi->ifp, "Vlan in use, detach first\n"); | if_printf(vsi->ifp, "Vlan in use, detach first\n"); | ||||
INIT_DBG_DEV(dev, "end"); | |||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
/* Stop driver */ | /* Stop driver */ | ||||
ether_ifdetach(vsi->ifp); | ether_ifdetach(vsi->ifp); | ||||
if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) { | if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
mtx_lock(&sc->mtx); | mtx_lock(&sc->mtx); | ||||
ixlv_stop(sc); | ixlv_stop(sc); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
} | } | ||||
/* Unregister VLAN events */ | /* Unregister VLAN events */ | ||||
if (vsi->vlan_attach != NULL) | if (vsi->vlan_attach != NULL) | ||||
EVENTHANDLER_DEREGISTER(vlan_config, vsi->vlan_attach); | EVENTHANDLER_DEREGISTER(vlan_config, vsi->vlan_attach); | ||||
if (vsi->vlan_detach != NULL) | if (vsi->vlan_detach != NULL) | ||||
EVENTHANDLER_DEREGISTER(vlan_unconfig, vsi->vlan_detach); | EVENTHANDLER_DEREGISTER(vlan_unconfig, vsi->vlan_detach); | ||||
/* Drain VC mgr */ | /* Drain VC mgr */ | ||||
callout_drain(&sc->vc_mgr.callout); | callout_drain(&sc->vc_mgr.callout); | ||||
i40e_shutdown_adminq(&sc->hw); | ixlv_disable_adminq_irq(hw); | ||||
ixlv_teardown_adminq_msix(sc); | |||||
/* Drain admin queue taskqueue */ | |||||
taskqueue_free(sc->tq); | taskqueue_free(sc->tq); | ||||
status = i40e_shutdown_adminq(&sc->hw); | |||||
if (status != I40E_SUCCESS) { | |||||
device_printf(dev, | |||||
"i40e_shutdown_adminq() failed with status %s\n", | |||||
i40e_stat_str(hw, status)); | |||||
} | |||||
if_free(vsi->ifp); | if_free(vsi->ifp); | ||||
free(sc->vf_res, M_DEVBUF); | free(sc->vf_res, M_DEVBUF); | ||||
ixlv_free_pci_resources(sc); | ixlv_free_pci_resources(sc); | ||||
ixlv_free_queues(vsi); | ixlv_free_queues(vsi); | ||||
mtx_destroy(&sc->mtx); | |||||
ixlv_free_filters(sc); | ixlv_free_filters(sc); | ||||
bus_generic_detach(dev); | bus_generic_detach(dev); | ||||
mtx_destroy(&sc->mtx); | |||||
INIT_DBG_DEV(dev, "end"); | INIT_DBG_DEV(dev, "end"); | ||||
return (0); | return (0); | ||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
* Shutdown entry point | * Shutdown entry point | ||||
* | * | ||||
▲ Show 20 Lines • Show All 425 Lines • ▼ Show 20 Lines | ixlv_init(void *arg) | ||||
} else | } else | ||||
sc->init_in_progress = true; | sc->init_in_progress = true; | ||||
ixlv_init_locked(sc); | ixlv_init_locked(sc); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
/* Wait for init_locked to finish */ | /* Wait for init_locked to finish */ | ||||
while (!(vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) | while (!(vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
&& ++retries < IXLV_AQ_MAX_ERR) { | && ++retries < IXLV_MAX_INIT_WAIT) { | ||||
i40e_msec_pause(25); | i40e_msec_pause(25); | ||||
} | } | ||||
if (retries >= IXLV_AQ_MAX_ERR) { | if (retries >= IXLV_MAX_INIT_WAIT) { | ||||
if_printf(vsi->ifp, | if_printf(vsi->ifp, | ||||
"Init failed to complete in allotted time!\n"); | "Init failed to complete in allotted time!\n"); | ||||
} | } | ||||
mtx_lock(&sc->mtx); | mtx_lock(&sc->mtx); | ||||
sc->init_in_progress = false; | sc->init_in_progress = false; | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static int | static int | ||||
ixlv_init_msix(struct ixlv_sc *sc) | ixlv_init_msix(struct ixlv_sc *sc) | ||||
{ | { | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
int rid, want, vectors, queues, available; | int rid, want, vectors, queues, available; | ||||
int auto_max_queues; | int auto_max_queues; | ||||
rid = PCIR_BAR(IXL_BAR); | rid = PCIR_BAR(IXL_MSIX_BAR); | ||||
sc->msix_mem = bus_alloc_resource_any(dev, | sc->msix_mem = bus_alloc_resource_any(dev, | ||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | SYS_RES_MEMORY, &rid, RF_ACTIVE); | ||||
if (!sc->msix_mem) { | if (!sc->msix_mem) { | ||||
/* May not be enabled */ | /* May not be enabled */ | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"Unable to map MSIX table\n"); | "Unable to map MSIX table\n"); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | #endif | ||||
if (pci_alloc_msix(dev, &vectors) == 0) { | if (pci_alloc_msix(dev, &vectors) == 0) { | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"Using MSIX interrupts with %d vectors\n", vectors); | "Using MSIX interrupts with %d vectors\n", vectors); | ||||
sc->msix = vectors; | sc->msix = vectors; | ||||
sc->vsi.num_queues = queues; | sc->vsi.num_queues = queues; | ||||
} | } | ||||
/* Next we need to setup the vector for the Admin Queue */ | /* Next we need to setup the vector for the Admin Queue */ | ||||
rid = 1; // zero vector + 1 | rid = 1; /* zero vector + 1 */ | ||||
sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | ||||
&rid, RF_SHAREABLE | RF_ACTIVE); | &rid, RF_SHAREABLE | RF_ACTIVE); | ||||
if (sc->res == NULL) { | if (sc->res == NULL) { | ||||
device_printf(dev,"Unable to allocate" | device_printf(dev, "Unable to allocate" | ||||
" bus resource: AQ interrupt \n"); | " bus resource: AQ interrupt \n"); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
if (bus_setup_intr(dev, sc->res, | if (bus_setup_intr(dev, sc->res, | ||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | INTR_TYPE_NET | INTR_MPSAFE, NULL, | ||||
ixlv_msix_adminq, sc, &sc->tag)) { | ixlv_msix_adminq, sc, &sc->tag)) { | ||||
sc->res = NULL; | sc->res = NULL; | ||||
device_printf(dev, "Failed to register AQ handler"); | device_printf(dev, "Failed to register AQ handler"); | ||||
▲ Show 20 Lines • Show All 82 Lines • ▼ Show 20 Lines | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
} | } | ||||
if (que->res != NULL) { | if (que->res != NULL) { | ||||
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | ||||
que->res = NULL; | que->res = NULL; | ||||
} | } | ||||
} | } | ||||
early: | early: | ||||
/* Clean the AdminQ interrupt */ | |||||
if (sc->tag != NULL) { | |||||
bus_teardown_intr(dev, sc->res, sc->tag); | |||||
sc->tag = NULL; | |||||
} | |||||
if (sc->res != NULL) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, 1, sc->res); | |||||
sc->res = NULL; | |||||
} | |||||
pci_release_msi(dev); | pci_release_msi(dev); | ||||
if (sc->msix_mem != NULL) | if (sc->msix_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
PCIR_BAR(IXL_BAR), sc->msix_mem); | PCIR_BAR(IXL_MSIX_BAR), sc->msix_mem); | ||||
if (sc->pci_mem != NULL) | if (sc->pci_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
PCIR_BAR(0), sc->pci_mem); | PCIR_BAR(0), sc->pci_mem); | ||||
} | } | ||||
/* | /* | ||||
* Create taskqueue and tasklet for Admin Queue interrupts. | * Create taskqueue and tasklet for Admin Queue interrupts. | ||||
▲ Show 20 Lines • Show All 253 Lines • ▼ Show 20 Lines | vsi->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { | ||||
goto early; | goto early; | ||||
} | } | ||||
for (int i = 0; i < vsi->num_queues; i++) { | for (int i = 0; i < vsi->num_queues; i++) { | ||||
que = &vsi->queues[i]; | que = &vsi->queues[i]; | ||||
que->num_desc = ixlv_ringsz; | que->num_desc = ixlv_ringsz; | ||||
que->me = i; | que->me = i; | ||||
que->vsi = vsi; | que->vsi = vsi; | ||||
/* mark the queue as active */ | |||||
vsi->active_queues |= (u64)1 << que->me; | |||||
txr = &que->txr; | txr = &que->txr; | ||||
txr->que = que; | txr->que = que; | ||||
txr->tail = I40E_QTX_TAIL1(que->me); | txr->tail = I40E_QTX_TAIL1(que->me); | ||||
/* Initialize the TX lock */ | /* Initialize the TX lock */ | ||||
snprintf(txr->mtx_name, sizeof(txr->mtx_name), "%s:tx(%d)", | snprintf(txr->mtx_name, sizeof(txr->mtx_name), "%s:tx(%d)", | ||||
device_get_nameunit(dev), que->me); | device_get_nameunit(dev), que->me); | ||||
mtx_init(&txr->mtx, txr->mtx_name, NULL, MTX_DEF); | mtx_init(&txr->mtx, txr->mtx_name, NULL, MTX_DEF); | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | SLIST_FOREACH(f, sc->mac_filters, next) { | ||||
} | } | ||||
} | } | ||||
if (!match) | if (!match) | ||||
f = NULL; | f = NULL; | ||||
return (f); | return (f); | ||||
} | } | ||||
static int | |||||
ixlv_teardown_adminq_msix(struct ixlv_sc *sc) | |||||
{ | |||||
device_t dev = sc->dev; | |||||
int error = 0; | |||||
if (sc->tag != NULL) { | |||||
bus_teardown_intr(dev, sc->res, sc->tag); | |||||
if (error) { | |||||
device_printf(dev, "bus_teardown_intr() for" | |||||
" interrupt 0 failed\n"); | |||||
// return (ENXIO); | |||||
} | |||||
sc->tag = NULL; | |||||
} | |||||
if (sc->res != NULL) { | |||||
bus_release_resource(dev, SYS_RES_IRQ, 1, sc->res); | |||||
if (error) { | |||||
device_printf(dev, "bus_release_resource() for" | |||||
" interrupt 0 failed\n"); | |||||
// return (ENXIO); | |||||
} | |||||
sc->res = NULL; | |||||
} | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
** Admin Queue interrupt handler | ** Admin Queue interrupt handler | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_msix_adminq(void *arg) | ixlv_msix_adminq(void *arg) | ||||
{ | { | ||||
struct ixlv_sc *sc = arg; | struct ixlv_sc *sc = arg; | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | case IXL_BULK_LATENCY: | ||||
} | } | ||||
rxr->latency = rx_latency; | rxr->latency = rx_latency; | ||||
if (rx_itr != rxr->itr) { | if (rx_itr != rxr->itr) { | ||||
/* do an exponential smoothing */ | /* do an exponential smoothing */ | ||||
rx_itr = (10 * rx_itr * rxr->itr) / | rx_itr = (10 * rx_itr * rxr->itr) / | ||||
((9 * rx_itr) + rxr->itr); | ((9 * rx_itr) + rxr->itr); | ||||
rxr->itr = rx_itr & IXL_MAX_ITR; | rxr->itr = min(rx_itr, IXL_MAX_ITR); | ||||
wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, | wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, | ||||
que->me), rxr->itr); | que->me), rxr->itr); | ||||
} | } | ||||
} else { /* We may have have toggled to non-dynamic */ | } else { /* We may have have toggled to non-dynamic */ | ||||
if (vsi->rx_itr_setting & IXL_ITR_DYNAMIC) | if (vsi->rx_itr_setting & IXL_ITR_DYNAMIC) | ||||
vsi->rx_itr_setting = ixlv_rx_itr; | vsi->rx_itr_setting = ixlv_rx_itr; | ||||
/* Update the hardware if needed */ | /* Update the hardware if needed */ | ||||
if (rxr->itr != vsi->rx_itr_setting) { | if (rxr->itr != vsi->rx_itr_setting) { | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | if (ixlv_dynamic_tx_itr) { | ||||
} | } | ||||
txr->latency = tx_latency; | txr->latency = tx_latency; | ||||
if (tx_itr != txr->itr) { | if (tx_itr != txr->itr) { | ||||
/* do an exponential smoothing */ | /* do an exponential smoothing */ | ||||
tx_itr = (10 * tx_itr * txr->itr) / | tx_itr = (10 * tx_itr * txr->itr) / | ||||
((9 * tx_itr) + txr->itr); | ((9 * tx_itr) + txr->itr); | ||||
txr->itr = tx_itr & IXL_MAX_ITR; | txr->itr = min(tx_itr, IXL_MAX_ITR); | ||||
wr32(hw, I40E_VFINT_ITRN1(IXL_TX_ITR, | wr32(hw, I40E_VFINT_ITRN1(IXL_TX_ITR, | ||||
que->me), txr->itr); | que->me), txr->itr); | ||||
} | } | ||||
} else { /* We may have have toggled to non-dynamic */ | } else { /* We may have have toggled to non-dynamic */ | ||||
if (vsi->tx_itr_setting & IXL_ITR_DYNAMIC) | if (vsi->tx_itr_setting & IXL_ITR_DYNAMIC) | ||||
vsi->tx_itr_setting = ixlv_tx_itr; | vsi->tx_itr_setting = ixlv_tx_itr; | ||||
/* Update the hardware if needed */ | /* Update the hardware if needed */ | ||||
▲ Show 20 Lines • Show All 300 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
ixlv_local_timer(void *arg) | ixlv_local_timer(void *arg) | ||||
{ | { | ||||
struct ixlv_sc *sc = arg; | struct ixlv_sc *sc = arg; | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_queue *que = vsi->queues; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
struct tx_ring *txr; | |||||
int hung = 0; | int hung = 0; | ||||
u32 mask, val; | u32 mask, val; | ||||
s32 timer, new_timer; | |||||
IXLV_CORE_LOCK_ASSERT(sc); | IXLV_CORE_LOCK_ASSERT(sc); | ||||
/* If Reset is in progress just bail */ | /* If Reset is in progress just bail */ | ||||
if (sc->init_state == IXLV_RESET_PENDING) | if (sc->init_state == IXLV_RESET_PENDING) | ||||
return; | return; | ||||
/* Check for when PF triggers a VF reset */ | /* Check for when PF triggers a VF reset */ | ||||
Show All 13 Lines | ixlv_local_timer(void *arg) | ||||
/* | /* | ||||
** Check status on the queues for a hang | ** Check status on the queues for a hang | ||||
*/ | */ | ||||
mask = (I40E_VFINT_DYN_CTLN1_INTENA_MASK | | mask = (I40E_VFINT_DYN_CTLN1_INTENA_MASK | | ||||
I40E_VFINT_DYN_CTLN1_SWINT_TRIG_MASK | | I40E_VFINT_DYN_CTLN1_SWINT_TRIG_MASK | | ||||
I40E_VFINT_DYN_CTLN1_ITR_INDX_MASK); | I40E_VFINT_DYN_CTLN1_ITR_INDX_MASK); | ||||
for (int i = 0; i < vsi->num_queues; i++,que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
/* Any queues with outstanding work get a sw irq */ | txr = &que->txr; | ||||
if (que->busy) | timer = atomic_load_acq_32(&txr->watchdog_timer); | ||||
wr32(hw, I40E_VFINT_DYN_CTLN1(que->me), mask); | if (timer > 0) { | ||||
/* | new_timer = timer - hz; | ||||
** Each time txeof runs without cleaning, but there | if (new_timer <= 0) { | ||||
** are uncleaned descriptors it increments busy. If | atomic_store_rel_32(&txr->watchdog_timer, -1); | ||||
** we get to 5 we declare it hung. | device_printf(dev, "WARNING: queue %d " | ||||
*/ | "appears to be hung!\n", que->me); | ||||
if (que->busy == IXL_QUEUE_HUNG) { | |||||
++hung; | ++hung; | ||||
/* Mark the queue as inactive */ | |||||
vsi->active_queues &= ~((u64)1 << que->me); | |||||
continue; | |||||
} else { | } else { | ||||
/* Check if we've come back from hung */ | /* | ||||
if ((vsi->active_queues & ((u64)1 << que->me)) == 0) | * If this fails, that means something in the TX path has updated | ||||
vsi->active_queues |= ((u64)1 << que->me); | * the watchdog, so it means the TX path is still working and | ||||
* the watchdog doesn't need to countdown. | |||||
*/ | |||||
atomic_cmpset_rel_32(&txr->watchdog_timer, timer, new_timer); | |||||
/* Any queues with outstanding work get a sw irq */ | |||||
wr32(hw, I40E_VFINT_DYN_CTLN1(que->me), mask); | |||||
} | } | ||||
if (que->busy >= IXL_MAX_TX_BUSY) { | |||||
device_printf(dev,"Warning queue %d " | |||||
"appears to be hung!\n", i); | |||||
que->busy = IXL_QUEUE_HUNG; | |||||
++hung; | |||||
} | } | ||||
} | } | ||||
/* Only reset when all queues show hung */ | /* Reset when a queue shows hung */ | ||||
if (hung == vsi->num_queues) | if (hung) | ||||
goto hung; | goto hung; | ||||
callout_reset(&sc->timer, hz, ixlv_local_timer, sc); | callout_reset(&sc->timer, hz, ixlv_local_timer, sc); | ||||
return; | return; | ||||
hung: | hung: | ||||
device_printf(dev, "Local Timer: TX HANG DETECTED - Resetting!!\n"); | device_printf(dev, "WARNING: Resetting!\n"); | ||||
sc->init_state = IXLV_RESET_REQUIRED; | sc->init_state = IXLV_RESET_REQUIRED; | ||||
sc->watchdog_events++; | |||||
ixlv_stop(sc); | |||||
ixlv_init_locked(sc); | ixlv_init_locked(sc); | ||||
} | } | ||||
/* | /* | ||||
** Note: this routine updates the OS on the link state | ** Note: this routine updates the OS on the link state | ||||
** the real check of the hardware only happens with | ** the real check of the hardware only happens with | ||||
** a link interrupt. | ** a link interrupt. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 138 Lines • ▼ Show 20 Lines | if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX) | if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6) | if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6) | if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP); | ||||
#else | #else | ||||
set_hena = IXL_DEFAULT_RSS_HENA; | set_hena = IXL_DEFAULT_RSS_HENA_XL710; | ||||
#endif | #endif | ||||
hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | ||||
((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | ||||
hena |= set_hena; | hena |= set_hena; | ||||
wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | ||||
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | ||||
/* Populate the LUT with max no. of queues in round robin fashion */ | /* Populate the LUT with max no. of queues in round robin fashion */ | ||||
▲ Show 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct i40e_arq_event_info event; | struct i40e_arq_event_info event; | ||||
struct i40e_virtchnl_msg *v_msg; | struct i40e_virtchnl_msg *v_msg; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
u16 result = 0; | u16 result = 0; | ||||
u32 reg, oldreg; | u32 reg, oldreg; | ||||
i40e_status ret; | i40e_status ret; | ||||
bool aq_error = false; | |||||
IXLV_CORE_LOCK_ASSERT(sc); | IXLV_CORE_LOCK_ASSERT(sc); | ||||
event.buf_len = IXL_AQ_BUF_SZ; | event.buf_len = IXL_AQ_BUF_SZ; | ||||
event.msg_buf = sc->aq_buffer; | event.msg_buf = sc->aq_buffer; | ||||
v_msg = (struct i40e_virtchnl_msg *)&event.desc; | v_msg = (struct i40e_virtchnl_msg *)&event.desc; | ||||
do { | do { | ||||
ret = i40e_clean_arq_element(hw, &event, &result); | ret = i40e_clean_arq_element(hw, &event, &result); | ||||
if (ret) | if (ret) | ||||
break; | break; | ||||
ixlv_vc_completion(sc, v_msg->v_opcode, | ixlv_vc_completion(sc, v_msg->v_opcode, | ||||
v_msg->v_retval, event.msg_buf, event.msg_len); | v_msg->v_retval, event.msg_buf, event.msg_len); | ||||
if (result != 0) | if (result != 0) | ||||
bzero(event.msg_buf, IXL_AQ_BUF_SZ); | bzero(event.msg_buf, IXL_AQ_BUF_SZ); | ||||
} while (result); | } while (result); | ||||
/* check for Admin queue errors */ | /* check for Admin queue errors */ | ||||
oldreg = reg = rd32(hw, hw->aq.arq.len); | oldreg = reg = rd32(hw, hw->aq.arq.len); | ||||
if (reg & I40E_VF_ARQLEN1_ARQVFE_MASK) { | if (reg & I40E_VF_ARQLEN1_ARQVFE_MASK) { | ||||
device_printf(dev, "ARQ VF Error detected\n"); | device_printf(dev, "ARQ VF Error detected\n"); | ||||
reg &= ~I40E_VF_ARQLEN1_ARQVFE_MASK; | reg &= ~I40E_VF_ARQLEN1_ARQVFE_MASK; | ||||
aq_error = true; | |||||
} | } | ||||
if (reg & I40E_VF_ARQLEN1_ARQOVFL_MASK) { | if (reg & I40E_VF_ARQLEN1_ARQOVFL_MASK) { | ||||
device_printf(dev, "ARQ Overflow Error detected\n"); | device_printf(dev, "ARQ Overflow Error detected\n"); | ||||
reg &= ~I40E_VF_ARQLEN1_ARQOVFL_MASK; | reg &= ~I40E_VF_ARQLEN1_ARQOVFL_MASK; | ||||
aq_error = true; | |||||
} | } | ||||
if (reg & I40E_VF_ARQLEN1_ARQCRIT_MASK) { | if (reg & I40E_VF_ARQLEN1_ARQCRIT_MASK) { | ||||
device_printf(dev, "ARQ Critical Error detected\n"); | device_printf(dev, "ARQ Critical Error detected\n"); | ||||
reg &= ~I40E_VF_ARQLEN1_ARQCRIT_MASK; | reg &= ~I40E_VF_ARQLEN1_ARQCRIT_MASK; | ||||
aq_error = true; | |||||
} | } | ||||
if (oldreg != reg) | if (oldreg != reg) | ||||
wr32(hw, hw->aq.arq.len, reg); | wr32(hw, hw->aq.arq.len, reg); | ||||
oldreg = reg = rd32(hw, hw->aq.asq.len); | oldreg = reg = rd32(hw, hw->aq.asq.len); | ||||
if (reg & I40E_VF_ATQLEN1_ATQVFE_MASK) { | if (reg & I40E_VF_ATQLEN1_ATQVFE_MASK) { | ||||
device_printf(dev, "ASQ VF Error detected\n"); | device_printf(dev, "ASQ VF Error detected\n"); | ||||
reg &= ~I40E_VF_ATQLEN1_ATQVFE_MASK; | reg &= ~I40E_VF_ATQLEN1_ATQVFE_MASK; | ||||
aq_error = true; | |||||
} | } | ||||
if (reg & I40E_VF_ATQLEN1_ATQOVFL_MASK) { | if (reg & I40E_VF_ATQLEN1_ATQOVFL_MASK) { | ||||
device_printf(dev, "ASQ Overflow Error detected\n"); | device_printf(dev, "ASQ Overflow Error detected\n"); | ||||
reg &= ~I40E_VF_ATQLEN1_ATQOVFL_MASK; | reg &= ~I40E_VF_ATQLEN1_ATQOVFL_MASK; | ||||
aq_error = true; | |||||
} | } | ||||
if (reg & I40E_VF_ATQLEN1_ATQCRIT_MASK) { | if (reg & I40E_VF_ATQLEN1_ATQCRIT_MASK) { | ||||
device_printf(dev, "ASQ Critical Error detected\n"); | device_printf(dev, "ASQ Critical Error detected\n"); | ||||
reg &= ~I40E_VF_ATQLEN1_ATQCRIT_MASK; | reg &= ~I40E_VF_ATQLEN1_ATQCRIT_MASK; | ||||
aq_error = true; | |||||
} | } | ||||
if (oldreg != reg) | if (oldreg != reg) | ||||
wr32(hw, hw->aq.asq.len, reg); | wr32(hw, hw->aq.asq.len, reg); | ||||
if (aq_error) { | |||||
/* Need to reset adapter */ | |||||
device_printf(dev, "WARNING: Resetting!\n"); | |||||
sc->init_state = IXLV_RESET_REQUIRED; | |||||
ixlv_stop(sc); | |||||
ixlv_init_locked(sc); | |||||
} | |||||
ixlv_enable_adminq_irq(hw); | ixlv_enable_adminq_irq(hw); | ||||
} | } | ||||
static void | static void | ||||
ixlv_add_sysctls(struct ixlv_sc *sc) | ixlv_add_sysctls(struct ixlv_sc *sc) | ||||
{ | { | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
▲ Show 20 Lines • Show All 110 Lines • ▼ Show 20 Lines | SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qtx_head", | ||||
sizeof(struct ixl_queue), | sizeof(struct ixl_queue), | ||||
ixlv_sysctl_qtx_tail_handler, "IU", | ixlv_sysctl_qtx_tail_handler, "IU", | ||||
"Queue Transmit Descriptor Tail"); | "Queue Transmit Descriptor Tail"); | ||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_head", | SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_head", | ||||
CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | ||||
sizeof(struct ixl_queue), | sizeof(struct ixl_queue), | ||||
ixlv_sysctl_qrx_tail_handler, "IU", | ixlv_sysctl_qrx_tail_handler, "IU", | ||||
"Queue Receive Descriptor Tail"); | "Queue Receive Descriptor Tail"); | ||||
SYSCTL_ADD_INT(ctx, queue_list, OID_AUTO, "watchdog_timer", | |||||
CTLFLAG_RD, &(txr.watchdog_timer), 0, | |||||
"Ticks before watchdog event is triggered"); | |||||
#endif | #endif | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
ixlv_init_filters(struct ixlv_sc *sc) | ixlv_init_filters(struct ixlv_sc *sc) | ||||
{ | { | ||||
sc->mac_filters = malloc(sizeof(struct ixlv_mac_filter), | sc->mac_filters = malloc(sizeof(struct ixlv_mac_filter), | ||||
▲ Show 20 Lines • Show All 73 Lines • Show Last 20 Lines |