version 1.2, 2014/03/25 16:19:14 |
version 1.2.6.7, 2017/02/05 13:40:45 |
|
|
/****************************************************************************** |
/****************************************************************************** |
|
|
Copyright (c) 2001-2010, Intel Corporation |
Copyright (c) 2001-2015, Intel Corporation |
All rights reserved. |
All rights reserved. |
|
|
Redistribution and use in source and binary forms, with or without |
Redistribution and use in source and binary forms, with or without |
|
|
POSSIBILITY OF SUCH DAMAGE. |
POSSIBILITY OF SUCH DAMAGE. |
|
|
******************************************************************************/ |
******************************************************************************/ |
/*$FreeBSD: src/sys/dev/ixgbe/ixv.c,v 1.2 2011/03/23 13:10:15 jhb Exp $*/ |
/*$FreeBSD: head/sys/dev/ixgbe/if_ixv.c 302384 2016-07-07 03:39:18Z sbruno $*/ |
/*$NetBSD$*/ |
/*$NetBSD$*/ |
|
|
#include "opt_inet.h" |
#include "opt_inet.h" |
|
#include "opt_inet6.h" |
|
|
#include "ixv.h" |
#include "ixgbe.h" |
|
#include "vlan.h" |
|
|
/********************************************************************* |
/********************************************************************* |
* Driver version |
* Driver version |
*********************************************************************/ |
*********************************************************************/ |
char ixv_driver_version[] = "1.0.0"; |
char ixv_driver_version[] = "1.4.6-k"; |
|
|
/********************************************************************* |
/********************************************************************* |
* PCI Device ID Table |
* PCI Device ID Table |
Line 52 char ixv_driver_version[] = "1.0.0"; |
|
Line 54 char ixv_driver_version[] = "1.0.0"; |
|
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } |
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } |
*********************************************************************/ |
*********************************************************************/ |
|
|
static ixv_vendor_info_t ixv_vendor_info_array[] = |
static ixgbe_vendor_info_t ixv_vendor_info_array[] = |
{ |
{ |
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_VF, 0, 0, 0}, |
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_VF, 0, 0, 0}, |
|
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540_VF, 0, 0, 0}, |
|
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550_VF, 0, 0, 0}, |
|
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_VF, 0, 0, 0}, |
/* required last entry */ |
/* required last entry */ |
{0, 0, 0, 0, 0} |
{0, 0, 0, 0, 0} |
}; |
}; |
Line 63 static ixv_vendor_info_t ixv_vendor_info |
|
Line 68 static ixv_vendor_info_t ixv_vendor_info |
|
* Table of branding strings |
* Table of branding strings |
*********************************************************************/ |
*********************************************************************/ |
|
|
static char *ixv_strings[] = { |
static const char *ixv_strings[] = { |
"Intel(R) PRO/10GbE Virtual Function Network Driver" |
"Intel(R) PRO/10GbE Virtual Function Network Driver" |
}; |
}; |
|
|
/********************************************************************* |
/********************************************************************* |
* Function prototypes |
* Function prototypes |
*********************************************************************/ |
*********************************************************************/ |
static int ixv_probe(device_t); |
static int ixv_probe(device_t, cfdata_t, void *); |
static int ixv_attach(device_t); |
static void ixv_attach(device_t, device_t, void *); |
static int ixv_detach(device_t); |
static int ixv_detach(device_t, int); |
|
#if 0 |
static int ixv_shutdown(device_t); |
static int ixv_shutdown(device_t); |
#if __FreeBSD_version < 800000 |
|
static void ixv_start(struct ifnet *); |
|
static void ixv_start_locked(struct tx_ring *, struct ifnet *); |
|
#else |
|
static int ixv_mq_start(struct ifnet *, struct mbuf *); |
|
static int ixv_mq_start_locked(struct ifnet *, |
|
struct tx_ring *, struct mbuf *); |
|
static void ixv_qflush(struct ifnet *); |
|
#endif |
#endif |
static int ixv_ioctl(struct ifnet *, u_long, caddr_t); |
static int ixv_ioctl(struct ifnet *, u_long, void *); |
static void ixv_init(void *); |
static int ixv_init(struct ifnet *); |
static void ixv_init_locked(struct adapter *); |
static void ixv_init_locked(struct adapter *); |
static void ixv_stop(void *); |
static void ixv_stop(void *); |
static void ixv_media_status(struct ifnet *, struct ifmediareq *); |
static void ixv_media_status(struct ifnet *, struct ifmediareq *); |
static int ixv_media_change(struct ifnet *); |
static int ixv_media_change(struct ifnet *); |
static void ixv_identify_hardware(struct adapter *); |
static void ixv_identify_hardware(struct adapter *); |
static int ixv_allocate_pci_resources(struct adapter *); |
static int ixv_allocate_pci_resources(struct adapter *, |
static int ixv_allocate_msix(struct adapter *); |
const struct pci_attach_args *); |
static int ixv_allocate_queues(struct adapter *); |
static int ixv_allocate_msix(struct adapter *, |
|
const struct pci_attach_args *); |
static int ixv_setup_msix(struct adapter *); |
static int ixv_setup_msix(struct adapter *); |
static void ixv_free_pci_resources(struct adapter *); |
static void ixv_free_pci_resources(struct adapter *); |
static void ixv_local_timer(void *); |
static void ixv_local_timer(void *); |
|
static void ixv_local_timer_locked(void *); |
static void ixv_setup_interface(device_t, struct adapter *); |
static void ixv_setup_interface(device_t, struct adapter *); |
static void ixv_config_link(struct adapter *); |
static void ixv_config_link(struct adapter *); |
|
|
static int ixv_allocate_transmit_buffers(struct tx_ring *); |
|
static int ixv_setup_transmit_structures(struct adapter *); |
|
static void ixv_setup_transmit_ring(struct tx_ring *); |
|
static void ixv_initialize_transmit_units(struct adapter *); |
static void ixv_initialize_transmit_units(struct adapter *); |
static void ixv_free_transmit_structures(struct adapter *); |
|
static void ixv_free_transmit_buffers(struct tx_ring *); |
|
|
|
static int ixv_allocate_receive_buffers(struct rx_ring *); |
|
static int ixv_setup_receive_structures(struct adapter *); |
|
static int ixv_setup_receive_ring(struct rx_ring *); |
|
static void ixv_initialize_receive_units(struct adapter *); |
static void ixv_initialize_receive_units(struct adapter *); |
static void ixv_free_receive_structures(struct adapter *); |
|
static void ixv_free_receive_buffers(struct rx_ring *); |
|
|
|
static void ixv_enable_intr(struct adapter *); |
static void ixv_enable_intr(struct adapter *); |
static void ixv_disable_intr(struct adapter *); |
static void ixv_disable_intr(struct adapter *); |
static bool ixv_txeof(struct tx_ring *); |
|
static bool ixv_rxeof(struct ix_queue *, int); |
|
static void ixv_rx_checksum(u32, struct mbuf *, u32); |
|
static void ixv_set_multi(struct adapter *); |
static void ixv_set_multi(struct adapter *); |
static void ixv_update_link_status(struct adapter *); |
static void ixv_update_link_status(struct adapter *); |
static void ixv_refresh_mbufs(struct rx_ring *, int); |
static int ixv_sysctl_debug(SYSCTLFN_PROTO); |
static int ixv_xmit(struct tx_ring *, struct mbuf **); |
|
static int ixv_sysctl_stats(SYSCTL_HANDLER_ARGS); |
|
static int ixv_sysctl_debug(SYSCTL_HANDLER_ARGS); |
|
static int ixv_set_flowcntl(SYSCTL_HANDLER_ARGS); |
|
static int ixv_dma_malloc(struct adapter *, bus_size_t, |
|
struct ixv_dma_alloc *, int); |
|
static void ixv_dma_free(struct adapter *, struct ixv_dma_alloc *); |
|
static void ixv_add_rx_process_limit(struct adapter *, const char *, |
|
const char *, int *, int); |
|
static bool ixv_tx_ctx_setup(struct tx_ring *, struct mbuf *); |
|
static bool ixv_tso_setup(struct tx_ring *, struct mbuf *, u32 *); |
|
static void ixv_set_ivar(struct adapter *, u8, u8, s8); |
static void ixv_set_ivar(struct adapter *, u8, u8, s8); |
static void ixv_configure_ivars(struct adapter *); |
static void ixv_configure_ivars(struct adapter *); |
static u8 * ixv_mc_array_itr(struct ixgbe_hw *, u8 **, u32 *); |
static u8 * ixv_mc_array_itr(struct ixgbe_hw *, u8 **, u32 *); |
|
|
static void ixv_setup_vlan_support(struct adapter *); |
static void ixv_setup_vlan_support(struct adapter *); |
|
#if 0 |
static void ixv_register_vlan(void *, struct ifnet *, u16); |
static void ixv_register_vlan(void *, struct ifnet *, u16); |
static void ixv_unregister_vlan(void *, struct ifnet *, u16); |
static void ixv_unregister_vlan(void *, struct ifnet *, u16); |
|
#endif |
|
|
static void ixv_save_stats(struct adapter *); |
static void ixv_save_stats(struct adapter *); |
static void ixv_init_stats(struct adapter *); |
static void ixv_init_stats(struct adapter *); |
static void ixv_update_stats(struct adapter *); |
static void ixv_update_stats(struct adapter *); |
|
static void ixv_add_stats_sysctls(struct adapter *); |
static __inline void ixv_rx_discard(struct rx_ring *, int); |
static void ixv_set_sysctl_value(struct adapter *, const char *, |
static __inline void ixv_rx_input(struct rx_ring *, struct ifnet *, |
const char *, int *, int); |
struct mbuf *, u32); |
|
|
|
/* The MSI/X Interrupt handlers */ |
/* The MSI/X Interrupt handlers */ |
static void ixv_msix_que(void *); |
static int ixv_msix_que(void *); |
static void ixv_msix_mbx(void *); |
static int ixv_msix_mbx(void *); |
|
|
/* Deferred interrupt tasklets */ |
/* Deferred interrupt tasklets */ |
static void ixv_handle_que(void *, int); |
static void ixv_handle_que(void *); |
static void ixv_handle_mbx(void *, int); |
static void ixv_handle_mbx(void *); |
|
|
|
const struct sysctlnode *ixv_sysctl_instance(struct adapter *); |
|
static ixgbe_vendor_info_t *ixv_lookup(const struct pci_attach_args *); |
|
|
|
#ifdef DEV_NETMAP |
|
/* |
|
* This is defined in <dev/netmap/ixgbe_netmap.h>, which is included by |
|
* if_ix.c. |
|
*/ |
|
extern void ixgbe_netmap_attach(struct adapter *adapter); |
|
|
|
#include <net/netmap.h> |
|
#include <sys/selinfo.h> |
|
#include <dev/netmap/netmap_kern.h> |
|
#endif /* DEV_NETMAP */ |
|
|
/********************************************************************* |
/********************************************************************* |
* FreeBSD Device Interface Entry Points |
* FreeBSD Device Interface Entry Points |
*********************************************************************/ |
*********************************************************************/ |
|
|
|
CFATTACH_DECL3_NEW(ixv, sizeof(struct adapter), |
|
ixv_probe, ixv_attach, ixv_detach, NULL, NULL, NULL, |
|
DVF_DETACH_SHUTDOWN); |
|
|
|
# if 0 |
static device_method_t ixv_methods[] = { |
static device_method_t ixv_methods[] = { |
/* Device interface */ |
/* Device interface */ |
DEVMETHOD(device_probe, ixv_probe), |
DEVMETHOD(device_probe, ixv_probe), |
DEVMETHOD(device_attach, ixv_attach), |
DEVMETHOD(device_attach, ixv_attach), |
DEVMETHOD(device_detach, ixv_detach), |
DEVMETHOD(device_detach, ixv_detach), |
DEVMETHOD(device_shutdown, ixv_shutdown), |
DEVMETHOD(device_shutdown, ixv_shutdown), |
{0, 0} |
DEVMETHOD_END |
}; |
}; |
|
#endif |
|
|
#if 0 |
#if 0 |
static driver_t ixv_driver = { |
static driver_t ixv_driver = { |
"ix", ixv_methods, sizeof(struct adapter), |
"ixv", ixv_methods, sizeof(struct adapter), |
}; |
}; |
|
|
extern devclass_t ixgbe_devclass; |
devclass_t ixv_devclass; |
DRIVER_MODULE(ixv, pci, ixv_driver, ixgbe_devclass, 0, 0); |
DRIVER_MODULE(ixv, pci, ixv_driver, ixv_devclass, 0, 0); |
MODULE_DEPEND(ixv, pci, 1, 1, 1); |
MODULE_DEPEND(ixv, pci, 1, 1, 1); |
MODULE_DEPEND(ixv, ether, 1, 1, 1); |
MODULE_DEPEND(ixv, ether, 1, 1, 1); |
|
#ifdef DEV_NETMAP |
|
MODULE_DEPEND(ix, netmap, 1, 1, 1); |
|
#endif /* DEV_NETMAP */ |
|
/* XXX depend on 'ix' ? */ |
#endif |
#endif |
|
|
/* |
/* |
** TUNEABLE PARAMETERS: |
** TUNEABLE PARAMETERS: |
*/ |
*/ |
|
|
|
/* Number of Queues - do not exceed MSIX vectors - 1 */ |
|
static int ixv_num_queues = 1; |
|
#define TUNABLE_INT(__x, __y) |
|
TUNABLE_INT("hw.ixv.num_queues", &ixv_num_queues); |
|
|
/* |
/* |
** AIM: Adaptive Interrupt Moderation |
** AIM: Adaptive Interrupt Moderation |
** which means that the interrupt rate |
** which means that the interrupt rate |
Line 191 MODULE_DEPEND(ixv, ether, 1, 1, 1); |
|
Line 197 MODULE_DEPEND(ixv, ether, 1, 1, 1); |
|
** traffic for that interrupt vector |
** traffic for that interrupt vector |
*/ |
*/ |
static int ixv_enable_aim = FALSE; |
static int ixv_enable_aim = FALSE; |
#define TUNABLE_INT(__x, __y) |
|
TUNABLE_INT("hw.ixv.enable_aim", &ixv_enable_aim); |
TUNABLE_INT("hw.ixv.enable_aim", &ixv_enable_aim); |
|
|
/* How many packets rxeof tries to clean at a time */ |
/* How many packets rxeof tries to clean at a time */ |
static int ixv_rx_process_limit = 128; |
static int ixv_rx_process_limit = 256; |
TUNABLE_INT("hw.ixv.rx_process_limit", &ixv_rx_process_limit); |
TUNABLE_INT("hw.ixv.rx_process_limit", &ixv_rx_process_limit); |
|
|
/* Flow control setting, default to full */ |
/* How many packets txeof tries to clean at a time */ |
static int ixv_flow_control = ixgbe_fc_full; |
static int ixv_tx_process_limit = 256; |
TUNABLE_INT("hw.ixv.flow_control", &ixv_flow_control); |
TUNABLE_INT("hw.ixv.tx_process_limit", &ixv_tx_process_limit); |
|
|
/* |
|
* Header split: this causes the hardware to DMA |
|
* the header into a seperate mbuf from the payload, |
|
* it can be a performance win in some workloads, but |
|
* in others it actually hurts, its off by default. |
|
*/ |
|
static bool ixv_header_split = FALSE; |
|
TUNABLE_INT("hw.ixv.hdr_split", &ixv_header_split); |
|
|
|
/* |
/* |
** Number of TX descriptors per ring, |
** Number of TX descriptors per ring, |
Line 228 TUNABLE_INT("hw.ixv.rxd", &ixv_rxd); |
|
Line 224 TUNABLE_INT("hw.ixv.rxd", &ixv_rxd); |
|
** the real filter table gets cleared during |
** the real filter table gets cleared during |
** a soft reset and we need to repopulate it. |
** a soft reset and we need to repopulate it. |
*/ |
*/ |
static u32 ixv_shadow_vfta[VFTA_SIZE]; |
static u32 ixv_shadow_vfta[IXGBE_VFTA_SIZE]; |
|
|
/********************************************************************* |
/********************************************************************* |
* Device identification routine |
* Device identification routine |
Line 236 static u32 ixv_shadow_vfta[VFTA_SIZE]; |
|
Line 232 static u32 ixv_shadow_vfta[VFTA_SIZE]; |
|
* ixv_probe determines if the driver should be loaded on |
* ixv_probe determines if the driver should be loaded on |
* adapter based on PCI vendor/device id of the adapter. |
* adapter based on PCI vendor/device id of the adapter. |
* |
* |
* return 0 on success, positive on failure |
* return 1 on success, 0 on failure |
*********************************************************************/ |
*********************************************************************/ |
|
|
static int |
static int |
ixv_probe(device_t dev) |
ixv_probe(device_t dev, cfdata_t cf, void *aux) |
|
{ |
|
#ifdef __HAVE_PCI_MSI_MSIX |
|
const struct pci_attach_args *pa = aux; |
|
|
|
return (ixv_lookup(pa) != NULL) ? 1 : 0; |
|
#else |
|
return 0; |
|
#endif |
|
} |
|
|
|
static ixgbe_vendor_info_t * |
|
ixv_lookup(const struct pci_attach_args *pa) |
{ |
{ |
ixv_vendor_info_t *ent; |
pcireg_t subid; |
|
ixgbe_vendor_info_t *ent; |
|
|
u16 pci_vendor_id = 0; |
INIT_DEBUGOUT("ixv_lookup: begin"); |
u16 pci_device_id = 0; |
|
u16 pci_subvendor_id = 0; |
|
u16 pci_subdevice_id = 0; |
|
char adapter_name[256]; |
|
|
|
|
if (PCI_VENDOR(pa->pa_id) != IXGBE_INTEL_VENDOR_ID) |
|
return NULL; |
|
|
pci_vendor_id = pci_get_vendor(dev); |
subid = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); |
if (pci_vendor_id != IXGBE_INTEL_VENDOR_ID) |
|
return (ENXIO); |
|
|
|
pci_device_id = pci_get_device(dev); |
for (ent = ixv_vendor_info_array; ent->vendor_id != 0; ent++) { |
pci_subvendor_id = pci_get_subvendor(dev); |
if ((PCI_VENDOR(pa->pa_id) == ent->vendor_id) && |
pci_subdevice_id = pci_get_subdevice(dev); |
(PCI_PRODUCT(pa->pa_id) == ent->device_id) && |
|
|
ent = ixv_vendor_info_array; |
|
while (ent->vendor_id != 0) { |
|
if ((pci_vendor_id == ent->vendor_id) && |
|
(pci_device_id == ent->device_id) && |
|
|
|
((pci_subvendor_id == ent->subvendor_id) || |
((PCI_SUBSYS_VENDOR(subid) == ent->subvendor_id) || |
(ent->subvendor_id == 0)) && |
(ent->subvendor_id == 0)) && |
|
|
((pci_subdevice_id == ent->subdevice_id) || |
((PCI_SUBSYS_ID(subid) == ent->subdevice_id) || |
(ent->subdevice_id == 0))) { |
(ent->subdevice_id == 0))) { |
snprintf(adapter_name, sizeof(adapter_name), |
return ent; |
"%s, Version - %s", ixv_strings[ent->index], |
|
ixv_driver_version); |
|
device_set_desc_copy(dev, adapter_name); |
|
return (0); |
|
} |
} |
ent++; |
|
} |
} |
return (ENXIO); |
return NULL; |
|
} |
|
|
|
|
|
static void |
|
ixv_sysctl_attach(struct adapter *adapter) |
|
{ |
|
struct sysctllog **log; |
|
const struct sysctlnode *rnode, *cnode; |
|
device_t dev; |
|
|
|
dev = adapter->dev; |
|
log = &adapter->sysctllog; |
|
|
|
if ((rnode = ixv_sysctl_instance(adapter)) == NULL) { |
|
aprint_error_dev(dev, "could not create sysctl root\n"); |
|
return; |
|
} |
|
|
|
if (sysctl_createv(log, 0, &rnode, &cnode, |
|
CTLFLAG_READWRITE, CTLTYPE_INT, |
|
"debug", SYSCTL_DESCR("Debug Info"), |
|
ixv_sysctl_debug, 0, (void *)adapter, 0, CTL_CREATE, CTL_EOL) != 0) |
|
aprint_error_dev(dev, "could not create sysctl\n"); |
|
|
|
/* XXX This is an *instance* sysctl controlling a *global* variable. |
|
* XXX It's that way in the FreeBSD driver that this derives from. |
|
*/ |
|
if (sysctl_createv(log, 0, &rnode, &cnode, |
|
CTLFLAG_READWRITE, CTLTYPE_INT, |
|
"enable_aim", SYSCTL_DESCR("Interrupt Moderation"), |
|
NULL, 0, &ixv_enable_aim, 0, CTL_CREATE, CTL_EOL) != 0) |
|
aprint_error_dev(dev, "could not create sysctl\n"); |
} |
} |
|
|
/********************************************************************* |
/********************************************************************* |
Line 290 ixv_probe(device_t dev) |
|
Line 317 ixv_probe(device_t dev) |
|
* return 0 on success, positive on failure |
* return 0 on success, positive on failure |
*********************************************************************/ |
*********************************************************************/ |
|
|
static int |
static void |
ixv_attach(device_t dev) |
ixv_attach(device_t parent, device_t dev, void *aux) |
{ |
{ |
struct adapter *adapter; |
struct adapter *adapter; |
struct ixgbe_hw *hw; |
struct ixgbe_hw *hw; |
int error = 0; |
int error = 0; |
|
ixgbe_vendor_info_t *ent; |
|
const struct pci_attach_args *pa = aux; |
|
|
INIT_DEBUGOUT("ixv_attach: begin"); |
INIT_DEBUGOUT("ixv_attach: begin"); |
|
|
/* Allocate, clear, and link in our adapter structure */ |
/* Allocate, clear, and link in our adapter structure */ |
adapter = device_get_softc(dev); |
adapter = device_private(dev); |
adapter->dev = adapter->osdep.dev = dev; |
adapter->dev = dev; |
hw = &adapter->hw; |
hw = &adapter->hw; |
|
|
|
#ifdef DEV_NETMAP |
|
adapter->init_locked = ixv_init_locked; |
|
adapter->stop_locked = ixv_stop; |
|
#endif |
|
|
|
adapter->osdep.pc = pa->pa_pc; |
|
adapter->osdep.tag = pa->pa_tag; |
|
adapter->osdep.dmat = pa->pa_dmat; |
|
adapter->osdep.attached = false; |
|
|
|
ent = ixv_lookup(pa); |
|
|
|
KASSERT(ent != NULL); |
|
|
|
aprint_normal(": %s, Version - %s\n", |
|
ixv_strings[ent->index], ixv_driver_version); |
|
|
/* Core Lock Init*/ |
/* Core Lock Init*/ |
IXV_CORE_LOCK_INIT(adapter, device_get_nameunit(dev)); |
IXGBE_CORE_LOCK_INIT(adapter, device_xname(dev)); |
|
|
/* SYSCTL APIs */ |
/* SYSCTL APIs */ |
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), |
ixv_sysctl_attach(adapter); |
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), |
|
OID_AUTO, "stats", CTLTYPE_INT | CTLFLAG_RW, |
|
adapter, 0, ixv_sysctl_stats, "I", "Statistics"); |
|
|
|
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), |
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), |
|
OID_AUTO, "debug", CTLTYPE_INT | CTLFLAG_RW, |
|
adapter, 0, ixv_sysctl_debug, "I", "Debug Info"); |
|
|
|
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), |
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), |
|
OID_AUTO, "flow_control", CTLTYPE_INT | CTLFLAG_RW, |
|
adapter, 0, ixv_set_flowcntl, "I", "Flow Control"); |
|
|
|
SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), |
|
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), |
|
OID_AUTO, "enable_aim", CTLTYPE_INT|CTLFLAG_RW, |
|
&ixv_enable_aim, 1, "Interrupt Moderation"); |
|
|
|
/* Set up the timer callout */ |
/* Set up the timer callout */ |
callout_init_mtx(&adapter->timer, &adapter->core_mtx, 0); |
callout_init(&adapter->timer, 0); |
|
|
/* Determine hardware revision */ |
/* Determine hardware revision */ |
ixv_identify_hardware(adapter); |
ixv_identify_hardware(adapter); |
|
|
/* Do base PCI setup - map BAR0 */ |
/* Do base PCI setup - map BAR0 */ |
if (ixv_allocate_pci_resources(adapter)) { |
if (ixv_allocate_pci_resources(adapter, pa)) { |
device_printf(dev, "Allocation of PCI resources failed\n"); |
aprint_error_dev(dev, "ixv_allocate_pci_resources() failed!\n"); |
error = ENXIO; |
error = ENXIO; |
goto err_out; |
goto err_out; |
} |
} |
|
|
|
/* Sysctls for limiting the amount of work done in the taskqueues */ |
|
ixv_set_sysctl_value(adapter, "rx_processing_limit", |
|
"max number of rx packets to process", |
|
&adapter->rx_process_limit, ixv_rx_process_limit); |
|
|
|
ixv_set_sysctl_value(adapter, "tx_processing_limit", |
|
"max number of tx packets to process", |
|
&adapter->tx_process_limit, ixv_tx_process_limit); |
|
|
/* Do descriptor calc and sanity checks */ |
/* Do descriptor calc and sanity checks */ |
if (((ixv_txd * sizeof(union ixgbe_adv_tx_desc)) % DBA_ALIGN) != 0 || |
if (((ixv_txd * sizeof(union ixgbe_adv_tx_desc)) % DBA_ALIGN) != 0 || |
ixv_txd < MIN_TXD || ixv_txd > MAX_TXD) { |
ixv_txd < MIN_TXD || ixv_txd > MAX_TXD) { |
device_printf(dev, "TXD config issue, using default!\n"); |
aprint_error_dev(dev, "TXD config issue, using default!\n"); |
adapter->num_tx_desc = DEFAULT_TXD; |
adapter->num_tx_desc = DEFAULT_TXD; |
} else |
} else |
adapter->num_tx_desc = ixv_txd; |
adapter->num_tx_desc = ixv_txd; |
|
|
if (((ixv_rxd * sizeof(union ixgbe_adv_rx_desc)) % DBA_ALIGN) != 0 || |
if (((ixv_rxd * sizeof(union ixgbe_adv_rx_desc)) % DBA_ALIGN) != 0 || |
ixv_rxd < MIN_TXD || ixv_rxd > MAX_TXD) { |
ixv_rxd < MIN_RXD || ixv_rxd > MAX_RXD) { |
device_printf(dev, "RXD config issue, using default!\n"); |
aprint_error_dev(dev, "RXD config issue, using default!\n"); |
adapter->num_rx_desc = DEFAULT_RXD; |
adapter->num_rx_desc = DEFAULT_RXD; |
} else |
} else |
adapter->num_rx_desc = ixv_rxd; |
adapter->num_rx_desc = ixv_rxd; |
|
|
/* Allocate our TX/RX Queues */ |
/* Allocate our TX/RX Queues */ |
if (ixv_allocate_queues(adapter)) { |
if (ixgbe_allocate_queues(adapter)) { |
|
aprint_error_dev(dev, "ixgbe_allocate_queues() failed!\n"); |
error = ENOMEM; |
error = ENOMEM; |
goto err_out; |
goto err_out; |
} |
} |
Line 368 ixv_attach(device_t dev) |
|
Line 406 ixv_attach(device_t dev) |
|
*/ |
*/ |
error = ixgbe_init_shared_code(hw); |
error = ixgbe_init_shared_code(hw); |
if (error) { |
if (error) { |
device_printf(dev,"Shared Code Initialization Failure\n"); |
aprint_error_dev(dev, "ixgbe_init_shared_code() failed!\n"); |
error = EIO; |
error = EIO; |
goto err_late; |
goto err_late; |
} |
} |
Line 376 ixv_attach(device_t dev) |
|
Line 414 ixv_attach(device_t dev) |
|
/* Setup the mailbox */ |
/* Setup the mailbox */ |
ixgbe_init_mbx_params_vf(hw); |
ixgbe_init_mbx_params_vf(hw); |
|
|
ixgbe_reset_hw(hw); |
/* Reset mbox api to 1.0 */ |
|
error = ixgbe_reset_hw(hw); |
|
if (error == IXGBE_ERR_RESET_FAILED) |
|
aprint_error_dev(dev, "ixgbe_reset_hw() failure: Reset Failed!\n"); |
|
else if (error) |
|
aprint_error_dev(dev, "ixgbe_reset_hw() failed with error %d\n", error); |
|
if (error) { |
|
error = EIO; |
|
goto err_late; |
|
} |
|
|
/* Get Hardware Flow Control setting */ |
/* Negotiate mailbox API version */ |
hw->fc.requested_mode = ixgbe_fc_full; |
error = ixgbevf_negotiate_api_version(hw, ixgbe_mbox_api_11); |
hw->fc.pause_time = IXV_FC_PAUSE; |
if (error) { |
hw->fc.low_water = IXV_FC_LO; |
device_printf(dev, "MBX API 1.1 negotiation failed! Error %d\n", error); |
hw->fc.high_water = IXV_FC_HI; |
error = EIO; |
hw->fc.send_xon = TRUE; |
goto err_late; |
|
} |
|
|
error = ixgbe_init_hw(hw); |
error = ixgbe_init_hw(hw); |
if (error) { |
if (error) { |
device_printf(dev,"Hardware Initialization Failure\n"); |
aprint_error_dev(dev, "ixgbe_init_hw() failed!\n"); |
error = EIO; |
error = EIO; |
goto err_late; |
goto err_late; |
} |
} |
|
|
error = ixv_allocate_msix(adapter); |
error = ixv_allocate_msix(adapter, pa); |
if (error) |
if (error) { |
|
device_printf(dev, "ixv_allocate_msix() failed!\n"); |
goto err_late; |
goto err_late; |
|
} |
|
|
|
/* If no mac address was assigned, make a random one */ |
|
if (!ixv_check_ether_addr(hw->mac.addr)) { |
|
u8 addr[ETHER_ADDR_LEN]; |
|
uint64_t rndval = cprng_fast64(); |
|
|
|
memcpy(addr, &rndval, sizeof(addr)); |
|
addr[0] &= 0xFE; |
|
addr[0] |= 0x02; |
|
bcopy(addr, hw->mac.addr, sizeof(addr)); |
|
} |
|
|
/* Setup OS specific network interface */ |
/* Setup OS specific network interface */ |
ixv_setup_interface(dev, adapter); |
ixv_setup_interface(dev, adapter); |
|
|
/* Sysctl for limiting the amount of work done in the taskqueue */ |
|
ixv_add_rx_process_limit(adapter, "rx_processing_limit", |
|
"max number of rx packets to process", &adapter->rx_process_limit, |
|
ixv_rx_process_limit); |
|
|
|
/* Do the stats setup */ |
/* Do the stats setup */ |
ixv_save_stats(adapter); |
ixv_save_stats(adapter); |
ixv_init_stats(adapter); |
ixv_init_stats(adapter); |
|
ixv_add_stats_sysctls(adapter); |
|
|
/* Register for VLAN events */ |
/* Register for VLAN events */ |
|
#if 0 /* XXX delete after write? */ |
adapter->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, |
adapter->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, |
ixv_register_vlan, adapter, EVENTHANDLER_PRI_FIRST); |
ixv_register_vlan, adapter, EVENTHANDLER_PRI_FIRST); |
adapter->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, |
adapter->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, |
ixv_unregister_vlan, adapter, EVENTHANDLER_PRI_FIRST); |
ixv_unregister_vlan, adapter, EVENTHANDLER_PRI_FIRST); |
|
#endif |
|
|
|
#ifdef DEV_NETMAP |
|
ixgbe_netmap_attach(adapter); |
|
#endif /* DEV_NETMAP */ |
INIT_DEBUGOUT("ixv_attach: end"); |
INIT_DEBUGOUT("ixv_attach: end"); |
return (0); |
adapter->osdep.attached = true; |
|
return; |
|
|
err_late: |
err_late: |
ixv_free_transmit_structures(adapter); |
ixgbe_free_transmit_structures(adapter); |
ixv_free_receive_structures(adapter); |
ixgbe_free_receive_structures(adapter); |
err_out: |
err_out: |
ixv_free_pci_resources(adapter); |
ixv_free_pci_resources(adapter); |
return (error); |
return; |
|
|
} |
} |
|
|
|
|
*********************************************************************/ |
*********************************************************************/ |
|
|
static int |
static int |
ixv_detach(device_t dev) |
ixv_detach(device_t dev, int flags) |
{ |
{ |
struct adapter *adapter = device_get_softc(dev); |
struct adapter *adapter = device_private(dev); |
struct ix_queue *que = adapter->queues; |
struct ix_queue *que = adapter->queues; |
|
|
INIT_DEBUGOUT("ixv_detach: begin"); |
INIT_DEBUGOUT("ixv_detach: begin"); |
|
if (adapter->osdep.attached == false) |
|
return 0; |
|
|
|
#if NVLAN > 0 |
/* Make sure VLANS are not using driver */ |
/* Make sure VLANS are not using driver */ |
if (adapter->ifp->if_vlantrunk != NULL) { |
if (!VLAN_ATTACHED(&adapter->osdep.ec)) |
device_printf(dev,"Vlan in use, detach first\n"); |
; /* nothing to do: no VLANs */ |
return (EBUSY); |
else if ((flags & (DETACH_SHUTDOWN|DETACH_FORCE)) != 0) |
|
vlan_ifdetach(adapter->ifp); |
|
else { |
|
aprint_error_dev(dev, "VLANs in use, detach first\n"); |
|
return EBUSY; |
} |
} |
|
#endif |
|
|
IXV_CORE_LOCK(adapter); |
IXGBE_CORE_LOCK(adapter); |
ixv_stop(adapter); |
ixv_stop(adapter); |
IXV_CORE_UNLOCK(adapter); |
IXGBE_CORE_UNLOCK(adapter); |
|
|
for (int i = 0; i < adapter->num_queues; i++, que++) { |
for (int i = 0; i < adapter->num_queues; i++, que++) { |
if (que->tq) { |
#ifndef IXGBE_LEGACY_TX |
taskqueue_drain(que->tq, &que->que_task); |
struct tx_ring *txr = adapter->tx_rings; |
taskqueue_free(que->tq); |
|
} |
|
} |
|
|
|
/* Drain the Link queue */ |
softint_disestablish(txr->txr_si); |
if (adapter->tq) { |
#endif |
taskqueue_drain(adapter->tq, &adapter->mbx_task); |
softint_disestablish(que->que_si); |
taskqueue_free(adapter->tq); |
|
} |
} |
|
|
|
/* Drain the Mailbox(link) queue */ |
|
softint_disestablish(adapter->link_si); |
|
|
/* Unregister VLAN events */ |
/* Unregister VLAN events */ |
|
#if 0 /* XXX msaitoh delete after write? */ |
if (adapter->vlan_attach != NULL) |
if (adapter->vlan_attach != NULL) |
EVENTHANDLER_DEREGISTER(vlan_config, adapter->vlan_attach); |
EVENTHANDLER_DEREGISTER(vlan_config, adapter->vlan_attach); |
if (adapter->vlan_detach != NULL) |
if (adapter->vlan_detach != NULL) |
EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach); |
EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach); |
|
#endif |
|
|
ether_ifdetach(adapter->ifp); |
ether_ifdetach(adapter->ifp); |
callout_drain(&adapter->timer); |
callout_halt(&adapter->timer, NULL); |
|
#ifdef DEV_NETMAP |
|
netmap_detach(adapter->ifp); |
|
#endif /* DEV_NETMAP */ |
ixv_free_pci_resources(adapter); |
ixv_free_pci_resources(adapter); |
|
#if 0 /* XXX the NetBSD port is probably missing something here */ |
bus_generic_detach(dev); |
bus_generic_detach(dev); |
if_free(adapter->ifp); |
#endif |
|
if_detach(adapter->ifp); |
|
|
ixv_free_transmit_structures(adapter); |
ixgbe_free_transmit_structures(adapter); |
ixv_free_receive_structures(adapter); |
ixgbe_free_receive_structures(adapter); |
|
|
IXV_CORE_LOCK_DESTROY(adapter); |
IXGBE_CORE_LOCK_DESTROY(adapter); |
return (0); |
return (0); |
} |
} |
|
|
Line 491 ixv_detach(device_t dev) |
|
Line 568 ixv_detach(device_t dev) |
|
* Shutdown entry point |
* Shutdown entry point |
* |
* |
**********************************************************************/ |
**********************************************************************/ |
|
#if 0 /* XXX NetBSD ought to register something like this through pmf(9) */ |
static int |
static int |
ixv_shutdown(device_t dev) |
ixv_shutdown(device_t dev) |
{ |
{ |
struct adapter *adapter = device_get_softc(dev); |
struct adapter *adapter = device_private(dev); |
IXV_CORE_LOCK(adapter); |
IXGBE_CORE_LOCK(adapter); |
ixv_stop(adapter); |
ixv_stop(adapter); |
IXV_CORE_UNLOCK(adapter); |
IXGBE_CORE_UNLOCK(adapter); |
return (0); |
return (0); |
} |
} |
|
#endif |
#if __FreeBSD_version < 800000 |
|
/********************************************************************* |
|
* Transmit entry point |
|
* |
|
* ixv_start is called by the stack to initiate a transmit. |
|
* The driver will remain in this routine as long as there are |
|
* packets to transmit and transmit resources are available. |
|
* In case resources are not available stack is notified and |
|
* the packet is requeued. |
|
**********************************************************************/ |
|
static void |
|
ixv_start_locked(struct tx_ring *txr, struct ifnet * ifp) |
|
{ |
|
struct mbuf *m_head; |
|
struct adapter *adapter = txr->adapter; |
|
|
|
IXV_TX_LOCK_ASSERT(txr); |
|
|
|
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) != |
|
IFF_DRV_RUNNING) |
|
return; |
|
if (!adapter->link_active) |
|
return; |
|
|
|
while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) { |
|
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); |
|
if (m_head == NULL) |
|
break; |
|
|
|
if (ixv_xmit(txr, &m_head)) { |
|
if (m_head == NULL) |
|
break; |
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE; |
|
IFQ_DRV_PREPEND(&ifp->if_snd, m_head); |
|
break; |
|
} |
|
/* Send a copy of the frame to the BPF listener */ |
|
ETHER_BPF_MTAP(ifp, m_head); |
|
|
|
/* Set watchdog on */ |
|
txr->watchdog_check = TRUE; |
|
txr->watchdog_time = ticks; |
|
|
|
} |
|
return; |
|
} |
|
|
|
/* |
|
* Legacy TX start - called by the stack, this |
|
* always uses the first tx ring, and should |
|
* not be used with multiqueue tx enabled. |
|
*/ |
|
static void |
|
ixv_start(struct ifnet *ifp) |
|
{ |
|
struct adapter *adapter = ifp->if_softc; |
|
struct tx_ring *txr = adapter->tx_rings; |
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { |
|
IXV_TX_LOCK(txr); |
|
ixv_start_locked(txr, ifp); |
|
IXV_TX_UNLOCK(txr); |
|
} |
|
return; |
|
} |
|
|
|
#else |
|
|
|
/* |
|
** Multiqueue Transmit driver |
|
** |
|
*/ |
|
static int |
|
ixv_mq_start(struct ifnet *ifp, struct mbuf *m) |
|
{ |
|
struct adapter *adapter = ifp->if_softc; |
|
struct ix_queue *que; |
|
struct tx_ring *txr; |
|
int i = 0, err = 0; |
|
|
|
/* Which queue to use */ |
|
if ((m->m_flags & M_FLOWID) != 0) |
|
i = m->m_pkthdr.flowid % adapter->num_queues; |
|
|
|
txr = &adapter->tx_rings[i]; |
|
que = &adapter->queues[i]; |
|
|
|
if (IXV_TX_TRYLOCK(txr)) { |
|
err = ixv_mq_start_locked(ifp, txr, m); |
|
IXV_TX_UNLOCK(txr); |
|
} else { |
|
err = drbr_enqueue(ifp, txr->br, m); |
|
taskqueue_enqueue(que->tq, &que->que_task); |
|
} |
|
|
|
return (err); |
|
} |
|
|
|
static int |
static int |
ixv_mq_start_locked(struct ifnet *ifp, struct tx_ring *txr, struct mbuf *m) |
ixv_ifflags_cb(struct ethercom *ec) |
{ |
{ |
struct adapter *adapter = txr->adapter; |
struct ifnet *ifp = &ec->ec_if; |
struct mbuf *next; |
struct adapter *adapter = ifp->if_softc; |
int enqueued, err = 0; |
int change = ifp->if_flags ^ adapter->if_flags, rc = 0; |
|
|
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != |
|
IFF_DRV_RUNNING || adapter->link_active == 0) { |
|
if (m != NULL) |
|
err = drbr_enqueue(ifp, txr->br, m); |
|
return (err); |
|
} |
|
|
|
/* Do a clean if descriptors are low */ |
|
if (txr->tx_avail <= IXV_TX_CLEANUP_THRESHOLD) |
|
ixv_txeof(txr); |
|
|
|
enqueued = 0; |
|
if (m == NULL) { |
|
next = drbr_dequeue(ifp, txr->br); |
|
} else if (drbr_needs_enqueue(ifp, txr->br)) { |
|
if ((err = drbr_enqueue(ifp, txr->br, m)) != 0) |
|
return (err); |
|
next = drbr_dequeue(ifp, txr->br); |
|
} else |
|
next = m; |
|
|
|
/* Process the queue */ |
IXGBE_CORE_LOCK(adapter); |
while (next != NULL) { |
|
if ((err = ixv_xmit(txr, &next)) != 0) { |
|
if (next != NULL) |
|
err = drbr_enqueue(ifp, txr->br, next); |
|
break; |
|
} |
|
enqueued++; |
|
drbr_stats_update(ifp, next->m_pkthdr.len, next->m_flags); |
|
/* Send a copy of the frame to the BPF listener */ |
|
ETHER_BPF_MTAP(ifp, next); |
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) |
|
break; |
|
if (txr->tx_avail <= IXV_TX_OP_THRESHOLD) { |
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE; |
|
break; |
|
} |
|
next = drbr_dequeue(ifp, txr->br); |
|
} |
|
|
|
if (enqueued > 0) { |
if (change != 0) |
/* Set watchdog on */ |
adapter->if_flags = ifp->if_flags; |
txr->watchdog_check = TRUE; |
|
txr->watchdog_time = ticks; |
|
} |
|
|
|
return (err); |
if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0) |
} |
rc = ENETRESET; |
|
|
/* |
IXGBE_CORE_UNLOCK(adapter); |
** Flush all ring buffers |
|
*/ |
|
static void |
|
ixv_qflush(struct ifnet *ifp) |
|
{ |
|
struct adapter *adapter = ifp->if_softc; |
|
struct tx_ring *txr = adapter->tx_rings; |
|
struct mbuf *m; |
|
|
|
for (int i = 0; i < adapter->num_queues; i++, txr++) { |
return rc; |
IXV_TX_LOCK(txr); |
|
while ((m = buf_ring_dequeue_sc(txr->br)) != NULL) |
|
m_freem(m); |
|
IXV_TX_UNLOCK(txr); |
|
} |
|
if_qflush(ifp); |
|
} |
} |
|
|
#endif |
|
|
|
/********************************************************************* |
/********************************************************************* |
* Ioctl entry point |
* Ioctl entry point |
* |
* |
Line 688 ixv_qflush(struct ifnet *ifp) |
|
Line 610 ixv_qflush(struct ifnet *ifp) |
|
**********************************************************************/ |
**********************************************************************/ |
|
|
static int |
static int |
ixv_ioctl(struct ifnet * ifp, u_long command, caddr_t data) |
ixv_ioctl(struct ifnet * ifp, u_long command, void *data) |
{ |
{ |
struct adapter *adapter = ifp->if_softc; |
struct adapter *adapter = ifp->if_softc; |
|
struct ifcapreq *ifcr = data; |
struct ifreq *ifr = (struct ifreq *) data; |
struct ifreq *ifr = (struct ifreq *) data; |
int error = 0; |
int error = 0; |
|
int l4csum_en; |
|
const int l4csum = IFCAP_CSUM_TCPv4_Rx|IFCAP_CSUM_UDPv4_Rx| |
|
IFCAP_CSUM_TCPv6_Rx|IFCAP_CSUM_UDPv6_Rx; |
|
|
switch (command) { |
switch (command) { |
|
|
case SIOCSIFMTU: |
|
IOCTL_DEBUGOUT("ioctl: SIOCSIFMTU (Set Interface MTU)"); |
|
if (ifr->ifr_mtu > IXV_MAX_FRAME_SIZE - ETHER_HDR_LEN) { |
|
error = EINVAL; |
|
} else { |
|
IXV_CORE_LOCK(adapter); |
|
ifp->if_mtu = ifr->ifr_mtu; |
|
adapter->max_frame_size = |
|
ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; |
|
ixv_init_locked(adapter); |
|
IXV_CORE_UNLOCK(adapter); |
|
} |
|
break; |
|
case SIOCSIFFLAGS: |
case SIOCSIFFLAGS: |
IOCTL_DEBUGOUT("ioctl: SIOCSIFFLAGS (Set Interface Flags)"); |
IOCTL_DEBUGOUT("ioctl: SIOCSIFFLAGS (Set Interface Flags)"); |
IXV_CORE_LOCK(adapter); |
|
if (ifp->if_flags & IFF_UP) { |
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) |
|
ixv_init_locked(adapter); |
|
} else |
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) |
|
ixv_stop(adapter); |
|
adapter->if_flags = ifp->if_flags; |
|
IXV_CORE_UNLOCK(adapter); |
|
break; |
break; |
case SIOCADDMULTI: |
case SIOCADDMULTI: |
case SIOCDELMULTI: |
case SIOCDELMULTI: |
IOCTL_DEBUGOUT("ioctl: SIOC(ADD|DEL)MULTI"); |
IOCTL_DEBUGOUT("ioctl: SIOC(ADD|DEL)MULTI"); |
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { |
|
IXV_CORE_LOCK(adapter); |
|
ixv_disable_intr(adapter); |
|
ixv_set_multi(adapter); |
|
ixv_enable_intr(adapter); |
|
IXV_CORE_UNLOCK(adapter); |
|
} |
|
break; |
break; |
case SIOCSIFMEDIA: |
case SIOCSIFMEDIA: |
case SIOCGIFMEDIA: |
case SIOCGIFMEDIA: |
IOCTL_DEBUGOUT("ioctl: SIOCxIFMEDIA (Get/Set Interface Media)"); |
IOCTL_DEBUGOUT("ioctl: SIOCxIFMEDIA (Get/Set Interface Media)"); |
error = ifmedia_ioctl(ifp, ifr, &adapter->media, command); |
|
break; |
break; |
case SIOCSIFCAP: |
case SIOCSIFCAP: |
{ |
|
int mask = ifr->ifr_reqcap ^ ifp->if_capenable; |
|
IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)"); |
IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)"); |
if (mask & IFCAP_HWCSUM) |
|
ifp->if_capenable ^= IFCAP_HWCSUM; |
|
if (mask & IFCAP_TSO4) |
|
ifp->if_capenable ^= IFCAP_TSO4; |
|
if (mask & IFCAP_LRO) |
|
ifp->if_capenable ^= IFCAP_LRO; |
|
if (mask & IFCAP_VLAN_HWTAGGING) |
|
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; |
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { |
|
IXV_CORE_LOCK(adapter); |
|
ixv_init_locked(adapter); |
|
IXV_CORE_UNLOCK(adapter); |
|
} |
|
VLAN_CAPABILITIES(ifp); |
|
break; |
break; |
} |
case SIOCSIFMTU: |
|
IOCTL_DEBUGOUT("ioctl: SIOCSIFMTU (Set Interface MTU)"); |
|
break; |
default: |
default: |
IOCTL_DEBUGOUT1("ioctl: UNKNOWN (0x%X)\n", (int)command); |
IOCTL_DEBUGOUT1("ioctl: UNKNOWN (0x%X)", (int)command); |
error = ether_ioctl(ifp, command, data); |
|
break; |
break; |
} |
} |
|
|
return (error); |
switch (command) { |
|
case SIOCSIFMEDIA: |
|
case SIOCGIFMEDIA: |
|
return ifmedia_ioctl(ifp, ifr, &adapter->media, command); |
|
case SIOCSIFCAP: |
|
/* Layer-4 Rx checksum offload has to be turned on and |
|
* off as a unit. |
|
*/ |
|
l4csum_en = ifcr->ifcr_capenable & l4csum; |
|
if (l4csum_en != l4csum && l4csum_en != 0) |
|
return EINVAL; |
|
/*FALLTHROUGH*/ |
|
case SIOCADDMULTI: |
|
case SIOCDELMULTI: |
|
case SIOCSIFFLAGS: |
|
case SIOCSIFMTU: |
|
default: |
|
if ((error = ether_ioctl(ifp, command, data)) != ENETRESET) |
|
return error; |
|
if ((ifp->if_flags & IFF_RUNNING) == 0) |
|
; |
|
else if (command == SIOCSIFCAP || command == SIOCSIFMTU) { |
|
IXGBE_CORE_LOCK(adapter); |
|
ixv_init_locked(adapter); |
|
IXGBE_CORE_UNLOCK(adapter); |
|
} else if (command == SIOCADDMULTI || command == SIOCDELMULTI) { |
|
/* |
|
* Multicast list has changed; set the hardware filter |
|
* accordingly. |
|
*/ |
|
IXGBE_CORE_LOCK(adapter); |
|
ixv_disable_intr(adapter); |
|
ixv_set_multi(adapter); |
|
ixv_enable_intr(adapter); |
|
IXGBE_CORE_UNLOCK(adapter); |
|
} |
|
return 0; |
|
} |
} |
} |
|
|
/********************************************************************* |
/********************************************************************* |
Line 785 ixv_init_locked(struct adapter *adapter) |
|
Line 701 ixv_init_locked(struct adapter *adapter) |
|
struct ifnet *ifp = adapter->ifp; |
struct ifnet *ifp = adapter->ifp; |
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
u32 mhadd, gpie; |
int error = 0; |
|
|
INIT_DEBUGOUT("ixv_init: begin"); |
INIT_DEBUGOUT("ixv_init_locked: begin"); |
mtx_assert(&adapter->core_mtx, MA_OWNED); |
KASSERT(mutex_owned(&adapter->core_mtx)); |
hw->adapter_stopped = FALSE; |
hw->adapter_stopped = FALSE; |
ixgbe_stop_adapter(hw); |
ixgbe_stop_adapter(hw); |
callout_stop(&adapter->timer); |
callout_stop(&adapter->timer); |
Line 797 ixv_init_locked(struct adapter *adapter) |
|
Line 713 ixv_init_locked(struct adapter *adapter) |
|
ixgbe_set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV); |
ixgbe_set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV); |
|
|
/* Get the latest mac address, User can use a LAA */ |
/* Get the latest mac address, User can use a LAA */ |
bcopy(IF_LLADDR(adapter->ifp), hw->mac.addr, |
memcpy(hw->mac.addr, CLLADDR(adapter->ifp->if_sadl), |
IXGBE_ETH_LENGTH_OF_ADDRESS); |
IXGBE_ETH_LENGTH_OF_ADDRESS); |
ixgbe_set_rar(hw, 0, hw->mac.addr, 0, 1); |
ixgbe_set_rar(hw, 0, hw->mac.addr, 0, 1); |
hw->addr_ctrl.rar_used_count = 1; |
hw->addr_ctrl.rar_used_count = 1; |
|
|
/* Prepare transmit descriptors and buffers */ |
/* Prepare transmit descriptors and buffers */ |
if (ixv_setup_transmit_structures(adapter)) { |
if (ixgbe_setup_transmit_structures(adapter)) { |
device_printf(dev,"Could not setup transmit structures\n"); |
aprint_error_dev(dev, "Could not setup transmit structures\n"); |
ixv_stop(adapter); |
ixv_stop(adapter); |
return; |
return; |
} |
} |
|
|
|
/* Reset VF and renegotiate mailbox API version */ |
ixgbe_reset_hw(hw); |
ixgbe_reset_hw(hw); |
|
error = ixgbevf_negotiate_api_version(hw, ixgbe_mbox_api_11); |
|
if (error) |
|
device_printf(dev, "MBX API 1.1 negotiation failed! Error %d\n", error); |
|
|
ixv_initialize_transmit_units(adapter); |
ixv_initialize_transmit_units(adapter); |
|
|
/* Setup Multicast table */ |
/* Setup Multicast table */ |
Line 825 ixv_init_locked(struct adapter *adapter) |
|
Line 746 ixv_init_locked(struct adapter *adapter) |
|
adapter->rx_mbuf_sz = MCLBYTES; |
adapter->rx_mbuf_sz = MCLBYTES; |
|
|
/* Prepare receive descriptors and buffers */ |
/* Prepare receive descriptors and buffers */ |
if (ixv_setup_receive_structures(adapter)) { |
if (ixgbe_setup_receive_structures(adapter)) { |
device_printf(dev,"Could not setup receive structures\n"); |
device_printf(dev, "Could not setup receive structures\n"); |
ixv_stop(adapter); |
ixv_stop(adapter); |
return; |
return; |
} |
} |
Line 834 ixv_init_locked(struct adapter *adapter) |
|
Line 755 ixv_init_locked(struct adapter *adapter) |
|
/* Configure RX settings */ |
/* Configure RX settings */ |
ixv_initialize_receive_units(adapter); |
ixv_initialize_receive_units(adapter); |
|
|
/* Enable Enhanced MSIX mode */ |
#if 0 /* XXX isn't it required? -- msaitoh */ |
gpie = IXGBE_READ_REG(&adapter->hw, IXGBE_GPIE); |
|
gpie |= IXGBE_GPIE_MSIX_MODE | IXGBE_GPIE_EIAME; |
|
gpie |= IXGBE_GPIE_PBA_SUPPORT | IXGBE_GPIE_OCD; |
|
IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); |
|
|
|
/* Set the various hardware offload abilities */ |
/* Set the various hardware offload abilities */ |
ifp->if_hwassist = 0; |
ifp->if_hwassist = 0; |
if (ifp->if_capenable & IFCAP_TSO4) |
if (ifp->if_capenable & IFCAP_TSO4) |
Line 850 ixv_init_locked(struct adapter *adapter) |
|
Line 766 ixv_init_locked(struct adapter *adapter) |
|
ifp->if_hwassist |= CSUM_SCTP; |
ifp->if_hwassist |= CSUM_SCTP; |
#endif |
#endif |
} |
} |
|
#endif |
|
|
/* Set MTU size */ |
|
if (ifp->if_mtu > ETHERMTU) { |
|
mhadd = IXGBE_READ_REG(hw, IXGBE_MHADD); |
|
mhadd &= ~IXGBE_MHADD_MFS_MASK; |
|
mhadd |= adapter->max_frame_size << IXGBE_MHADD_MFS_SHIFT; |
|
IXGBE_WRITE_REG(hw, IXGBE_MHADD, mhadd); |
|
} |
|
|
|
/* Set up VLAN offload and filter */ |
/* Set up VLAN offload and filter */ |
ixv_setup_vlan_support(adapter); |
ixv_setup_vlan_support(adapter); |
|
|
callout_reset(&adapter->timer, hz, ixv_local_timer, adapter); |
|
|
|
/* Set up MSI/X routing */ |
/* Set up MSI/X routing */ |
ixv_configure_ivars(adapter); |
ixv_configure_ivars(adapter); |
|
|
Line 871 ixv_init_locked(struct adapter *adapter) |
|
Line 778 ixv_init_locked(struct adapter *adapter) |
|
IXGBE_WRITE_REG(hw, IXGBE_VTEIAM, IXGBE_EICS_RTX_QUEUE); |
IXGBE_WRITE_REG(hw, IXGBE_VTEIAM, IXGBE_EICS_RTX_QUEUE); |
|
|
/* Set moderation on the Link interrupt */ |
/* Set moderation on the Link interrupt */ |
IXGBE_WRITE_REG(hw, IXGBE_VTEITR(adapter->mbxvec), IXV_LINK_ITR); |
IXGBE_WRITE_REG(hw, IXGBE_VTEITR(adapter->vector), IXGBE_LINK_ITR); |
|
|
/* Stats init */ |
/* Stats init */ |
ixv_init_stats(adapter); |
ixv_init_stats(adapter); |
Line 879 ixv_init_locked(struct adapter *adapter) |
|
Line 786 ixv_init_locked(struct adapter *adapter) |
|
/* Config/Enable Link */ |
/* Config/Enable Link */ |
ixv_config_link(adapter); |
ixv_config_link(adapter); |
|
|
|
/* Start watchdog */ |
|
callout_reset(&adapter->timer, hz, ixv_local_timer, adapter); |
|
|
/* And now turn on interrupts */ |
/* And now turn on interrupts */ |
ixv_enable_intr(adapter); |
ixv_enable_intr(adapter); |
|
|
/* Now inform the stack we're ready */ |
/* Now inform the stack we're ready */ |
ifp->if_drv_flags |= IFF_DRV_RUNNING; |
ifp->if_flags |= IFF_RUNNING; |
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; |
ifp->if_flags &= ~IFF_OACTIVE; |
|
|
return; |
return; |
} |
} |
|
|
static void |
static int |
ixv_init(void *arg) |
ixv_init(struct ifnet *ifp) |
{ |
{ |
struct adapter *adapter = arg; |
struct adapter *adapter = ifp->if_softc; |
|
|
IXV_CORE_LOCK(adapter); |
IXGBE_CORE_LOCK(adapter); |
ixv_init_locked(adapter); |
ixv_init_locked(adapter); |
IXV_CORE_UNLOCK(adapter); |
IXGBE_CORE_UNLOCK(adapter); |
return; |
return 0; |
} |
} |
|
|
|
|
Line 942 ixv_handle_que(void *context) |
|
Line 852 ixv_handle_que(void *context) |
|
{ |
{ |
struct ix_queue *que = context; |
struct ix_queue *que = context; |
struct adapter *adapter = que->adapter; |
struct adapter *adapter = que->adapter; |
struct tx_ring *txr = que->txr; |
struct tx_ring *txr = que->txr; |
struct ifnet *ifp = adapter->ifp; |
struct ifnet *ifp = adapter->ifp; |
bool more; |
bool more; |
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { |
if (ifp->if_flags & IFF_RUNNING) { |
more = ixv_rxeof(que, adapter->rx_process_limit); |
more = ixgbe_rxeof(que); |
IXV_TX_LOCK(txr); |
IXGBE_TX_LOCK(txr); |
ixv_txeof(txr); |
ixgbe_txeof(txr); |
#if __FreeBSD_version >= 800000 |
#ifndef IXGBE_LEGACY_TX |
if (!drbr_empty(ifp, txr->br)) |
if (pcq_peek(txr->txr_interq) != NULL) |
ixv_mq_start_locked(ifp, txr, NULL); |
ixgbe_mq_start_locked(ifp, txr); |
#else |
#else |
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) |
if (!IFQ_IS_EMPTY(&ifp->if_snd)) |
ixv_start_locked(txr, ifp); |
ixgbe_start_locked(txr, ifp); |
#endif |
#endif |
IXV_TX_UNLOCK(txr); |
IXGBE_TX_UNLOCK(txr); |
if (more) { |
if (more) { |
taskqueue_enqueue(que->tq, &que->que_task); |
adapter->req.ev_count++; |
|
softint_schedule(que->que_si); |
return; |
return; |
} |
} |
} |
} |
Line 974 ixv_handle_que(void *context) |
|
Line 885 ixv_handle_que(void *context) |
|
* MSI Queue Interrupt Service routine |
* MSI Queue Interrupt Service routine |
* |
* |
**********************************************************************/ |
**********************************************************************/ |
void |
int |
ixv_msix_que(void *arg) |
ixv_msix_que(void *arg) |
{ |
{ |
struct ix_queue *que = arg; |
struct ix_queue *que = arg; |
struct adapter *adapter = que->adapter; |
struct adapter *adapter = que->adapter; |
|
struct ifnet *ifp = adapter->ifp; |
struct tx_ring *txr = que->txr; |
struct tx_ring *txr = que->txr; |
struct rx_ring *rxr = que->rxr; |
struct rx_ring *rxr = que->rxr; |
bool more_tx, more_rx; |
bool more; |
u32 newitr = 0; |
u32 newitr = 0; |
|
|
ixv_disable_queue(adapter, que->msix); |
ixv_disable_queue(adapter, que->msix); |
++que->irqs; |
++que->irqs.ev_count; |
|
|
more_rx = ixv_rxeof(que, adapter->rx_process_limit); |
#ifdef __NetBSD__ |
|
/* Don't run ixgbe_rxeof in interrupt context */ |
IXV_TX_LOCK(txr); |
more = true; |
more_tx = ixv_txeof(txr); |
#else |
IXV_TX_UNLOCK(txr); |
more = ixgbe_rxeof(que); |
|
#endif |
|
|
more_rx = ixv_rxeof(que, adapter->rx_process_limit); |
IXGBE_TX_LOCK(txr); |
|
ixgbe_txeof(txr); |
|
/* |
|
** Make certain that if the stack |
|
** has anything queued the task gets |
|
** scheduled to handle it. |
|
*/ |
|
#ifdef IXGBE_LEGACY_TX |
|
if (!IFQ_IS_EMPTY(&adapter->ifp->if_snd)) |
|
ixgbe_start_locked(txr, ifp); |
|
#else |
|
if (pcq_peek(txr->txr_interq) != NULL) |
|
ixgbe_mq_start_locked(ifp, txr); |
|
#endif |
|
IXGBE_TX_UNLOCK(txr); |
|
|
/* Do AIM now? */ |
/* Do AIM now? */ |
|
|
Line 1044 ixv_msix_que(void *arg) |
|
Line 971 ixv_msix_que(void *arg) |
|
rxr->packets = 0; |
rxr->packets = 0; |
|
|
no_calc: |
no_calc: |
if (more_tx || more_rx) |
if (more) |
taskqueue_enqueue(que->tq, &que->que_task); |
softint_schedule(que->que_si); |
else /* Reenable this interrupt */ |
else /* Reenable this interrupt */ |
ixv_enable_queue(adapter, que->msix); |
ixv_enable_queue(adapter, que->msix); |
return; |
return 1; |
} |
} |
|
|
static void |
static int |
ixv_msix_mbx(void *arg) |
ixv_msix_mbx(void *arg) |
{ |
{ |
struct adapter *adapter = arg; |
struct adapter *adapter = arg; |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
u32 reg; |
u32 reg; |
|
|
++adapter->mbx_irq; |
++adapter->link_irq.ev_count; |
|
|
/* First get the cause */ |
/* First get the cause */ |
reg = IXGBE_READ_REG(hw, IXGBE_VTEICS); |
reg = IXGBE_READ_REG(hw, IXGBE_VTEICS); |
Line 1067 ixv_msix_mbx(void *arg) |
|
Line 994 ixv_msix_mbx(void *arg) |
|
|
|
/* Link status change */ |
/* Link status change */ |
if (reg & IXGBE_EICR_LSC) |
if (reg & IXGBE_EICR_LSC) |
taskqueue_enqueue(adapter->tq, &adapter->mbx_task); |
softint_schedule(adapter->link_si); |
|
|
IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, IXGBE_EIMS_OTHER); |
IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, IXGBE_EIMS_OTHER); |
return; |
return 1; |
} |
} |
|
|
/********************************************************************* |
/********************************************************************* |
Line 1087 ixv_media_status(struct ifnet * ifp, str |
|
Line 1014 ixv_media_status(struct ifnet * ifp, str |
|
struct adapter *adapter = ifp->if_softc; |
struct adapter *adapter = ifp->if_softc; |
|
|
INIT_DEBUGOUT("ixv_media_status: begin"); |
INIT_DEBUGOUT("ixv_media_status: begin"); |
IXV_CORE_LOCK(adapter); |
IXGBE_CORE_LOCK(adapter); |
ixv_update_link_status(adapter); |
ixv_update_link_status(adapter); |
|
|
ifmr->ifm_status = IFM_AVALID; |
ifmr->ifm_status = IFM_AVALID; |
ifmr->ifm_active = IFM_ETHER; |
ifmr->ifm_active = IFM_ETHER; |
|
|
if (!adapter->link_active) { |
if (!adapter->link_active) { |
IXV_CORE_UNLOCK(adapter); |
IXGBE_CORE_UNLOCK(adapter); |
return; |
return; |
} |
} |
|
|
Line 1109 ixv_media_status(struct ifnet * ifp, str |
|
Line 1036 ixv_media_status(struct ifnet * ifp, str |
|
break; |
break; |
} |
} |
|
|
IXV_CORE_UNLOCK(adapter); |
IXGBE_CORE_UNLOCK(adapter); |
|
|
return; |
return; |
} |
} |
Line 1144 ixv_media_change(struct ifnet * ifp) |
|
Line 1071 ixv_media_change(struct ifnet * ifp) |
|
return (0); |
return (0); |
} |
} |
|
|
/********************************************************************* |
|
* |
|
* This routine maps the mbufs to tx descriptors, allowing the |
|
* TX engine to transmit the packets. |
|
* - return 0 on success, positive on failure |
|
* |
|
**********************************************************************/ |
|
|
|
static int |
|
ixv_xmit(struct tx_ring *txr, struct mbuf **m_headp) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
u32 olinfo_status = 0, cmd_type_len; |
|
u32 paylen = 0; |
|
int i, j, error, nsegs; |
|
int first, last = 0; |
|
struct mbuf *m_head; |
|
bus_dma_segment_t segs[32]; |
|
bus_dmamap_t map; |
|
struct ixv_tx_buf *txbuf, *txbuf_mapped; |
|
union ixgbe_adv_tx_desc *txd = NULL; |
|
|
|
m_head = *m_headp; |
|
|
|
/* Basic descriptor defines */ |
|
cmd_type_len = (IXGBE_ADVTXD_DTYP_DATA | |
|
IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT); |
|
|
|
if (m_head->m_flags & M_VLANTAG) |
|
cmd_type_len |= IXGBE_ADVTXD_DCMD_VLE; |
|
|
|
/* |
|
* Important to capture the first descriptor |
|
* used because it will contain the index of |
|
* the one we tell the hardware to report back |
|
*/ |
|
first = txr->next_avail_desc; |
|
txbuf = &txr->tx_buffers[first]; |
|
txbuf_mapped = txbuf; |
|
map = txbuf->map; |
|
|
|
/* |
|
* Map the packet for DMA. |
|
*/ |
|
error = bus_dmamap_load_mbuf_sg(txr->txtag, map, |
|
*m_headp, segs, &nsegs, BUS_DMA_NOWAIT); |
|
|
|
if (error == EFBIG) { |
|
struct mbuf *m; |
|
|
|
m = m_defrag(*m_headp, M_DONTWAIT); |
|
if (m == NULL) { |
|
adapter->mbuf_defrag_failed++; |
|
m_freem(*m_headp); |
|
*m_headp = NULL; |
|
return (ENOBUFS); |
|
} |
|
*m_headp = m; |
|
|
|
/* Try it again */ |
|
error = bus_dmamap_load_mbuf_sg(txr->txtag, map, |
|
*m_headp, segs, &nsegs, BUS_DMA_NOWAIT); |
|
|
|
if (error == ENOMEM) { |
|
adapter->no_tx_dma_setup++; |
|
return (error); |
|
} else if (error != 0) { |
|
adapter->no_tx_dma_setup++; |
|
m_freem(*m_headp); |
|
*m_headp = NULL; |
|
return (error); |
|
} |
|
} else if (error == ENOMEM) { |
|
adapter->no_tx_dma_setup++; |
|
return (error); |
|
} else if (error != 0) { |
|
adapter->no_tx_dma_setup++; |
|
m_freem(*m_headp); |
|
*m_headp = NULL; |
|
return (error); |
|
} |
|
|
|
/* Make certain there are enough descriptors */ |
|
if (nsegs > txr->tx_avail - 2) { |
|
txr->no_desc_avail++; |
|
error = ENOBUFS; |
|
goto xmit_fail; |
|
} |
|
m_head = *m_headp; |
|
|
|
/* |
|
** Set up the appropriate offload context |
|
** this becomes the first descriptor of |
|
** a packet. |
|
*/ |
|
if (m_head->m_pkthdr.csum_flags & CSUM_TSO) { |
|
if (ixv_tso_setup(txr, m_head, &paylen)) { |
|
cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE; |
|
olinfo_status |= IXGBE_TXD_POPTS_IXSM << 8; |
|
olinfo_status |= IXGBE_TXD_POPTS_TXSM << 8; |
|
olinfo_status |= paylen << IXGBE_ADVTXD_PAYLEN_SHIFT; |
|
++adapter->tso_tx; |
|
} else |
|
return (ENXIO); |
|
} else if (ixv_tx_ctx_setup(txr, m_head)) |
|
olinfo_status |= IXGBE_TXD_POPTS_TXSM << 8; |
|
|
|
/* Record payload length */ |
|
if (paylen == 0) |
|
olinfo_status |= m_head->m_pkthdr.len << |
|
IXGBE_ADVTXD_PAYLEN_SHIFT; |
|
|
|
i = txr->next_avail_desc; |
|
for (j = 0; j < nsegs; j++) { |
|
bus_size_t seglen; |
|
bus_addr_t segaddr; |
|
|
|
txbuf = &txr->tx_buffers[i]; |
|
txd = &txr->tx_base[i]; |
|
seglen = segs[j].ds_len; |
|
segaddr = htole64(segs[j].ds_addr); |
|
|
|
txd->read.buffer_addr = segaddr; |
|
txd->read.cmd_type_len = htole32(txr->txd_cmd | |
|
cmd_type_len |seglen); |
|
txd->read.olinfo_status = htole32(olinfo_status); |
|
last = i; /* descriptor that will get completion IRQ */ |
|
|
|
if (++i == adapter->num_tx_desc) |
|
i = 0; |
|
|
|
txbuf->m_head = NULL; |
|
txbuf->eop_index = -1; |
|
} |
|
|
|
txd->read.cmd_type_len |= |
|
htole32(IXGBE_TXD_CMD_EOP | IXGBE_TXD_CMD_RS); |
|
txr->tx_avail -= nsegs; |
|
txr->next_avail_desc = i; |
|
|
|
txbuf->m_head = m_head; |
|
txbuf->map = map; |
|
bus_dmamap_sync(txr->txtag, map, BUS_DMASYNC_PREWRITE); |
|
|
|
/* Set the index of the descriptor that will be marked done */ |
|
txbuf = &txr->tx_buffers[first]; |
|
txbuf->eop_index = last; |
|
|
|
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, |
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
|
/* |
|
* Advance the Transmit Descriptor Tail (Tdt), this tells the |
|
* hardware that this frame is available to transmit. |
|
*/ |
|
++txr->total_packets; |
|
IXGBE_WRITE_REG(&adapter->hw, IXGBE_VFTDT(txr->me), i); |
|
|
|
return (0); |
|
|
|
xmit_fail: |
|
bus_dmamap_unload(txr->txtag, txbuf->map); |
|
return (error); |
|
|
|
} |
|
|
|
|
|
/********************************************************************* |
/********************************************************************* |
* Multicast Update |
* Multicast Update |
|
|
static void |
static void |
ixv_set_multi(struct adapter *adapter) |
ixv_set_multi(struct adapter *adapter) |
{ |
{ |
|
struct ether_multi *enm; |
|
struct ether_multistep step; |
u8 mta[MAX_NUM_MULTICAST_ADDRESSES * IXGBE_ETH_LENGTH_OF_ADDRESS]; |
u8 mta[MAX_NUM_MULTICAST_ADDRESSES * IXGBE_ETH_LENGTH_OF_ADDRESS]; |
u8 *update_ptr; |
u8 *update_ptr; |
struct ifmultiaddr *ifma; |
|
int mcnt = 0; |
int mcnt = 0; |
struct ifnet *ifp = adapter->ifp; |
struct ethercom *ec = &adapter->osdep.ec; |
|
|
IOCTL_DEBUGOUT("ixv_set_multi: begin"); |
IOCTL_DEBUGOUT("ixv_set_multi: begin"); |
|
|
#if __FreeBSD_version < 800000 |
ETHER_FIRST_MULTI(step, ec, enm); |
IF_ADDR_LOCK(ifp); |
while (enm != NULL) { |
#else |
bcopy(enm->enm_addrlo, |
if_maddr_rlock(ifp); |
|
#endif |
|
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { |
|
if (ifma->ifma_addr->sa_family != AF_LINK) |
|
continue; |
|
bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), |
|
&mta[mcnt * IXGBE_ETH_LENGTH_OF_ADDRESS], |
&mta[mcnt * IXGBE_ETH_LENGTH_OF_ADDRESS], |
IXGBE_ETH_LENGTH_OF_ADDRESS); |
IXGBE_ETH_LENGTH_OF_ADDRESS); |
mcnt++; |
mcnt++; |
|
/* XXX This might be required --msaitoh */ |
|
if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES) |
|
break; |
|
ETHER_NEXT_MULTI(step, enm); |
} |
} |
#if __FreeBSD_version < 800000 |
|
IF_ADDR_UNLOCK(ifp); |
|
#else |
|
if_maddr_runlock(ifp); |
|
#endif |
|
|
|
update_ptr = mta; |
update_ptr = mta; |
|
|
ixgbe_update_mc_addr_list(&adapter->hw, |
ixgbe_update_mc_addr_list(&adapter->hw, |
update_ptr, mcnt, ixv_mc_array_itr); |
update_ptr, mcnt, ixv_mc_array_itr, TRUE); |
|
|
return; |
return; |
} |
} |
Line 1384 ixv_mc_array_itr(struct ixgbe_hw *hw, u8 |
|
Line 1140 ixv_mc_array_itr(struct ixgbe_hw *hw, u8 |
|
static void |
static void |
ixv_local_timer(void *arg) |
ixv_local_timer(void *arg) |
{ |
{ |
|
struct adapter *adapter = arg; |
|
|
|
IXGBE_CORE_LOCK(adapter); |
|
ixv_local_timer_locked(adapter); |
|
IXGBE_CORE_UNLOCK(adapter); |
|
} |
|
|
|
static void |
|
ixv_local_timer_locked(void *arg) |
|
{ |
struct adapter *adapter = arg; |
struct adapter *adapter = arg; |
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
struct tx_ring *txr = adapter->tx_rings; |
struct ix_queue *que = adapter->queues; |
int i; |
u64 queues = 0; |
|
int hung = 0; |
|
|
mtx_assert(&adapter->core_mtx, MA_OWNED); |
KASSERT(mutex_owned(&adapter->core_mtx)); |
|
|
ixv_update_link_status(adapter); |
ixv_update_link_status(adapter); |
|
|
Line 1397 ixv_local_timer(void *arg) |
|
Line 1164 ixv_local_timer(void *arg) |
|
ixv_update_stats(adapter); |
ixv_update_stats(adapter); |
|
|
/* |
/* |
* If the interface has been paused |
** Check the TX queues status |
* then don't do the watchdog check |
** - mark hung queues so we don't schedule on them |
*/ |
** - watchdog only if all queues show hung |
if (IXGBE_READ_REG(&adapter->hw, IXGBE_TFCS) & IXGBE_TFCS_TXOFF) |
|
goto out; |
|
/* |
|
** Check for time since any descriptor was cleaned |
|
*/ |
*/ |
for (i = 0; i < adapter->num_queues; i++, txr++) { |
for (int i = 0; i < adapter->num_queues; i++, que++) { |
IXV_TX_LOCK(txr); |
/* Keep track of queues with work for soft irq */ |
if (txr->watchdog_check == FALSE) { |
if (que->txr->busy) |
IXV_TX_UNLOCK(txr); |
queues |= ((u64)1 << que->me); |
continue; |
/* |
|
** Each time txeof runs without cleaning, but there |
|
** are uncleaned descriptors it increments busy. If |
|
** we get to the MAX we declare it hung. |
|
*/ |
|
if (que->busy == IXGBE_QUEUE_HUNG) { |
|
++hung; |
|
/* Mark the queue as inactive */ |
|
adapter->active_queues &= ~((u64)1 << que->me); |
|
continue; |
|
} else { |
|
/* Check if we've come back from hung */ |
|
if ((adapter->active_queues & ((u64)1 << que->me)) == 0) |
|
adapter->active_queues |= ((u64)1 << que->me); |
|
} |
|
if (que->busy >= IXGBE_MAX_TX_BUSY) { |
|
device_printf(dev,"Warning queue %d " |
|
"appears to be hung!\n", i); |
|
que->txr->busy = IXGBE_QUEUE_HUNG; |
|
++hung; |
} |
} |
if ((ticks - txr->watchdog_time) > IXV_WATCHDOG) |
|
goto hung; |
} |
IXV_TX_UNLOCK(txr); |
|
|
/* Only truly watchdog if all queues show hung */ |
|
if (hung == adapter->num_queues) |
|
goto watchdog; |
|
else if (queues != 0) { /* Force an IRQ on queues with work */ |
|
ixv_rearm_queues(adapter, queues); |
} |
} |
out: |
|
ixv_rearm_queues(adapter, adapter->que_mask); |
|
callout_reset(&adapter->timer, hz, ixv_local_timer, adapter); |
callout_reset(&adapter->timer, hz, ixv_local_timer, adapter); |
return; |
return; |
|
|
hung: |
watchdog: |
device_printf(adapter->dev, "Watchdog timeout -- resetting\n"); |
device_printf(adapter->dev, "Watchdog timeout -- resetting\n"); |
device_printf(dev,"Queue(%d) tdh = %d, hw tdt = %d\n", txr->me, |
adapter->ifp->if_flags &= ~IFF_RUNNING; |
IXGBE_READ_REG(&adapter->hw, IXGBE_VFTDH(i)), |
adapter->watchdog_events.ev_count++; |
IXGBE_READ_REG(&adapter->hw, IXGBE_VFTDT(i))); |
|
device_printf(dev,"TX(%d) desc avail = %d," |
|
"Next TX to Clean = %d\n", |
|
txr->me, txr->tx_avail, txr->next_to_clean); |
|
adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; |
|
adapter->watchdog_events++; |
|
IXV_TX_UNLOCK(txr); |
|
ixv_init_locked(adapter); |
ixv_init_locked(adapter); |
} |
} |
|
|
|
|
ixv_update_link_status(struct adapter *adapter) |
ixv_update_link_status(struct adapter *adapter) |
{ |
{ |
struct ifnet *ifp = adapter->ifp; |
struct ifnet *ifp = adapter->ifp; |
struct tx_ring *txr = adapter->tx_rings; |
|
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
|
|
|
|
if (adapter->link_up){ |
if (adapter->link_up){ |
if (adapter->link_active == FALSE) { |
if (adapter->link_active == FALSE) { |
if (bootverbose) |
if (bootverbose) |
Line 1462 ixv_update_link_status(struct adapter *a |
|
Line 1239 ixv_update_link_status(struct adapter *a |
|
device_printf(dev,"Link is Down\n"); |
device_printf(dev,"Link is Down\n"); |
if_link_state_change(ifp, LINK_STATE_DOWN); |
if_link_state_change(ifp, LINK_STATE_DOWN); |
adapter->link_active = FALSE; |
adapter->link_active = FALSE; |
for (int i = 0; i < adapter->num_queues; |
|
i++, txr++) |
|
txr->watchdog_check = FALSE; |
|
} |
} |
} |
} |
|
|
Line 1472 ixv_update_link_status(struct adapter *a |
|
Line 1246 ixv_update_link_status(struct adapter *a |
|
} |
} |
|
|
|
|
|
static void |
|
ixv_ifstop(struct ifnet *ifp, int disable) |
|
{ |
|
struct adapter *adapter = ifp->if_softc; |
|
|
|
IXGBE_CORE_LOCK(adapter); |
|
ixv_stop(adapter); |
|
IXGBE_CORE_UNLOCK(adapter); |
|
} |
|
|
/********************************************************************* |
/********************************************************************* |
* |
* |
* This routine disables all traffic on the adapter by issuing a |
* This routine disables all traffic on the adapter by issuing a |
Line 1487 ixv_stop(void *arg) |
|
Line 1271 ixv_stop(void *arg) |
|
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
ifp = adapter->ifp; |
ifp = adapter->ifp; |
|
|
mtx_assert(&adapter->core_mtx, MA_OWNED); |
KASSERT(mutex_owned(&adapter->core_mtx)); |
|
|
INIT_DEBUGOUT("ixv_stop: begin\n"); |
INIT_DEBUGOUT("ixv_stop: begin\n"); |
ixv_disable_intr(adapter); |
ixv_disable_intr(adapter); |
|
|
/* Tell the stack that the interface is no longer active */ |
/* Tell the stack that the interface is no longer active */ |
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); |
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); |
|
|
ixgbe_reset_hw(hw); |
ixgbe_reset_hw(hw); |
adapter->hw.adapter_stopped = FALSE; |
adapter->hw.adapter_stopped = FALSE; |
Line 1515 ixv_stop(void *arg) |
|
Line 1299 ixv_stop(void *arg) |
|
static void |
static void |
ixv_identify_hardware(struct adapter *adapter) |
ixv_identify_hardware(struct adapter *adapter) |
{ |
{ |
device_t dev = adapter->dev; |
pcitag_t tag; |
u16 pci_cmd_word; |
pci_chipset_tag_t pc; |
|
pcireg_t subid, id; |
|
struct ixgbe_hw *hw = &adapter->hw; |
|
|
|
pc = adapter->osdep.pc; |
|
tag = adapter->osdep.tag; |
|
|
/* |
/* |
** Make sure BUSMASTER is set, on a VM under |
** Make sure BUSMASTER is set, on a VM under |
** KVM it may not be and will break things. |
** KVM it may not be and will break things. |
*/ |
*/ |
pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); |
ixgbe_pci_enable_busmaster(pc, tag); |
if (!((pci_cmd_word & PCIM_CMD_BUSMASTEREN) && |
|
(pci_cmd_word & PCIM_CMD_MEMEN))) { |
id = pci_conf_read(pc, tag, PCI_ID_REG); |
INIT_DEBUGOUT("Memory Access and/or Bus Master " |
subid = pci_conf_read(pc, tag, PCI_SUBSYS_ID_REG); |
"bits were not set!\n"); |
|
pci_cmd_word |= (PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN); |
|
pci_write_config(dev, PCIR_COMMAND, pci_cmd_word, 2); |
|
} |
|
|
|
/* Save off the information about this board */ |
/* Save off the information about this board */ |
adapter->hw.vendor_id = pci_get_vendor(dev); |
hw->vendor_id = PCI_VENDOR(id); |
adapter->hw.device_id = pci_get_device(dev); |
hw->device_id = PCI_PRODUCT(id); |
adapter->hw.revision_id = pci_read_config(dev, PCIR_REVID, 1); |
hw->revision_id = PCI_REVISION(pci_conf_read(pc, tag, PCI_CLASS_REG)); |
adapter->hw.subsystem_vendor_id = |
hw->subsystem_vendor_id = PCI_SUBSYS_VENDOR(subid); |
pci_read_config(dev, PCIR_SUBVEND_0, 2); |
hw->subsystem_device_id = PCI_SUBSYS_ID(subid); |
adapter->hw.subsystem_device_id = |
|
pci_read_config(dev, PCIR_SUBDEV_0, 2); |
/* We need this to determine device-specific things */ |
|
ixgbe_set_mac_type(hw); |
|
|
|
/* Set the right number of segments */ |
|
adapter->num_segs = IXGBE_82599_SCATTER; |
|
|
return; |
return; |
} |
} |
Line 1549 ixv_identify_hardware(struct adapter *ad |
|
Line 1338 ixv_identify_hardware(struct adapter *ad |
|
* |
* |
**********************************************************************/ |
**********************************************************************/ |
static int |
static int |
ixv_allocate_msix(struct adapter *adapter) |
ixv_allocate_msix(struct adapter *adapter, const struct pci_attach_args *pa) |
{ |
{ |
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
struct ix_queue *que = adapter->queues; |
struct ix_queue *que = adapter->queues; |
|
struct tx_ring *txr = adapter->tx_rings; |
int error, rid, vector = 0; |
int error, rid, vector = 0; |
|
pci_chipset_tag_t pc; |
|
pcitag_t tag; |
|
char intrbuf[PCI_INTRSTR_LEN]; |
|
const char *intrstr = NULL; |
|
kcpuset_t *affinity; |
|
int cpu_id = 0; |
|
|
for (int i = 0; i < adapter->num_queues; i++, vector++, que++) { |
pc = adapter->osdep.pc; |
rid = vector + 1; |
tag = adapter->osdep.tag; |
que->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, |
|
RF_SHAREABLE | RF_ACTIVE); |
if (pci_msix_alloc_exact(pa, |
if (que->res == NULL) { |
&adapter->osdep.intrs, IXG_MAX_NINTR) != 0) |
device_printf(dev,"Unable to allocate" |
return (ENXIO); |
" bus resource: que interrupt [%d]\n", vector); |
|
return (ENXIO); |
kcpuset_create(&affinity, false); |
} |
for (int i = 0; i < adapter->num_queues; i++, vector++, que++, txr++) { |
|
intrstr = pci_intr_string(pc, adapter->osdep.intrs[i], intrbuf, |
|
sizeof(intrbuf)); |
|
#ifdef IXV_MPSAFE |
|
pci_intr_setattr(pc, adapter->osdep.intrs[i], PCI_INTR_MPSAFE, |
|
true); |
|
#endif |
/* Set the handler function */ |
/* Set the handler function */ |
error = bus_setup_intr(dev, que->res, |
adapter->osdep.ihs[i] = pci_intr_establish(pc, |
INTR_TYPE_NET | INTR_MPSAFE, NULL, |
adapter->osdep.intrs[i], IPL_NET, ixv_msix_que, que); |
ixv_msix_que, que, &que->tag); |
if (adapter->osdep.ihs[i] == NULL) { |
if (error) { |
|
que->res = NULL; |
que->res = NULL; |
device_printf(dev, "Failed to register QUE handler"); |
aprint_error_dev(dev, |
return (error); |
"Failed to register QUE handler"); |
|
kcpuset_destroy(affinity); |
|
return (ENXIO); |
} |
} |
#if __FreeBSD_version >= 800504 |
|
bus_describe_intr(dev, que->res, que->tag, "que %d", i); |
|
#endif |
|
que->msix = vector; |
que->msix = vector; |
adapter->que_mask |= (u64)(1 << que->msix); |
adapter->active_queues |= (u64)(1 << que->msix); |
/* |
|
** Bind the msix vector, and thus the |
|
** ring to the corresponding cpu. |
|
*/ |
|
if (adapter->num_queues > 1) |
|
bus_bind_intr(dev, que->res, i); |
|
|
|
ixgbe_task_init(&que->que_task, ixv_handle_que, que); |
cpu_id = i; |
que->tq = taskqueue_create_fast("ixv_que", M_NOWAIT, |
/* Round-robin affinity */ |
taskqueue_thread_enqueue, &que->tq); |
kcpuset_zero(affinity); |
taskqueue_start_threads(&que->tq, 1, PI_NET, "%s que", |
kcpuset_set(affinity, cpu_id % ncpu); |
device_get_nameunit(adapter->dev)); |
error = interrupt_distribute(adapter->osdep.ihs[i], affinity, |
|
NULL); |
|
aprint_normal_dev(dev, "for TX/RX, interrupting at %s", |
|
intrstr); |
|
if (error == 0) |
|
aprint_normal(", bound queue %d to cpu %d\n", |
|
i, cpu_id); |
|
else |
|
aprint_normal("\n"); |
|
|
|
#ifndef IXGBE_LEGACY_TX |
|
txr->txr_si = softint_establish(SOFTINT_NET, |
|
ixgbe_deferred_mq_start, txr); |
|
#endif |
|
que->que_si = softint_establish(SOFTINT_NET, ixv_handle_que, |
|
que); |
|
if (que->que_si == NULL) { |
|
aprint_error_dev(dev, |
|
"could not establish software interrupt\n"); |
|
} |
} |
} |
|
|
/* and Mailbox */ |
/* and Mailbox */ |
rid = vector + 1; |
cpu_id++; |
adapter->res = bus_alloc_resource_any(dev, |
intrstr = pci_intr_string(pc, adapter->osdep.intrs[vector], intrbuf, |
SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); |
sizeof(intrbuf)); |
if (!adapter->res) { |
#ifdef IXG_MPSAFE |
device_printf(dev,"Unable to allocate" |
pci_intr_setattr(pc, &adapter->osdep.intrs[vector], PCI_INTR_MPSAFE, true); |
" bus resource: MBX interrupt [%d]\n", rid); |
#endif |
return (ENXIO); |
|
} |
|
/* Set the mbx handler function */ |
/* Set the mbx handler function */ |
error = bus_setup_intr(dev, adapter->res, |
adapter->osdep.ihs[vector] = pci_intr_establish(pc, |
INTR_TYPE_NET | INTR_MPSAFE, NULL, |
adapter->osdep.intrs[vector], IPL_NET, ixv_msix_mbx, adapter); |
ixv_msix_mbx, adapter, &adapter->tag); |
if (adapter->osdep.ihs[vector] == NULL) { |
if (error) { |
|
adapter->res = NULL; |
adapter->res = NULL; |
device_printf(dev, "Failed to register LINK handler"); |
aprint_error_dev(dev, "Failed to register LINK handler\n"); |
return (error); |
kcpuset_destroy(affinity); |
|
return (ENXIO); |
} |
} |
#if __FreeBSD_version >= 800504 |
/* Round-robin affinity */ |
bus_describe_intr(dev, adapter->res, adapter->tag, "mbx"); |
kcpuset_zero(affinity); |
#endif |
kcpuset_set(affinity, cpu_id % ncpu); |
adapter->mbxvec = vector; |
error = interrupt_distribute(adapter->osdep.ihs[vector], affinity,NULL); |
|
|
|
aprint_normal_dev(dev, |
|
"for link, interrupting at %s, ", intrstr); |
|
if (error == 0) { |
|
aprint_normal("affinity to cpu %d\n", cpu_id); |
|
} |
|
adapter->vector = vector; |
/* Tasklets for Mailbox */ |
/* Tasklets for Mailbox */ |
ixgbe_task_init(&adapter->mbx_task, ixv_handle_mbx, adapter); |
adapter->link_si = softint_establish(SOFTINT_NET, ixv_handle_mbx, |
adapter->tq = taskqueue_create_fast("ixv_mbx", M_NOWAIT, |
adapter); |
taskqueue_thread_enqueue, &adapter->tq); |
|
taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s mbxq", |
|
device_get_nameunit(adapter->dev)); |
|
/* |
/* |
** Due to a broken design QEMU will fail to properly |
** Due to a broken design QEMU will fail to properly |
** enable the guest for MSIX unless the vectors in |
** enable the guest for MSIX unless the vectors in |
Line 1629 ixv_allocate_msix(struct adapter *adapte |
|
Line 1444 ixv_allocate_msix(struct adapter *adapte |
|
*/ |
*/ |
if (adapter->hw.mac.type == ixgbe_mac_82599_vf) { |
if (adapter->hw.mac.type == ixgbe_mac_82599_vf) { |
int msix_ctrl; |
int msix_ctrl; |
pci_find_cap(dev, PCIY_MSIX, &rid); |
pci_get_capability(pc, tag, PCI_CAP_MSIX, &rid, NULL); |
rid += PCIR_MSIX_CTRL; |
rid += PCI_MSIX_CTL; |
msix_ctrl = pci_read_config(dev, rid, 2); |
msix_ctrl = pci_conf_read(pc, tag, rid); |
msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE; |
msix_ctrl |= PCI_MSIX_CTL_ENABLE; |
pci_write_config(dev, rid, msix_ctrl, 2); |
pci_conf_write(pc, tag, rid, msix_ctrl); |
} |
} |
|
|
return (0); |
return (0); |
|
|
ixv_setup_msix(struct adapter *adapter) |
ixv_setup_msix(struct adapter *adapter) |
{ |
{ |
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
int rid, vectors, want = 2; |
int want, msgs; |
|
|
|
|
/* First try MSI/X */ |
/* Must have at least 2 MSIX vectors */ |
rid = PCIR_BAR(3); |
msgs = pci_msix_count(adapter->osdep.pc, adapter->osdep.tag); |
adapter->msix_mem = bus_alloc_resource_any(dev, |
if (msgs < 2) { |
SYS_RES_MEMORY, &rid, RF_ACTIVE); |
aprint_error_dev(dev,"MSIX config error\n"); |
if (!adapter->msix_mem) { |
return (ENXIO); |
device_printf(adapter->dev, |
|
"Unable to map MSIX table \n"); |
|
goto out; |
|
} |
|
|
|
vectors = pci_msix_count(dev); |
|
if (vectors < 2) { |
|
bus_release_resource(dev, SYS_RES_MEMORY, |
|
rid, adapter->msix_mem); |
|
adapter->msix_mem = NULL; |
|
goto out; |
|
} |
} |
|
msgs = MIN(msgs, IXG_MAX_NINTR); |
|
|
/* |
/* |
** Want two vectors: one for a queue, |
** Want vectors for the queues, |
** plus an additional for mailbox. |
** plus an additional for mailbox. |
*/ |
*/ |
if (pci_alloc_msix(dev, &want) == 0) { |
want = adapter->num_queues + 1; |
device_printf(adapter->dev, |
if (want > msgs) { |
"Using MSIX interrupts with %d vectors\n", want); |
want = msgs; |
return (want); |
adapter->num_queues = msgs - 1; |
} |
} else |
out: |
msgs = want; |
device_printf(adapter->dev,"MSIX config error\n"); |
|
return (ENXIO); |
adapter->msix_mem = (void *)1; /* XXX */ |
|
aprint_normal_dev(dev, |
|
"Using MSIX interrupts with %d vectors\n", msgs); |
|
return (want); |
} |
} |
|
|
|
|
static int |
static int |
ixv_allocate_pci_resources(struct adapter *adapter) |
ixv_allocate_pci_resources(struct adapter *adapter, |
|
const struct pci_attach_args *pa) |
{ |
{ |
int rid; |
pcireg_t memtype; |
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
|
bus_addr_t addr; |
|
int flags; |
|
|
rid = PCIR_BAR(0); |
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_BAR(0)); |
adapter->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, |
|
&rid, RF_ACTIVE); |
|
|
|
if (!(adapter->pci_mem)) { |
switch (memtype) { |
device_printf(dev,"Unable to allocate bus resource: memory\n"); |
case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT: |
return (ENXIO); |
case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT: |
|
adapter->osdep.mem_bus_space_tag = pa->pa_memt; |
|
if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, PCI_BAR(0), |
|
memtype, &addr, &adapter->osdep.mem_size, &flags) != 0) |
|
goto map_err; |
|
if ((flags & BUS_SPACE_MAP_PREFETCHABLE) != 0) { |
|
aprint_normal_dev(dev, "clearing prefetchable bit\n"); |
|
flags &= ~BUS_SPACE_MAP_PREFETCHABLE; |
|
} |
|
if (bus_space_map(adapter->osdep.mem_bus_space_tag, addr, |
|
adapter->osdep.mem_size, flags, |
|
&adapter->osdep.mem_bus_space_handle) != 0) { |
|
map_err: |
|
adapter->osdep.mem_size = 0; |
|
aprint_error_dev(dev, "unable to map BAR0\n"); |
|
return ENXIO; |
|
} |
|
break; |
|
default: |
|
aprint_error_dev(dev, "unexpected type on BAR0\n"); |
|
return ENXIO; |
} |
} |
|
|
adapter->osdep.mem_bus_space_tag = |
/* Pick up the tuneable queues */ |
rman_get_bustag(adapter->pci_mem); |
adapter->num_queues = ixv_num_queues; |
adapter->osdep.mem_bus_space_handle = |
adapter->hw.back = adapter; |
rman_get_bushandle(adapter->pci_mem); |
|
adapter->hw.hw_addr = (u8 *) &adapter->osdep.mem_bus_space_handle; |
|
|
|
adapter->num_queues = 1; |
|
adapter->hw.back = &adapter->osdep; |
|
|
|
/* |
/* |
** Now setup MSI/X, should |
** Now setup MSI/X, should |
|
|
ixv_free_pci_resources(struct adapter * adapter) |
ixv_free_pci_resources(struct adapter * adapter) |
{ |
{ |
struct ix_queue *que = adapter->queues; |
struct ix_queue *que = adapter->queues; |
device_t dev = adapter->dev; |
int rid; |
int rid, memrid; |
|
|
|
memrid = PCIR_BAR(MSIX_BAR); |
|
|
|
/* |
|
** There is a slight possibility of a failure mode |
|
** in attach that will result in entering this function |
|
** before interrupt resources have been initialized, and |
|
** in that case we do not want to execute the loops below |
|
** We can detect this reliably by the state of the adapter |
|
** res pointer. |
|
*/ |
|
if (adapter->res == NULL) |
|
goto mem; |
|
|
|
/* |
/* |
** Release all msix queue resources: |
** Release all msix queue resources: |
*/ |
*/ |
for (int i = 0; i < adapter->num_queues; i++, que++) { |
for (int i = 0; i < adapter->num_queues; i++, que++) { |
rid = que->msix + 1; |
rid = que->msix + 1; |
if (que->tag != NULL) { |
|
bus_teardown_intr(dev, que->res, que->tag); |
|
que->tag = NULL; |
|
} |
|
if (que->res != NULL) |
if (que->res != NULL) |
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); |
pci_intr_disestablish(adapter->osdep.pc, |
|
adapter->osdep.ihs[i]); |
} |
} |
|
|
|
|
/* Clean the Legacy or Link interrupt last */ |
/* Clean the Legacy or Link interrupt last */ |
if (adapter->mbxvec) /* we are doing MSIX */ |
if (adapter->vector) /* we are doing MSIX */ |
rid = adapter->mbxvec + 1; |
rid = adapter->vector + 1; |
else |
else |
(adapter->msix != 0) ? (rid = 1):(rid = 0); |
(adapter->msix != 0) ? (rid = 1):(rid = 0); |
|
|
if (adapter->tag != NULL) { |
if (adapter->osdep.ihs[rid] != NULL) |
bus_teardown_intr(dev, adapter->res, adapter->tag); |
pci_intr_disestablish(adapter->osdep.pc, |
adapter->tag = NULL; |
adapter->osdep.ihs[rid]); |
} |
adapter->osdep.ihs[rid] = NULL; |
if (adapter->res != NULL) |
|
bus_release_resource(dev, SYS_RES_IRQ, rid, adapter->res); |
#if defined(NETBSD_MSI_OR_MSIX) |
|
pci_intr_release(adapter->osdep.pc, adapter->osdep.intrs, |
mem: |
adapter->osdep.nintrs); |
if (adapter->msix) |
#endif |
pci_release_msi(dev); |
|
|
if (adapter->osdep.mem_size != 0) { |
if (adapter->msix_mem != NULL) |
bus_space_unmap(adapter->osdep.mem_bus_space_tag, |
bus_release_resource(dev, SYS_RES_MEMORY, |
adapter->osdep.mem_bus_space_handle, |
memrid, adapter->msix_mem); |
adapter->osdep.mem_size); |
|
} |
if (adapter->pci_mem != NULL) |
|
bus_release_resource(dev, SYS_RES_MEMORY, |
|
PCIR_BAR(0), adapter->pci_mem); |
|
|
|
return; |
return; |
} |
} |
|
|
static void |
static void |
ixv_setup_interface(device_t dev, struct adapter *adapter) |
ixv_setup_interface(device_t dev, struct adapter *adapter) |
{ |
{ |
|
struct ethercom *ec = &adapter->osdep.ec; |
struct ifnet *ifp; |
struct ifnet *ifp; |
|
|
INIT_DEBUGOUT("ixv_setup_interface: begin"); |
INIT_DEBUGOUT("ixv_setup_interface: begin"); |
|
|
ifp = adapter->ifp = if_alloc(IFT_ETHER); |
ifp = adapter->ifp = &ec->ec_if; |
if (ifp == NULL) |
strlcpy(ifp->if_xname, device_xname(dev), IFNAMSIZ); |
panic("%s: can not if_alloc()\n", device_get_nameunit(dev)); |
|
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); |
|
ifp->if_mtu = ETHERMTU; |
|
ifp->if_baudrate = 1000000000; |
ifp->if_baudrate = 1000000000; |
ifp->if_init = ixv_init; |
ifp->if_init = ixv_init; |
|
ifp->if_stop = ixv_ifstop; |
ifp->if_softc = adapter; |
ifp->if_softc = adapter; |
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
ifp->if_ioctl = ixv_ioctl; |
ifp->if_ioctl = ixv_ioctl; |
#if __FreeBSD_version >= 800000 |
#ifndef IXGBE_LEGACY_TX |
ifp->if_transmit = ixv_mq_start; |
ifp->if_transmit = ixgbe_mq_start; |
ifp->if_qflush = ixv_qflush; |
|
#else |
|
ifp->if_start = ixv_start; |
|
#endif |
#endif |
|
ifp->if_start = ixgbe_start; |
ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 2; |
ifp->if_snd.ifq_maxlen = adapter->num_tx_desc - 2; |
|
|
|
if_initialize(ifp); |
ether_ifattach(ifp, adapter->hw.mac.addr); |
ether_ifattach(ifp, adapter->hw.mac.addr); |
|
#ifndef IXGBE_LEGACY_TX |
|
#if 0 /* We use per TX queue softint */ |
|
if_deferred_start_init(ifp, ixgbe_deferred_mq_start); |
|
#endif |
|
#endif |
|
if_register(ifp); |
|
ether_set_ifflags_cb(ec, ixv_ifflags_cb); |
|
|
adapter->max_frame_size = |
adapter->max_frame_size = |
ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; |
ifp->if_mtu + IXGBE_MTU_HDR_VLAN; |
|
|
/* |
/* |
* Tell the upper layer(s) we support long frames. |
* Tell the upper layer(s) we support long frames. |
*/ |
*/ |
ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); |
ifp->if_hdrlen = sizeof(struct ether_vlan_header); |
|
|
ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_TSO4 | IFCAP_VLAN_HWCSUM; |
ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_TSOv4; |
ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU; |
ifp->if_capenable = 0; |
ifp->if_capabilities |= IFCAP_JUMBO_MTU | IFCAP_LRO; |
|
|
|
|
ec->ec_capabilities |= ETHERCAP_VLAN_HWCSUM; |
|
ec->ec_capabilities |= ETHERCAP_JUMBO_MTU; |
|
ec->ec_capabilities |= ETHERCAP_VLAN_HWTAGGING |
|
| ETHERCAP_VLAN_MTU; |
|
ec->ec_capenable = ec->ec_capabilities; |
|
|
|
/* Don't enable LRO by default */ |
|
ifp->if_capabilities |= IFCAP_LRO; |
|
#if 0 |
ifp->if_capenable = ifp->if_capabilities; |
ifp->if_capenable = ifp->if_capabilities; |
|
#endif |
|
|
|
/* |
|
** Dont turn this on by default, if vlans are |
|
** created on another pseudo device (eg. lagg) |
|
** then vlan events are not passed thru, breaking |
|
** operation, but with HW FILTER off it works. If |
|
** using vlans directly on the em driver you can |
|
** enable this and get full hardware tag filtering. |
|
*/ |
|
ec->ec_capabilities |= ETHERCAP_VLAN_HWFILTER; |
|
|
/* |
/* |
* Specify the media types supported by this adapter and register |
* Specify the media types supported by this adapter and register |
Line 1833 ixv_setup_interface(device_t dev, struct |
|
Line 1660 ixv_setup_interface(device_t dev, struct |
|
*/ |
*/ |
ifmedia_init(&adapter->media, IFM_IMASK, ixv_media_change, |
ifmedia_init(&adapter->media, IFM_IMASK, ixv_media_change, |
ixv_media_status); |
ixv_media_status); |
ifmedia_add(&adapter->media, IFM_ETHER | IFM_FDX, 0, NULL); |
|
ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL); |
ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL); |
ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO); |
ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO); |
|
|
Line 1842 ixv_setup_interface(device_t dev, struct |
|
Line 1668 ixv_setup_interface(device_t dev, struct |
|
|
|
static void |
static void |
ixv_config_link(struct adapter *adapter) |
ixv_config_link(struct adapter *adapter) |
{ |
{ |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
u32 autoneg, err = 0; |
u32 autoneg; |
bool negotiate = TRUE; |
|
|
|
if (hw->mac.ops.check_link) |
|
err = hw->mac.ops.check_link(hw, &autoneg, |
|
&adapter->link_up, FALSE); |
|
if (err) |
|
goto out; |
|
|
|
if (hw->mac.ops.setup_link) |
|
err = hw->mac.ops.setup_link(hw, autoneg, |
|
negotiate, adapter->link_up); |
|
out: |
|
return; |
|
} |
|
|
|
/******************************************************************** |
|
* Manage DMA'able memory. |
|
*******************************************************************/ |
|
static void |
|
ixv_dmamap_cb(void *arg, bus_dma_segment_t * segs, int nseg, int error) |
|
{ |
|
if (error) |
|
return; |
|
*(bus_addr_t *) arg = segs->ds_addr; |
|
return; |
|
} |
|
|
|
static int |
|
ixv_dma_malloc(struct adapter *adapter, bus_size_t size, |
|
struct ixv_dma_alloc *dma, int mapflags) |
|
{ |
|
device_t dev = adapter->dev; |
|
int r; |
|
|
|
r = bus_dma_tag_create(bus_get_dma_tag(adapter->dev), /* parent */ |
|
DBA_ALIGN, 0, /* alignment, bounds */ |
|
BUS_SPACE_MAXADDR, /* lowaddr */ |
|
BUS_SPACE_MAXADDR, /* highaddr */ |
|
NULL, NULL, /* filter, filterarg */ |
|
size, /* maxsize */ |
|
1, /* nsegments */ |
|
size, /* maxsegsize */ |
|
BUS_DMA_ALLOCNOW, /* flags */ |
|
NULL, /* lockfunc */ |
|
NULL, /* lockfuncarg */ |
|
&dma->dma_tag); |
|
if (r != 0) { |
|
device_printf(dev,"ixv_dma_malloc: bus_dma_tag_create failed; " |
|
"error %u\n", r); |
|
goto fail_0; |
|
} |
|
r = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, |
|
BUS_DMA_NOWAIT, &dma->dma_map); |
|
if (r != 0) { |
|
device_printf(dev,"ixv_dma_malloc: bus_dmamem_alloc failed; " |
|
"error %u\n", r); |
|
goto fail_1; |
|
} |
|
r = bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, |
|
size, |
|
ixv_dmamap_cb, |
|
&dma->dma_paddr, |
|
mapflags | BUS_DMA_NOWAIT); |
|
if (r != 0) { |
|
device_printf(dev,"ixv_dma_malloc: bus_dmamap_load failed; " |
|
"error %u\n", r); |
|
goto fail_2; |
|
} |
|
dma->dma_size = size; |
|
return (0); |
|
fail_2: |
|
bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); |
|
fail_1: |
|
bus_dma_tag_destroy(dma->dma_tag); |
|
fail_0: |
|
dma->dma_map = NULL; |
|
dma->dma_tag = NULL; |
|
return (r); |
|
} |
|
|
|
static void |
|
ixv_dma_free(struct adapter *adapter, struct ixv_dma_alloc *dma) |
|
{ |
|
bus_dmamap_sync(dma->dma_tag, dma->dma_map, |
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); |
|
bus_dmamap_unload(dma->dma_tag, dma->dma_map); |
|
bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); |
|
bus_dma_tag_destroy(dma->dma_tag); |
|
} |
|
|
|
|
|
/********************************************************************* |
|
* |
|
* Allocate memory for the transmit and receive rings, and then |
|
* the descriptors associated with each, called only once at attach. |
|
* |
|
**********************************************************************/ |
|
static int |
|
ixv_allocate_queues(struct adapter *adapter) |
|
{ |
|
device_t dev = adapter->dev; |
|
struct ix_queue *que; |
|
struct tx_ring *txr; |
|
struct rx_ring *rxr; |
|
int rsize, tsize, error = 0; |
|
int txconf = 0, rxconf = 0; |
|
|
|
/* First allocate the top level queue structs */ |
|
if (!(adapter->queues = |
|
(struct ix_queue *) malloc(sizeof(struct ix_queue) * |
|
adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { |
|
device_printf(dev, "Unable to allocate queue memory\n"); |
|
error = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
/* First allocate the TX ring struct memory */ |
|
if (!(adapter->tx_rings = |
|
(struct tx_ring *) malloc(sizeof(struct tx_ring) * |
|
adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { |
|
device_printf(dev, "Unable to allocate TX ring memory\n"); |
|
error = ENOMEM; |
|
goto tx_fail; |
|
} |
|
|
|
/* Next allocate the RX */ |
|
if (!(adapter->rx_rings = |
|
(struct rx_ring *) malloc(sizeof(struct rx_ring) * |
|
adapter->num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { |
|
device_printf(dev, "Unable to allocate RX ring memory\n"); |
|
error = ENOMEM; |
|
goto rx_fail; |
|
} |
|
|
|
/* For the ring itself */ |
|
tsize = roundup2(adapter->num_tx_desc * |
|
sizeof(union ixgbe_adv_tx_desc), DBA_ALIGN); |
|
|
|
/* |
|
* Now set up the TX queues, txconf is needed to handle the |
|
* possibility that things fail midcourse and we need to |
|
* undo memory gracefully |
|
*/ |
|
for (int i = 0; i < adapter->num_queues; i++, txconf++) { |
|
/* Set up some basics */ |
|
txr = &adapter->tx_rings[i]; |
|
txr->adapter = adapter; |
|
txr->me = i; |
|
|
|
/* Initialize the TX side lock */ |
|
snprintf(txr->mtx_name, sizeof(txr->mtx_name), "%s:tx(%d)", |
|
device_get_nameunit(dev), txr->me); |
|
mtx_init(&txr->tx_mtx, txr->mtx_name, NULL, MTX_DEF); |
|
|
|
if (ixv_dma_malloc(adapter, tsize, |
|
&txr->txdma, BUS_DMA_NOWAIT)) { |
|
device_printf(dev, |
|
"Unable to allocate TX Descriptor memory\n"); |
|
error = ENOMEM; |
|
goto err_tx_desc; |
|
} |
|
txr->tx_base = (union ixgbe_adv_tx_desc *)txr->txdma.dma_vaddr; |
|
bzero((void *)txr->tx_base, tsize); |
|
|
|
/* Now allocate transmit buffers for the ring */ |
|
if (ixv_allocate_transmit_buffers(txr)) { |
|
device_printf(dev, |
|
"Critical Failure setting up transmit buffers\n"); |
|
error = ENOMEM; |
|
goto err_tx_desc; |
|
} |
|
#if __FreeBSD_version >= 800000 |
|
/* Allocate a buf ring */ |
|
txr->br = buf_ring_alloc(IXV_BR_SIZE, M_DEVBUF, |
|
M_WAITOK, &txr->tx_mtx); |
|
if (txr->br == NULL) { |
|
device_printf(dev, |
|
"Critical Failure setting up buf ring\n"); |
|
error = ENOMEM; |
|
goto err_tx_desc; |
|
} |
|
#endif |
|
} |
|
|
|
/* |
|
* Next the RX queues... |
|
*/ |
|
rsize = roundup2(adapter->num_rx_desc * |
|
sizeof(union ixgbe_adv_rx_desc), DBA_ALIGN); |
|
for (int i = 0; i < adapter->num_queues; i++, rxconf++) { |
|
rxr = &adapter->rx_rings[i]; |
|
/* Set up some basics */ |
|
rxr->adapter = adapter; |
|
rxr->me = i; |
|
|
|
/* Initialize the RX side lock */ |
|
snprintf(rxr->mtx_name, sizeof(rxr->mtx_name), "%s:rx(%d)", |
|
device_get_nameunit(dev), rxr->me); |
|
mtx_init(&rxr->rx_mtx, rxr->mtx_name, NULL, MTX_DEF); |
|
|
|
if (ixv_dma_malloc(adapter, rsize, |
|
&rxr->rxdma, BUS_DMA_NOWAIT)) { |
|
device_printf(dev, |
|
"Unable to allocate RxDescriptor memory\n"); |
|
error = ENOMEM; |
|
goto err_rx_desc; |
|
} |
|
rxr->rx_base = (union ixgbe_adv_rx_desc *)rxr->rxdma.dma_vaddr; |
|
bzero((void *)rxr->rx_base, rsize); |
|
|
|
/* Allocate receive buffers for the ring*/ |
|
if (ixv_allocate_receive_buffers(rxr)) { |
|
device_printf(dev, |
|
"Critical Failure setting up receive buffers\n"); |
|
error = ENOMEM; |
|
goto err_rx_desc; |
|
} |
|
} |
|
|
|
/* |
|
** Finally set up the queue holding structs |
|
*/ |
|
for (int i = 0; i < adapter->num_queues; i++) { |
|
que = &adapter->queues[i]; |
|
que->adapter = adapter; |
|
que->txr = &adapter->tx_rings[i]; |
|
que->rxr = &adapter->rx_rings[i]; |
|
} |
|
|
|
return (0); |
|
|
|
err_rx_desc: |
|
for (rxr = adapter->rx_rings; rxconf > 0; rxr++, rxconf--) |
|
ixv_dma_free(adapter, &rxr->rxdma); |
|
err_tx_desc: |
|
for (txr = adapter->tx_rings; txconf > 0; txr++, txconf--) |
|
ixv_dma_free(adapter, &txr->txdma); |
|
free(adapter->rx_rings, M_DEVBUF); |
|
rx_fail: |
|
free(adapter->tx_rings, M_DEVBUF); |
|
tx_fail: |
|
free(adapter->queues, M_DEVBUF); |
|
fail: |
|
return (error); |
|
} |
|
|
|
|
|
/********************************************************************* |
|
* |
|
* Allocate memory for tx_buffer structures. The tx_buffer stores all |
|
* the information needed to transmit a packet on the wire. This is |
|
* called only once at attach, setup is done every reset. |
|
* |
|
**********************************************************************/ |
|
static int |
|
ixv_allocate_transmit_buffers(struct tx_ring *txr) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
device_t dev = adapter->dev; |
|
struct ixv_tx_buf *txbuf; |
|
int error, i; |
|
|
|
/* |
|
* Setup DMA descriptor areas. |
|
*/ |
|
if ((error = bus_dma_tag_create(NULL, /* parent */ |
|
1, 0, /* alignment, bounds */ |
|
BUS_SPACE_MAXADDR, /* lowaddr */ |
|
BUS_SPACE_MAXADDR, /* highaddr */ |
|
NULL, NULL, /* filter, filterarg */ |
|
IXV_TSO_SIZE, /* maxsize */ |
|
32, /* nsegments */ |
|
PAGE_SIZE, /* maxsegsize */ |
|
0, /* flags */ |
|
NULL, /* lockfunc */ |
|
NULL, /* lockfuncarg */ |
|
&txr->txtag))) { |
|
device_printf(dev,"Unable to allocate TX DMA tag\n"); |
|
goto fail; |
|
} |
|
|
|
if (!(txr->tx_buffers = |
|
(struct ixv_tx_buf *) malloc(sizeof(struct ixv_tx_buf) * |
|
adapter->num_tx_desc, M_DEVBUF, M_NOWAIT | M_ZERO))) { |
|
device_printf(dev, "Unable to allocate tx_buffer memory\n"); |
|
error = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
/* Create the descriptor buffer dma maps */ |
|
txbuf = txr->tx_buffers; |
|
for (i = 0; i < adapter->num_tx_desc; i++, txbuf++) { |
|
error = bus_dmamap_create(txr->txtag, 0, &txbuf->map); |
|
if (error != 0) { |
|
device_printf(dev, "Unable to create TX DMA map\n"); |
|
goto fail; |
|
} |
|
} |
|
|
|
return 0; |
|
fail: |
|
/* We free all, it handles case where we are in the middle */ |
|
ixv_free_transmit_structures(adapter); |
|
return (error); |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Initialize a transmit ring. |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_setup_transmit_ring(struct tx_ring *txr) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
struct ixv_tx_buf *txbuf; |
|
int i; |
|
|
|
/* Clear the old ring contents */ |
|
IXV_TX_LOCK(txr); |
|
bzero((void *)txr->tx_base, |
|
(sizeof(union ixgbe_adv_tx_desc)) * adapter->num_tx_desc); |
|
/* Reset indices */ |
|
txr->next_avail_desc = 0; |
|
txr->next_to_clean = 0; |
|
|
|
/* Free any existing tx buffers. */ |
|
txbuf = txr->tx_buffers; |
|
for (i = 0; i < adapter->num_tx_desc; i++, txbuf++) { |
|
if (txbuf->m_head != NULL) { |
|
bus_dmamap_sync(txr->txtag, txbuf->map, |
|
BUS_DMASYNC_POSTWRITE); |
|
bus_dmamap_unload(txr->txtag, txbuf->map); |
|
m_freem(txbuf->m_head); |
|
txbuf->m_head = NULL; |
|
} |
|
/* Clear the EOP index */ |
|
txbuf->eop_index = -1; |
|
} |
|
|
|
/* Set number of descriptors available */ |
|
txr->tx_avail = adapter->num_tx_desc; |
|
|
|
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, |
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
|
IXV_TX_UNLOCK(txr); |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Initialize all transmit rings. |
|
* |
|
**********************************************************************/ |
|
static int |
|
ixv_setup_transmit_structures(struct adapter *adapter) |
|
{ |
|
struct tx_ring *txr = adapter->tx_rings; |
|
|
|
for (int i = 0; i < adapter->num_queues; i++, txr++) |
|
ixv_setup_transmit_ring(txr); |
|
|
|
return (0); |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Enable transmit unit. |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_initialize_transmit_units(struct adapter *adapter) |
|
{ |
|
struct tx_ring *txr = adapter->tx_rings; |
|
struct ixgbe_hw *hw = &adapter->hw; |
|
|
|
|
|
for (int i = 0; i < adapter->num_queues; i++, txr++) { |
|
u64 tdba = txr->txdma.dma_paddr; |
|
u32 txctrl, txdctl; |
|
|
|
/* Set WTHRESH to 8, burst writeback */ |
|
txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(i)); |
|
txdctl |= (8 << 16); |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), txdctl); |
|
/* Now enable */ |
|
txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(i)); |
|
txdctl |= IXGBE_TXDCTL_ENABLE; |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), txdctl); |
|
|
|
/* Set the HW Tx Head and Tail indices */ |
|
IXGBE_WRITE_REG(&adapter->hw, IXGBE_VFTDH(i), 0); |
|
IXGBE_WRITE_REG(&adapter->hw, IXGBE_VFTDT(i), 0); |
|
|
|
/* Setup Transmit Descriptor Cmd Settings */ |
|
txr->txd_cmd = IXGBE_TXD_CMD_IFCS; |
|
txr->watchdog_check = FALSE; |
|
|
|
/* Set Ring parameters */ |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTDBAL(i), |
|
(tdba & 0x00000000ffffffffULL)); |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTDBAH(i), (tdba >> 32)); |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTDLEN(i), |
|
adapter->num_tx_desc * |
|
sizeof(struct ixgbe_legacy_tx_desc)); |
|
txctrl = IXGBE_READ_REG(hw, IXGBE_VFDCA_TXCTRL(i)); |
|
txctrl &= ~IXGBE_DCA_TXCTRL_TX_WB_RO_EN; |
|
IXGBE_WRITE_REG(hw, IXGBE_VFDCA_TXCTRL(i), txctrl); |
|
break; |
|
} |
|
|
|
return; |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Free all transmit rings. |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_free_transmit_structures(struct adapter *adapter) |
|
{ |
|
struct tx_ring *txr = adapter->tx_rings; |
|
|
|
for (int i = 0; i < adapter->num_queues; i++, txr++) { |
|
IXV_TX_LOCK(txr); |
|
ixv_free_transmit_buffers(txr); |
|
ixv_dma_free(adapter, &txr->txdma); |
|
IXV_TX_UNLOCK(txr); |
|
IXV_TX_LOCK_DESTROY(txr); |
|
} |
|
free(adapter->tx_rings, M_DEVBUF); |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Free transmit ring related data structures. |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_free_transmit_buffers(struct tx_ring *txr) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
struct ixv_tx_buf *tx_buffer; |
|
int i; |
|
|
|
INIT_DEBUGOUT("free_transmit_ring: begin"); |
|
|
|
if (txr->tx_buffers == NULL) |
|
return; |
|
|
|
tx_buffer = txr->tx_buffers; |
|
for (i = 0; i < adapter->num_tx_desc; i++, tx_buffer++) { |
|
if (tx_buffer->m_head != NULL) { |
|
bus_dmamap_sync(txr->txtag, tx_buffer->map, |
|
BUS_DMASYNC_POSTWRITE); |
|
bus_dmamap_unload(txr->txtag, |
|
tx_buffer->map); |
|
m_freem(tx_buffer->m_head); |
|
tx_buffer->m_head = NULL; |
|
if (tx_buffer->map != NULL) { |
|
bus_dmamap_destroy(txr->txtag, |
|
tx_buffer->map); |
|
tx_buffer->map = NULL; |
|
} |
|
} else if (tx_buffer->map != NULL) { |
|
bus_dmamap_unload(txr->txtag, |
|
tx_buffer->map); |
|
bus_dmamap_destroy(txr->txtag, |
|
tx_buffer->map); |
|
tx_buffer->map = NULL; |
|
} |
|
} |
|
#if __FreeBSD_version >= 800000 |
|
if (txr->br != NULL) |
|
buf_ring_free(txr->br, M_DEVBUF); |
|
#endif |
|
if (txr->tx_buffers != NULL) { |
|
free(txr->tx_buffers, M_DEVBUF); |
|
txr->tx_buffers = NULL; |
|
} |
|
if (txr->txtag != NULL) { |
|
bus_dma_tag_destroy(txr->txtag); |
|
txr->txtag = NULL; |
|
} |
|
return; |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Advanced Context Descriptor setup for VLAN or CSUM |
|
* |
|
**********************************************************************/ |
|
|
|
static boolean_t |
|
ixv_tx_ctx_setup(struct tx_ring *txr, struct mbuf *mp) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
struct ixgbe_adv_tx_context_desc *TXD; |
|
struct ixv_tx_buf *tx_buffer; |
|
u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0; |
|
struct ether_vlan_header *eh; |
|
struct ip *ip; |
|
struct ip6_hdr *ip6; |
|
int ehdrlen, ip_hlen = 0; |
|
u16 etype; |
|
u8 ipproto = 0; |
|
bool offload = TRUE; |
|
int ctxd = txr->next_avail_desc; |
|
u16 vtag = 0; |
|
|
|
|
|
if ((mp->m_pkthdr.csum_flags & CSUM_OFFLOAD) == 0) |
|
offload = FALSE; |
|
|
|
|
|
tx_buffer = &txr->tx_buffers[ctxd]; |
|
TXD = (struct ixgbe_adv_tx_context_desc *) &txr->tx_base[ctxd]; |
|
|
|
/* |
|
** In advanced descriptors the vlan tag must |
|
** be placed into the descriptor itself. |
|
*/ |
|
if (mp->m_flags & M_VLANTAG) { |
|
vtag = htole16(mp->m_pkthdr.ether_vtag); |
|
vlan_macip_lens |= (vtag << IXGBE_ADVTXD_VLAN_SHIFT); |
|
} else if (offload == FALSE) |
|
return FALSE; |
|
|
|
/* |
|
* Determine where frame payload starts. |
|
* Jump over vlan headers if already present, |
|
* helpful for QinQ too. |
|
*/ |
|
eh = mtod(mp, struct ether_vlan_header *); |
|
if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) { |
|
etype = ntohs(eh->evl_proto); |
|
ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; |
|
} else { |
|
etype = ntohs(eh->evl_encap_proto); |
|
ehdrlen = ETHER_HDR_LEN; |
|
} |
|
|
|
/* Set the ether header length */ |
|
vlan_macip_lens |= ehdrlen << IXGBE_ADVTXD_MACLEN_SHIFT; |
|
|
|
switch (etype) { |
|
case ETHERTYPE_IP: |
|
ip = (struct ip *)(mp->m_data + ehdrlen); |
|
ip_hlen = ip->ip_hl << 2; |
|
if (mp->m_len < ehdrlen + ip_hlen) |
|
return (FALSE); |
|
ipproto = ip->ip_p; |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4; |
|
break; |
|
case ETHERTYPE_IPV6: |
|
ip6 = (struct ip6_hdr *)(mp->m_data + ehdrlen); |
|
ip_hlen = sizeof(struct ip6_hdr); |
|
if (mp->m_len < ehdrlen + ip_hlen) |
|
return (FALSE); |
|
ipproto = ip6->ip6_nxt; |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV6; |
|
break; |
|
default: |
|
offload = FALSE; |
|
break; |
|
} |
|
|
|
vlan_macip_lens |= ip_hlen; |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT; |
|
|
|
switch (ipproto) { |
|
case IPPROTO_TCP: |
|
if (mp->m_pkthdr.csum_flags & CSUM_TCP) |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP; |
|
break; |
|
|
|
case IPPROTO_UDP: |
|
if (mp->m_pkthdr.csum_flags & CSUM_UDP) |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_UDP; |
|
break; |
|
|
|
#if __FreeBSD_version >= 800000 |
|
case IPPROTO_SCTP: |
|
if (mp->m_pkthdr.csum_flags & CSUM_SCTP) |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_SCTP; |
|
break; |
|
#endif |
|
default: |
|
offload = FALSE; |
|
break; |
|
} |
|
|
|
/* Now copy bits into descriptor */ |
|
TXD->vlan_macip_lens |= htole32(vlan_macip_lens); |
|
TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl); |
|
TXD->seqnum_seed = htole32(0); |
|
TXD->mss_l4len_idx = htole32(0); |
|
|
|
tx_buffer->m_head = NULL; |
|
tx_buffer->eop_index = -1; |
|
|
|
/* We've consumed the first desc, adjust counters */ |
|
if (++ctxd == adapter->num_tx_desc) |
|
ctxd = 0; |
|
txr->next_avail_desc = ctxd; |
|
--txr->tx_avail; |
|
|
|
return (offload); |
|
} |
|
|
|
/********************************************************************** |
|
* |
|
* Setup work for hardware segmentation offload (TSO) on |
|
* adapters using advanced tx descriptors |
|
* |
|
**********************************************************************/ |
|
static boolean_t |
|
ixv_tso_setup(struct tx_ring *txr, struct mbuf *mp, u32 *paylen) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
struct ixgbe_adv_tx_context_desc *TXD; |
|
struct ixv_tx_buf *tx_buffer; |
|
u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0; |
|
u32 mss_l4len_idx = 0; |
|
u16 vtag = 0; |
|
int ctxd, ehdrlen, hdrlen, ip_hlen, tcp_hlen; |
|
struct ether_vlan_header *eh; |
|
struct ip *ip; |
|
struct tcphdr *th; |
|
|
|
|
|
/* |
|
* Determine where frame payload starts. |
|
* Jump over vlan headers if already present |
|
*/ |
|
eh = mtod(mp, struct ether_vlan_header *); |
|
if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) |
|
ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN; |
|
else |
|
ehdrlen = ETHER_HDR_LEN; |
|
|
|
/* Ensure we have at least the IP+TCP header in the first mbuf. */ |
|
if (mp->m_len < ehdrlen + sizeof(struct ip) + sizeof(struct tcphdr)) |
|
return FALSE; |
|
|
|
ctxd = txr->next_avail_desc; |
|
tx_buffer = &txr->tx_buffers[ctxd]; |
|
TXD = (struct ixgbe_adv_tx_context_desc *) &txr->tx_base[ctxd]; |
|
|
|
ip = (struct ip *)(mp->m_data + ehdrlen); |
|
if (ip->ip_p != IPPROTO_TCP) |
|
return FALSE; /* 0 */ |
|
ip->ip_sum = 0; |
|
ip_hlen = ip->ip_hl << 2; |
|
th = (struct tcphdr *)((caddr_t)ip + ip_hlen); |
|
th->th_sum = in_pseudo(ip->ip_src.s_addr, |
|
ip->ip_dst.s_addr, htons(IPPROTO_TCP)); |
|
tcp_hlen = th->th_off << 2; |
|
hdrlen = ehdrlen + ip_hlen + tcp_hlen; |
|
|
|
/* This is used in the transmit desc in encap */ |
|
*paylen = mp->m_pkthdr.len - hdrlen; |
|
|
|
/* VLAN MACLEN IPLEN */ |
|
if (mp->m_flags & M_VLANTAG) { |
|
vtag = htole16(mp->m_pkthdr.ether_vtag); |
|
vlan_macip_lens |= (vtag << IXGBE_ADVTXD_VLAN_SHIFT); |
|
} |
|
|
|
vlan_macip_lens |= ehdrlen << IXGBE_ADVTXD_MACLEN_SHIFT; |
|
vlan_macip_lens |= ip_hlen; |
|
TXD->vlan_macip_lens |= htole32(vlan_macip_lens); |
|
|
|
/* ADV DTYPE TUCMD */ |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_DCMD_DEXT | IXGBE_ADVTXD_DTYP_CTXT; |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP; |
|
type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4; |
|
TXD->type_tucmd_mlhl |= htole32(type_tucmd_mlhl); |
|
|
|
|
|
/* MSS L4LEN IDX */ |
|
mss_l4len_idx |= (mp->m_pkthdr.tso_segsz << IXGBE_ADVTXD_MSS_SHIFT); |
|
mss_l4len_idx |= (tcp_hlen << IXGBE_ADVTXD_L4LEN_SHIFT); |
|
TXD->mss_l4len_idx = htole32(mss_l4len_idx); |
|
|
|
TXD->seqnum_seed = htole32(0); |
|
tx_buffer->m_head = NULL; |
|
tx_buffer->eop_index = -1; |
|
|
|
if (++ctxd == adapter->num_tx_desc) |
|
ctxd = 0; |
|
|
|
txr->tx_avail--; |
|
txr->next_avail_desc = ctxd; |
|
return TRUE; |
|
} |
|
|
|
|
|
/********************************************************************** |
|
* |
|
* Examine each tx_buffer in the used queue. If the hardware is done |
|
* processing the packet then free associated resources. The |
|
* tx_buffer is put back on the free queue. |
|
* |
|
**********************************************************************/ |
|
static boolean_t |
|
ixv_txeof(struct tx_ring *txr) |
|
{ |
|
struct adapter *adapter = txr->adapter; |
|
struct ifnet *ifp = adapter->ifp; |
|
u32 first, last, done; |
|
struct ixv_tx_buf *tx_buffer; |
|
struct ixgbe_legacy_tx_desc *tx_desc, *eop_desc; |
|
|
|
mtx_assert(&txr->tx_mtx, MA_OWNED); |
|
|
|
if (txr->tx_avail == adapter->num_tx_desc) |
|
return FALSE; |
|
|
|
first = txr->next_to_clean; |
|
tx_buffer = &txr->tx_buffers[first]; |
|
/* For cleanup we just use legacy struct */ |
|
tx_desc = (struct ixgbe_legacy_tx_desc *)&txr->tx_base[first]; |
|
last = tx_buffer->eop_index; |
|
if (last == -1) |
|
return FALSE; |
|
eop_desc = (struct ixgbe_legacy_tx_desc *)&txr->tx_base[last]; |
|
|
|
/* |
|
** Get the index of the first descriptor |
|
** BEYOND the EOP and call that 'done'. |
|
** I do this so the comparison in the |
|
** inner while loop below can be simple |
|
*/ |
|
if (++last == adapter->num_tx_desc) last = 0; |
|
done = last; |
|
|
|
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, |
|
BUS_DMASYNC_POSTREAD); |
|
/* |
|
** Only the EOP descriptor of a packet now has the DD |
|
** bit set, this is what we look for... |
|
*/ |
|
while (eop_desc->upper.fields.status & IXGBE_TXD_STAT_DD) { |
|
/* We clean the range of the packet */ |
|
while (first != done) { |
|
tx_desc->upper.data = 0; |
|
tx_desc->lower.data = 0; |
|
tx_desc->buffer_addr = 0; |
|
++txr->tx_avail; |
|
|
|
if (tx_buffer->m_head) { |
|
bus_dmamap_sync(txr->txtag, |
|
tx_buffer->map, |
|
BUS_DMASYNC_POSTWRITE); |
|
bus_dmamap_unload(txr->txtag, |
|
tx_buffer->map); |
|
m_freem(tx_buffer->m_head); |
|
tx_buffer->m_head = NULL; |
|
tx_buffer->map = NULL; |
|
} |
|
tx_buffer->eop_index = -1; |
|
txr->watchdog_time = ticks; |
|
|
|
if (++first == adapter->num_tx_desc) |
|
first = 0; |
|
|
|
tx_buffer = &txr->tx_buffers[first]; |
|
tx_desc = |
|
(struct ixgbe_legacy_tx_desc *)&txr->tx_base[first]; |
|
} |
|
++ifp->if_opackets; |
|
/* See if there is more work now */ |
|
last = tx_buffer->eop_index; |
|
if (last != -1) { |
|
eop_desc = |
|
(struct ixgbe_legacy_tx_desc *)&txr->tx_base[last]; |
|
/* Get next done point */ |
|
if (++last == adapter->num_tx_desc) last = 0; |
|
done = last; |
|
} else |
|
break; |
|
} |
|
bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, |
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
|
|
|
txr->next_to_clean = first; |
|
|
|
/* |
|
* If we have enough room, clear IFF_DRV_OACTIVE to tell the stack that |
|
* it is OK to send packets. If there are no pending descriptors, |
|
* clear the timeout. Otherwise, if some descriptors have been freed, |
|
* restart the timeout. |
|
*/ |
|
if (txr->tx_avail > IXV_TX_CLEANUP_THRESHOLD) { |
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; |
|
if (txr->tx_avail == adapter->num_tx_desc) { |
|
txr->watchdog_check = FALSE; |
|
return FALSE; |
|
} |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Refresh mbuf buffers for RX descriptor rings |
|
* - now keeps its own state so discards due to resource |
|
* exhaustion are unnecessary, if an mbuf cannot be obtained |
|
* it just returns, keeping its placeholder, thus it can simply |
|
* be recalled to try again. |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_refresh_mbufs(struct rx_ring *rxr, int limit) |
|
{ |
|
struct adapter *adapter = rxr->adapter; |
|
bus_dma_segment_t hseg[1]; |
|
bus_dma_segment_t pseg[1]; |
|
struct ixv_rx_buf *rxbuf; |
|
struct mbuf *mh, *mp; |
|
int i, nsegs, error, cleaned; |
|
|
|
i = rxr->next_to_refresh; |
|
cleaned = -1; /* Signify no completions */ |
|
while (i != limit) { |
|
rxbuf = &rxr->rx_buffers[i]; |
|
if ((rxbuf->m_head == NULL) && (rxr->hdr_split)) { |
|
mh = m_gethdr(M_DONTWAIT, MT_DATA); |
|
if (mh == NULL) |
|
goto update; |
|
mh->m_pkthdr.len = mh->m_len = MHLEN; |
|
mh->m_len = MHLEN; |
|
mh->m_flags |= M_PKTHDR; |
|
m_adj(mh, ETHER_ALIGN); |
|
/* Get the memory mapping */ |
|
error = bus_dmamap_load_mbuf_sg(rxr->htag, |
|
rxbuf->hmap, mh, hseg, &nsegs, BUS_DMA_NOWAIT); |
|
if (error != 0) { |
|
printf("GET BUF: dmamap load" |
|
" failure - %d\n", error); |
|
m_free(mh); |
|
goto update; |
|
} |
|
rxbuf->m_head = mh; |
|
bus_dmamap_sync(rxr->htag, rxbuf->hmap, |
|
BUS_DMASYNC_PREREAD); |
|
rxr->rx_base[i].read.hdr_addr = |
|
htole64(hseg[0].ds_addr); |
|
} |
|
|
|
if (rxbuf->m_pack == NULL) { |
|
mp = m_getjcl(M_DONTWAIT, MT_DATA, |
|
M_PKTHDR, adapter->rx_mbuf_sz); |
|
if (mp == NULL) |
|
goto update; |
|
mp->m_pkthdr.len = mp->m_len = adapter->rx_mbuf_sz; |
|
/* Get the memory mapping */ |
|
error = bus_dmamap_load_mbuf_sg(rxr->ptag, |
|
rxbuf->pmap, mp, pseg, &nsegs, BUS_DMA_NOWAIT); |
|
if (error != 0) { |
|
printf("GET BUF: dmamap load" |
|
" failure - %d\n", error); |
|
m_free(mp); |
|
goto update; |
|
} |
|
rxbuf->m_pack = mp; |
|
bus_dmamap_sync(rxr->ptag, rxbuf->pmap, |
|
BUS_DMASYNC_PREREAD); |
|
rxr->rx_base[i].read.pkt_addr = |
|
htole64(pseg[0].ds_addr); |
|
} |
|
|
|
cleaned = i; |
|
/* Calculate next index */ |
|
if (++i == adapter->num_rx_desc) |
|
i = 0; |
|
/* This is the work marker for refresh */ |
|
rxr->next_to_refresh = i; |
|
} |
|
update: |
|
if (cleaned != -1) /* If we refreshed some, bump tail */ |
|
IXGBE_WRITE_REG(&adapter->hw, |
|
IXGBE_VFRDT(rxr->me), cleaned); |
|
return; |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Allocate memory for rx_buffer structures. Since we use one |
|
* rx_buffer per received packet, the maximum number of rx_buffer's |
|
* that we'll need is equal to the number of receive descriptors |
|
* that we've allocated. |
|
* |
|
**********************************************************************/ |
|
static int |
|
ixv_allocate_receive_buffers(struct rx_ring *rxr) |
|
{ |
|
struct adapter *adapter = rxr->adapter; |
|
device_t dev = adapter->dev; |
|
struct ixv_rx_buf *rxbuf; |
|
int i, bsize, error; |
|
|
|
bsize = sizeof(struct ixv_rx_buf) * adapter->num_rx_desc; |
|
if (!(rxr->rx_buffers = |
|
(struct ixv_rx_buf *) malloc(bsize, |
|
M_DEVBUF, M_NOWAIT | M_ZERO))) { |
|
device_printf(dev, "Unable to allocate rx_buffer memory\n"); |
|
error = ENOMEM; |
|
goto fail; |
|
} |
|
|
|
if ((error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ |
|
1, 0, /* alignment, bounds */ |
|
BUS_SPACE_MAXADDR, /* lowaddr */ |
|
BUS_SPACE_MAXADDR, /* highaddr */ |
|
NULL, NULL, /* filter, filterarg */ |
|
MSIZE, /* maxsize */ |
|
1, /* nsegments */ |
|
MSIZE, /* maxsegsize */ |
|
0, /* flags */ |
|
NULL, /* lockfunc */ |
|
NULL, /* lockfuncarg */ |
|
&rxr->htag))) { |
|
device_printf(dev, "Unable to create RX DMA tag\n"); |
|
goto fail; |
|
} |
|
|
|
if ((error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ |
|
1, 0, /* alignment, bounds */ |
|
BUS_SPACE_MAXADDR, /* lowaddr */ |
|
BUS_SPACE_MAXADDR, /* highaddr */ |
|
NULL, NULL, /* filter, filterarg */ |
|
MJUMPAGESIZE, /* maxsize */ |
|
1, /* nsegments */ |
|
MJUMPAGESIZE, /* maxsegsize */ |
|
0, /* flags */ |
|
NULL, /* lockfunc */ |
|
NULL, /* lockfuncarg */ |
|
&rxr->ptag))) { |
|
device_printf(dev, "Unable to create RX DMA tag\n"); |
|
goto fail; |
|
} |
|
|
|
for (i = 0; i < adapter->num_rx_desc; i++, rxbuf++) { |
|
rxbuf = &rxr->rx_buffers[i]; |
|
error = bus_dmamap_create(rxr->htag, |
|
BUS_DMA_NOWAIT, &rxbuf->hmap); |
|
if (error) { |
|
device_printf(dev, "Unable to create RX head map\n"); |
|
goto fail; |
|
} |
|
error = bus_dmamap_create(rxr->ptag, |
|
BUS_DMA_NOWAIT, &rxbuf->pmap); |
|
if (error) { |
|
device_printf(dev, "Unable to create RX pkt map\n"); |
|
goto fail; |
|
} |
|
} |
|
|
|
return (0); |
|
|
|
fail: |
if (hw->mac.ops.check_link) |
/* Frees all, but can handle partial completion */ |
hw->mac.ops.check_link(hw, &autoneg, |
ixv_free_receive_structures(adapter); |
&adapter->link_up, FALSE); |
return (error); |
|
} |
|
|
|
static void |
|
ixv_free_receive_ring(struct rx_ring *rxr) |
|
{ |
|
struct adapter *adapter; |
|
struct ixv_rx_buf *rxbuf; |
|
int i; |
|
|
|
adapter = rxr->adapter; |
|
for (i = 0; i < adapter->num_rx_desc; i++) { |
|
rxbuf = &rxr->rx_buffers[i]; |
|
if (rxbuf->m_head != NULL) { |
|
bus_dmamap_sync(rxr->htag, rxbuf->hmap, |
|
BUS_DMASYNC_POSTREAD); |
|
bus_dmamap_unload(rxr->htag, rxbuf->hmap); |
|
rxbuf->m_head->m_flags |= M_PKTHDR; |
|
m_freem(rxbuf->m_head); |
|
} |
|
if (rxbuf->m_pack != NULL) { |
|
bus_dmamap_sync(rxr->ptag, rxbuf->pmap, |
|
BUS_DMASYNC_POSTREAD); |
|
bus_dmamap_unload(rxr->ptag, rxbuf->pmap); |
|
rxbuf->m_pack->m_flags |= M_PKTHDR; |
|
m_freem(rxbuf->m_pack); |
|
} |
|
rxbuf->m_head = NULL; |
|
rxbuf->m_pack = NULL; |
|
} |
|
} |
} |
|
|
|
|
/********************************************************************* |
/********************************************************************* |
* |
* |
* Initialize a receive ring and its buffers. |
* Enable transmit unit. |
* |
* |
**********************************************************************/ |
**********************************************************************/ |
static int |
static void |
ixv_setup_receive_ring(struct rx_ring *rxr) |
ixv_initialize_transmit_units(struct adapter *adapter) |
{ |
{ |
struct adapter *adapter; |
struct tx_ring *txr = adapter->tx_rings; |
struct ifnet *ifp; |
struct ixgbe_hw *hw = &adapter->hw; |
device_t dev; |
|
struct ixv_rx_buf *rxbuf; |
|
bus_dma_segment_t pseg[1], hseg[1]; |
|
struct lro_ctrl *lro = &rxr->lro; |
|
int rsize, nsegs, error = 0; |
|
|
|
adapter = rxr->adapter; |
|
ifp = adapter->ifp; |
|
dev = adapter->dev; |
|
|
|
/* Clear the ring contents */ |
for (int i = 0; i < adapter->num_queues; i++, txr++) { |
IXV_RX_LOCK(rxr); |
u64 tdba = txr->txdma.dma_paddr; |
rsize = roundup2(adapter->num_rx_desc * |
u32 txctrl, txdctl; |
sizeof(union ixgbe_adv_rx_desc), DBA_ALIGN); |
|
bzero((void *)rxr->rx_base, rsize); |
|
|
|
/* Free current RX buffer structs and their mbufs */ |
|
ixv_free_receive_ring(rxr); |
|
|
|
/* Configure header split? */ |
|
if (ixv_header_split) |
|
rxr->hdr_split = TRUE; |
|
|
|
/* Now replenish the mbufs */ |
|
for (int j = 0; j != adapter->num_rx_desc; ++j) { |
|
struct mbuf *mh, *mp; |
|
|
|
rxbuf = &rxr->rx_buffers[j]; |
/* Set WTHRESH to 8, burst writeback */ |
/* |
txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(i)); |
** Dont allocate mbufs if not |
txdctl |= (8 << 16); |
** doing header split, its wasteful |
IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), txdctl); |
*/ |
|
if (rxr->hdr_split == FALSE) |
|
goto skip_head; |
|
|
|
/* First the header */ |
|
rxbuf->m_head = m_gethdr(M_NOWAIT, MT_DATA); |
|
if (rxbuf->m_head == NULL) { |
|
error = ENOBUFS; |
|
goto fail; |
|
} |
|
m_adj(rxbuf->m_head, ETHER_ALIGN); |
|
mh = rxbuf->m_head; |
|
mh->m_len = mh->m_pkthdr.len = MHLEN; |
|
mh->m_flags |= M_PKTHDR; |
|
/* Get the memory mapping */ |
|
error = bus_dmamap_load_mbuf_sg(rxr->htag, |
|
rxbuf->hmap, rxbuf->m_head, hseg, |
|
&nsegs, BUS_DMA_NOWAIT); |
|
if (error != 0) /* Nothing elegant to do here */ |
|
goto fail; |
|
bus_dmamap_sync(rxr->htag, |
|
rxbuf->hmap, BUS_DMASYNC_PREREAD); |
|
/* Update descriptor */ |
|
rxr->rx_base[j].read.hdr_addr = htole64(hseg[0].ds_addr); |
|
|
|
skip_head: |
|
/* Now the payload cluster */ |
|
rxbuf->m_pack = m_getjcl(M_NOWAIT, MT_DATA, |
|
M_PKTHDR, adapter->rx_mbuf_sz); |
|
if (rxbuf->m_pack == NULL) { |
|
error = ENOBUFS; |
|
goto fail; |
|
} |
|
mp = rxbuf->m_pack; |
|
mp->m_pkthdr.len = mp->m_len = adapter->rx_mbuf_sz; |
|
/* Get the memory mapping */ |
|
error = bus_dmamap_load_mbuf_sg(rxr->ptag, |
|
rxbuf->pmap, mp, pseg, |
|
&nsegs, BUS_DMA_NOWAIT); |
|
if (error != 0) |
|
goto fail; |
|
bus_dmamap_sync(rxr->ptag, |
|
rxbuf->pmap, BUS_DMASYNC_PREREAD); |
|
/* Update descriptor */ |
|
rxr->rx_base[j].read.pkt_addr = htole64(pseg[0].ds_addr); |
|
} |
|
|
|
|
/* Set the HW Tx Head and Tail indices */ |
|
IXGBE_WRITE_REG(&adapter->hw, IXGBE_VFTDH(i), 0); |
|
IXGBE_WRITE_REG(&adapter->hw, IXGBE_VFTDT(i), 0); |
|
|
/* Setup our descriptor indices */ |
/* Set Tx Tail register */ |
rxr->next_to_check = 0; |
txr->tail = IXGBE_VFTDT(i); |
rxr->next_to_refresh = 0; |
|
rxr->lro_enabled = FALSE; |
|
rxr->rx_split_packets = 0; |
|
rxr->rx_bytes = 0; |
|
|
|
bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, |
/* Set Ring parameters */ |
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
IXGBE_WRITE_REG(hw, IXGBE_VFTDBAL(i), |
|
(tdba & 0x00000000ffffffffULL)); |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTDBAH(i), (tdba >> 32)); |
|
IXGBE_WRITE_REG(hw, IXGBE_VFTDLEN(i), |
|
adapter->num_tx_desc * |
|
sizeof(struct ixgbe_legacy_tx_desc)); |
|
txctrl = IXGBE_READ_REG(hw, IXGBE_VFDCA_TXCTRL(i)); |
|
txctrl &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; |
|
IXGBE_WRITE_REG(hw, IXGBE_VFDCA_TXCTRL(i), txctrl); |
|
|
/* |
/* Now enable */ |
** Now set up the LRO interface: |
txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(i)); |
*/ |
txdctl |= IXGBE_TXDCTL_ENABLE; |
if (ifp->if_capenable & IFCAP_LRO) { |
IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), txdctl); |
int err = tcp_lro_init(lro); |
|
if (err) { |
|
device_printf(dev, "LRO Initialization failed!\n"); |
|
goto fail; |
|
} |
|
INIT_DEBUGOUT("RX Soft LRO Initialized\n"); |
|
rxr->lro_enabled = TRUE; |
|
lro->ifp = adapter->ifp; |
|
} |
} |
|
|
IXV_RX_UNLOCK(rxr); |
return; |
return (0); |
|
|
|
fail: |
|
ixv_free_receive_ring(rxr); |
|
IXV_RX_UNLOCK(rxr); |
|
return (error); |
|
} |
} |
|
|
/********************************************************************* |
|
* |
|
* Initialize all receive rings. |
|
* |
|
**********************************************************************/ |
|
static int |
|
ixv_setup_receive_structures(struct adapter *adapter) |
|
{ |
|
struct rx_ring *rxr = adapter->rx_rings; |
|
int j; |
|
|
|
for (j = 0; j < adapter->num_queues; j++, rxr++) |
|
if (ixv_setup_receive_ring(rxr)) |
|
goto fail; |
|
|
|
return (0); |
|
fail: |
|
/* |
|
* Free RX buffers allocated so far, we will only handle |
|
* the rings that completed, the failing case will have |
|
* cleaned up for itself. 'j' failed, so its the terminus. |
|
*/ |
|
for (int i = 0; i < j; ++i) { |
|
rxr = &adapter->rx_rings[i]; |
|
ixv_free_receive_ring(rxr); |
|
} |
|
|
|
return (ENOBUFS); |
|
} |
|
|
|
/********************************************************************* |
/********************************************************************* |
* |
* |
Line 3008 ixv_initialize_receive_units(struct adap |
|
Line 1739 ixv_initialize_receive_units(struct adap |
|
{ |
{ |
struct rx_ring *rxr = adapter->rx_rings; |
struct rx_ring *rxr = adapter->rx_rings; |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
struct ifnet *ifp = adapter->ifp; |
struct ifnet *ifp = adapter->ifp; |
u32 bufsz, fctrl, rxcsum, hlreg; |
u32 bufsz, rxcsum, psrtype; |
|
|
|
|
/* Enable broadcasts */ |
if (ifp->if_mtu > ETHERMTU) |
fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); |
|
fctrl |= IXGBE_FCTRL_BAM; |
|
fctrl |= IXGBE_FCTRL_DPF; |
|
fctrl |= IXGBE_FCTRL_PMCF; |
|
IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl); |
|
|
|
/* Set for Jumbo Frames? */ |
|
hlreg = IXGBE_READ_REG(hw, IXGBE_HLREG0); |
|
if (ifp->if_mtu > ETHERMTU) { |
|
hlreg |= IXGBE_HLREG0_JUMBOEN; |
|
bufsz = 4096 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; |
bufsz = 4096 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; |
} else { |
else |
hlreg &= ~IXGBE_HLREG0_JUMBOEN; |
|
bufsz = 2048 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; |
bufsz = 2048 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; |
} |
|
IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg); |
psrtype = IXGBE_PSRTYPE_TCPHDR | IXGBE_PSRTYPE_UDPHDR | |
|
IXGBE_PSRTYPE_IPV4HDR | IXGBE_PSRTYPE_IPV6HDR | |
|
IXGBE_PSRTYPE_L2HDR; |
|
|
|
IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, psrtype); |
|
|
|
/* Tell PF our max_frame size */ |
|
ixgbevf_rlpml_set_vf(hw, adapter->max_frame_size); |
|
|
for (int i = 0; i < adapter->num_queues; i++, rxr++) { |
for (int i = 0; i < adapter->num_queues; i++, rxr++) { |
u64 rdba = rxr->rxdma.dma_paddr; |
u64 rdba = rxr->rxdma.dma_paddr; |
u32 reg, rxdctl; |
u32 reg, rxdctl; |
|
|
/* Do the queue enabling first */ |
/* Disable the queue */ |
rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)); |
rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)); |
rxdctl |= IXGBE_RXDCTL_ENABLE; |
rxdctl &= ~IXGBE_RXDCTL_ENABLE; |
IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), rxdctl); |
IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), rxdctl); |
for (int k = 0; k < 10; k++) { |
for (int j = 0; j < 10; j++) { |
if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)) & |
if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)) & |
IXGBE_RXDCTL_ENABLE) |
IXGBE_RXDCTL_ENABLE) |
break; |
|
else |
|
msec_delay(1); |
msec_delay(1); |
|
else |
|
break; |
} |
} |
wmb(); |
wmb(); |
|
|
/* Setup the Base and Length of the Rx Descriptor Ring */ |
/* Setup the Base and Length of the Rx Descriptor Ring */ |
IXGBE_WRITE_REG(hw, IXGBE_VFRDBAL(i), |
IXGBE_WRITE_REG(hw, IXGBE_VFRDBAL(i), |
(rdba & 0x00000000ffffffffULL)); |
(rdba & 0x00000000ffffffffULL)); |
Line 3055 ixv_initialize_receive_units(struct adap |
|
Line 1780 ixv_initialize_receive_units(struct adap |
|
IXGBE_WRITE_REG(hw, IXGBE_VFRDLEN(i), |
IXGBE_WRITE_REG(hw, IXGBE_VFRDLEN(i), |
adapter->num_rx_desc * sizeof(union ixgbe_adv_rx_desc)); |
adapter->num_rx_desc * sizeof(union ixgbe_adv_rx_desc)); |
|
|
|
/* Reset the ring indices */ |
|
IXGBE_WRITE_REG(hw, IXGBE_VFRDH(rxr->me), 0); |
|
IXGBE_WRITE_REG(hw, IXGBE_VFRDT(rxr->me), 0); |
|
|
/* Set up the SRRCTL register */ |
/* Set up the SRRCTL register */ |
reg = IXGBE_READ_REG(hw, IXGBE_VFSRRCTL(i)); |
reg = IXGBE_READ_REG(hw, IXGBE_VFSRRCTL(i)); |
reg &= ~IXGBE_SRRCTL_BSIZEHDR_MASK; |
reg &= ~IXGBE_SRRCTL_BSIZEHDR_MASK; |
reg &= ~IXGBE_SRRCTL_BSIZEPKT_MASK; |
reg &= ~IXGBE_SRRCTL_BSIZEPKT_MASK; |
reg |= bufsz; |
reg |= bufsz; |
if (rxr->hdr_split) { |
reg |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF; |
/* Use a standard mbuf for the header */ |
|
reg |= ((IXV_RX_HDR << |
|
IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT) |
|
& IXGBE_SRRCTL_BSIZEHDR_MASK); |
|
reg |= IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS; |
|
} else |
|
reg |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF; |
|
IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(i), reg); |
IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(i), reg); |
|
|
/* Setup the HW Rx Head and Tail Descriptor Pointers */ |
/* Capture Rx Tail index */ |
IXGBE_WRITE_REG(hw, IXGBE_VFRDH(rxr->me), 0); |
rxr->tail = IXGBE_VFRDT(rxr->me); |
IXGBE_WRITE_REG(hw, IXGBE_VFRDT(rxr->me), |
|
adapter->num_rx_desc - 1); |
|
} |
|
|
|
rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM); |
|
|
|
if (ifp->if_capenable & IFCAP_RXCSUM) |
|
rxcsum |= IXGBE_RXCSUM_PCSD; |
|
|
|
if (!(rxcsum & IXGBE_RXCSUM_PCSD)) |
|
rxcsum |= IXGBE_RXCSUM_IPPCSE; |
|
|
|
IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum); |
|
|
|
return; |
|
} |
|
|
|
/********************************************************************* |
|
* |
|
* Free all receive rings. |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_free_receive_structures(struct adapter *adapter) |
|
{ |
|
struct rx_ring *rxr = adapter->rx_rings; |
|
|
|
for (int i = 0; i < adapter->num_queues; i++, rxr++) { |
|
struct lro_ctrl *lro = &rxr->lro; |
|
ixv_free_receive_buffers(rxr); |
|
/* Free LRO memory */ |
|
tcp_lro_free(lro); |
|
/* Free the ring memory as well */ |
|
ixv_dma_free(adapter, &rxr->rxdma); |
|
} |
|
|
|
free(adapter->rx_rings, M_DEVBUF); |
|
} |
|
|
|
|
|
/********************************************************************* |
|
* |
|
* Free receive ring data structures |
|
* |
|
**********************************************************************/ |
|
static void |
|
ixv_free_receive_buffers(struct rx_ring *rxr) |
|
{ |
|
struct adapter *adapter = rxr->adapter; |
|
struct ixv_rx_buf *rxbuf; |
|
|
|
INIT_DEBUGOUT("free_receive_structures: begin"); |
|
|
|
/* Cleanup any existing buffers */ |
|
if (rxr->rx_buffers != NULL) { |
|
for (int i = 0; i < adapter->num_rx_desc; i++) { |
|
rxbuf = &rxr->rx_buffers[i]; |
|
if (rxbuf->m_head != NULL) { |
|
bus_dmamap_sync(rxr->htag, rxbuf->hmap, |
|
BUS_DMASYNC_POSTREAD); |
|
bus_dmamap_unload(rxr->htag, rxbuf->hmap); |
|
rxbuf->m_head->m_flags |= M_PKTHDR; |
|
m_freem(rxbuf->m_head); |
|
} |
|
if (rxbuf->m_pack != NULL) { |
|
bus_dmamap_sync(rxr->ptag, rxbuf->pmap, |
|
BUS_DMASYNC_POSTREAD); |
|
bus_dmamap_unload(rxr->ptag, rxbuf->pmap); |
|
rxbuf->m_pack->m_flags |= M_PKTHDR; |
|
m_freem(rxbuf->m_pack); |
|
} |
|
rxbuf->m_head = NULL; |
|
rxbuf->m_pack = NULL; |
|
if (rxbuf->hmap != NULL) { |
|
bus_dmamap_destroy(rxr->htag, rxbuf->hmap); |
|
rxbuf->hmap = NULL; |
|
} |
|
if (rxbuf->pmap != NULL) { |
|
bus_dmamap_destroy(rxr->ptag, rxbuf->pmap); |
|
rxbuf->pmap = NULL; |
|
} |
|
} |
|
if (rxr->rx_buffers != NULL) { |
|
free(rxr->rx_buffers, M_DEVBUF); |
|
rxr->rx_buffers = NULL; |
|
} |
|
} |
|
|
|
if (rxr->htag != NULL) { |
|
bus_dma_tag_destroy(rxr->htag); |
|
rxr->htag = NULL; |
|
} |
|
if (rxr->ptag != NULL) { |
|
bus_dma_tag_destroy(rxr->ptag); |
|
rxr->ptag = NULL; |
|
} |
|
|
|
return; |
|
} |
|
|
|
static __inline void |
|
ixv_rx_input(struct rx_ring *rxr, struct ifnet *ifp, struct mbuf *m, u32 ptype) |
|
{ |
|
|
|
/* |
|
* ATM LRO is only for IPv4/TCP packets and TCP checksum of the packet |
|
* should be computed by hardware. Also it should not have VLAN tag in |
|
* ethernet header. |
|
*/ |
|
if (rxr->lro_enabled && |
|
(ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && |
|
(ptype & IXGBE_RXDADV_PKTTYPE_ETQF) == 0 && |
|
(ptype & (IXGBE_RXDADV_PKTTYPE_IPV4 | IXGBE_RXDADV_PKTTYPE_TCP)) == |
|
(IXGBE_RXDADV_PKTTYPE_IPV4 | IXGBE_RXDADV_PKTTYPE_TCP) && |
|
(m->m_pkthdr.csum_flags & (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) == |
|
(CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) { |
|
/* |
|
* Send to the stack if: |
|
** - LRO not enabled, or |
|
** - no LRO resources, or |
|
** - lro enqueue fails |
|
*/ |
|
if (rxr->lro.lro_cnt != 0) |
|
if (tcp_lro_rx(&rxr->lro, m, 0) == 0) |
|
return; |
|
} |
|
(*ifp->if_input)(ifp, m); |
|
} |
|
|
|
static __inline void |
|
ixv_rx_discard(struct rx_ring *rxr, int i) |
|
{ |
|
struct adapter *adapter = rxr->adapter; |
|
struct ixv_rx_buf *rbuf; |
|
struct mbuf *mh, *mp; |
|
|
|
rbuf = &rxr->rx_buffers[i]; |
|
if (rbuf->fmp != NULL) /* Partial chain ? */ |
|
m_freem(rbuf->fmp); |
|
|
|
mh = rbuf->m_head; |
|
mp = rbuf->m_pack; |
|
|
|
/* Reuse loaded DMA map and just update mbuf chain */ |
|
mh->m_len = MHLEN; |
|
mh->m_flags |= M_PKTHDR; |
|
mh->m_next = NULL; |
|
|
|
mp->m_len = mp->m_pkthdr.len = adapter->rx_mbuf_sz; |
|
mp->m_data = mp->m_ext.ext_buf; |
|
mp->m_next = NULL; |
|
return; |
|
} |
|
|
|
|
|
/********************************************************************* |
|
* |
|
* This routine executes in interrupt context. It replenishes |
|
* the mbufs in the descriptor and sends data which has been |
|
* dma'ed into host memory to upper layer. |
|
* |
|
* We loop at most count times if count is > 0, or until done if |
|
* count < 0. |
|
* |
|
* Return TRUE for more work, FALSE for all clean. |
|
*********************************************************************/ |
|
static bool |
|
ixv_rxeof(struct ix_queue *que, int count) |
|
{ |
|
struct adapter *adapter = que->adapter; |
|
struct rx_ring *rxr = que->rxr; |
|
struct ifnet *ifp = adapter->ifp; |
|
struct lro_ctrl *lro = &rxr->lro; |
|
struct lro_entry *queued; |
|
int i, nextp, processed = 0; |
|
u32 staterr = 0; |
|
union ixgbe_adv_rx_desc *cur; |
|
struct ixv_rx_buf *rbuf, *nbuf; |
|
|
|
IXV_RX_LOCK(rxr); |
|
|
|
for (i = rxr->next_to_check; count != 0;) { |
|
struct mbuf *sendmp, *mh, *mp; |
|
u32 rsc, ptype; |
|
u16 hlen, plen, hdr, vtag; |
|
bool eop; |
|
|
|
/* Sync the ring. */ |
|
bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, |
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); |
|
|
|
cur = &rxr->rx_base[i]; |
|
staterr = le32toh(cur->wb.upper.status_error); |
|
|
|
if ((staterr & IXGBE_RXD_STAT_DD) == 0) |
/* Do the queue enabling last */ |
break; |
rxdctl |= IXGBE_RXDCTL_ENABLE | IXGBE_RXDCTL_VME; |
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) |
IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), rxdctl); |
break; |
for (int k = 0; k < 10; k++) { |
|
if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)) & |
count--; |
IXGBE_RXDCTL_ENABLE) |
sendmp = NULL; |
break; |
nbuf = NULL; |
|
rsc = 0; |
|
cur->wb.upper.status_error = 0; |
|
rbuf = &rxr->rx_buffers[i]; |
|
mh = rbuf->m_head; |
|
mp = rbuf->m_pack; |
|
|
|
plen = le16toh(cur->wb.upper.length); |
|
ptype = le32toh(cur->wb.lower.lo_dword.data) & |
|
IXGBE_RXDADV_PKTTYPE_MASK; |
|
hdr = le16toh(cur->wb.lower.lo_dword.hs_rss.hdr_info); |
|
vtag = le16toh(cur->wb.upper.vlan); |
|
eop = ((staterr & IXGBE_RXD_STAT_EOP) != 0); |
|
|
|
/* Make sure all parts of a bad packet are discarded */ |
|
if (((staterr & IXGBE_RXDADV_ERR_FRAME_ERR_MASK) != 0) || |
|
(rxr->discard)) { |
|
ifp->if_ierrors++; |
|
rxr->rx_discarded++; |
|
if (!eop) |
|
rxr->discard = TRUE; |
|
else |
else |
rxr->discard = FALSE; |
msec_delay(1); |
ixv_rx_discard(rxr, i); |
|
goto next_desc; |
|
} |
} |
|
wmb(); |
|
|
if (!eop) { |
/* Set the Tail Pointer */ |
nextp = i + 1; |
#ifdef DEV_NETMAP |
if (nextp == adapter->num_rx_desc) |
|
nextp = 0; |
|
nbuf = &rxr->rx_buffers[nextp]; |
|
prefetch(nbuf); |
|
} |
|
/* |
/* |
** The header mbuf is ONLY used when header |
* In netmap mode, we must preserve the buffers made |
** split is enabled, otherwise we get normal |
* available to userspace before the if_init() |
** behavior, ie, both header and payload |
* (this is true by default on the TX side, because |
** are DMA'd into the payload buffer. |
* init makes all buffers available to userspace). |
** |
* |
** Rather than using the fmp/lmp global pointers |
* netmap_reset() and the device specific routines |
** we now keep the head of a packet chain in the |
* (e.g. ixgbe_setup_receive_rings()) map these |
** buffer struct and pass this along from one |
* buffers at the end of the NIC ring, so here we |
** descriptor to the next, until we get EOP. |
* must set the RDT (tail) register to make sure |
*/ |
* they are not overwritten. |
if (rxr->hdr_split && (rbuf->fmp == NULL)) { |
* |
/* This must be an initial descriptor */ |
* In this driver the NIC ring starts at RDH = 0, |
hlen = (hdr & IXGBE_RXDADV_HDRBUFLEN_MASK) >> |
* RDT points to the last slot available for reception (?), |
IXGBE_RXDADV_HDRBUFLEN_SHIFT; |
* so RDT = num_rx_desc - 1 means the whole ring is available. |
if (hlen > IXV_RX_HDR) |
*/ |
hlen = IXV_RX_HDR; |
if (ifp->if_capenable & IFCAP_NETMAP) { |
mh->m_len = hlen; |
struct netmap_adapter *na = NA(adapter->ifp); |
mh->m_flags |= M_PKTHDR; |
struct netmap_kring *kring = &na->rx_rings[i]; |
mh->m_next = NULL; |
int t = na->num_rx_desc - 1 - nm_kr_rxspace(kring); |
mh->m_pkthdr.len = mh->m_len; |
|
/* Null buf pointer so it is refreshed */ |
|
rbuf->m_head = NULL; |
|
/* |
|
** Check the payload length, this |
|
** could be zero if its a small |
|
** packet. |
|
*/ |
|
if (plen > 0) { |
|
mp->m_len = plen; |
|
mp->m_next = NULL; |
|
mp->m_flags &= ~M_PKTHDR; |
|
mh->m_next = mp; |
|
mh->m_pkthdr.len += mp->m_len; |
|
/* Null buf pointer so it is refreshed */ |
|
rbuf->m_pack = NULL; |
|
rxr->rx_split_packets++; |
|
} |
|
/* |
|
** Now create the forward |
|
** chain so when complete |
|
** we wont have to. |
|
*/ |
|
if (eop == 0) { |
|
/* stash the chain head */ |
|
nbuf->fmp = mh; |
|
/* Make forward chain */ |
|
if (plen) |
|
mp->m_next = nbuf->m_pack; |
|
else |
|
mh->m_next = nbuf->m_pack; |
|
} else { |
|
/* Singlet, prepare to send */ |
|
sendmp = mh; |
|
if (staterr & IXGBE_RXD_STAT_VP) { |
|
sendmp->m_pkthdr.ether_vtag = vtag; |
|
sendmp->m_flags |= M_VLANTAG; |
|
} |
|
} |
|
} else { |
|
/* |
|
** Either no header split, or a |
|
** secondary piece of a fragmented |
|
** split packet. |
|
*/ |
|
mp->m_len = plen; |
|
/* |
|
** See if there is a stored head |
|
** that determines what we are |
|
*/ |
|
sendmp = rbuf->fmp; |
|
rbuf->m_pack = rbuf->fmp = NULL; |
|
|
|
if (sendmp != NULL) /* secondary frag */ |
|
sendmp->m_pkthdr.len += mp->m_len; |
|
else { |
|
/* first desc of a non-ps chain */ |
|
sendmp = mp; |
|
sendmp->m_flags |= M_PKTHDR; |
|
sendmp->m_pkthdr.len = mp->m_len; |
|
if (staterr & IXGBE_RXD_STAT_VP) { |
|
sendmp->m_pkthdr.ether_vtag = vtag; |
|
sendmp->m_flags |= M_VLANTAG; |
|
} |
|
} |
|
/* Pass the head pointer on */ |
|
if (eop == 0) { |
|
nbuf->fmp = sendmp; |
|
sendmp = NULL; |
|
mp->m_next = nbuf->m_pack; |
|
} |
|
} |
|
++processed; |
|
/* Sending this frame? */ |
|
if (eop) { |
|
sendmp->m_pkthdr.rcvif = ifp; |
|
ifp->if_ipackets++; |
|
rxr->rx_packets++; |
|
/* capture data for AIM */ |
|
rxr->bytes += sendmp->m_pkthdr.len; |
|
rxr->rx_bytes += sendmp->m_pkthdr.len; |
|
if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) |
|
ixv_rx_checksum(staterr, sendmp, ptype); |
|
#if __FreeBSD_version >= 800000 |
|
sendmp->m_pkthdr.flowid = que->msix; |
|
sendmp->m_flags |= M_FLOWID; |
|
#endif |
|
} |
|
next_desc: |
|
bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, |
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); |
|
|
|
/* Advance our pointers to the next descriptor. */ |
|
if (++i == adapter->num_rx_desc) |
|
i = 0; |
|
|
|
/* Now send to the stack or do LRO */ |
|
if (sendmp != NULL) |
|
ixv_rx_input(rxr, ifp, sendmp, ptype); |
|
|
|
/* Every 8 descriptors we go to refresh mbufs */ |
|
if (processed == 8) { |
|
ixv_refresh_mbufs(rxr, i); |
|
processed = 0; |
|
} |
|
} |
|
|
|
/* Refresh any remaining buf structs */ |
|
if (processed != 0) { |
|
ixv_refresh_mbufs(rxr, i); |
|
processed = 0; |
|
} |
|
|
|
rxr->next_to_check = i; |
|
|
|
/* |
IXGBE_WRITE_REG(hw, IXGBE_VFRDT(rxr->me), t); |
* Flush any outstanding LRO work |
} else |
*/ |
#endif /* DEV_NETMAP */ |
while ((queued = SLIST_FIRST(&lro->lro_active)) != NULL) { |
IXGBE_WRITE_REG(hw, IXGBE_VFRDT(rxr->me), |
SLIST_REMOVE_HEAD(&lro->lro_active, next); |
adapter->num_rx_desc - 1); |
tcp_lro_flush(lro, queued); |
|
} |
} |
|
|
IXV_RX_UNLOCK(rxr); |
rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM); |
|
|
/* |
|
** We still have cleaning to do? |
|
** Schedule another interrupt if so. |
|
*/ |
|
if ((staterr & IXGBE_RXD_STAT_DD) != 0) { |
|
ixv_rearm_queues(adapter, (u64)(1 << que->msix)); |
|
return (TRUE); |
|
} |
|
|
|
return (FALSE); |
if (ifp->if_capenable & IFCAP_RXCSUM) |
} |
rxcsum |= IXGBE_RXCSUM_PCSD; |
|
|
|
if (!(rxcsum & IXGBE_RXCSUM_PCSD)) |
|
rxcsum |= IXGBE_RXCSUM_IPPCSE; |
|
|
/********************************************************************* |
IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum); |
* |
|
* Verify that the hardware indicated that the checksum is valid. |
|
* Inform the stack about the status of checksum so that stack |
|
* doesn't spend time verifying the checksum. |
|
* |
|
*********************************************************************/ |
|
static void |
|
ixv_rx_checksum(u32 staterr, struct mbuf * mp, u32 ptype) |
|
{ |
|
u16 status = (u16) staterr; |
|
u8 errors = (u8) (staterr >> 24); |
|
bool sctp = FALSE; |
|
|
|
if ((ptype & IXGBE_RXDADV_PKTTYPE_ETQF) == 0 && |
|
(ptype & IXGBE_RXDADV_PKTTYPE_SCTP) != 0) |
|
sctp = TRUE; |
|
|
|
if (status & IXGBE_RXD_STAT_IPCS) { |
|
if (!(errors & IXGBE_RXD_ERR_IPE)) { |
|
/* IP Checksum Good */ |
|
mp->m_pkthdr.csum_flags = CSUM_IP_CHECKED; |
|
mp->m_pkthdr.csum_flags |= CSUM_IP_VALID; |
|
|
|
} else |
|
mp->m_pkthdr.csum_flags = 0; |
|
} |
|
if (status & IXGBE_RXD_STAT_L4CS) { |
|
u16 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); |
|
#if __FreeBSD_version >= 800000 |
|
if (sctp) |
|
type = CSUM_SCTP_VALID; |
|
#endif |
|
if (!(errors & IXGBE_RXD_ERR_TCPE)) { |
|
mp->m_pkthdr.csum_flags |= type; |
|
if (!sctp) |
|
mp->m_pkthdr.csum_data = htons(0xffff); |
|
} |
|
} |
|
return; |
return; |
} |
} |
|
|
Line 3512 ixv_setup_vlan_support(struct adapter *a |
|
Line 1855 ixv_setup_vlan_support(struct adapter *a |
|
{ |
{ |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
u32 ctrl, vid, vfta, retry; |
u32 ctrl, vid, vfta, retry; |
|
struct rx_ring *rxr; |
|
|
/* |
/* |
** We get here thru init_locked, meaning |
** We get here thru init_locked, meaning |
Line 3520 ixv_setup_vlan_support(struct adapter *a |
|
Line 1863 ixv_setup_vlan_support(struct adapter *a |
|
** the VFTA and other state, so if there |
** the VFTA and other state, so if there |
** have been no vlan's registered do nothing. |
** have been no vlan's registered do nothing. |
*/ |
*/ |
if (adapter->num_vlans == 0) |
if (!VLAN_ATTACHED(&adapter->osdep.ec)) |
return; |
return; |
|
|
/* Enable the queues */ |
/* Enable the queues */ |
Line 3528 ixv_setup_vlan_support(struct adapter *a |
|
Line 1871 ixv_setup_vlan_support(struct adapter *a |
|
ctrl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)); |
ctrl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)); |
ctrl |= IXGBE_RXDCTL_VME; |
ctrl |= IXGBE_RXDCTL_VME; |
IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), ctrl); |
IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), ctrl); |
|
/* |
|
* Let Rx path know that it needs to store VLAN tag |
|
* as part of extra mbuf info. |
|
*/ |
|
rxr = &adapter->rx_rings[i]; |
|
rxr->vtag_strip = TRUE; |
} |
} |
|
|
/* |
/* |
** A soft reset zero's out the VFTA, so |
** A soft reset zero's out the VFTA, so |
** we need to repopulate it now. |
** we need to repopulate it now. |
*/ |
*/ |
for (int i = 0; i < VFTA_SIZE; i++) { |
for (int i = 0; i < IXGBE_VFTA_SIZE; i++) { |
if (ixv_shadow_vfta[i] == 0) |
if (ixv_shadow_vfta[i] == 0) |
continue; |
continue; |
vfta = ixv_shadow_vfta[i]; |
vfta = ixv_shadow_vfta[i]; |
Line 3543 ixv_setup_vlan_support(struct adapter *a |
|
Line 1892 ixv_setup_vlan_support(struct adapter *a |
|
** based on the bits set in each |
** based on the bits set in each |
** of the array ints. |
** of the array ints. |
*/ |
*/ |
for ( int j = 0; j < 32; j++) { |
for (int j = 0; j < 32; j++) { |
retry = 0; |
retry = 0; |
if ((vfta & (1 << j)) == 0) |
if ((vfta & (1 << j)) == 0) |
continue; |
continue; |
Line 3557 ixv_setup_vlan_support(struct adapter *a |
|
Line 1906 ixv_setup_vlan_support(struct adapter *a |
|
} |
} |
} |
} |
|
|
|
#if 0 /* XXX Badly need to overhaul vlan(4) on NetBSD. */ |
/* |
/* |
** This routine is run via an vlan config EVENT, |
** This routine is run via an vlan config EVENT, |
** it enables us to use the HW Filter table since |
** it enables us to use the HW Filter table since |
Line 3570 ixv_register_vlan(void *arg, struct ifne |
|
Line 1920 ixv_register_vlan(void *arg, struct ifne |
|
struct adapter *adapter = ifp->if_softc; |
struct adapter *adapter = ifp->if_softc; |
u16 index, bit; |
u16 index, bit; |
|
|
if (ifp->if_softc != arg) /* Not our event */ |
if (ifp->if_softc != arg) /* Not our event */ |
return; |
return; |
|
|
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ |
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ |
return; |
return; |
|
|
|
IXGBE_CORE_LOCK(adapter); |
index = (vtag >> 5) & 0x7F; |
index = (vtag >> 5) & 0x7F; |
bit = vtag & 0x1F; |
bit = vtag & 0x1F; |
ixv_shadow_vfta[index] |= (1 << bit); |
ixv_shadow_vfta[index] |= (1 << bit); |
++adapter->num_vlans; |
|
/* Re-init to load the changes */ |
/* Re-init to load the changes */ |
ixv_init(adapter); |
ixv_init_locked(adapter); |
|
IXGBE_CORE_UNLOCK(adapter); |
} |
} |
|
|
/* |
/* |
Line 3601 ixv_unregister_vlan(void *arg, struct if |
|
Line 1952 ixv_unregister_vlan(void *arg, struct if |
|
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ |
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ |
return; |
return; |
|
|
|
IXGBE_CORE_LOCK(adapter); |
index = (vtag >> 5) & 0x7F; |
index = (vtag >> 5) & 0x7F; |
bit = vtag & 0x1F; |
bit = vtag & 0x1F; |
ixv_shadow_vfta[index] &= ~(1 << bit); |
ixv_shadow_vfta[index] &= ~(1 << bit); |
--adapter->num_vlans; |
|
/* Re-init to load the changes */ |
/* Re-init to load the changes */ |
ixv_init(adapter); |
ixv_init_locked(adapter); |
|
IXGBE_CORE_UNLOCK(adapter); |
} |
} |
|
#endif |
|
|
static void |
static void |
ixv_enable_intr(struct adapter *adapter) |
ixv_enable_intr(struct adapter *adapter) |
Line 3683 ixv_configure_ivars(struct adapter *adap |
|
Line 2036 ixv_configure_ivars(struct adapter *adap |
|
IXGBE_VTEITR(que->msix), IXV_EITR_DEFAULT); |
IXGBE_VTEITR(que->msix), IXV_EITR_DEFAULT); |
} |
} |
|
|
/* For the Link interrupt */ |
/* For the mailbox interrupt */ |
ixv_set_ivar(adapter, 1, adapter->mbxvec, -1); |
ixv_set_ivar(adapter, 1, adapter->vector, -1); |
} |
} |
|
|
|
|
Line 3703 ixv_handle_mbx(void *context) |
|
Line 2056 ixv_handle_mbx(void *context) |
|
} |
} |
|
|
/* |
/* |
** The VF stats registers never have a truely virgin |
** The VF stats registers never have a truly virgin |
** starting point, so this routine tries to make an |
** starting point, so this routine tries to make an |
** artificial one, marking ground zero on attach as |
** artificial one, marking ground zero on attach as |
** it were. |
** it were. |
Line 3711 ixv_handle_mbx(void *context) |
|
Line 2064 ixv_handle_mbx(void *context) |
|
static void |
static void |
ixv_save_stats(struct adapter *adapter) |
ixv_save_stats(struct adapter *adapter) |
{ |
{ |
if (adapter->stats.vfgprc || adapter->stats.vfgptc) { |
struct ixgbevf_hw_stats *stats = &adapter->stats.vf; |
adapter->stats.saved_reset_vfgprc += |
|
adapter->stats.vfgprc - adapter->stats.base_vfgprc; |
if (stats->vfgprc.ev_count || stats->vfgptc.ev_count) { |
adapter->stats.saved_reset_vfgptc += |
stats->saved_reset_vfgprc += |
adapter->stats.vfgptc - adapter->stats.base_vfgptc; |
stats->vfgprc.ev_count - stats->base_vfgprc; |
adapter->stats.saved_reset_vfgorc += |
stats->saved_reset_vfgptc += |
adapter->stats.vfgorc - adapter->stats.base_vfgorc; |
stats->vfgptc.ev_count - stats->base_vfgptc; |
adapter->stats.saved_reset_vfgotc += |
stats->saved_reset_vfgorc += |
adapter->stats.vfgotc - adapter->stats.base_vfgotc; |
stats->vfgorc.ev_count - stats->base_vfgorc; |
adapter->stats.saved_reset_vfmprc += |
stats->saved_reset_vfgotc += |
adapter->stats.vfmprc - adapter->stats.base_vfmprc; |
stats->vfgotc.ev_count - stats->base_vfgotc; |
|
stats->saved_reset_vfmprc += |
|
stats->vfmprc.ev_count - stats->base_vfmprc; |
} |
} |
} |
} |
|
|
Line 3730 ixv_init_stats(struct adapter *adapter) |
|
Line 2085 ixv_init_stats(struct adapter *adapter) |
|
{ |
{ |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
|
|
adapter->stats.last_vfgprc = IXGBE_READ_REG(hw, IXGBE_VFGPRC); |
adapter->stats.vf.last_vfgprc = IXGBE_READ_REG(hw, IXGBE_VFGPRC); |
adapter->stats.last_vfgorc = IXGBE_READ_REG(hw, IXGBE_VFGORC_LSB); |
adapter->stats.vf.last_vfgorc = IXGBE_READ_REG(hw, IXGBE_VFGORC_LSB); |
adapter->stats.last_vfgorc |= |
adapter->stats.vf.last_vfgorc |= |
(((u64)(IXGBE_READ_REG(hw, IXGBE_VFGORC_MSB))) << 32); |
(((u64)(IXGBE_READ_REG(hw, IXGBE_VFGORC_MSB))) << 32); |
|
|
adapter->stats.last_vfgptc = IXGBE_READ_REG(hw, IXGBE_VFGPTC); |
adapter->stats.vf.last_vfgptc = IXGBE_READ_REG(hw, IXGBE_VFGPTC); |
adapter->stats.last_vfgotc = IXGBE_READ_REG(hw, IXGBE_VFGOTC_LSB); |
adapter->stats.vf.last_vfgotc = IXGBE_READ_REG(hw, IXGBE_VFGOTC_LSB); |
adapter->stats.last_vfgotc |= |
adapter->stats.vf.last_vfgotc |= |
(((u64)(IXGBE_READ_REG(hw, IXGBE_VFGOTC_MSB))) << 32); |
(((u64)(IXGBE_READ_REG(hw, IXGBE_VFGOTC_MSB))) << 32); |
|
|
adapter->stats.last_vfmprc = IXGBE_READ_REG(hw, IXGBE_VFMPRC); |
adapter->stats.vf.last_vfmprc = IXGBE_READ_REG(hw, IXGBE_VFMPRC); |
|
|
adapter->stats.base_vfgprc = adapter->stats.last_vfgprc; |
adapter->stats.vf.base_vfgprc = adapter->stats.vf.last_vfgprc; |
adapter->stats.base_vfgorc = adapter->stats.last_vfgorc; |
adapter->stats.vf.base_vfgorc = adapter->stats.vf.last_vfgorc; |
adapter->stats.base_vfgptc = adapter->stats.last_vfgptc; |
adapter->stats.vf.base_vfgptc = adapter->stats.vf.last_vfgptc; |
adapter->stats.base_vfgotc = adapter->stats.last_vfgotc; |
adapter->stats.vf.base_vfgotc = adapter->stats.vf.last_vfgotc; |
adapter->stats.base_vfmprc = adapter->stats.last_vfmprc; |
adapter->stats.vf.base_vfmprc = adapter->stats.vf.last_vfmprc; |
} |
} |
|
|
#define UPDATE_STAT_32(reg, last, count) \ |
#define UPDATE_STAT_32(reg, last, count) \ |
{ \ |
{ \ |
u32 current = IXGBE_READ_REG(hw, reg); \ |
u32 current = IXGBE_READ_REG(hw, reg); \ |
if (current < last) \ |
if (current < last) \ |
count += 0x100000000LL; \ |
count.ev_count += 0x100000000LL; \ |
last = current; \ |
last = current; \ |
count &= 0xFFFFFFFF00000000LL; \ |
count.ev_count &= 0xFFFFFFFF00000000LL; \ |
count |= current; \ |
count.ev_count |= current; \ |
} |
} |
|
|
#define UPDATE_STAT_36(lsb, msb, last, count) \ |
#define UPDATE_STAT_36(lsb, msb, last, count) \ |
Line 3765 ixv_init_stats(struct adapter *adapter) |
|
Line 2120 ixv_init_stats(struct adapter *adapter) |
|
u64 cur_msb = IXGBE_READ_REG(hw, msb); \ |
u64 cur_msb = IXGBE_READ_REG(hw, msb); \ |
u64 current = ((cur_msb << 32) | cur_lsb); \ |
u64 current = ((cur_msb << 32) | cur_lsb); \ |
if (current < last) \ |
if (current < last) \ |
count += 0x1000000000LL; \ |
count.ev_count += 0x1000000000LL; \ |
last = current; \ |
last = current; \ |
count &= 0xFFFFFFF000000000LL; \ |
count.ev_count &= 0xFFFFFFF000000000LL; \ |
count |= current; \ |
count.ev_count |= current; \ |
} |
} |
|
|
/* |
/* |
Line 3779 ixv_update_stats(struct adapter *adapter |
|
Line 2134 ixv_update_stats(struct adapter *adapter |
|
{ |
{ |
struct ixgbe_hw *hw = &adapter->hw; |
struct ixgbe_hw *hw = &adapter->hw; |
|
|
UPDATE_STAT_32(IXGBE_VFGPRC, adapter->stats.last_vfgprc, |
UPDATE_STAT_32(IXGBE_VFGPRC, adapter->stats.vf.last_vfgprc, |
adapter->stats.vfgprc); |
adapter->stats.vf.vfgprc); |
UPDATE_STAT_32(IXGBE_VFGPTC, adapter->stats.last_vfgptc, |
UPDATE_STAT_32(IXGBE_VFGPTC, adapter->stats.vf.last_vfgptc, |
adapter->stats.vfgptc); |
adapter->stats.vf.vfgptc); |
UPDATE_STAT_36(IXGBE_VFGORC_LSB, IXGBE_VFGORC_MSB, |
UPDATE_STAT_36(IXGBE_VFGORC_LSB, IXGBE_VFGORC_MSB, |
adapter->stats.last_vfgorc, adapter->stats.vfgorc); |
adapter->stats.vf.last_vfgorc, adapter->stats.vf.vfgorc); |
UPDATE_STAT_36(IXGBE_VFGOTC_LSB, IXGBE_VFGOTC_MSB, |
UPDATE_STAT_36(IXGBE_VFGOTC_LSB, IXGBE_VFGOTC_MSB, |
adapter->stats.last_vfgotc, adapter->stats.vfgotc); |
adapter->stats.vf.last_vfgotc, adapter->stats.vf.vfgotc); |
UPDATE_STAT_32(IXGBE_VFMPRC, adapter->stats.last_vfmprc, |
UPDATE_STAT_32(IXGBE_VFMPRC, adapter->stats.vf.last_vfmprc, |
adapter->stats.vfmprc); |
adapter->stats.vf.vfmprc); |
} |
} |
|
|
/********************************************************************** |
/* |
* |
* Add statistic sysctls for the VF. |
* This routine is called only when ixgbe_display_debug_stats is enabled. |
*/ |
* This routine provides a way to take a look at important statistics |
|
* maintained by the driver and hardware. |
|
* |
|
**********************************************************************/ |
|
static void |
static void |
ixv_print_hw_stats(struct adapter * adapter) |
ixv_add_stats_sysctls(struct adapter *adapter) |
{ |
{ |
device_t dev = adapter->dev; |
device_t dev = adapter->dev; |
|
struct ix_queue *que = &adapter->queues[0]; |
device_printf(dev,"Std Mbuf Failed = %lu\n", |
struct tx_ring *txr = que->txr; |
adapter->mbuf_defrag_failed); |
struct rx_ring *rxr = que->rxr; |
device_printf(dev,"Driver dropped packets = %lu\n", |
|
adapter->dropped_pkts); |
struct ixgbevf_hw_stats *stats = &adapter->stats.vf; |
device_printf(dev, "watchdog timeouts = %ld\n", |
|
adapter->watchdog_events); |
const char *xname = device_xname(dev); |
|
|
device_printf(dev,"Good Packets Rcvd = %llu\n", |
/* Driver Statistics */ |
(long long)adapter->stats.vfgprc); |
evcnt_attach_dynamic(&adapter->dropped_pkts, EVCNT_TYPE_MISC, |
device_printf(dev,"Good Packets Xmtd = %llu\n", |
NULL, xname, "Driver dropped packets"); |
(long long)adapter->stats.vfgptc); |
evcnt_attach_dynamic(&adapter->mbuf_defrag_failed, EVCNT_TYPE_MISC, |
device_printf(dev,"TSO Transmissions = %lu\n", |
NULL, xname, "m_defrag() failed"); |
adapter->tso_tx); |
evcnt_attach_dynamic(&adapter->watchdog_events, EVCNT_TYPE_MISC, |
|
NULL, xname, "Watchdog timeouts"); |
|
|
|
evcnt_attach_dynamic(&stats->vfgprc, EVCNT_TYPE_MISC, NULL, |
|
xname, "Good Packets Received"); |
|
evcnt_attach_dynamic(&stats->vfgorc, EVCNT_TYPE_MISC, NULL, |
|
xname, "Good Octets Received"); |
|
evcnt_attach_dynamic(&stats->vfmprc, EVCNT_TYPE_MISC, NULL, |
|
xname, "Multicast Packets Received"); |
|
evcnt_attach_dynamic(&stats->vfgptc, EVCNT_TYPE_MISC, NULL, |
|
xname, "Good Packets Transmitted"); |
|
evcnt_attach_dynamic(&stats->vfgotc, EVCNT_TYPE_MISC, NULL, |
|
xname, "Good Octets Transmitted"); |
|
evcnt_attach_dynamic(&que->irqs, EVCNT_TYPE_INTR, NULL, |
|
xname, "IRQs on queue"); |
|
evcnt_attach_dynamic(&rxr->rx_irq, EVCNT_TYPE_INTR, NULL, |
|
xname, "RX irqs on queue"); |
|
evcnt_attach_dynamic(&rxr->rx_packets, EVCNT_TYPE_MISC, NULL, |
|
xname, "RX packets"); |
|
evcnt_attach_dynamic(&rxr->rx_bytes, EVCNT_TYPE_MISC, NULL, |
|
xname, "RX bytes"); |
|
evcnt_attach_dynamic(&rxr->rx_discarded, EVCNT_TYPE_MISC, NULL, |
|
xname, "Discarded RX packets"); |
|
evcnt_attach_dynamic(&txr->total_packets, EVCNT_TYPE_MISC, NULL, |
|
xname, "TX Packets"); |
|
evcnt_attach_dynamic(&txr->no_desc_avail, EVCNT_TYPE_MISC, NULL, |
|
xname, "# of times not enough descriptors were available during TX"); |
|
evcnt_attach_dynamic(&txr->tso_tx, EVCNT_TYPE_MISC, NULL, |
|
xname, "TX TSO"); |
|
} |
|
|
|
static void |
|
ixv_set_sysctl_value(struct adapter *adapter, const char *name, |
|
const char *description, int *limit, int value) |
|
{ |
|
device_t dev = adapter->dev; |
|
struct sysctllog **log; |
|
const struct sysctlnode *rnode, *cnode; |
|
|
|
log = &adapter->sysctllog; |
|
if ((rnode = ixv_sysctl_instance(adapter)) == NULL) { |
|
aprint_error_dev(dev, "could not create sysctl root\n"); |
|
return; |
|
} |
|
if (sysctl_createv(log, 0, &rnode, &cnode, |
|
CTLFLAG_READWRITE, CTLTYPE_INT, |
|
name, SYSCTL_DESCR(description), |
|
NULL, 0, limit, 0, CTL_CREATE, CTL_EOL) != 0) |
|
aprint_error_dev(dev, "could not create sysctl\n"); |
|
*limit = value; |
} |
} |
|
|
/********************************************************************** |
/********************************************************************** |
Line 3834 ixv_print_debug_info(struct adapter *ada |
|
Line 2233 ixv_print_debug_info(struct adapter *ada |
|
struct ix_queue *que = adapter->queues; |
struct ix_queue *que = adapter->queues; |
struct rx_ring *rxr; |
struct rx_ring *rxr; |
struct tx_ring *txr; |
struct tx_ring *txr; |
|
#ifdef LRO |
struct lro_ctrl *lro; |
struct lro_ctrl *lro; |
|
#endif /* LRO */ |
|
|
device_printf(dev,"Error Byte Count = %u \n", |
device_printf(dev,"Error Byte Count = %u \n", |
IXGBE_READ_REG(hw, IXGBE_ERRBC)); |
IXGBE_READ_REG(hw, IXGBE_ERRBC)); |
Line 3842 ixv_print_debug_info(struct adapter *ada |
|
Line 2243 ixv_print_debug_info(struct adapter *ada |
|
for (int i = 0; i < adapter->num_queues; i++, que++) { |
for (int i = 0; i < adapter->num_queues; i++, que++) { |
txr = que->txr; |
txr = que->txr; |
rxr = que->rxr; |
rxr = que->rxr; |
|
#ifdef LRO |
lro = &rxr->lro; |
lro = &rxr->lro; |
|
#endif /* LRO */ |
device_printf(dev,"QUE(%d) IRQs Handled: %lu\n", |
device_printf(dev,"QUE(%d) IRQs Handled: %lu\n", |
que->msix, (long)que->irqs); |
que->msix, (long)que->irqs.ev_count); |
device_printf(dev,"RX(%d) Packets Received: %lld\n", |
device_printf(dev,"RX(%d) Packets Received: %lld\n", |
rxr->me, (long long)rxr->rx_packets); |
rxr->me, (long long)rxr->rx_packets.ev_count); |
device_printf(dev,"RX(%d) Split RX Packets: %lld\n", |
|
rxr->me, (long long)rxr->rx_split_packets); |
|
device_printf(dev,"RX(%d) Bytes Received: %lu\n", |
device_printf(dev,"RX(%d) Bytes Received: %lu\n", |
rxr->me, (long)rxr->rx_bytes); |
rxr->me, (long)rxr->rx_bytes.ev_count); |
device_printf(dev,"RX(%d) LRO Queued= %d\n", |
#ifdef LRO |
rxr->me, lro->lro_queued); |
device_printf(dev,"RX(%d) LRO Queued= %lld\n", |
device_printf(dev,"RX(%d) LRO Flushed= %d\n", |
rxr->me, (long long)lro->lro_queued); |
rxr->me, lro->lro_flushed); |
device_printf(dev,"RX(%d) LRO Flushed= %lld\n", |
|
rxr->me, (long long)lro->lro_flushed); |
|
#endif /* LRO */ |
device_printf(dev,"TX(%d) Packets Sent: %lu\n", |
device_printf(dev,"TX(%d) Packets Sent: %lu\n", |
txr->me, (long)txr->total_packets); |
txr->me, (long)txr->total_packets.ev_count); |
device_printf(dev,"TX(%d) NO Desc Avail: %lu\n", |
device_printf(dev,"TX(%d) NO Desc Avail: %lu\n", |
txr->me, (long)txr->no_desc_avail); |
txr->me, (long)txr->no_desc_avail.ev_count); |
} |
} |
|
|
device_printf(dev,"MBX IRQ Handled: %lu\n", |
device_printf(dev,"MBX IRQ Handled: %lu\n", |
(long)adapter->mbx_irq); |
(long)adapter->link_irq.ev_count); |
return; |
return; |
} |
} |
|
|
static int |
static int |
ixv_sysctl_stats(SYSCTL_HANDLER_ARGS) |
ixv_sysctl_debug(SYSCTLFN_ARGS) |
{ |
|
int error; |
|
int result; |
|
struct adapter *adapter; |
|
|
|
result = -1; |
|
error = sysctl_handle_int(oidp, &result, 0, req); |
|
|
|
if (error || !req->newptr) |
|
return (error); |
|
|
|
if (result == 1) { |
|
adapter = (struct adapter *) arg1; |
|
ixv_print_hw_stats(adapter); |
|
} |
|
return error; |
|
} |
|
|
|
static int |
|
ixv_sysctl_debug(SYSCTL_HANDLER_ARGS) |
|
{ |
{ |
|
struct sysctlnode node; |
int error, result; |
int error, result; |
struct adapter *adapter; |
struct adapter *adapter; |
|
|
result = -1; |
node = *rnode; |
error = sysctl_handle_int(oidp, &result, 0, req); |
adapter = (struct adapter *)node.sysctl_data; |
|
node.sysctl_data = &result; |
if (error || !req->newptr) |
error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
return (error); |
|
|
|
if (result == 1) { |
|
adapter = (struct adapter *) arg1; |
|
ixv_print_debug_info(adapter); |
|
} |
|
return error; |
|
} |
|
|
|
/* |
|
** Set flow control using sysctl: |
|
** Flow control values: |
|
** 0 - off |
|
** 1 - rx pause |
|
** 2 - tx pause |
|
** 3 - full |
|
*/ |
|
static int |
|
ixv_set_flowcntl(SYSCTL_HANDLER_ARGS) |
|
{ |
|
int error; |
|
struct adapter *adapter; |
|
|
|
error = sysctl_handle_int(oidp, &ixv_flow_control, 0, req); |
|
|
|
if (error) |
if (error) |
return (error); |
return error; |
|
|
adapter = (struct adapter *) arg1; |
if (result == 1) |
switch (ixv_flow_control) { |
ixv_print_debug_info(adapter); |
case ixgbe_fc_rx_pause: |
|
case ixgbe_fc_tx_pause: |
|
case ixgbe_fc_full: |
|
adapter->hw.fc.requested_mode = ixv_flow_control; |
|
break; |
|
case ixgbe_fc_none: |
|
default: |
|
adapter->hw.fc.requested_mode = ixgbe_fc_none; |
|
} |
|
|
|
ixgbe_fc_enable(&adapter->hw, 0); |
return 0; |
return error; |
|
} |
} |
|
|
static void |
const struct sysctlnode * |
ixv_add_rx_process_limit(struct adapter *adapter, const char *name, |
ixv_sysctl_instance(struct adapter *adapter) |
const char *description, int *limit, int value) |
|
{ |
{ |
*limit = value; |
const char *dvname; |
SYSCTL_ADD_INT(device_get_sysctl_ctx(adapter->dev), |
struct sysctllog **log; |
SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)), |
int rc; |
OID_AUTO, name, CTLTYPE_INT|CTLFLAG_RW, limit, value, description); |
const struct sysctlnode *rnode; |
|
|
|
log = &adapter->sysctllog; |
|
dvname = device_xname(adapter->dev); |
|
|
|
if ((rc = sysctl_createv(log, 0, NULL, &rnode, |
|
0, CTLTYPE_NODE, dvname, |
|
SYSCTL_DESCR("ixv information and settings"), |
|
NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) |
|
goto err; |
|
|
|
return rnode; |
|
err: |
|
printf("%s: sysctl_createv failed, rc = %d\n", __func__, rc); |
|
return NULL; |
} |
} |
|
|