Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixgbe/if_ix.c
Show All 26 Lines | /****************************************************************************** | ||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
POSSIBILITY OF SUCH DAMAGE. | POSSIBILITY OF SUCH DAMAGE. | ||||
******************************************************************************/ | ******************************************************************************/ | ||||
/*$FreeBSD$*/ | /*$FreeBSD$*/ | ||||
#ifndef KLD_MODULE | |||||
#ifndef IXGBE_STANDALONE_BUILD | #include "opt_iflib.h" | ||||
#include "opt_inet.h" | |||||
#include "opt_inet6.h" | |||||
#include "opt_rss.h" | |||||
#endif | #endif | ||||
#include "ixgbe.h" | #ifdef IFLIB | ||||
#include <dev/ixgbe/iflib_if_ix.c> | |||||
#ifdef RSS | |||||
#include <net/rss_config.h> | |||||
#include <netinet/in_rss.h> | |||||
#endif | |||||
/********************************************************************* | |||||
* Driver version | |||||
*********************************************************************/ | |||||
char ixgbe_driver_version[] = "3.1.13-k"; | |||||
/********************************************************************* | |||||
* PCI Device ID Table | |||||
* | |||||
* Used by probe to select devices to load on | |||||
* Last field stores an index into ixgbe_strings | |||||
* Last entry must be all 0s | |||||
* | |||||
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | |||||
*********************************************************************/ | |||||
static ixgbe_vendor_info_t ixgbe_vendor_info_array[] = | |||||
{ | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AF_DUAL_PORT, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AF_SINGLE_PORT, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598EB_CX4, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AT, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AT2, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598_DA_DUAL_PORT, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598_CX4_DUAL_PORT, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598EB_XF_LR, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598EB_SFP_LOM, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_KX4, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_KX4_MEZZ, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_XAUI_LOM, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_CX4, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_T3_LOM, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_COMBO_BACKPLANE, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_BACKPLANE_FCOE, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_SF2, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_FCOE, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599EN_SFP, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_SF_QP, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_QSFP_SF_QP, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540T, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540T1, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550T, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550T1, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_KR, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_KX4, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_10G_T, 0, 0, 0}, | |||||
{IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_SFP, 0, 0, 0}, | |||||
/* required last entry */ | |||||
{0, 0, 0, 0, 0} | |||||
}; | |||||
/********************************************************************* | |||||
* Table of branding strings | |||||
*********************************************************************/ | |||||
static char *ixgbe_strings[] = { | |||||
"Intel(R) PRO/10GbE PCI-Express Network Driver" | |||||
}; | |||||
/********************************************************************* | |||||
* Function prototypes | |||||
*********************************************************************/ | |||||
static int ixgbe_probe(device_t); | |||||
static int ixgbe_attach(device_t); | |||||
static int ixgbe_detach(device_t); | |||||
static int ixgbe_shutdown(device_t); | |||||
static int ixgbe_suspend(device_t); | |||||
static int ixgbe_resume(device_t); | |||||
static int ixgbe_ioctl(struct ifnet *, u_long, caddr_t); | |||||
static void ixgbe_init(void *); | |||||
static void ixgbe_init_locked(struct adapter *); | |||||
static void ixgbe_stop(void *); | |||||
#if __FreeBSD_version >= 1100036 | |||||
static uint64_t ixgbe_get_counter(struct ifnet *, ift_counter); | |||||
#endif | |||||
static void ixgbe_add_media_types(struct adapter *); | |||||
static void ixgbe_media_status(struct ifnet *, struct ifmediareq *); | |||||
static int ixgbe_media_change(struct ifnet *); | |||||
static void ixgbe_identify_hardware(struct adapter *); | |||||
static int ixgbe_allocate_pci_resources(struct adapter *); | |||||
static void ixgbe_get_slot_info(struct adapter *); | |||||
static int ixgbe_allocate_msix(struct adapter *); | |||||
static int ixgbe_allocate_legacy(struct adapter *); | |||||
static int ixgbe_setup_msix(struct adapter *); | |||||
static void ixgbe_free_pci_resources(struct adapter *); | |||||
static void ixgbe_local_timer(void *); | |||||
static int ixgbe_setup_interface(device_t, struct adapter *); | |||||
static void ixgbe_config_gpie(struct adapter *); | |||||
static void ixgbe_config_dmac(struct adapter *); | |||||
static void ixgbe_config_delay_values(struct adapter *); | |||||
static void ixgbe_config_link(struct adapter *); | |||||
static void ixgbe_check_wol_support(struct adapter *); | |||||
static int ixgbe_setup_low_power_mode(struct adapter *); | |||||
static void ixgbe_rearm_queues(struct adapter *, u64); | |||||
static void ixgbe_initialize_transmit_units(struct adapter *); | |||||
static void ixgbe_initialize_receive_units(struct adapter *); | |||||
static void ixgbe_enable_rx_drop(struct adapter *); | |||||
static void ixgbe_disable_rx_drop(struct adapter *); | |||||
static void ixgbe_initialize_rss_mapping(struct adapter *); | |||||
static void ixgbe_enable_intr(struct adapter *); | |||||
static void ixgbe_disable_intr(struct adapter *); | |||||
static void ixgbe_update_stats_counters(struct adapter *); | |||||
static void ixgbe_set_promisc(struct adapter *); | |||||
static void ixgbe_set_multi(struct adapter *); | |||||
static void ixgbe_update_link_status(struct adapter *); | |||||
static void ixgbe_set_ivar(struct adapter *, u8, u8, s8); | |||||
static void ixgbe_configure_ivars(struct adapter *); | |||||
static u8 * ixgbe_mc_array_itr(struct ixgbe_hw *, u8 **, u32 *); | |||||
static void ixgbe_setup_vlan_hw_support(struct adapter *); | |||||
static void ixgbe_register_vlan(void *, struct ifnet *, u16); | |||||
static void ixgbe_unregister_vlan(void *, struct ifnet *, u16); | |||||
static void ixgbe_add_device_sysctls(struct adapter *); | |||||
static void ixgbe_add_hw_stats(struct adapter *); | |||||
static int ixgbe_set_flowcntl(struct adapter *, int); | |||||
static int ixgbe_set_advertise(struct adapter *, int); | |||||
/* Sysctl handlers */ | |||||
static void ixgbe_set_sysctl_value(struct adapter *, const char *, | |||||
const char *, int *, int); | |||||
static int ixgbe_sysctl_flowcntl(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_advertise(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_thermal_test(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_dmac(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_phy_temp(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_phy_overtemp_occurred(SYSCTL_HANDLER_ARGS); | |||||
#ifdef IXGBE_DEBUG | |||||
static int ixgbe_sysctl_power_state(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_print_rss_config(SYSCTL_HANDLER_ARGS); | |||||
#endif | |||||
static int ixgbe_sysctl_wol_enable(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_wufc(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_eee_enable(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_eee_negotiated(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_eee_rx_lpi_status(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_eee_tx_lpi_status(SYSCTL_HANDLER_ARGS); | |||||
static int ixgbe_sysctl_eee_tx_lpi_delay(SYSCTL_HANDLER_ARGS); | |||||
/* Support for pluggable optic modules */ | |||||
static bool ixgbe_sfp_probe(struct adapter *); | |||||
static void ixgbe_setup_optics(struct adapter *); | |||||
/* Legacy (single vector interrupt handler */ | |||||
static void ixgbe_legacy_irq(void *); | |||||
/* The MSI/X Interrupt handlers */ | |||||
static void ixgbe_msix_que(void *); | |||||
static void ixgbe_msix_link(void *); | |||||
/* Deferred interrupt tasklets */ | |||||
static void ixgbe_handle_que(void *, int); | |||||
static void ixgbe_handle_link(void *, int); | |||||
static void ixgbe_handle_msf(void *, int); | |||||
static void ixgbe_handle_mod(void *, int); | |||||
static void ixgbe_handle_phy(void *, int); | |||||
#ifdef IXGBE_FDIR | |||||
static void ixgbe_reinit_fdir(void *, int); | |||||
#endif | |||||
#ifdef PCI_IOV | |||||
static void ixgbe_ping_all_vfs(struct adapter *); | |||||
static void ixgbe_handle_mbx(void *, int); | |||||
static int ixgbe_init_iov(device_t, u16, const nvlist_t *); | |||||
static void ixgbe_uninit_iov(device_t); | |||||
static int ixgbe_add_vf(device_t, u16, const nvlist_t *); | |||||
static void ixgbe_initialize_iov(struct adapter *); | |||||
static void ixgbe_recalculate_max_frame(struct adapter *); | |||||
static void ixgbe_init_vf(struct adapter *, struct ixgbe_vf *); | |||||
#endif /* PCI_IOV */ | |||||
/********************************************************************* | |||||
* FreeBSD Device Interface Entry Points | |||||
*********************************************************************/ | |||||
static device_method_t ix_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_probe, ixgbe_probe), | |||||
DEVMETHOD(device_attach, ixgbe_attach), | |||||
DEVMETHOD(device_detach, ixgbe_detach), | |||||
DEVMETHOD(device_shutdown, ixgbe_shutdown), | |||||
DEVMETHOD(device_suspend, ixgbe_suspend), | |||||
DEVMETHOD(device_resume, ixgbe_resume), | |||||
#ifdef PCI_IOV | |||||
DEVMETHOD(pci_iov_init, ixgbe_init_iov), | |||||
DEVMETHOD(pci_iov_uninit, ixgbe_uninit_iov), | |||||
DEVMETHOD(pci_iov_add_vf, ixgbe_add_vf), | |||||
#endif /* PCI_IOV */ | |||||
DEVMETHOD_END | |||||
}; | |||||
static driver_t ix_driver = { | |||||
"ix", ix_methods, sizeof(struct adapter), | |||||
}; | |||||
devclass_t ix_devclass; | |||||
DRIVER_MODULE(ix, pci, ix_driver, ix_devclass, 0, 0); | |||||
MODULE_DEPEND(ix, pci, 1, 1, 1); | |||||
MODULE_DEPEND(ix, ether, 1, 1, 1); | |||||
#ifdef DEV_NETMAP | |||||
MODULE_DEPEND(ix, netmap, 1, 1, 1); | |||||
#endif /* DEV_NETMAP */ | |||||
/* | |||||
** TUNEABLE PARAMETERS: | |||||
*/ | |||||
static SYSCTL_NODE(_hw, OID_AUTO, ix, CTLFLAG_RD, 0, | |||||
"IXGBE driver parameters"); | |||||
/* | |||||
** AIM: Adaptive Interrupt Moderation | |||||
** which means that the interrupt rate | |||||
** is varied over time based on the | |||||
** traffic for that interrupt vector | |||||
*/ | |||||
static int ixgbe_enable_aim = TRUE; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, enable_aim, CTLFLAG_RWTUN, &ixgbe_enable_aim, 0, | |||||
"Enable adaptive interrupt moderation"); | |||||
static int ixgbe_max_interrupt_rate = (4000000 / IXGBE_LOW_LATENCY); | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, max_interrupt_rate, CTLFLAG_RDTUN, | |||||
&ixgbe_max_interrupt_rate, 0, "Maximum interrupts per second"); | |||||
/* How many packets rxeof tries to clean at a time */ | |||||
static int ixgbe_rx_process_limit = 256; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, rx_process_limit, CTLFLAG_RDTUN, | |||||
&ixgbe_rx_process_limit, 0, | |||||
"Maximum number of received packets to process at a time," | |||||
"-1 means unlimited"); | |||||
/* How many packets txeof tries to clean at a time */ | |||||
static int ixgbe_tx_process_limit = 256; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, tx_process_limit, CTLFLAG_RDTUN, | |||||
&ixgbe_tx_process_limit, 0, | |||||
"Maximum number of sent packets to process at a time," | |||||
"-1 means unlimited"); | |||||
/* Flow control setting, default to full */ | |||||
static int ixgbe_flow_control = ixgbe_fc_full; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, flow_control, CTLFLAG_RDTUN, | |||||
&ixgbe_flow_control, 0, "Default flow control used for all adapters"); | |||||
/* Advertise Speed, default to 0 (auto) */ | |||||
static int ixgbe_advertise_speed = 0; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, advertise_speed, CTLFLAG_RDTUN, | |||||
&ixgbe_advertise_speed, 0, "Default advertised speed for all adapters"); | |||||
/* | |||||
** Smart speed setting, default to on | |||||
** this only works as a compile option | |||||
** right now as its during attach, set | |||||
** this to 'ixgbe_smart_speed_off' to | |||||
** disable. | |||||
*/ | |||||
static int ixgbe_smart_speed = ixgbe_smart_speed_on; | |||||
/* | |||||
* MSIX should be the default for best performance, | |||||
* but this allows it to be forced off for testing. | |||||
*/ | |||||
static int ixgbe_enable_msix = 1; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, enable_msix, CTLFLAG_RDTUN, &ixgbe_enable_msix, 0, | |||||
"Enable MSI-X interrupts"); | |||||
/* | |||||
* Number of Queues, can be set to 0, | |||||
* it then autoconfigures based on the | |||||
* number of cpus with a max of 8. This | |||||
* can be overriden manually here. | |||||
*/ | |||||
static int ixgbe_num_queues = 0; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, num_queues, CTLFLAG_RDTUN, &ixgbe_num_queues, 0, | |||||
"Number of queues to configure, 0 indicates autoconfigure"); | |||||
/* | |||||
** Number of TX descriptors per ring, | |||||
** setting higher than RX as this seems | |||||
** the better performing choice. | |||||
*/ | |||||
static int ixgbe_txd = PERFORM_TXD; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, txd, CTLFLAG_RDTUN, &ixgbe_txd, 0, | |||||
"Number of transmit descriptors per queue"); | |||||
/* Number of RX descriptors per ring */ | |||||
static int ixgbe_rxd = PERFORM_RXD; | |||||
SYSCTL_INT(_hw_ix, OID_AUTO, rxd, CTLFLAG_RDTUN, &ixgbe_rxd, 0, | |||||
"Number of receive descriptors per queue"); | |||||
/* | |||||
** Defining this on will allow the use | |||||
** of unsupported SFP+ modules, note that | |||||
** doing so you are on your own :) | |||||
*/ | |||||
static int allow_unsupported_sfp = FALSE; | |||||
TUNABLE_INT("hw.ix.unsupported_sfp", &allow_unsupported_sfp); | |||||
/* Keep running tab on them for sanity check */ | |||||
static int ixgbe_total_ports; | |||||
#ifdef IXGBE_FDIR | |||||
/* | |||||
** Flow Director actually 'steals' | |||||
** part of the packet buffer as its | |||||
** filter pool, this variable controls | |||||
** how much it uses: | |||||
** 0 = 64K, 1 = 128K, 2 = 256K | |||||
*/ | |||||
static int fdir_pballoc = 1; | |||||
#endif | |||||
#ifdef DEV_NETMAP | |||||
/* | |||||
* The #ifdef DEV_NETMAP / #endif blocks in this file are meant to | |||||
* be a reference on how to implement netmap support in a driver. | |||||
* Additional comments are in ixgbe_netmap.h . | |||||
* | |||||
* <dev/netmap/ixgbe_netmap.h> contains functions for netmap support | |||||
* that extend the standard driver. | |||||
*/ | |||||
#include <dev/netmap/ixgbe_netmap.h> | |||||
#endif /* DEV_NETMAP */ | |||||
static MALLOC_DEFINE(M_IXGBE, "ix", "ix driver allocations"); | |||||
/********************************************************************* | |||||
* Device identification routine | |||||
* | |||||
* ixgbe_probe determines if the driver should be loaded on | |||||
* adapter based on PCI vendor/device id of the adapter. | |||||
* | |||||
* return BUS_PROBE_DEFAULT on success, positive on failure | |||||
*********************************************************************/ | |||||
static int | |||||
ixgbe_probe(device_t dev) | |||||
{ | |||||
ixgbe_vendor_info_t *ent; | |||||
u16 pci_vendor_id = 0; | |||||
u16 pci_device_id = 0; | |||||
u16 pci_subvendor_id = 0; | |||||
u16 pci_subdevice_id = 0; | |||||
char adapter_name[256]; | |||||
INIT_DEBUGOUT("ixgbe_probe: begin"); | |||||
pci_vendor_id = pci_get_vendor(dev); | |||||
if (pci_vendor_id != IXGBE_INTEL_VENDOR_ID) | |||||
return (ENXIO); | |||||
pci_device_id = pci_get_device(dev); | |||||
pci_subvendor_id = pci_get_subvendor(dev); | |||||
pci_subdevice_id = pci_get_subdevice(dev); | |||||
ent = ixgbe_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) || | |||||
(ent->subvendor_id == 0)) && | |||||
((pci_subdevice_id == ent->subdevice_id) || | |||||
(ent->subdevice_id == 0))) { | |||||
sprintf(adapter_name, "%s, Version - %s", | |||||
ixgbe_strings[ent->index], | |||||
ixgbe_driver_version); | |||||
device_set_desc_copy(dev, adapter_name); | |||||
++ixgbe_total_ports; | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | |||||
ent++; | |||||
} | |||||
return (ENXIO); | |||||
} | |||||
/********************************************************************* | |||||
* Device initialization routine | |||||
* | |||||
* The attach entry point is called when the driver is being loaded. | |||||
* This routine identifies the type of hardware, allocates all resources | |||||
* and initializes the hardware. | |||||
* | |||||
* return 0 on success, positive on failure | |||||
*********************************************************************/ | |||||
static int | |||||
ixgbe_attach(device_t dev) | |||||
{ | |||||
struct adapter *adapter; | |||||
struct ixgbe_hw *hw; | |||||
int error = 0; | |||||
u16 csum; | |||||
u32 ctrl_ext; | |||||
INIT_DEBUGOUT("ixgbe_attach: begin"); | |||||
/* Allocate, clear, and link in our adapter structure */ | |||||
adapter = device_get_softc(dev); | |||||
adapter->dev = dev; | |||||
hw = &adapter->hw; | |||||
#ifdef DEV_NETMAP | |||||
adapter->init_locked = ixgbe_init_locked; | |||||
adapter->stop_locked = ixgbe_stop; | |||||
#endif | |||||
/* Core Lock Init*/ | |||||
IXGBE_CORE_LOCK_INIT(adapter, device_get_nameunit(dev)); | |||||
/* Set up the timer callout */ | |||||
callout_init_mtx(&adapter->timer, &adapter->core_mtx, 0); | |||||
/* Determine hardware revision */ | |||||
ixgbe_identify_hardware(adapter); | |||||
/* Do base PCI setup - map BAR0 */ | |||||
if (ixgbe_allocate_pci_resources(adapter)) { | |||||
device_printf(dev, "Allocation of PCI resources failed\n"); | |||||
error = ENXIO; | |||||
goto err_out; | |||||
} | |||||
/* Sysctls for limiting the amount of work done in the taskqueues */ | |||||
ixgbe_set_sysctl_value(adapter, "rx_processing_limit", | |||||
"max number of rx packets to process", | |||||
&adapter->rx_process_limit, ixgbe_rx_process_limit); | |||||
ixgbe_set_sysctl_value(adapter, "tx_processing_limit", | |||||
"max number of tx packets to process", | |||||
&adapter->tx_process_limit, ixgbe_tx_process_limit); | |||||
/* Do descriptor calc and sanity checks */ | |||||
if (((ixgbe_txd * sizeof(union ixgbe_adv_tx_desc)) % DBA_ALIGN) != 0 || | |||||
ixgbe_txd < MIN_TXD || ixgbe_txd > MAX_TXD) { | |||||
device_printf(dev, "TXD config issue, using default!\n"); | |||||
adapter->num_tx_desc = DEFAULT_TXD; | |||||
} else | |||||
adapter->num_tx_desc = ixgbe_txd; | |||||
/* | |||||
** With many RX rings it is easy to exceed the | |||||
** system mbuf allocation. Tuning nmbclusters | |||||
** can alleviate this. | |||||
*/ | |||||
if (nmbclusters > 0) { | |||||
int s; | |||||
s = (ixgbe_rxd * adapter->num_queues) * ixgbe_total_ports; | |||||
if (s > nmbclusters) { | |||||
device_printf(dev, "RX Descriptors exceed " | |||||
"system mbuf max, using default instead!\n"); | |||||
ixgbe_rxd = DEFAULT_RXD; | |||||
} | |||||
} | |||||
if (((ixgbe_rxd * sizeof(union ixgbe_adv_rx_desc)) % DBA_ALIGN) != 0 || | |||||
ixgbe_rxd < MIN_RXD || ixgbe_rxd > MAX_RXD) { | |||||
device_printf(dev, "RXD config issue, using default!\n"); | |||||
adapter->num_rx_desc = DEFAULT_RXD; | |||||
} else | |||||
adapter->num_rx_desc = ixgbe_rxd; | |||||
/* Allocate our TX/RX Queues */ | |||||
if (ixgbe_allocate_queues(adapter)) { | |||||
error = ENOMEM; | |||||
goto err_out; | |||||
} | |||||
/* Allocate multicast array memory. */ | |||||
adapter->mta = malloc(sizeof(*adapter->mta) * | |||||
MAX_NUM_MULTICAST_ADDRESSES, M_DEVBUF, M_NOWAIT); | |||||
if (adapter->mta == NULL) { | |||||
device_printf(dev, "Can not allocate multicast setup array\n"); | |||||
error = ENOMEM; | |||||
goto err_late; | |||||
} | |||||
/* Initialize the shared code */ | |||||
hw->allow_unsupported_sfp = allow_unsupported_sfp; | |||||
error = ixgbe_init_shared_code(hw); | |||||
if (error == IXGBE_ERR_SFP_NOT_PRESENT) { | |||||
/* | |||||
** No optics in this port, set up | |||||
** so the timer routine will probe | |||||
** for later insertion. | |||||
*/ | |||||
adapter->sfp_probe = TRUE; | |||||
error = 0; | |||||
} else if (error == IXGBE_ERR_SFP_NOT_SUPPORTED) { | |||||
device_printf(dev, "Unsupported SFP+ module detected!\n"); | |||||
error = EIO; | |||||
goto err_late; | |||||
} else if (error) { | |||||
device_printf(dev, "Unable to initialize the shared code\n"); | |||||
error = EIO; | |||||
goto err_late; | |||||
} | |||||
/* Make sure we have a good EEPROM before we read from it */ | |||||
if (ixgbe_validate_eeprom_checksum(&adapter->hw, &csum) < 0) { | |||||
device_printf(dev, "The EEPROM Checksum Is Not Valid\n"); | |||||
error = EIO; | |||||
goto err_late; | |||||
} | |||||
error = ixgbe_init_hw(hw); | |||||
switch (error) { | |||||
case IXGBE_ERR_EEPROM_VERSION: | |||||
device_printf(dev, "This device is a pre-production adapter/" | |||||
"LOM. Please be aware there may be issues associated " | |||||
"with your hardware.\nIf you are experiencing problems " | |||||
"please contact your Intel or hardware representative " | |||||
"who provided you with this hardware.\n"); | |||||
break; | |||||
case IXGBE_ERR_SFP_NOT_SUPPORTED: | |||||
device_printf(dev, "Unsupported SFP+ Module\n"); | |||||
error = EIO; | |||||
goto err_late; | |||||
case IXGBE_ERR_SFP_NOT_PRESENT: | |||||
device_printf(dev, "No SFP+ Module found\n"); | |||||
/* falls thru */ | |||||
default: | |||||
break; | |||||
} | |||||
/* hw.ix defaults init */ | |||||
ixgbe_set_advertise(adapter, ixgbe_advertise_speed); | |||||
ixgbe_set_flowcntl(adapter, ixgbe_flow_control); | |||||
adapter->enable_aim = ixgbe_enable_aim; | |||||
if ((adapter->msix > 1) && (ixgbe_enable_msix)) | |||||
error = ixgbe_allocate_msix(adapter); | |||||
else | |||||
error = ixgbe_allocate_legacy(adapter); | |||||
if (error) | |||||
goto err_late; | |||||
/* Enable the optics for 82599 SFP+ fiber */ | |||||
ixgbe_enable_tx_laser(hw); | |||||
/* Enable power to the phy. */ | |||||
ixgbe_set_phy_power(hw, TRUE); | |||||
/* Setup OS specific network interface */ | |||||
if (ixgbe_setup_interface(dev, adapter) != 0) | |||||
goto err_late; | |||||
/* Initialize statistics */ | |||||
ixgbe_update_stats_counters(adapter); | |||||
/* Register for VLAN events */ | |||||
adapter->vlan_attach = EVENTHANDLER_REGISTER(vlan_config, | |||||
ixgbe_register_vlan, adapter, EVENTHANDLER_PRI_FIRST); | |||||
adapter->vlan_detach = EVENTHANDLER_REGISTER(vlan_unconfig, | |||||
ixgbe_unregister_vlan, adapter, EVENTHANDLER_PRI_FIRST); | |||||
/* Check PCIE slot type/speed/width */ | |||||
ixgbe_get_slot_info(adapter); | |||||
/* Set an initial default flow control & dmac value */ | |||||
adapter->fc = ixgbe_fc_full; | |||||
adapter->dmac = 0; | |||||
adapter->eee_enabled = 0; | |||||
#ifdef PCI_IOV | |||||
if ((hw->mac.type != ixgbe_mac_82598EB) && (adapter->msix > 1)) { | |||||
nvlist_t *pf_schema, *vf_schema; | |||||
hw->mbx.ops.init_params(hw); | |||||
pf_schema = pci_iov_schema_alloc_node(); | |||||
vf_schema = pci_iov_schema_alloc_node(); | |||||
pci_iov_schema_add_unicast_mac(vf_schema, "mac-addr", 0, NULL); | |||||
pci_iov_schema_add_bool(vf_schema, "mac-anti-spoof", | |||||
IOV_SCHEMA_HASDEFAULT, TRUE); | |||||
pci_iov_schema_add_bool(vf_schema, "allow-set-mac", | |||||
IOV_SCHEMA_HASDEFAULT, FALSE); | |||||
pci_iov_schema_add_bool(vf_schema, "allow-promisc", | |||||
IOV_SCHEMA_HASDEFAULT, FALSE); | |||||
error = pci_iov_attach(dev, pf_schema, vf_schema); | |||||
if (error != 0) { | |||||
device_printf(dev, | |||||
"Error %d setting up SR-IOV\n", error); | |||||
} | |||||
} | |||||
#endif /* PCI_IOV */ | |||||
/* Check for certain supported features */ | |||||
ixgbe_check_wol_support(adapter); | |||||
/* Add sysctls */ | |||||
ixgbe_add_device_sysctls(adapter); | |||||
ixgbe_add_hw_stats(adapter); | |||||
/* let hardware know driver is loaded */ | |||||
ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT); | |||||
ctrl_ext |= IXGBE_CTRL_EXT_DRV_LOAD; | |||||
IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext); | |||||
#ifdef DEV_NETMAP | |||||
ixgbe_netmap_attach(adapter); | |||||
#endif /* DEV_NETMAP */ | |||||
INIT_DEBUGOUT("ixgbe_attach: end"); | |||||
return (0); | |||||
err_late: | |||||
ixgbe_free_transmit_structures(adapter); | |||||
ixgbe_free_receive_structures(adapter); | |||||
err_out: | |||||
if (adapter->ifp != NULL) | |||||
if_free(adapter->ifp); | |||||
ixgbe_free_pci_resources(adapter); | |||||
free(adapter->mta, M_DEVBUF); | |||||
return (error); | |||||
} | |||||
/********************************************************************* | |||||
* Device removal routine | |||||
* | |||||
* The detach entry point is called when the driver is being removed. | |||||
* This routine stops the adapter and deallocates all the resources | |||||
* that were allocated for driver operation. | |||||
* | |||||
* return 0 on success, positive on failure | |||||
*********************************************************************/ | |||||
static int | |||||
ixgbe_detach(device_t dev) | |||||
{ | |||||
struct adapter *adapter = device_get_softc(dev); | |||||
struct ix_queue *que = adapter->queues; | |||||
struct tx_ring *txr = adapter->tx_rings; | |||||
u32 ctrl_ext; | |||||
INIT_DEBUGOUT("ixgbe_detach: begin"); | |||||
/* Make sure VLANS are not using driver */ | |||||
if (adapter->ifp->if_vlantrunk != NULL) { | |||||
device_printf(dev,"Vlan in use, detach first\n"); | |||||
return (EBUSY); | |||||
} | |||||
#ifdef PCI_IOV | |||||
if (pci_iov_detach(dev) != 0) { | |||||
device_printf(dev, "SR-IOV in use; detach first.\n"); | |||||
return (EBUSY); | |||||
} | |||||
#endif /* PCI_IOV */ | |||||
ether_ifdetach(adapter->ifp); | |||||
/* Stop the adapter */ | |||||
IXGBE_CORE_LOCK(adapter); | |||||
ixgbe_setup_low_power_mode(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
for (int i = 0; i < adapter->num_queues; i++, que++, txr++) { | |||||
if (que->tq) { | |||||
#ifndef IXGBE_LEGACY_TX | |||||
taskqueue_drain(que->tq, &txr->txq_task); | |||||
#endif | |||||
taskqueue_drain(que->tq, &que->que_task); | |||||
taskqueue_free(que->tq); | |||||
} | |||||
} | |||||
/* Drain the Link queue */ | |||||
if (adapter->tq) { | |||||
taskqueue_drain(adapter->tq, &adapter->link_task); | |||||
taskqueue_drain(adapter->tq, &adapter->mod_task); | |||||
taskqueue_drain(adapter->tq, &adapter->msf_task); | |||||
#ifdef PCI_IOV | |||||
taskqueue_drain(adapter->tq, &adapter->mbx_task); | |||||
#endif | |||||
taskqueue_drain(adapter->tq, &adapter->phy_task); | |||||
#ifdef IXGBE_FDIR | |||||
taskqueue_drain(adapter->tq, &adapter->fdir_task); | |||||
#endif | |||||
taskqueue_free(adapter->tq); | |||||
} | |||||
/* let hardware know driver is unloading */ | |||||
ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT); | |||||
ctrl_ext &= ~IXGBE_CTRL_EXT_DRV_LOAD; | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT, ctrl_ext); | |||||
/* Unregister VLAN events */ | |||||
if (adapter->vlan_attach != NULL) | |||||
EVENTHANDLER_DEREGISTER(vlan_config, adapter->vlan_attach); | |||||
if (adapter->vlan_detach != NULL) | |||||
EVENTHANDLER_DEREGISTER(vlan_unconfig, adapter->vlan_detach); | |||||
callout_drain(&adapter->timer); | |||||
#ifdef DEV_NETMAP | |||||
netmap_detach(adapter->ifp); | |||||
#endif /* DEV_NETMAP */ | |||||
ixgbe_free_pci_resources(adapter); | |||||
bus_generic_detach(dev); | |||||
if_free(adapter->ifp); | |||||
ixgbe_free_transmit_structures(adapter); | |||||
ixgbe_free_receive_structures(adapter); | |||||
free(adapter->mta, M_DEVBUF); | |||||
IXGBE_CORE_LOCK_DESTROY(adapter); | |||||
return (0); | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Shutdown entry point | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixgbe_shutdown(device_t dev) | |||||
{ | |||||
struct adapter *adapter = device_get_softc(dev); | |||||
int error = 0; | |||||
INIT_DEBUGOUT("ixgbe_shutdown: begin"); | |||||
IXGBE_CORE_LOCK(adapter); | |||||
error = ixgbe_setup_low_power_mode(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return (error); | |||||
} | |||||
/** | |||||
* Methods for going from: | |||||
* D0 -> D3: ixgbe_suspend | |||||
* D3 -> D0: ixgbe_resume | |||||
*/ | |||||
static int | |||||
ixgbe_suspend(device_t dev) | |||||
{ | |||||
struct adapter *adapter = device_get_softc(dev); | |||||
int error = 0; | |||||
INIT_DEBUGOUT("ixgbe_suspend: begin"); | |||||
IXGBE_CORE_LOCK(adapter); | |||||
error = ixgbe_setup_low_power_mode(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return (error); | |||||
} | |||||
static int | |||||
ixgbe_resume(device_t dev) | |||||
{ | |||||
struct adapter *adapter = device_get_softc(dev); | |||||
struct ifnet *ifp = adapter->ifp; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 wus; | |||||
INIT_DEBUGOUT("ixgbe_resume: begin"); | |||||
IXGBE_CORE_LOCK(adapter); | |||||
/* Read & clear WUS register */ | |||||
wus = IXGBE_READ_REG(hw, IXGBE_WUS); | |||||
if (wus) | |||||
device_printf(dev, "Woken up by (WUS): %#010x\n", | |||||
IXGBE_READ_REG(hw, IXGBE_WUS)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_WUS, 0xffffffff); | |||||
/* And clear WUFC until next low-power transition */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_WUFC, 0); | |||||
/* | |||||
* Required after D3->D0 transition; | |||||
* will re-advertise all previous advertised speeds | |||||
*/ | |||||
if (ifp->if_flags & IFF_UP) | |||||
ixgbe_init_locked(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return (0); | |||||
} | |||||
/********************************************************************* | |||||
* Ioctl entry point | |||||
* | |||||
* ixgbe_ioctl is called when the user wants to configure the | |||||
* interface. | |||||
* | |||||
* return 0 on success, positive on failure | |||||
**********************************************************************/ | |||||
static int | |||||
ixgbe_ioctl(struct ifnet * ifp, u_long command, caddr_t data) | |||||
{ | |||||
struct adapter *adapter = ifp->if_softc; | |||||
struct ifreq *ifr = (struct ifreq *) data; | |||||
#if defined(INET) || defined(INET6) | |||||
struct ifaddr *ifa = (struct ifaddr *)data; | |||||
#endif | |||||
int error = 0; | |||||
bool avoid_reset = FALSE; | |||||
switch (command) { | |||||
case SIOCSIFADDR: | |||||
#ifdef INET | |||||
if (ifa->ifa_addr->sa_family == AF_INET) | |||||
avoid_reset = TRUE; | |||||
#endif | |||||
#ifdef INET6 | |||||
if (ifa->ifa_addr->sa_family == AF_INET6) | |||||
avoid_reset = TRUE; | |||||
#endif | |||||
/* | |||||
** Calling init results in link renegotiation, | |||||
** so we avoid doing it when possible. | |||||
*/ | |||||
if (avoid_reset) { | |||||
ifp->if_flags |= IFF_UP; | |||||
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) | |||||
ixgbe_init(adapter); | |||||
#ifdef INET | |||||
if (!(ifp->if_flags & IFF_NOARP)) | |||||
arp_ifinit(ifp, ifa); | |||||
#endif | |||||
} else | |||||
error = ether_ioctl(ifp, command, data); | |||||
break; | |||||
case SIOCSIFMTU: | |||||
IOCTL_DEBUGOUT("ioctl: SIOCSIFMTU (Set Interface MTU)"); | |||||
if (ifr->ifr_mtu > IXGBE_MAX_MTU) { | |||||
error = EINVAL; | |||||
} else { | |||||
IXGBE_CORE_LOCK(adapter); | |||||
ifp->if_mtu = ifr->ifr_mtu; | |||||
adapter->max_frame_size = | |||||
ifp->if_mtu + IXGBE_MTU_HDR; | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | |||||
ixgbe_init_locked(adapter); | |||||
#ifdef PCI_IOV | |||||
ixgbe_recalculate_max_frame(adapter); | |||||
#endif | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
break; | |||||
case SIOCSIFFLAGS: | |||||
IOCTL_DEBUGOUT("ioctl: SIOCSIFFLAGS (Set Interface Flags)"); | |||||
IXGBE_CORE_LOCK(adapter); | |||||
if (ifp->if_flags & IFF_UP) { | |||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING)) { | |||||
if ((ifp->if_flags ^ adapter->if_flags) & | |||||
(IFF_PROMISC | IFF_ALLMULTI)) { | |||||
ixgbe_set_promisc(adapter); | |||||
} | |||||
} else | |||||
ixgbe_init_locked(adapter); | |||||
} else | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | |||||
ixgbe_stop(adapter); | |||||
adapter->if_flags = ifp->if_flags; | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
break; | |||||
case SIOCADDMULTI: | |||||
case SIOCDELMULTI: | |||||
IOCTL_DEBUGOUT("ioctl: SIOC(ADD|DEL)MULTI"); | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
IXGBE_CORE_LOCK(adapter); | |||||
ixgbe_disable_intr(adapter); | |||||
ixgbe_set_multi(adapter); | |||||
ixgbe_enable_intr(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
break; | |||||
case SIOCSIFMEDIA: | |||||
case SIOCGIFMEDIA: | |||||
IOCTL_DEBUGOUT("ioctl: SIOCxIFMEDIA (Get/Set Interface Media)"); | |||||
error = ifmedia_ioctl(ifp, ifr, &adapter->media, command); | |||||
break; | |||||
case SIOCSIFCAP: | |||||
{ | |||||
IOCTL_DEBUGOUT("ioctl: SIOCSIFCAP (Set Capabilities)"); | |||||
int mask = ifr->ifr_reqcap ^ ifp->if_capenable; | |||||
if (!mask) | |||||
break; | |||||
/* HW cannot turn these on/off separately */ | |||||
if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { | |||||
ifp->if_capenable ^= IFCAP_RXCSUM; | |||||
ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; | |||||
} | |||||
if (mask & IFCAP_TXCSUM) | |||||
ifp->if_capenable ^= IFCAP_TXCSUM; | |||||
if (mask & IFCAP_TXCSUM_IPV6) | |||||
ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; | |||||
if (mask & IFCAP_TSO4) | |||||
ifp->if_capenable ^= IFCAP_TSO4; | |||||
if (mask & IFCAP_TSO6) | |||||
ifp->if_capenable ^= IFCAP_TSO6; | |||||
if (mask & IFCAP_LRO) | |||||
ifp->if_capenable ^= IFCAP_LRO; | |||||
if (mask & IFCAP_VLAN_HWTAGGING) | |||||
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; | |||||
if (mask & IFCAP_VLAN_HWFILTER) | |||||
ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; | |||||
if (mask & IFCAP_VLAN_HWTSO) | |||||
ifp->if_capenable ^= IFCAP_VLAN_HWTSO; | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
IXGBE_CORE_LOCK(adapter); | |||||
ixgbe_init_locked(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
VLAN_CAPABILITIES(ifp); | |||||
break; | |||||
} | |||||
#if __FreeBSD_version >= 1100036 | |||||
case SIOCGI2C: | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ifi2creq i2c; | |||||
int i; | |||||
IOCTL_DEBUGOUT("ioctl: SIOCGI2C (Get I2C Data)"); | |||||
error = copyin(ifr->ifr_data, &i2c, sizeof(i2c)); | |||||
if (error != 0) | |||||
break; | |||||
if (i2c.dev_addr != 0xA0 && i2c.dev_addr != 0xA2) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
if (i2c.len > sizeof(i2c.data)) { | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
for (i = 0; i < i2c.len; i++) | |||||
hw->phy.ops.read_i2c_byte(hw, i2c.offset + i, | |||||
i2c.dev_addr, &i2c.data[i]); | |||||
error = copyout(&i2c, ifr->ifr_data, sizeof(i2c)); | |||||
break; | |||||
} | |||||
#endif | |||||
default: | |||||
IOCTL_DEBUGOUT1("ioctl: UNKNOWN (0x%X)\n", (int)command); | |||||
error = ether_ioctl(ifp, command, data); | |||||
break; | |||||
} | |||||
return (error); | |||||
} | |||||
/* | |||||
* Set the various hardware offload abilities. | |||||
* | |||||
* This takes the ifnet's if_capenable flags (e.g. set by the user using | |||||
* ifconfig) and indicates to the OS via the ifnet's if_hwassist field what | |||||
* mbuf offload flags the driver will understand. | |||||
*/ | |||||
static void | |||||
ixgbe_set_if_hwassist(struct adapter *adapter) | |||||
{ | |||||
struct ifnet *ifp = adapter->ifp; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
ifp->if_hwassist = 0; | |||||
#if __FreeBSD_version >= 1000000 | |||||
if (ifp->if_capenable & IFCAP_TSO4) | |||||
ifp->if_hwassist |= CSUM_IP_TSO; | |||||
if (ifp->if_capenable & IFCAP_TSO6) | |||||
ifp->if_hwassist |= CSUM_IP6_TSO; | |||||
if (ifp->if_capenable & IFCAP_TXCSUM) { | |||||
ifp->if_hwassist |= (CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP); | |||||
if (hw->mac.type != ixgbe_mac_82598EB) | |||||
ifp->if_hwassist |= CSUM_IP_SCTP; | |||||
} | |||||
if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) { | |||||
ifp->if_hwassist |= (CSUM_IP6_UDP | CSUM_IP6_TCP); | |||||
if (hw->mac.type != ixgbe_mac_82598EB) | |||||
ifp->if_hwassist |= CSUM_IP6_SCTP; | |||||
} | |||||
#else | #else | ||||
if (ifp->if_capenable & IFCAP_TSO) | #include <dev/ixgbe/legacy_if_ix.c> | ||||
ifp->if_hwassist |= CSUM_TSO; | |||||
if (ifp->if_capenable & IFCAP_TXCSUM) { | |||||
ifp->if_hwassist |= (CSUM_TCP | CSUM_UDP); | |||||
if (hw->mac.type != ixgbe_mac_82598EB) | |||||
ifp->if_hwassist |= CSUM_SCTP; | |||||
} | |||||
#endif | #endif | ||||
} | |||||
/********************************************************************* | |||||
* Init entry point | |||||
* | |||||
* This routine is used in two ways. It is used by the stack as | |||||
* init entry point in network interface structure. It is also used | |||||
* by the driver as a hw/sw initialization routine to get to a | |||||
* consistent state. | |||||
* | |||||
* return 0 on success, positive on failure | |||||
**********************************************************************/ | |||||
#define IXGBE_MHADD_MFS_SHIFT 16 | |||||
static void | |||||
ixgbe_init_locked(struct adapter *adapter) | |||||
{ | |||||
struct ifnet *ifp = adapter->ifp; | |||||
device_t dev = adapter->dev; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct tx_ring *txr; | |||||
struct rx_ring *rxr; | |||||
u32 txdctl, mhadd; | |||||
u32 rxdctl, rxctrl; | |||||
int err = 0; | |||||
#ifdef PCI_IOV | |||||
enum ixgbe_iov_mode mode; | |||||
#endif | |||||
mtx_assert(&adapter->core_mtx, MA_OWNED); | |||||
INIT_DEBUGOUT("ixgbe_init_locked: begin"); | |||||
hw->adapter_stopped = FALSE; | |||||
ixgbe_stop_adapter(hw); | |||||
callout_stop(&adapter->timer); | |||||
#ifdef PCI_IOV | |||||
mode = ixgbe_get_iov_mode(adapter); | |||||
adapter->pool = ixgbe_max_vfs(mode); | |||||
/* Queue indices may change with IOV mode */ | |||||
for (int i = 0; i < adapter->num_queues; i++) { | |||||
adapter->rx_rings[i].me = ixgbe_pf_que_index(mode, i); | |||||
adapter->tx_rings[i].me = ixgbe_pf_que_index(mode, i); | |||||
} | |||||
#endif | |||||
/* reprogram the RAR[0] in case user changed it. */ | |||||
ixgbe_set_rar(hw, 0, hw->mac.addr, adapter->pool, IXGBE_RAH_AV); | |||||
/* Get the latest mac address, User can use a LAA */ | |||||
bcopy(IF_LLADDR(ifp), hw->mac.addr, IXGBE_ETH_LENGTH_OF_ADDRESS); | |||||
ixgbe_set_rar(hw, 0, hw->mac.addr, adapter->pool, 1); | |||||
hw->addr_ctrl.rar_used_count = 1; | |||||
/* Set hardware offload abilities from ifnet flags */ | |||||
ixgbe_set_if_hwassist(adapter); | |||||
/* Prepare transmit descriptors and buffers */ | |||||
if (ixgbe_setup_transmit_structures(adapter)) { | |||||
device_printf(dev, "Could not setup transmit structures\n"); | |||||
ixgbe_stop(adapter); | |||||
return; | |||||
} | |||||
ixgbe_init_hw(hw); | |||||
#ifdef PCI_IOV | |||||
ixgbe_initialize_iov(adapter); | |||||
#endif | |||||
ixgbe_initialize_transmit_units(adapter); | |||||
/* Setup Multicast table */ | |||||
ixgbe_set_multi(adapter); | |||||
/* Determine the correct mbuf pool, based on frame size */ | |||||
if (adapter->max_frame_size <= MCLBYTES) | |||||
adapter->rx_mbuf_sz = MCLBYTES; | |||||
else | |||||
adapter->rx_mbuf_sz = MJUMPAGESIZE; | |||||
/* Prepare receive descriptors and buffers */ | |||||
if (ixgbe_setup_receive_structures(adapter)) { | |||||
device_printf(dev, "Could not setup receive structures\n"); | |||||
ixgbe_stop(adapter); | |||||
return; | |||||
} | |||||
/* Configure RX settings */ | |||||
ixgbe_initialize_receive_units(adapter); | |||||
/* Enable SDP & MSIX interrupts based on adapter */ | |||||
ixgbe_config_gpie(adapter); | |||||
/* Set MTU size */ | |||||
if (ifp->if_mtu > ETHERMTU) { | |||||
/* aka IXGBE_MAXFRS on 82599 and newer */ | |||||
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); | |||||
} | |||||
/* Now enable all the queues */ | |||||
for (int i = 0; i < adapter->num_queues; i++) { | |||||
txr = &adapter->tx_rings[i]; | |||||
txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(txr->me)); | |||||
txdctl |= IXGBE_TXDCTL_ENABLE; | |||||
/* Set WTHRESH to 8, burst writeback */ | |||||
txdctl |= (8 << 16); | |||||
/* | |||||
* When the internal queue falls below PTHRESH (32), | |||||
* start prefetching as long as there are at least | |||||
* HTHRESH (1) buffers ready. The values are taken | |||||
* from the Intel linux driver 3.8.21. | |||||
* Prefetching enables tx line rate even with 1 queue. | |||||
*/ | |||||
txdctl |= (32 << 0) | (1 << 8); | |||||
IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(txr->me), txdctl); | |||||
} | |||||
for (int i = 0, j = 0; i < adapter->num_queues; i++) { | |||||
rxr = &adapter->rx_rings[i]; | |||||
rxdctl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me)); | |||||
if (hw->mac.type == ixgbe_mac_82598EB) { | |||||
/* | |||||
** PTHRESH = 21 | |||||
** HTHRESH = 4 | |||||
** WTHRESH = 8 | |||||
*/ | |||||
rxdctl &= ~0x3FFFFF; | |||||
rxdctl |= 0x080420; | |||||
} | |||||
rxdctl |= IXGBE_RXDCTL_ENABLE; | |||||
IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me), rxdctl); | |||||
for (; j < 10; j++) { | |||||
if (IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me)) & | |||||
IXGBE_RXDCTL_ENABLE) | |||||
break; | |||||
else | |||||
msec_delay(1); | |||||
} | |||||
wmb(); | |||||
#ifdef DEV_NETMAP | |||||
/* | |||||
* In netmap mode, we must preserve the buffers made | |||||
* available to userspace before the if_init() | |||||
* (this is true by default on the TX side, because | |||||
* init makes all buffers available to userspace). | |||||
* | |||||
* netmap_reset() and the device specific routines | |||||
* (e.g. ixgbe_setup_receive_rings()) map these | |||||
* buffers at the end of the NIC ring, so here we | |||||
* must set the RDT (tail) register to make sure | |||||
* they are not overwritten. | |||||
* | |||||
* In this driver the NIC ring starts at RDH = 0, | |||||
* RDT points to the last slot available for reception (?), | |||||
* so RDT = num_rx_desc - 1 means the whole ring is available. | |||||
*/ | |||||
if (ifp->if_capenable & IFCAP_NETMAP) { | |||||
struct netmap_adapter *na = NA(adapter->ifp); | |||||
struct netmap_kring *kring = &na->rx_rings[i]; | |||||
int t = na->num_rx_desc - 1 - nm_kr_rxspace(kring); | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDT(rxr->me), t); | |||||
} else | |||||
#endif /* DEV_NETMAP */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDT(rxr->me), adapter->num_rx_desc - 1); | |||||
} | |||||
/* Enable Receive engine */ | |||||
rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL); | |||||
if (hw->mac.type == ixgbe_mac_82598EB) | |||||
rxctrl |= IXGBE_RXCTRL_DMBYPS; | |||||
rxctrl |= IXGBE_RXCTRL_RXEN; | |||||
ixgbe_enable_rx_dma(hw, rxctrl); | |||||
callout_reset(&adapter->timer, hz, ixgbe_local_timer, adapter); | |||||
/* Set up MSI/X routing */ | |||||
if (ixgbe_enable_msix) { | |||||
ixgbe_configure_ivars(adapter); | |||||
/* Set up auto-mask */ | |||||
if (hw->mac.type == ixgbe_mac_82598EB) | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE); | |||||
else { | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(0), 0xFFFFFFFF); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(1), 0xFFFFFFFF); | |||||
} | |||||
} else { /* Simple settings for Legacy/MSI */ | |||||
ixgbe_set_ivar(adapter, 0, 0, 0); | |||||
ixgbe_set_ivar(adapter, 0, 0, 1); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE); | |||||
} | |||||
#ifdef IXGBE_FDIR | |||||
/* Init Flow director */ | |||||
if (hw->mac.type != ixgbe_mac_82598EB) { | |||||
u32 hdrm = 32 << fdir_pballoc; | |||||
hw->mac.ops.setup_rxpba(hw, 0, hdrm, PBA_STRATEGY_EQUAL); | |||||
ixgbe_init_fdir_signature_82599(&adapter->hw, fdir_pballoc); | |||||
} | |||||
#endif | |||||
/* | |||||
* Check on any SFP devices that | |||||
* need to be kick-started | |||||
*/ | |||||
if (hw->phy.type == ixgbe_phy_none) { | |||||
err = hw->phy.ops.identify(hw); | |||||
if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { | |||||
device_printf(dev, | |||||
"Unsupported SFP+ module type was detected.\n"); | |||||
return; | |||||
} | |||||
} | |||||
/* Set moderation on the Link interrupt */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_EITR(adapter->vector), IXGBE_LINK_ITR); | |||||
/* Configure Energy Efficient Ethernet for supported devices */ | |||||
if (hw->mac.ops.setup_eee) { | |||||
err = hw->mac.ops.setup_eee(hw, adapter->eee_enabled); | |||||
if (err) | |||||
device_printf(dev, "Error setting up EEE: %d\n", err); | |||||
} | |||||
/* Enable power to the phy. */ | |||||
ixgbe_set_phy_power(hw, TRUE); | |||||
/* Config/Enable Link */ | |||||
ixgbe_config_link(adapter); | |||||
/* Hardware Packet Buffer & Flow Control setup */ | |||||
ixgbe_config_delay_values(adapter); | |||||
/* Initialize the FC settings */ | |||||
ixgbe_start_hw(hw); | |||||
/* Set up VLAN support and filter */ | |||||
ixgbe_setup_vlan_hw_support(adapter); | |||||
/* Setup DMA Coalescing */ | |||||
ixgbe_config_dmac(adapter); | |||||
/* And now turn on interrupts */ | |||||
ixgbe_enable_intr(adapter); | |||||
#ifdef PCI_IOV | |||||
/* Enable the use of the MBX by the VF's */ | |||||
{ | |||||
u32 reg = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT); | |||||
reg |= IXGBE_CTRL_EXT_PFRSTD; | |||||
IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, reg); | |||||
} | |||||
#endif | |||||
/* Now inform the stack we're ready */ | |||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | |||||
return; | |||||
} | |||||
static void | |||||
ixgbe_init(void *arg) | |||||
{ | |||||
struct adapter *adapter = arg; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
ixgbe_init_locked(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return; | |||||
} | |||||
static void | |||||
ixgbe_config_gpie(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 gpie; | |||||
gpie = IXGBE_READ_REG(hw, IXGBE_GPIE); | |||||
/* Fan Failure Interrupt */ | |||||
if (hw->device_id == IXGBE_DEV_ID_82598AT) | |||||
gpie |= IXGBE_SDP1_GPIEN; | |||||
/* | |||||
* Module detection (SDP2) | |||||
* Media ready (SDP1) | |||||
*/ | |||||
if (hw->mac.type == ixgbe_mac_82599EB) { | |||||
gpie |= IXGBE_SDP2_GPIEN; | |||||
if (hw->device_id != IXGBE_DEV_ID_82599_QSFP_SF_QP) | |||||
gpie |= IXGBE_SDP1_GPIEN; | |||||
} | |||||
/* | |||||
* Thermal Failure Detection (X540) | |||||
* Link Detection (X552 SFP+, X552/X557-AT) | |||||
*/ | |||||
if (hw->mac.type == ixgbe_mac_X540 || | |||||
hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP || | |||||
hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) | |||||
gpie |= IXGBE_SDP0_GPIEN_X540; | |||||
if (adapter->msix > 1) { | |||||
/* Enable Enhanced MSIX mode */ | |||||
gpie |= IXGBE_GPIE_MSIX_MODE; | |||||
gpie |= IXGBE_GPIE_EIAME | IXGBE_GPIE_PBA_SUPPORT | | |||||
IXGBE_GPIE_OCD; | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); | |||||
return; | |||||
} | |||||
/* | |||||
* Requires adapter->max_frame_size to be set. | |||||
*/ | |||||
static void | |||||
ixgbe_config_delay_values(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 rxpb, frame, size, tmp; | |||||
frame = adapter->max_frame_size; | |||||
/* Calculate High Water */ | |||||
switch (hw->mac.type) { | |||||
case ixgbe_mac_X540: | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
tmp = IXGBE_DV_X540(frame, frame); | |||||
break; | |||||
default: | |||||
tmp = IXGBE_DV(frame, frame); | |||||
break; | |||||
} | |||||
size = IXGBE_BT2KB(tmp); | |||||
rxpb = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) >> 10; | |||||
hw->fc.high_water[0] = rxpb - size; | |||||
/* Now calculate Low Water */ | |||||
switch (hw->mac.type) { | |||||
case ixgbe_mac_X540: | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
tmp = IXGBE_LOW_DV_X540(frame); | |||||
break; | |||||
default: | |||||
tmp = IXGBE_LOW_DV(frame); | |||||
break; | |||||
} | |||||
hw->fc.low_water[0] = IXGBE_BT2KB(tmp); | |||||
hw->fc.requested_mode = adapter->fc; | |||||
hw->fc.pause_time = IXGBE_FC_PAUSE; | |||||
hw->fc.send_xon = TRUE; | |||||
} | |||||
/* | |||||
** | |||||
** MSIX Interrupt Handlers and Tasklets | |||||
** | |||||
*/ | |||||
static inline void | |||||
ixgbe_enable_queue(struct adapter *adapter, u32 vector) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u64 queue = (u64)(1 << vector); | |||||
u32 mask; | |||||
if (hw->mac.type == ixgbe_mac_82598EB) { | |||||
mask = (IXGBE_EIMS_RTX_QUEUE & queue); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMS, mask); | |||||
} else { | |||||
mask = (queue & 0xFFFFFFFF); | |||||
if (mask) | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMS_EX(0), mask); | |||||
mask = (queue >> 32); | |||||
if (mask) | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMS_EX(1), mask); | |||||
} | |||||
} | |||||
static inline void | |||||
ixgbe_disable_queue(struct adapter *adapter, u32 vector) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u64 queue = (u64)(1 << vector); | |||||
u32 mask; | |||||
if (hw->mac.type == ixgbe_mac_82598EB) { | |||||
mask = (IXGBE_EIMS_RTX_QUEUE & queue); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMC, mask); | |||||
} else { | |||||
mask = (queue & 0xFFFFFFFF); | |||||
if (mask) | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMC_EX(0), mask); | |||||
mask = (queue >> 32); | |||||
if (mask) | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMC_EX(1), mask); | |||||
} | |||||
} | |||||
static void | |||||
ixgbe_handle_que(void *context, int pending) | |||||
{ | |||||
struct ix_queue *que = context; | |||||
struct adapter *adapter = que->adapter; | |||||
struct tx_ring *txr = que->txr; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
ixgbe_rxeof(que); | |||||
IXGBE_TX_LOCK(txr); | |||||
ixgbe_txeof(txr); | |||||
#ifndef IXGBE_LEGACY_TX | |||||
if (!drbr_empty(ifp, txr->br)) | |||||
ixgbe_mq_start_locked(ifp, txr); | |||||
#else | |||||
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) | |||||
ixgbe_start_locked(txr, ifp); | |||||
#endif | |||||
IXGBE_TX_UNLOCK(txr); | |||||
} | |||||
/* Reenable this interrupt */ | |||||
if (que->res != NULL) | |||||
ixgbe_enable_queue(adapter, que->msix); | |||||
else | |||||
ixgbe_enable_intr(adapter); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Legacy Interrupt Service routine | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_legacy_irq(void *arg) | |||||
{ | |||||
struct ix_queue *que = arg; | |||||
struct adapter *adapter = que->adapter; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
struct tx_ring *txr = adapter->tx_rings; | |||||
bool more; | |||||
u32 reg_eicr; | |||||
reg_eicr = IXGBE_READ_REG(hw, IXGBE_EICR); | |||||
++que->irqs; | |||||
if (reg_eicr == 0) { | |||||
ixgbe_enable_intr(adapter); | |||||
return; | |||||
} | |||||
more = ixgbe_rxeof(que); | |||||
IXGBE_TX_LOCK(txr); | |||||
ixgbe_txeof(txr); | |||||
#ifdef IXGBE_LEGACY_TX | |||||
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) | |||||
ixgbe_start_locked(txr, ifp); | |||||
#else | |||||
if (!drbr_empty(ifp, txr->br)) | |||||
ixgbe_mq_start_locked(ifp, txr); | |||||
#endif | |||||
IXGBE_TX_UNLOCK(txr); | |||||
/* Check for fan failure */ | |||||
if ((hw->device_id == IXGBE_DEV_ID_82598AT) && | |||||
(reg_eicr & IXGBE_EICR_GPI_SDP1)) { | |||||
device_printf(adapter->dev, "\nCRITICAL: FAN FAILURE!! " | |||||
"REPLACE IMMEDIATELY!!\n"); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EICR_GPI_SDP1_BY_MAC(hw)); | |||||
} | |||||
/* Link status change */ | |||||
if (reg_eicr & IXGBE_EICR_LSC) | |||||
taskqueue_enqueue(adapter->tq, &adapter->link_task); | |||||
/* External PHY interrupt */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T && | |||||
(reg_eicr & IXGBE_EICR_GPI_SDP0_X540)) | |||||
taskqueue_enqueue(adapter->tq, &adapter->phy_task); | |||||
if (more) | |||||
taskqueue_enqueue(que->tq, &que->que_task); | |||||
else | |||||
ixgbe_enable_intr(adapter); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* MSIX Queue Interrupt Service routine | |||||
* | |||||
**********************************************************************/ | |||||
void | |||||
ixgbe_msix_que(void *arg) | |||||
{ | |||||
struct ix_queue *que = arg; | |||||
struct adapter *adapter = que->adapter; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
struct tx_ring *txr = que->txr; | |||||
struct rx_ring *rxr = que->rxr; | |||||
bool more; | |||||
u32 newitr = 0; | |||||
/* Protect against spurious interrupts */ | |||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) | |||||
return; | |||||
ixgbe_disable_queue(adapter, que->msix); | |||||
++que->irqs; | |||||
more = ixgbe_rxeof(que); | |||||
IXGBE_TX_LOCK(txr); | |||||
ixgbe_txeof(txr); | |||||
#ifdef IXGBE_LEGACY_TX | |||||
if (!IFQ_DRV_IS_EMPTY(ifp->if_snd)) | |||||
ixgbe_start_locked(txr, ifp); | |||||
#else | |||||
if (!drbr_empty(ifp, txr->br)) | |||||
ixgbe_mq_start_locked(ifp, txr); | |||||
#endif | |||||
IXGBE_TX_UNLOCK(txr); | |||||
/* Do AIM now? */ | |||||
if (adapter->enable_aim == FALSE) | |||||
goto no_calc; | |||||
/* | |||||
** Do Adaptive Interrupt Moderation: | |||||
** - Write out last calculated setting | |||||
** - Calculate based on average size over | |||||
** the last interval. | |||||
*/ | |||||
if (que->eitr_setting) | |||||
IXGBE_WRITE_REG(&adapter->hw, | |||||
IXGBE_EITR(que->msix), que->eitr_setting); | |||||
que->eitr_setting = 0; | |||||
/* Idle, do nothing */ | |||||
if ((txr->bytes == 0) && (rxr->bytes == 0)) | |||||
goto no_calc; | |||||
if ((txr->bytes) && (txr->packets)) | |||||
newitr = txr->bytes/txr->packets; | |||||
if ((rxr->bytes) && (rxr->packets)) | |||||
newitr = max(newitr, | |||||
(rxr->bytes / rxr->packets)); | |||||
newitr += 24; /* account for hardware frame, crc */ | |||||
/* set an upper boundary */ | |||||
newitr = min(newitr, 3000); | |||||
/* Be nice to the mid range */ | |||||
if ((newitr > 300) && (newitr < 1200)) | |||||
newitr = (newitr / 3); | |||||
else | |||||
newitr = (newitr / 2); | |||||
if (adapter->hw.mac.type == ixgbe_mac_82598EB) | |||||
newitr |= newitr << 16; | |||||
else | |||||
newitr |= IXGBE_EITR_CNT_WDIS; | |||||
/* save for next interrupt */ | |||||
que->eitr_setting = newitr; | |||||
/* Reset state */ | |||||
txr->bytes = 0; | |||||
txr->packets = 0; | |||||
rxr->bytes = 0; | |||||
rxr->packets = 0; | |||||
no_calc: | |||||
if (more) | |||||
taskqueue_enqueue(que->tq, &que->que_task); | |||||
else | |||||
ixgbe_enable_queue(adapter, que->msix); | |||||
return; | |||||
} | |||||
static void | |||||
ixgbe_msix_link(void *arg) | |||||
{ | |||||
struct adapter *adapter = arg; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 reg_eicr, mod_mask; | |||||
++adapter->link_irq; | |||||
/* Pause other interrupts */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_OTHER); | |||||
/* First get the cause */ | |||||
reg_eicr = IXGBE_READ_REG(hw, IXGBE_EICS); | |||||
/* Be sure the queue bits are not cleared */ | |||||
reg_eicr &= ~IXGBE_EICR_RTX_QUEUE; | |||||
/* Clear interrupt with write */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, reg_eicr); | |||||
/* Link status change */ | |||||
if (reg_eicr & IXGBE_EICR_LSC) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_LSC); | |||||
taskqueue_enqueue(adapter->tq, &adapter->link_task); | |||||
} | |||||
if (adapter->hw.mac.type != ixgbe_mac_82598EB) { | |||||
#ifdef IXGBE_FDIR | |||||
if (reg_eicr & IXGBE_EICR_FLOW_DIR) { | |||||
/* This is probably overkill :) */ | |||||
if (!atomic_cmpset_int(&adapter->fdir_reinit, 0, 1)) | |||||
return; | |||||
/* Disable the interrupt */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EICR_FLOW_DIR); | |||||
taskqueue_enqueue(adapter->tq, &adapter->fdir_task); | |||||
} else | |||||
#endif | |||||
if (reg_eicr & IXGBE_EICR_ECC) { | |||||
device_printf(adapter->dev, "CRITICAL: ECC ERROR!! " | |||||
"Please Reboot!!\n"); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_ECC); | |||||
} | |||||
/* Check for over temp condition */ | |||||
if (reg_eicr & IXGBE_EICR_TS) { | |||||
device_printf(adapter->dev, "CRITICAL: OVER TEMP!! " | |||||
"PHY IS SHUT DOWN!!\n"); | |||||
device_printf(adapter->dev, "System shutdown required!\n"); | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_TS); | |||||
} | |||||
#ifdef PCI_IOV | |||||
if (reg_eicr & IXGBE_EICR_MAILBOX) | |||||
taskqueue_enqueue(adapter->tq, &adapter->mbx_task); | |||||
#endif | |||||
} | |||||
/* Pluggable optics-related interrupt */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP) | |||||
mod_mask = IXGBE_EICR_GPI_SDP0_X540; | |||||
else | |||||
mod_mask = IXGBE_EICR_GPI_SDP2_BY_MAC(hw); | |||||
if (ixgbe_is_sfp(hw)) { | |||||
if (reg_eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw)) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1_BY_MAC(hw)); | |||||
taskqueue_enqueue(adapter->tq, &adapter->msf_task); | |||||
} else if (reg_eicr & mod_mask) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, mod_mask); | |||||
taskqueue_enqueue(adapter->tq, &adapter->mod_task); | |||||
} | |||||
} | |||||
/* Check for fan failure */ | |||||
if ((hw->device_id == IXGBE_DEV_ID_82598AT) && | |||||
(reg_eicr & IXGBE_EICR_GPI_SDP1)) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1); | |||||
device_printf(adapter->dev, "\nCRITICAL: FAN FAILURE!! " | |||||
"REPLACE IMMEDIATELY!!\n"); | |||||
} | |||||
/* External PHY interrupt */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T && | |||||
(reg_eicr & IXGBE_EICR_GPI_SDP0_X540)) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP0_X540); | |||||
taskqueue_enqueue(adapter->tq, &adapter->phy_task); | |||||
} | |||||
/* Re-enable other interrupts */ | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, IXGBE_EIMS_OTHER); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Media Ioctl callback | |||||
* | |||||
* This routine is called whenever the user queries the status of | |||||
* the interface using ifconfig. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_media_status(struct ifnet * ifp, struct ifmediareq * ifmr) | |||||
{ | |||||
struct adapter *adapter = ifp->if_softc; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
int layer; | |||||
INIT_DEBUGOUT("ixgbe_media_status: begin"); | |||||
IXGBE_CORE_LOCK(adapter); | |||||
ixgbe_update_link_status(adapter); | |||||
ifmr->ifm_status = IFM_AVALID; | |||||
ifmr->ifm_active = IFM_ETHER; | |||||
if (!adapter->link_active) { | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return; | |||||
} | |||||
ifmr->ifm_status |= IFM_ACTIVE; | |||||
layer = adapter->phy_layer; | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_T || | |||||
layer & IXGBE_PHYSICAL_LAYER_1000BASE_T || | |||||
layer & IXGBE_PHYSICAL_LAYER_100BASE_TX) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_T | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_T | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_100_FULL: | |||||
ifmr->ifm_active |= IFM_100_TX | IFM_FDX; | |||||
break; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU || | |||||
layer & IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_TWINAX | IFM_FDX; | |||||
break; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LR) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_LR | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_LX | IFM_FDX; | |||||
break; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LRM) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_LRM | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_LX | IFM_FDX; | |||||
break; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_SR || | |||||
layer & IXGBE_PHYSICAL_LAYER_1000BASE_SX) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_SR | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_SX | IFM_FDX; | |||||
break; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_CX4) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_CX4 | IFM_FDX; | |||||
break; | |||||
} | |||||
/* | |||||
** XXX: These need to use the proper media types once | |||||
** they're added. | |||||
*/ | |||||
#ifndef IFM_ETH_XTYPE | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_SR | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_2_5GB_FULL: | |||||
ifmr->ifm_active |= IFM_2500_SX | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_CX | IFM_FDX; | |||||
break; | |||||
} | |||||
else if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4 | |||||
|| layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_CX4 | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_2_5GB_FULL: | |||||
ifmr->ifm_active |= IFM_2500_SX | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_CX | IFM_FDX; | |||||
break; | |||||
} | |||||
#else | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_KR | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_2_5GB_FULL: | |||||
ifmr->ifm_active |= IFM_2500_KX | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_KX | IFM_FDX; | |||||
break; | |||||
} | |||||
else if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4 | |||||
|| layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) | |||||
switch (adapter->link_speed) { | |||||
case IXGBE_LINK_SPEED_10GB_FULL: | |||||
ifmr->ifm_active |= IFM_10G_KX4 | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_2_5GB_FULL: | |||||
ifmr->ifm_active |= IFM_2500_KX | IFM_FDX; | |||||
break; | |||||
case IXGBE_LINK_SPEED_1GB_FULL: | |||||
ifmr->ifm_active |= IFM_1000_KX | IFM_FDX; | |||||
break; | |||||
} | |||||
#endif | |||||
/* If nothing is recognized... */ | |||||
if (IFM_SUBTYPE(ifmr->ifm_active) == 0) | |||||
ifmr->ifm_active |= IFM_UNKNOWN; | |||||
#if __FreeBSD_version >= 900025 | |||||
/* Display current flow control setting used on link */ | |||||
if (hw->fc.current_mode == ixgbe_fc_rx_pause || | |||||
hw->fc.current_mode == ixgbe_fc_full) | |||||
ifmr->ifm_active |= IFM_ETH_RXPAUSE; | |||||
if (hw->fc.current_mode == ixgbe_fc_tx_pause || | |||||
hw->fc.current_mode == ixgbe_fc_full) | |||||
ifmr->ifm_active |= IFM_ETH_TXPAUSE; | |||||
#endif | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Media Ioctl callback | |||||
* | |||||
* This routine is called when the user changes speed/duplex using | |||||
* media/mediopt option with ifconfig. | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixgbe_media_change(struct ifnet * ifp) | |||||
{ | |||||
struct adapter *adapter = ifp->if_softc; | |||||
struct ifmedia *ifm = &adapter->media; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
ixgbe_link_speed speed = 0; | |||||
INIT_DEBUGOUT("ixgbe_media_change: begin"); | |||||
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) | |||||
return (EINVAL); | |||||
if (hw->phy.media_type == ixgbe_media_type_backplane) | |||||
return (ENODEV); | |||||
/* | |||||
** We don't actually need to check against the supported | |||||
** media types of the adapter; ifmedia will take care of | |||||
** that for us. | |||||
*/ | |||||
#ifndef IFM_ETH_XTYPE | |||||
switch (IFM_SUBTYPE(ifm->ifm_media)) { | |||||
case IFM_AUTO: | |||||
case IFM_10G_T: | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
case IFM_10G_LRM: | |||||
case IFM_10G_SR: /* KR, too */ | |||||
case IFM_10G_LR: | |||||
case IFM_10G_CX4: /* KX4 */ | |||||
speed |= IXGBE_LINK_SPEED_1GB_FULL; | |||||
case IFM_10G_TWINAX: | |||||
speed |= IXGBE_LINK_SPEED_10GB_FULL; | |||||
break; | |||||
case IFM_1000_T: | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
case IFM_1000_LX: | |||||
case IFM_1000_SX: | |||||
case IFM_1000_CX: /* KX */ | |||||
speed |= IXGBE_LINK_SPEED_1GB_FULL; | |||||
break; | |||||
case IFM_100_TX: | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
break; | |||||
default: | |||||
goto invalid; | |||||
} | |||||
#else | |||||
switch (IFM_SUBTYPE(ifm->ifm_media)) { | |||||
case IFM_AUTO: | |||||
case IFM_10G_T: | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
case IFM_10G_LRM: | |||||
case IFM_10G_KR: | |||||
case IFM_10G_LR: | |||||
case IFM_10G_KX4: | |||||
speed |= IXGBE_LINK_SPEED_1GB_FULL; | |||||
case IFM_10G_TWINAX: | |||||
speed |= IXGBE_LINK_SPEED_10GB_FULL; | |||||
break; | |||||
case IFM_1000_T: | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
case IFM_1000_LX: | |||||
case IFM_1000_SX: | |||||
case IFM_1000_KX: | |||||
speed |= IXGBE_LINK_SPEED_1GB_FULL; | |||||
break; | |||||
case IFM_100_TX: | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
break; | |||||
default: | |||||
goto invalid; | |||||
} | |||||
#endif | |||||
hw->mac.autotry_restart = TRUE; | |||||
hw->mac.ops.setup_link(hw, speed, TRUE); | |||||
if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) { | |||||
adapter->advertise = 0; | |||||
} else { | |||||
if ((speed & IXGBE_LINK_SPEED_10GB_FULL) != 0) | |||||
adapter->advertise |= 1 << 2; | |||||
if ((speed & IXGBE_LINK_SPEED_1GB_FULL) != 0) | |||||
adapter->advertise |= 1 << 1; | |||||
if ((speed & IXGBE_LINK_SPEED_100_FULL) != 0) | |||||
adapter->advertise |= 1 << 0; | |||||
} | |||||
return (0); | |||||
invalid: | |||||
device_printf(adapter->dev, "Invalid media type!\n"); | |||||
return (EINVAL); | |||||
} | |||||
static void | |||||
ixgbe_set_promisc(struct adapter *adapter) | |||||
{ | |||||
u_int32_t reg_rctl; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
int mcnt = 0; | |||||
reg_rctl = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL); | |||||
reg_rctl &= (~IXGBE_FCTRL_UPE); | |||||
if (ifp->if_flags & IFF_ALLMULTI) | |||||
mcnt = MAX_NUM_MULTICAST_ADDRESSES; | |||||
else { | |||||
struct ifmultiaddr *ifma; | |||||
#if __FreeBSD_version < 800000 | |||||
IF_ADDR_LOCK(ifp); | |||||
#else | |||||
if_maddr_rlock(ifp); | |||||
#endif | |||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | |||||
if (ifma->ifma_addr->sa_family != AF_LINK) | |||||
continue; | |||||
if (mcnt == MAX_NUM_MULTICAST_ADDRESSES) | |||||
break; | |||||
mcnt++; | |||||
} | |||||
#if __FreeBSD_version < 800000 | |||||
IF_ADDR_UNLOCK(ifp); | |||||
#else | |||||
if_maddr_runlock(ifp); | |||||
#endif | |||||
} | |||||
if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) | |||||
reg_rctl &= (~IXGBE_FCTRL_MPE); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, reg_rctl); | |||||
if (ifp->if_flags & IFF_PROMISC) { | |||||
reg_rctl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, reg_rctl); | |||||
} else if (ifp->if_flags & IFF_ALLMULTI) { | |||||
reg_rctl |= IXGBE_FCTRL_MPE; | |||||
reg_rctl &= ~IXGBE_FCTRL_UPE; | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, reg_rctl); | |||||
} | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* Multicast Update | |||||
* | |||||
* This routine is called whenever multicast address list is updated. | |||||
* | |||||
**********************************************************************/ | |||||
#define IXGBE_RAR_ENTRIES 16 | |||||
static void | |||||
ixgbe_set_multi(struct adapter *adapter) | |||||
{ | |||||
u32 fctrl; | |||||
u8 *update_ptr; | |||||
struct ifmultiaddr *ifma; | |||||
struct ixgbe_mc_addr *mta; | |||||
int mcnt = 0; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
IOCTL_DEBUGOUT("ixgbe_set_multi: begin"); | |||||
mta = adapter->mta; | |||||
bzero(mta, sizeof(*mta) * MAX_NUM_MULTICAST_ADDRESSES); | |||||
#if __FreeBSD_version < 800000 | |||||
IF_ADDR_LOCK(ifp); | |||||
#else | |||||
if_maddr_rlock(ifp); | |||||
#endif | |||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | |||||
if (ifma->ifma_addr->sa_family != AF_LINK) | |||||
continue; | |||||
if (mcnt == MAX_NUM_MULTICAST_ADDRESSES) | |||||
break; | |||||
bcopy(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), | |||||
mta[mcnt].addr, IXGBE_ETH_LENGTH_OF_ADDRESS); | |||||
mta[mcnt].vmdq = adapter->pool; | |||||
mcnt++; | |||||
} | |||||
#if __FreeBSD_version < 800000 | |||||
IF_ADDR_UNLOCK(ifp); | |||||
#else | |||||
if_maddr_runlock(ifp); | |||||
#endif | |||||
fctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL); | |||||
fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); | |||||
if (ifp->if_flags & IFF_PROMISC) | |||||
fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); | |||||
else if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES || | |||||
ifp->if_flags & IFF_ALLMULTI) { | |||||
fctrl |= IXGBE_FCTRL_MPE; | |||||
fctrl &= ~IXGBE_FCTRL_UPE; | |||||
} else | |||||
fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, fctrl); | |||||
if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) { | |||||
update_ptr = (u8 *)mta; | |||||
ixgbe_update_mc_addr_list(&adapter->hw, | |||||
update_ptr, mcnt, ixgbe_mc_array_itr, TRUE); | |||||
} | |||||
return; | |||||
} | |||||
/* | |||||
* This is an iterator function now needed by the multicast | |||||
* shared code. It simply feeds the shared code routine the | |||||
* addresses in the array of ixgbe_set_multi() one by one. | |||||
*/ | |||||
static u8 * | |||||
ixgbe_mc_array_itr(struct ixgbe_hw *hw, u8 **update_ptr, u32 *vmdq) | |||||
{ | |||||
struct ixgbe_mc_addr *mta; | |||||
mta = (struct ixgbe_mc_addr *)*update_ptr; | |||||
*vmdq = mta->vmdq; | |||||
*update_ptr = (u8*)(mta + 1); | |||||
return (mta->addr); | |||||
} | |||||
/********************************************************************* | |||||
* Timer routine | |||||
* | |||||
* This routine checks for link status,updates statistics, | |||||
* and runs the watchdog check. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_local_timer(void *arg) | |||||
{ | |||||
struct adapter *adapter = arg; | |||||
device_t dev = adapter->dev; | |||||
struct ix_queue *que = adapter->queues; | |||||
u64 queues = 0; | |||||
int hung = 0; | |||||
mtx_assert(&adapter->core_mtx, MA_OWNED); | |||||
/* Check for pluggable optics */ | |||||
if (adapter->sfp_probe) | |||||
if (!ixgbe_sfp_probe(adapter)) | |||||
goto out; /* Nothing to do */ | |||||
ixgbe_update_link_status(adapter); | |||||
ixgbe_update_stats_counters(adapter); | |||||
/* | |||||
** Check the TX queues status | |||||
** - mark hung queues so we don't schedule on them | |||||
** - watchdog only if all queues show hung | |||||
*/ | |||||
for (int i = 0; i < adapter->num_queues; i++, que++) { | |||||
/* Keep track of queues with work for soft irq */ | |||||
if (que->txr->busy) | |||||
queues |= ((u64)1 << que->me); | |||||
/* | |||||
** 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; | |||||
} | |||||
} | |||||
/* 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 */ | |||||
ixgbe_rearm_queues(adapter, queues); | |||||
} | |||||
out: | |||||
callout_reset(&adapter->timer, hz, ixgbe_local_timer, adapter); | |||||
return; | |||||
watchdog: | |||||
device_printf(adapter->dev, "Watchdog timeout -- resetting\n"); | |||||
adapter->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | |||||
adapter->watchdog_events++; | |||||
ixgbe_init_locked(adapter); | |||||
} | |||||
/* | |||||
** Note: this routine updates the OS on the link state | |||||
** the real check of the hardware only happens with | |||||
** a link interrupt. | |||||
*/ | |||||
static void | |||||
ixgbe_update_link_status(struct adapter *adapter) | |||||
{ | |||||
struct ifnet *ifp = adapter->ifp; | |||||
device_t dev = adapter->dev; | |||||
if (adapter->link_up){ | |||||
if (adapter->link_active == FALSE) { | |||||
if (bootverbose) | |||||
device_printf(dev,"Link is up %d Gbps %s \n", | |||||
((adapter->link_speed == 128)? 10:1), | |||||
"Full Duplex"); | |||||
adapter->link_active = TRUE; | |||||
/* Update any Flow Control changes */ | |||||
ixgbe_fc_enable(&adapter->hw); | |||||
/* Update DMA coalescing config */ | |||||
ixgbe_config_dmac(adapter); | |||||
if_link_state_change(ifp, LINK_STATE_UP); | |||||
#ifdef PCI_IOV | |||||
ixgbe_ping_all_vfs(adapter); | |||||
#endif | |||||
} | |||||
} else { /* Link down */ | |||||
if (adapter->link_active == TRUE) { | |||||
if (bootverbose) | |||||
device_printf(dev,"Link is Down\n"); | |||||
if_link_state_change(ifp, LINK_STATE_DOWN); | |||||
adapter->link_active = FALSE; | |||||
#ifdef PCI_IOV | |||||
ixgbe_ping_all_vfs(adapter); | |||||
#endif | |||||
} | |||||
} | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* This routine disables all traffic on the adapter by issuing a | |||||
* global reset on the MAC and deallocates TX/RX buffers. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_stop(void *arg) | |||||
{ | |||||
struct ifnet *ifp; | |||||
struct adapter *adapter = arg; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
ifp = adapter->ifp; | |||||
mtx_assert(&adapter->core_mtx, MA_OWNED); | |||||
INIT_DEBUGOUT("ixgbe_stop: begin\n"); | |||||
ixgbe_disable_intr(adapter); | |||||
callout_stop(&adapter->timer); | |||||
/* Let the stack know...*/ | |||||
ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | |||||
ixgbe_reset_hw(hw); | |||||
hw->adapter_stopped = FALSE; | |||||
ixgbe_stop_adapter(hw); | |||||
if (hw->mac.type == ixgbe_mac_82599EB) | |||||
ixgbe_stop_mac_link_on_d3_82599(hw); | |||||
/* Turn off the laser - noop with no optics */ | |||||
ixgbe_disable_tx_laser(hw); | |||||
/* Update the stack */ | |||||
adapter->link_up = FALSE; | |||||
ixgbe_update_link_status(adapter); | |||||
/* reprogram the RAR[0] in case user changed it. */ | |||||
ixgbe_set_rar(&adapter->hw, 0, adapter->hw.mac.addr, 0, IXGBE_RAH_AV); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Determine hardware revision. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_identify_hardware(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
/* Save off the information about this board */ | |||||
hw->vendor_id = pci_get_vendor(dev); | |||||
hw->device_id = pci_get_device(dev); | |||||
hw->revision_id = pci_read_config(dev, PCIR_REVID, 1); | |||||
hw->subsystem_vendor_id = | |||||
pci_read_config(dev, PCIR_SUBVEND_0, 2); | |||||
hw->subsystem_device_id = | |||||
pci_read_config(dev, PCIR_SUBDEV_0, 2); | |||||
/* | |||||
** Make sure BUSMASTER is set | |||||
*/ | |||||
pci_enable_busmaster(dev); | |||||
/* We need this here to set the num_segs below */ | |||||
ixgbe_set_mac_type(hw); | |||||
/* Pick up the 82599 settings */ | |||||
if (hw->mac.type != ixgbe_mac_82598EB) { | |||||
hw->phy.smart_speed = ixgbe_smart_speed; | |||||
adapter->num_segs = IXGBE_82599_SCATTER; | |||||
} else | |||||
adapter->num_segs = IXGBE_82598_SCATTER; | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Determine optic type | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_setup_optics(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
int layer; | |||||
layer = adapter->phy_layer = ixgbe_get_supported_physical_layer(hw); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_T) { | |||||
adapter->optics = IFM_10G_T; | |||||
return; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_T) { | |||||
adapter->optics = IFM_1000_T; | |||||
return; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_SX) { | |||||
adapter->optics = IFM_1000_SX; | |||||
return; | |||||
} | |||||
if (layer & (IXGBE_PHYSICAL_LAYER_10GBASE_LR | | |||||
IXGBE_PHYSICAL_LAYER_10GBASE_LRM)) { | |||||
adapter->optics = IFM_10G_LR; | |||||
return; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_SR) { | |||||
adapter->optics = IFM_10G_SR; | |||||
return; | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU) { | |||||
adapter->optics = IFM_10G_TWINAX; | |||||
return; | |||||
} | |||||
if (layer & (IXGBE_PHYSICAL_LAYER_10GBASE_KX4 | | |||||
IXGBE_PHYSICAL_LAYER_10GBASE_CX4)) { | |||||
adapter->optics = IFM_10G_CX4; | |||||
return; | |||||
} | |||||
/* If we get here just set the default */ | |||||
adapter->optics = IFM_ETHER | IFM_AUTO; | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Setup the Legacy or MSI Interrupt handler | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixgbe_allocate_legacy(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
struct ix_queue *que = adapter->queues; | |||||
#ifndef IXGBE_LEGACY_TX | |||||
struct tx_ring *txr = adapter->tx_rings; | |||||
#endif | |||||
int error, rid = 0; | |||||
/* MSI RID at 1 */ | |||||
if (adapter->msix == 1) | |||||
rid = 1; | |||||
/* We allocate a single interrupt resource */ | |||||
adapter->res = bus_alloc_resource_any(dev, | |||||
SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); | |||||
if (adapter->res == NULL) { | |||||
device_printf(dev, "Unable to allocate bus resource: " | |||||
"interrupt\n"); | |||||
return (ENXIO); | |||||
} | |||||
/* | |||||
* Try allocating a fast interrupt and the associated deferred | |||||
* processing contexts. | |||||
*/ | |||||
#ifndef IXGBE_LEGACY_TX | |||||
TASK_INIT(&txr->txq_task, 0, ixgbe_deferred_mq_start, txr); | |||||
#endif | |||||
TASK_INIT(&que->que_task, 0, ixgbe_handle_que, que); | |||||
que->tq = taskqueue_create_fast("ixgbe_que", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &que->tq); | |||||
taskqueue_start_threads(&que->tq, 1, PI_NET, "%s ixq", | |||||
device_get_nameunit(adapter->dev)); | |||||
/* Tasklets for Link, SFP and Multispeed Fiber */ | |||||
TASK_INIT(&adapter->link_task, 0, ixgbe_handle_link, adapter); | |||||
TASK_INIT(&adapter->mod_task, 0, ixgbe_handle_mod, adapter); | |||||
TASK_INIT(&adapter->msf_task, 0, ixgbe_handle_msf, adapter); | |||||
TASK_INIT(&adapter->phy_task, 0, ixgbe_handle_phy, adapter); | |||||
#ifdef IXGBE_FDIR | |||||
TASK_INIT(&adapter->fdir_task, 0, ixgbe_reinit_fdir, adapter); | |||||
#endif | |||||
adapter->tq = taskqueue_create_fast("ixgbe_link", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &adapter->tq); | |||||
taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s linkq", | |||||
device_get_nameunit(adapter->dev)); | |||||
if ((error = bus_setup_intr(dev, adapter->res, | |||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, ixgbe_legacy_irq, | |||||
que, &adapter->tag)) != 0) { | |||||
device_printf(dev, "Failed to register fast interrupt " | |||||
"handler: %d\n", error); | |||||
taskqueue_free(que->tq); | |||||
taskqueue_free(adapter->tq); | |||||
que->tq = NULL; | |||||
adapter->tq = NULL; | |||||
return (error); | |||||
} | |||||
/* For simplicity in the handlers */ | |||||
adapter->active_queues = IXGBE_EIMS_ENABLE_MASK; | |||||
return (0); | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Setup MSIX Interrupt resources and handlers | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixgbe_allocate_msix(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
struct ix_queue *que = adapter->queues; | |||||
struct tx_ring *txr = adapter->tx_rings; | |||||
int error, rid, vector = 0; | |||||
int cpu_id = 0; | |||||
#ifdef RSS | |||||
cpuset_t cpu_mask; | |||||
#endif | |||||
#ifdef RSS | |||||
/* | |||||
* If we're doing RSS, the number of queues needs to | |||||
* match the number of RSS buckets that are configured. | |||||
* | |||||
* + If there's more queues than RSS buckets, we'll end | |||||
* up with queues that get no traffic. | |||||
* | |||||
* + If there's more RSS buckets than queues, we'll end | |||||
* up having multiple RSS buckets map to the same queue, | |||||
* so there'll be some contention. | |||||
*/ | |||||
if (adapter->num_queues != rss_getnumbuckets()) { | |||||
device_printf(dev, | |||||
"%s: number of queues (%d) != number of RSS buckets (%d)" | |||||
"; performance will be impacted.\n", | |||||
__func__, | |||||
adapter->num_queues, | |||||
rss_getnumbuckets()); | |||||
} | |||||
#endif | |||||
for (int i = 0; i < adapter->num_queues; i++, vector++, que++, txr++) { | |||||
rid = vector + 1; | |||||
que->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | |||||
RF_SHAREABLE | RF_ACTIVE); | |||||
if (que->res == NULL) { | |||||
device_printf(dev,"Unable to allocate" | |||||
" bus resource: que interrupt [%d]\n", vector); | |||||
return (ENXIO); | |||||
} | |||||
/* Set the handler function */ | |||||
error = bus_setup_intr(dev, que->res, | |||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | |||||
ixgbe_msix_que, que, &que->tag); | |||||
if (error) { | |||||
que->res = NULL; | |||||
device_printf(dev, "Failed to register QUE handler"); | |||||
return (error); | |||||
} | |||||
#if __FreeBSD_version >= 800504 | |||||
bus_describe_intr(dev, que->res, que->tag, "q%d", i); | |||||
#endif | |||||
que->msix = vector; | |||||
adapter->active_queues |= (u64)(1 << que->msix); | |||||
#ifdef RSS | |||||
/* | |||||
* The queue ID is used as the RSS layer bucket ID. | |||||
* We look up the queue ID -> RSS CPU ID and select | |||||
* that. | |||||
*/ | |||||
cpu_id = rss_getcpu(i % rss_getnumbuckets()); | |||||
#else | |||||
/* | |||||
* Bind the msix vector, and thus the | |||||
* rings to the corresponding cpu. | |||||
* | |||||
* This just happens to match the default RSS round-robin | |||||
* bucket -> queue -> CPU allocation. | |||||
*/ | |||||
if (adapter->num_queues > 1) | |||||
cpu_id = i; | |||||
#endif | |||||
if (adapter->num_queues > 1) | |||||
bus_bind_intr(dev, que->res, cpu_id); | |||||
#ifdef IXGBE_DEBUG | |||||
#ifdef RSS | |||||
device_printf(dev, | |||||
"Bound RSS bucket %d to CPU %d\n", | |||||
i, cpu_id); | |||||
#else | |||||
device_printf(dev, | |||||
"Bound queue %d to cpu %d\n", | |||||
i, cpu_id); | |||||
#endif | |||||
#endif /* IXGBE_DEBUG */ | |||||
#ifndef IXGBE_LEGACY_TX | |||||
TASK_INIT(&txr->txq_task, 0, ixgbe_deferred_mq_start, txr); | |||||
#endif | |||||
TASK_INIT(&que->que_task, 0, ixgbe_handle_que, que); | |||||
que->tq = taskqueue_create_fast("ixgbe_que", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &que->tq); | |||||
#ifdef RSS | |||||
CPU_SETOF(cpu_id, &cpu_mask); | |||||
taskqueue_start_threads_cpuset(&que->tq, 1, PI_NET, | |||||
&cpu_mask, | |||||
"%s (bucket %d)", | |||||
device_get_nameunit(adapter->dev), | |||||
cpu_id); | |||||
#else | |||||
taskqueue_start_threads(&que->tq, 1, PI_NET, "%s:q%d", | |||||
device_get_nameunit(adapter->dev), i); | |||||
#endif | |||||
} | |||||
/* and Link */ | |||||
rid = vector + 1; | |||||
adapter->res = bus_alloc_resource_any(dev, | |||||
SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); | |||||
if (!adapter->res) { | |||||
device_printf(dev,"Unable to allocate" | |||||
" bus resource: Link interrupt [%d]\n", rid); | |||||
return (ENXIO); | |||||
} | |||||
/* Set the link handler function */ | |||||
error = bus_setup_intr(dev, adapter->res, | |||||
INTR_TYPE_NET | INTR_MPSAFE, NULL, | |||||
ixgbe_msix_link, adapter, &adapter->tag); | |||||
if (error) { | |||||
adapter->res = NULL; | |||||
device_printf(dev, "Failed to register LINK handler"); | |||||
return (error); | |||||
} | |||||
#if __FreeBSD_version >= 800504 | |||||
bus_describe_intr(dev, adapter->res, adapter->tag, "link"); | |||||
#endif | |||||
adapter->vector = vector; | |||||
/* Tasklets for Link, SFP and Multispeed Fiber */ | |||||
TASK_INIT(&adapter->link_task, 0, ixgbe_handle_link, adapter); | |||||
TASK_INIT(&adapter->mod_task, 0, ixgbe_handle_mod, adapter); | |||||
TASK_INIT(&adapter->msf_task, 0, ixgbe_handle_msf, adapter); | |||||
#ifdef PCI_IOV | |||||
TASK_INIT(&adapter->mbx_task, 0, ixgbe_handle_mbx, adapter); | |||||
#endif | |||||
TASK_INIT(&adapter->phy_task, 0, ixgbe_handle_phy, adapter); | |||||
#ifdef IXGBE_FDIR | |||||
TASK_INIT(&adapter->fdir_task, 0, ixgbe_reinit_fdir, adapter); | |||||
#endif | |||||
adapter->tq = taskqueue_create_fast("ixgbe_link", M_NOWAIT, | |||||
taskqueue_thread_enqueue, &adapter->tq); | |||||
taskqueue_start_threads(&adapter->tq, 1, PI_NET, "%s linkq", | |||||
device_get_nameunit(adapter->dev)); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Setup Either MSI/X or MSI | |||||
*/ | |||||
static int | |||||
ixgbe_setup_msix(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
int rid, want, queues, msgs; | |||||
/* Override by tuneable */ | |||||
if (ixgbe_enable_msix == 0) | |||||
goto msi; | |||||
/* First try MSI/X */ | |||||
msgs = pci_msix_count(dev); | |||||
if (msgs == 0) | |||||
goto msi; | |||||
rid = PCIR_BAR(MSIX_82598_BAR); | |||||
adapter->msix_mem = bus_alloc_resource_any(dev, | |||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | |||||
if (adapter->msix_mem == NULL) { | |||||
rid += 4; /* 82599 maps in higher BAR */ | |||||
adapter->msix_mem = bus_alloc_resource_any(dev, | |||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | |||||
} | |||||
if (adapter->msix_mem == NULL) { | |||||
/* May not be enabled */ | |||||
device_printf(adapter->dev, | |||||
"Unable to map MSIX table \n"); | |||||
goto msi; | |||||
} | |||||
/* Figure out a reasonable auto config value */ | |||||
queues = (mp_ncpus > (msgs - 1)) ? (msgs - 1) : mp_ncpus; | |||||
#ifdef RSS | |||||
/* If we're doing RSS, clamp at the number of RSS buckets */ | |||||
if (queues > rss_getnumbuckets()) | |||||
queues = rss_getnumbuckets(); | |||||
#endif | |||||
if (ixgbe_num_queues != 0) | |||||
queues = ixgbe_num_queues; | |||||
/* Set max queues to 8 when autoconfiguring */ | |||||
else if ((ixgbe_num_queues == 0) && (queues > 8)) | |||||
queues = 8; | |||||
/* reflect correct sysctl value */ | |||||
ixgbe_num_queues = queues; | |||||
/* | |||||
** Want one vector (RX/TX pair) per queue | |||||
** plus an additional for Link. | |||||
*/ | |||||
want = queues + 1; | |||||
if (msgs >= want) | |||||
msgs = want; | |||||
else { | |||||
device_printf(adapter->dev, | |||||
"MSIX Configuration Problem, " | |||||
"%d vectors but %d queues wanted!\n", | |||||
msgs, want); | |||||
goto msi; | |||||
} | |||||
if ((pci_alloc_msix(dev, &msgs) == 0) && (msgs == want)) { | |||||
device_printf(adapter->dev, | |||||
"Using MSIX interrupts with %d vectors\n", msgs); | |||||
adapter->num_queues = queues; | |||||
return (msgs); | |||||
} | |||||
/* | |||||
** If MSIX alloc failed or provided us with | |||||
** less than needed, free and fall through to MSI | |||||
*/ | |||||
pci_release_msi(dev); | |||||
msi: | |||||
if (adapter->msix_mem != NULL) { | |||||
bus_release_resource(dev, SYS_RES_MEMORY, | |||||
rid, adapter->msix_mem); | |||||
adapter->msix_mem = NULL; | |||||
} | |||||
msgs = 1; | |||||
if (pci_alloc_msi(dev, &msgs) == 0) { | |||||
device_printf(adapter->dev, "Using an MSI interrupt\n"); | |||||
return (msgs); | |||||
} | |||||
device_printf(adapter->dev, "Using a Legacy interrupt\n"); | |||||
return (0); | |||||
} | |||||
static int | |||||
ixgbe_allocate_pci_resources(struct adapter *adapter) | |||||
{ | |||||
int rid; | |||||
device_t dev = adapter->dev; | |||||
rid = PCIR_BAR(0); | |||||
adapter->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | |||||
&rid, RF_ACTIVE); | |||||
if (!(adapter->pci_mem)) { | |||||
device_printf(dev, "Unable to allocate bus resource: memory\n"); | |||||
return (ENXIO); | |||||
} | |||||
/* Save bus_space values for READ/WRITE_REG macros */ | |||||
adapter->osdep.mem_bus_space_tag = | |||||
rman_get_bustag(adapter->pci_mem); | |||||
adapter->osdep.mem_bus_space_handle = | |||||
rman_get_bushandle(adapter->pci_mem); | |||||
/* Set hw values for shared code */ | |||||
adapter->hw.hw_addr = (u8 *) &adapter->osdep.mem_bus_space_handle; | |||||
adapter->hw.back = adapter; | |||||
/* Default to 1 queue if MSI-X setup fails */ | |||||
adapter->num_queues = 1; | |||||
/* | |||||
** Now setup MSI or MSI-X, should | |||||
** return us the number of supported | |||||
** vectors. (Will be 1 for MSI) | |||||
*/ | |||||
adapter->msix = ixgbe_setup_msix(adapter); | |||||
return (0); | |||||
} | |||||
static void | |||||
ixgbe_free_pci_resources(struct adapter * adapter) | |||||
{ | |||||
struct ix_queue *que = adapter->queues; | |||||
device_t dev = adapter->dev; | |||||
int rid, memrid; | |||||
if (adapter->hw.mac.type == ixgbe_mac_82598EB) | |||||
memrid = PCIR_BAR(MSIX_82598_BAR); | |||||
else | |||||
memrid = PCIR_BAR(MSIX_82599_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: | |||||
*/ | |||||
for (int i = 0; i < adapter->num_queues; i++, que++) { | |||||
rid = que->msix + 1; | |||||
if (que->tag != NULL) { | |||||
bus_teardown_intr(dev, que->res, que->tag); | |||||
que->tag = NULL; | |||||
} | |||||
if (que->res != NULL) | |||||
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | |||||
} | |||||
/* Clean the Legacy or Link interrupt last */ | |||||
if (adapter->vector) /* we are doing MSIX */ | |||||
rid = adapter->vector + 1; | |||||
else | |||||
(adapter->msix != 0) ? (rid = 1):(rid = 0); | |||||
if (adapter->tag != NULL) { | |||||
bus_teardown_intr(dev, adapter->res, adapter->tag); | |||||
adapter->tag = NULL; | |||||
} | |||||
if (adapter->res != NULL) | |||||
bus_release_resource(dev, SYS_RES_IRQ, rid, adapter->res); | |||||
mem: | |||||
if (adapter->msix) | |||||
pci_release_msi(dev); | |||||
if (adapter->msix_mem != NULL) | |||||
bus_release_resource(dev, SYS_RES_MEMORY, | |||||
memrid, adapter->msix_mem); | |||||
if (adapter->pci_mem != NULL) | |||||
bus_release_resource(dev, SYS_RES_MEMORY, | |||||
PCIR_BAR(0), adapter->pci_mem); | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Setup networking device structure and register an interface. | |||||
* | |||||
**********************************************************************/ | |||||
static int | |||||
ixgbe_setup_interface(device_t dev, struct adapter *adapter) | |||||
{ | |||||
struct ifnet *ifp; | |||||
INIT_DEBUGOUT("ixgbe_setup_interface: begin"); | |||||
ifp = adapter->ifp = if_alloc(IFT_ETHER); | |||||
if (ifp == NULL) { | |||||
device_printf(dev, "can not allocate ifnet structure\n"); | |||||
return (-1); | |||||
} | |||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | |||||
ifp->if_baudrate = IF_Gbps(10); | |||||
ifp->if_init = ixgbe_init; | |||||
ifp->if_softc = adapter; | |||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |||||
ifp->if_ioctl = ixgbe_ioctl; | |||||
#if __FreeBSD_version >= 1100036 | |||||
if_setgetcounterfn(ifp, ixgbe_get_counter); | |||||
#endif | |||||
#if __FreeBSD_version >= 1100045 | |||||
/* TSO parameters */ | |||||
ifp->if_hw_tsomax = 65518; | |||||
ifp->if_hw_tsomaxsegcount = IXGBE_82599_SCATTER; | |||||
ifp->if_hw_tsomaxsegsize = 2048; | |||||
#endif | |||||
#ifndef IXGBE_LEGACY_TX | |||||
ifp->if_transmit = ixgbe_mq_start; | |||||
ifp->if_qflush = ixgbe_qflush; | |||||
#else | |||||
ifp->if_start = ixgbe_start; | |||||
IFQ_SET_MAXLEN(&ifp->if_snd, adapter->num_tx_desc - 2); | |||||
ifp->if_snd.ifq_drv_maxlen = adapter->num_tx_desc - 2; | |||||
IFQ_SET_READY(&ifp->if_snd); | |||||
#endif | |||||
ether_ifattach(ifp, adapter->hw.mac.addr); | |||||
adapter->max_frame_size = | |||||
ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; | |||||
/* | |||||
* Tell the upper layer(s) we support long frames. | |||||
*/ | |||||
ifp->if_hdrlen = sizeof(struct ether_vlan_header); | |||||
/* Set capability flags */ | |||||
ifp->if_capabilities |= IFCAP_RXCSUM | |||||
| IFCAP_TXCSUM | |||||
| IFCAP_RXCSUM_IPV6 | |||||
| IFCAP_TXCSUM_IPV6 | |||||
| IFCAP_TSO4 | |||||
| IFCAP_TSO6 | |||||
| IFCAP_LRO | |||||
| IFCAP_VLAN_HWTAGGING | |||||
| IFCAP_VLAN_HWTSO | |||||
| IFCAP_VLAN_HWCSUM | |||||
| IFCAP_JUMBO_MTU | |||||
| IFCAP_VLAN_MTU | |||||
| IFCAP_HWSTATS; | |||||
/* Enable the above capabilities by default */ | |||||
ifp->if_capenable = ifp->if_capabilities; | |||||
/* | |||||
** Don't 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 ixgbe driver you can | |||||
** enable this and get full hardware tag filtering. | |||||
*/ | |||||
ifp->if_capabilities |= IFCAP_VLAN_HWFILTER; | |||||
/* | |||||
* Specify the media types supported by this adapter and register | |||||
* callbacks to update media and link information | |||||
*/ | |||||
ifmedia_init(&adapter->media, IFM_IMASK, ixgbe_media_change, | |||||
ixgbe_media_status); | |||||
adapter->phy_layer = ixgbe_get_supported_physical_layer(&adapter->hw); | |||||
ixgbe_add_media_types(adapter); | |||||
/* Set autoselect media by default */ | |||||
ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO); | |||||
return (0); | |||||
} | |||||
static void | |||||
ixgbe_add_media_types(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
device_t dev = adapter->dev; | |||||
int layer; | |||||
layer = adapter->phy_layer; | |||||
/* Media types with matching FreeBSD media defines */ | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_T) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_T, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_T) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_T, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_100BASE_TX) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_100_TX, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU || | |||||
layer & IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_TWINAX, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LR) { | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_LR, 0, NULL); | |||||
if (hw->phy.multispeed_fiber) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_LX, 0, NULL); | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_SR) { | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_SR, 0, NULL); | |||||
if (hw->phy.multispeed_fiber) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_SX, 0, NULL); | |||||
} else if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_SX) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_SX, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_CX4) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_CX4, 0, NULL); | |||||
#ifdef IFM_ETH_XTYPE | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_KR, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_KX4, 0, NULL); | |||||
if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_KX, 0, NULL); | |||||
#else | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) { | |||||
device_printf(dev, "Media supported: 10GbaseKR\n"); | |||||
device_printf(dev, "10GbaseKR mapped to 10GbaseSR\n"); | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_SR, 0, NULL); | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4) { | |||||
device_printf(dev, "Media supported: 10GbaseKX4\n"); | |||||
device_printf(dev, "10GbaseKX4 mapped to 10GbaseCX4\n"); | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_CX4, 0, NULL); | |||||
} | |||||
if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) { | |||||
device_printf(dev, "Media supported: 1000baseKX\n"); | |||||
device_printf(dev, "1000baseKX mapped to 1000baseCX\n"); | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_CX, 0, NULL); | |||||
} | |||||
#endif | |||||
if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_BX) | |||||
device_printf(dev, "Media supported: 1000baseBX\n"); | |||||
if (hw->device_id == IXGBE_DEV_ID_82598AT) { | |||||
ifmedia_add(&adapter->media, | |||||
IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); | |||||
ifmedia_add(&adapter->media, | |||||
IFM_ETHER | IFM_1000_T, 0, NULL); | |||||
} | |||||
ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL); | |||||
} | |||||
static void | |||||
ixgbe_config_link(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 autoneg, err = 0; | |||||
bool sfp, negotiate; | |||||
sfp = ixgbe_is_sfp(hw); | |||||
if (sfp) { | |||||
taskqueue_enqueue(adapter->tq, &adapter->mod_task); | |||||
} else { | |||||
if (hw->mac.ops.check_link) | |||||
err = ixgbe_check_link(hw, &adapter->link_speed, | |||||
&adapter->link_up, FALSE); | |||||
if (err) | |||||
goto out; | |||||
autoneg = hw->phy.autoneg_advertised; | |||||
if ((!autoneg) && (hw->mac.ops.get_link_capabilities)) | |||||
err = hw->mac.ops.get_link_capabilities(hw, | |||||
&autoneg, &negotiate); | |||||
if (err) | |||||
goto out; | |||||
if (hw->mac.ops.setup_link) | |||||
err = hw->mac.ops.setup_link(hw, | |||||
autoneg, adapter->link_up); | |||||
} | |||||
out: | |||||
return; | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Enable transmit units. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_initialize_transmit_units(struct adapter *adapter) | |||||
{ | |||||
struct tx_ring *txr = adapter->tx_rings; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
/* Setup the Base and Length of the Tx Descriptor Ring */ | |||||
for (int i = 0; i < adapter->num_queues; i++, txr++) { | |||||
u64 tdba = txr->txdma.dma_paddr; | |||||
u32 txctrl = 0; | |||||
int j = txr->me; | |||||
IXGBE_WRITE_REG(hw, IXGBE_TDBAL(j), | |||||
(tdba & 0x00000000ffffffffULL)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_TDBAH(j), (tdba >> 32)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_TDLEN(j), | |||||
adapter->num_tx_desc * sizeof(union ixgbe_adv_tx_desc)); | |||||
/* Setup the HW Tx Head and Tail descriptor pointers */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_TDH(j), 0); | |||||
IXGBE_WRITE_REG(hw, IXGBE_TDT(j), 0); | |||||
/* Cache the tail address */ | |||||
txr->tail = IXGBE_TDT(j); | |||||
/* Disable Head Writeback */ | |||||
/* | |||||
* Note: for X550 series devices, these registers are actually | |||||
* prefixed with TPH_ isntead of DCA_, but the addresses and | |||||
* fields remain the same. | |||||
*/ | |||||
switch (hw->mac.type) { | |||||
case ixgbe_mac_82598EB: | |||||
txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(j)); | |||||
break; | |||||
default: | |||||
txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(j)); | |||||
break; | |||||
} | |||||
txctrl &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; | |||||
switch (hw->mac.type) { | |||||
case ixgbe_mac_82598EB: | |||||
IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(j), txctrl); | |||||
break; | |||||
default: | |||||
IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(j), txctrl); | |||||
break; | |||||
} | |||||
} | |||||
if (hw->mac.type != ixgbe_mac_82598EB) { | |||||
u32 dmatxctl, rttdcs; | |||||
#ifdef PCI_IOV | |||||
enum ixgbe_iov_mode mode = ixgbe_get_iov_mode(adapter); | |||||
#endif | |||||
dmatxctl = IXGBE_READ_REG(hw, IXGBE_DMATXCTL); | |||||
dmatxctl |= IXGBE_DMATXCTL_TE; | |||||
IXGBE_WRITE_REG(hw, IXGBE_DMATXCTL, dmatxctl); | |||||
/* Disable arbiter to set MTQC */ | |||||
rttdcs = IXGBE_READ_REG(hw, IXGBE_RTTDCS); | |||||
rttdcs |= IXGBE_RTTDCS_ARBDIS; | |||||
IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs); | |||||
#ifdef PCI_IOV | |||||
IXGBE_WRITE_REG(hw, IXGBE_MTQC, ixgbe_get_mtqc(mode)); | |||||
#else | |||||
IXGBE_WRITE_REG(hw, IXGBE_MTQC, IXGBE_MTQC_64Q_1PB); | |||||
#endif | |||||
rttdcs &= ~IXGBE_RTTDCS_ARBDIS; | |||||
IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs); | |||||
} | |||||
return; | |||||
} | |||||
static void | |||||
ixgbe_initialize_rss_mapping(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 reta = 0, mrqc, rss_key[10]; | |||||
int queue_id, table_size, index_mult; | |||||
#ifdef RSS | |||||
u32 rss_hash_config; | |||||
#endif | |||||
#ifdef PCI_IOV | |||||
enum ixgbe_iov_mode mode; | |||||
#endif | |||||
#ifdef RSS | |||||
/* Fetch the configured RSS key */ | |||||
rss_getkey((uint8_t *) &rss_key); | |||||
#else | |||||
/* set up random bits */ | |||||
arc4rand(&rss_key, sizeof(rss_key), 0); | |||||
#endif | |||||
/* Set multiplier for RETA setup and table size based on MAC */ | |||||
index_mult = 0x1; | |||||
table_size = 128; | |||||
switch (adapter->hw.mac.type) { | |||||
case ixgbe_mac_82598EB: | |||||
index_mult = 0x11; | |||||
break; | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
table_size = 512; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
/* Set up the redirection table */ | |||||
for (int i = 0, j = 0; i < table_size; i++, j++) { | |||||
if (j == adapter->num_queues) j = 0; | |||||
#ifdef RSS | |||||
/* | |||||
* Fetch the RSS bucket id for the given indirection entry. | |||||
* Cap it at the number of configured buckets (which is | |||||
* num_queues.) | |||||
*/ | |||||
queue_id = rss_get_indirection_to_bucket(i); | |||||
queue_id = queue_id % adapter->num_queues; | |||||
#else | |||||
queue_id = (j * index_mult); | |||||
#endif | |||||
/* | |||||
* The low 8 bits are for hash value (n+0); | |||||
* The next 8 bits are for hash value (n+1), etc. | |||||
*/ | |||||
reta = reta >> 8; | |||||
reta = reta | ( ((uint32_t) queue_id) << 24); | |||||
if ((i & 3) == 3) { | |||||
if (i < 128) | |||||
IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), reta); | |||||
else | |||||
IXGBE_WRITE_REG(hw, IXGBE_ERETA((i >> 2) - 32), reta); | |||||
reta = 0; | |||||
} | |||||
} | |||||
/* Now fill our hash function seeds */ | |||||
for (int i = 0; i < 10; i++) | |||||
IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), rss_key[i]); | |||||
/* Perform hash on these packet types */ | |||||
#ifdef RSS | |||||
mrqc = IXGBE_MRQC_RSSEN; | |||||
rss_hash_config = rss_gethashconfig(); | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4_TCP; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_TCP; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6_EX) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4_UDP; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4_EX) | |||||
device_printf(adapter->dev, | |||||
"%s: RSS_HASHTYPE_RSS_UDP_IPV4_EX defined, " | |||||
"but not supported\n", __func__); | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_UDP; | |||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6_EX) | |||||
mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP; | |||||
#else | |||||
/* | |||||
* Disable UDP - IP fragments aren't currently being handled | |||||
* and so we end up with a mix of 2-tuple and 4-tuple | |||||
* traffic. | |||||
*/ | |||||
mrqc = IXGBE_MRQC_RSSEN | |||||
| IXGBE_MRQC_RSS_FIELD_IPV4 | |||||
| IXGBE_MRQC_RSS_FIELD_IPV4_TCP | |||||
| IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP | |||||
| IXGBE_MRQC_RSS_FIELD_IPV6_EX | |||||
| IXGBE_MRQC_RSS_FIELD_IPV6 | |||||
| IXGBE_MRQC_RSS_FIELD_IPV6_TCP | |||||
; | |||||
#endif /* RSS */ | |||||
#ifdef PCI_IOV | |||||
mode = ixgbe_get_iov_mode(adapter); | |||||
mrqc |= ixgbe_get_mrqc(mode); | |||||
#endif | |||||
IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc); | |||||
} | |||||
/********************************************************************* | |||||
* | |||||
* Setup receive registers and features. | |||||
* | |||||
**********************************************************************/ | |||||
#define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2 | |||||
#define BSIZEPKT_ROUNDUP ((1<<IXGBE_SRRCTL_BSIZEPKT_SHIFT)-1) | |||||
static void | |||||
ixgbe_initialize_receive_units(struct adapter *adapter) | |||||
{ | |||||
struct rx_ring *rxr = adapter->rx_rings; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
u32 bufsz, fctrl, srrctl, rxcsum; | |||||
u32 hlreg; | |||||
/* | |||||
* Make sure receives are disabled while | |||||
* setting up the descriptor ring | |||||
*/ | |||||
ixgbe_disable_rx(hw); | |||||
/* Enable broadcasts */ | |||||
fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); | |||||
fctrl |= IXGBE_FCTRL_BAM; | |||||
if (adapter->hw.mac.type == ixgbe_mac_82598EB) { | |||||
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; | |||||
else | |||||
hlreg &= ~IXGBE_HLREG0_JUMBOEN; | |||||
#ifdef DEV_NETMAP | |||||
/* crcstrip is conditional in netmap (in RDRXCTL too ?) */ | |||||
if (ifp->if_capenable & IFCAP_NETMAP && !ix_crcstrip) | |||||
hlreg &= ~IXGBE_HLREG0_RXCRCSTRP; | |||||
else | |||||
hlreg |= IXGBE_HLREG0_RXCRCSTRP; | |||||
#endif /* DEV_NETMAP */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg); | |||||
bufsz = (adapter->rx_mbuf_sz + | |||||
BSIZEPKT_ROUNDUP) >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; | |||||
for (int i = 0; i < adapter->num_queues; i++, rxr++) { | |||||
u64 rdba = rxr->rxdma.dma_paddr; | |||||
int j = rxr->me; | |||||
/* Setup the Base and Length of the Rx Descriptor Ring */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDBAL(j), | |||||
(rdba & 0x00000000ffffffffULL)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDBAH(j), (rdba >> 32)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDLEN(j), | |||||
adapter->num_rx_desc * sizeof(union ixgbe_adv_rx_desc)); | |||||
/* Set up the SRRCTL register */ | |||||
srrctl = IXGBE_READ_REG(hw, IXGBE_SRRCTL(j)); | |||||
srrctl &= ~IXGBE_SRRCTL_BSIZEHDR_MASK; | |||||
srrctl &= ~IXGBE_SRRCTL_BSIZEPKT_MASK; | |||||
srrctl |= bufsz; | |||||
srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF; | |||||
/* | |||||
* Set DROP_EN iff we have no flow control and >1 queue. | |||||
* Note that srrctl was cleared shortly before during reset, | |||||
* so we do not need to clear the bit, but do it just in case | |||||
* this code is moved elsewhere. | |||||
*/ | |||||
if (adapter->num_queues > 1 && | |||||
adapter->hw.fc.requested_mode == ixgbe_fc_none) { | |||||
srrctl |= IXGBE_SRRCTL_DROP_EN; | |||||
} else { | |||||
srrctl &= ~IXGBE_SRRCTL_DROP_EN; | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(j), srrctl); | |||||
/* Setup the HW Rx Head and Tail Descriptor Pointers */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDH(j), 0); | |||||
IXGBE_WRITE_REG(hw, IXGBE_RDT(j), 0); | |||||
/* Set the driver rx tail address */ | |||||
rxr->tail = IXGBE_RDT(rxr->me); | |||||
} | |||||
if (adapter->hw.mac.type != ixgbe_mac_82598EB) { | |||||
u32 psrtype = IXGBE_PSRTYPE_TCPHDR | | |||||
IXGBE_PSRTYPE_UDPHDR | | |||||
IXGBE_PSRTYPE_IPV4HDR | | |||||
IXGBE_PSRTYPE_IPV6HDR; | |||||
IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(0), psrtype); | |||||
} | |||||
rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM); | |||||
ixgbe_initialize_rss_mapping(adapter); | |||||
if (adapter->num_queues > 1) { | |||||
/* RSS and RX IPP Checksum are mutually exclusive */ | |||||
rxcsum |= IXGBE_RXCSUM_PCSD; | |||||
} | |||||
if (ifp->if_capenable & IFCAP_RXCSUM) | |||||
rxcsum |= IXGBE_RXCSUM_PCSD; | |||||
/* This is useful for calculating UDP/IP fragment checksums */ | |||||
if (!(rxcsum & IXGBE_RXCSUM_PCSD)) | |||||
rxcsum |= IXGBE_RXCSUM_IPPCSE; | |||||
IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum); | |||||
return; | |||||
} | |||||
/* | |||||
** This routine is run via an vlan config EVENT, | |||||
** it enables us to use the HW Filter table since | |||||
** we can get the vlan id. This just creates the | |||||
** entry in the soft version of the VFTA, init will | |||||
** repopulate the real table. | |||||
*/ | |||||
static void | |||||
ixgbe_register_vlan(void *arg, struct ifnet *ifp, u16 vtag) | |||||
{ | |||||
struct adapter *adapter = ifp->if_softc; | |||||
u16 index, bit; | |||||
if (ifp->if_softc != arg) /* Not our event */ | |||||
return; | |||||
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ | |||||
return; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
index = (vtag >> 5) & 0x7F; | |||||
bit = vtag & 0x1F; | |||||
adapter->shadow_vfta[index] |= (1 << bit); | |||||
++adapter->num_vlans; | |||||
ixgbe_setup_vlan_hw_support(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
/* | |||||
** This routine is run via an vlan | |||||
** unconfig EVENT, remove our entry | |||||
** in the soft vfta. | |||||
*/ | |||||
static void | |||||
ixgbe_unregister_vlan(void *arg, struct ifnet *ifp, u16 vtag) | |||||
{ | |||||
struct adapter *adapter = ifp->if_softc; | |||||
u16 index, bit; | |||||
if (ifp->if_softc != arg) | |||||
return; | |||||
if ((vtag == 0) || (vtag > 4095)) /* Invalid */ | |||||
return; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
index = (vtag >> 5) & 0x7F; | |||||
bit = vtag & 0x1F; | |||||
adapter->shadow_vfta[index] &= ~(1 << bit); | |||||
--adapter->num_vlans; | |||||
/* Re-init to load the changes */ | |||||
ixgbe_setup_vlan_hw_support(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
static void | |||||
ixgbe_setup_vlan_hw_support(struct adapter *adapter) | |||||
{ | |||||
struct ifnet *ifp = adapter->ifp; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct rx_ring *rxr; | |||||
u32 ctrl; | |||||
/* | |||||
** We get here thru init_locked, meaning | |||||
** a soft reset, this has already cleared | |||||
** the VFTA and other state, so if there | |||||
** have been no vlan's registered do nothing. | |||||
*/ | |||||
if (adapter->num_vlans == 0) | |||||
return; | |||||
/* Setup the queues for vlans */ | |||||
for (int i = 0; i < adapter->num_queues; i++) { | |||||
rxr = &adapter->rx_rings[i]; | |||||
/* On 82599 the VLAN enable is per/queue in RXDCTL */ | |||||
if (hw->mac.type != ixgbe_mac_82598EB) { | |||||
ctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me)); | |||||
ctrl |= IXGBE_RXDCTL_VME; | |||||
IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me), ctrl); | |||||
} | |||||
rxr->vtag_strip = TRUE; | |||||
} | |||||
if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0) | |||||
return; | |||||
/* | |||||
** A soft reset zero's out the VFTA, so | |||||
** we need to repopulate it now. | |||||
*/ | |||||
for (int i = 0; i < IXGBE_VFTA_SIZE; i++) | |||||
if (adapter->shadow_vfta[i] != 0) | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), | |||||
adapter->shadow_vfta[i]); | |||||
ctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL); | |||||
/* Enable the Filter Table if enabled */ | |||||
if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) { | |||||
ctrl &= ~IXGBE_VLNCTRL_CFIEN; | |||||
ctrl |= IXGBE_VLNCTRL_VFE; | |||||
} | |||||
if (hw->mac.type == ixgbe_mac_82598EB) | |||||
ctrl |= IXGBE_VLNCTRL_VME; | |||||
IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, ctrl); | |||||
} | |||||
static void | |||||
ixgbe_enable_intr(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ix_queue *que = adapter->queues; | |||||
u32 mask, fwsm; | |||||
mask = (IXGBE_EIMS_ENABLE_MASK & ~IXGBE_EIMS_RTX_QUEUE); | |||||
/* Enable Fan Failure detection */ | |||||
if (hw->device_id == IXGBE_DEV_ID_82598AT) | |||||
mask |= IXGBE_EIMS_GPI_SDP1; | |||||
switch (adapter->hw.mac.type) { | |||||
case ixgbe_mac_82599EB: | |||||
mask |= IXGBE_EIMS_ECC; | |||||
/* Temperature sensor on some adapters */ | |||||
mask |= IXGBE_EIMS_GPI_SDP0; | |||||
/* SFP+ (RX_LOS_N & MOD_ABS_N) */ | |||||
mask |= IXGBE_EIMS_GPI_SDP1; | |||||
mask |= IXGBE_EIMS_GPI_SDP2; | |||||
#ifdef IXGBE_FDIR | |||||
mask |= IXGBE_EIMS_FLOW_DIR; | |||||
#endif | |||||
#ifdef PCI_IOV | |||||
mask |= IXGBE_EIMS_MAILBOX; | |||||
#endif | |||||
break; | |||||
case ixgbe_mac_X540: | |||||
/* Detect if Thermal Sensor is enabled */ | |||||
fwsm = IXGBE_READ_REG(hw, IXGBE_FWSM); | |||||
if (fwsm & IXGBE_FWSM_TS_ENABLED) | |||||
mask |= IXGBE_EIMS_TS; | |||||
mask |= IXGBE_EIMS_ECC; | |||||
#ifdef IXGBE_FDIR | |||||
mask |= IXGBE_EIMS_FLOW_DIR; | |||||
#endif | |||||
break; | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
/* MAC thermal sensor is automatically enabled */ | |||||
mask |= IXGBE_EIMS_TS; | |||||
/* Some devices use SDP0 for important information */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP || | |||||
hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) | |||||
mask |= IXGBE_EIMS_GPI_SDP0_BY_MAC(hw); | |||||
mask |= IXGBE_EIMS_ECC; | |||||
#ifdef IXGBE_FDIR | |||||
mask |= IXGBE_EIMS_FLOW_DIR; | |||||
#endif | |||||
#ifdef PCI_IOV | |||||
mask |= IXGBE_EIMS_MAILBOX; | |||||
#endif | |||||
/* falls through */ | |||||
default: | |||||
break; | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMS, mask); | |||||
/* With MSI-X we use auto clear */ | |||||
if (adapter->msix_mem) { | |||||
mask = IXGBE_EIMS_ENABLE_MASK; | |||||
/* Don't autoclear Link */ | |||||
mask &= ~IXGBE_EIMS_OTHER; | |||||
mask &= ~IXGBE_EIMS_LSC; | |||||
#ifdef PCI_IOV | |||||
mask &= ~IXGBE_EIMS_MAILBOX; | |||||
#endif | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIAC, mask); | |||||
} | |||||
/* | |||||
** Now enable all queues, this is done separately to | |||||
** allow for handling the extended (beyond 32) MSIX | |||||
** vectors that can be used by 82599 | |||||
*/ | |||||
for (int i = 0; i < adapter->num_queues; i++, que++) | |||||
ixgbe_enable_queue(adapter, que->msix); | |||||
IXGBE_WRITE_FLUSH(hw); | |||||
return; | |||||
} | |||||
static void | |||||
ixgbe_disable_intr(struct adapter *adapter) | |||||
{ | |||||
if (adapter->msix_mem) | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC, 0); | |||||
if (adapter->hw.mac.type == ixgbe_mac_82598EB) { | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0); | |||||
} else { | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, 0xFFFF0000); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC_EX(0), ~0); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC_EX(1), ~0); | |||||
} | |||||
IXGBE_WRITE_FLUSH(&adapter->hw); | |||||
return; | |||||
} | |||||
/* | |||||
** Get the width and transaction speed of | |||||
** the slot this adapter is plugged into. | |||||
*/ | |||||
static void | |||||
ixgbe_get_slot_info(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ixgbe_mac_info *mac = &hw->mac; | |||||
u16 link; | |||||
u32 offset; | |||||
/* For most devices simply call the shared code routine */ | |||||
if (hw->device_id != IXGBE_DEV_ID_82599_SFP_SF_QP) { | |||||
ixgbe_get_bus_info(hw); | |||||
/* These devices don't use PCI-E */ | |||||
switch (hw->mac.type) { | |||||
case ixgbe_mac_X550EM_x: | |||||
return; | |||||
default: | |||||
goto display; | |||||
} | |||||
} | |||||
/* | |||||
** For the Quad port adapter we need to parse back | |||||
** up the PCI tree to find the speed of the expansion | |||||
** slot into which this adapter is plugged. A bit more work. | |||||
*/ | |||||
dev = device_get_parent(device_get_parent(dev)); | |||||
#ifdef IXGBE_DEBUG | |||||
device_printf(dev, "parent pcib = %x,%x,%x\n", | |||||
pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev)); | |||||
#endif | |||||
dev = device_get_parent(device_get_parent(dev)); | |||||
#ifdef IXGBE_DEBUG | |||||
device_printf(dev, "slot pcib = %x,%x,%x\n", | |||||
pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev)); | |||||
#endif | |||||
/* Now get the PCI Express Capabilities offset */ | |||||
pci_find_cap(dev, PCIY_EXPRESS, &offset); | |||||
/* ...and read the Link Status Register */ | |||||
link = pci_read_config(dev, offset + PCIER_LINK_STA, 2); | |||||
switch (link & IXGBE_PCI_LINK_WIDTH) { | |||||
case IXGBE_PCI_LINK_WIDTH_1: | |||||
hw->bus.width = ixgbe_bus_width_pcie_x1; | |||||
break; | |||||
case IXGBE_PCI_LINK_WIDTH_2: | |||||
hw->bus.width = ixgbe_bus_width_pcie_x2; | |||||
break; | |||||
case IXGBE_PCI_LINK_WIDTH_4: | |||||
hw->bus.width = ixgbe_bus_width_pcie_x4; | |||||
break; | |||||
case IXGBE_PCI_LINK_WIDTH_8: | |||||
hw->bus.width = ixgbe_bus_width_pcie_x8; | |||||
break; | |||||
default: | |||||
hw->bus.width = ixgbe_bus_width_unknown; | |||||
break; | |||||
} | |||||
switch (link & IXGBE_PCI_LINK_SPEED) { | |||||
case IXGBE_PCI_LINK_SPEED_2500: | |||||
hw->bus.speed = ixgbe_bus_speed_2500; | |||||
break; | |||||
case IXGBE_PCI_LINK_SPEED_5000: | |||||
hw->bus.speed = ixgbe_bus_speed_5000; | |||||
break; | |||||
case IXGBE_PCI_LINK_SPEED_8000: | |||||
hw->bus.speed = ixgbe_bus_speed_8000; | |||||
break; | |||||
default: | |||||
hw->bus.speed = ixgbe_bus_speed_unknown; | |||||
break; | |||||
} | |||||
mac->ops.set_lan_id(hw); | |||||
display: | |||||
device_printf(dev,"PCI Express Bus: Speed %s %s\n", | |||||
((hw->bus.speed == ixgbe_bus_speed_8000) ? "8.0GT/s": | |||||
(hw->bus.speed == ixgbe_bus_speed_5000) ? "5.0GT/s": | |||||
(hw->bus.speed == ixgbe_bus_speed_2500) ? "2.5GT/s":"Unknown"), | |||||
(hw->bus.width == ixgbe_bus_width_pcie_x8) ? "Width x8" : | |||||
(hw->bus.width == ixgbe_bus_width_pcie_x4) ? "Width x4" : | |||||
(hw->bus.width == ixgbe_bus_width_pcie_x1) ? "Width x1" : | |||||
("Unknown")); | |||||
if ((hw->device_id != IXGBE_DEV_ID_82599_SFP_SF_QP) && | |||||
((hw->bus.width <= ixgbe_bus_width_pcie_x4) && | |||||
(hw->bus.speed == ixgbe_bus_speed_2500))) { | |||||
device_printf(dev, "PCI-Express bandwidth available" | |||||
" for this card\n is not sufficient for" | |||||
" optimal performance.\n"); | |||||
device_printf(dev, "For optimal performance a x8 " | |||||
"PCIE, or x4 PCIE Gen2 slot is required.\n"); | |||||
} | |||||
if ((hw->device_id == IXGBE_DEV_ID_82599_SFP_SF_QP) && | |||||
((hw->bus.width <= ixgbe_bus_width_pcie_x8) && | |||||
(hw->bus.speed < ixgbe_bus_speed_8000))) { | |||||
device_printf(dev, "PCI-Express bandwidth available" | |||||
" for this card\n is not sufficient for" | |||||
" optimal performance.\n"); | |||||
device_printf(dev, "For optimal performance a x8 " | |||||
"PCIE Gen3 slot is required.\n"); | |||||
} | |||||
return; | |||||
} | |||||
/* | |||||
** Setup the correct IVAR register for a particular MSIX interrupt | |||||
** (yes this is all very magic and confusing :) | |||||
** - entry is the register array entry | |||||
** - vector is the MSIX vector for this queue | |||||
** - type is RX/TX/MISC | |||||
*/ | |||||
static void | |||||
ixgbe_set_ivar(struct adapter *adapter, u8 entry, u8 vector, s8 type) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 ivar, index; | |||||
vector |= IXGBE_IVAR_ALLOC_VAL; | |||||
switch (hw->mac.type) { | |||||
case ixgbe_mac_82598EB: | |||||
if (type == -1) | |||||
entry = IXGBE_IVAR_OTHER_CAUSES_INDEX; | |||||
else | |||||
entry += (type * 64); | |||||
index = (entry >> 2) & 0x1F; | |||||
ivar = IXGBE_READ_REG(hw, IXGBE_IVAR(index)); | |||||
ivar &= ~(0xFF << (8 * (entry & 0x3))); | |||||
ivar |= (vector << (8 * (entry & 0x3))); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_IVAR(index), ivar); | |||||
break; | |||||
case ixgbe_mac_82599EB: | |||||
case ixgbe_mac_X540: | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
if (type == -1) { /* MISC IVAR */ | |||||
index = (entry & 1) * 8; | |||||
ivar = IXGBE_READ_REG(hw, IXGBE_IVAR_MISC); | |||||
ivar &= ~(0xFF << index); | |||||
ivar |= (vector << index); | |||||
IXGBE_WRITE_REG(hw, IXGBE_IVAR_MISC, ivar); | |||||
} else { /* RX/TX IVARS */ | |||||
index = (16 * (entry & 1)) + (8 * type); | |||||
ivar = IXGBE_READ_REG(hw, IXGBE_IVAR(entry >> 1)); | |||||
ivar &= ~(0xFF << index); | |||||
ivar |= (vector << index); | |||||
IXGBE_WRITE_REG(hw, IXGBE_IVAR(entry >> 1), ivar); | |||||
} | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
static void | |||||
ixgbe_configure_ivars(struct adapter *adapter) | |||||
{ | |||||
struct ix_queue *que = adapter->queues; | |||||
u32 newitr; | |||||
if (ixgbe_max_interrupt_rate > 0) | |||||
newitr = (4000000 / ixgbe_max_interrupt_rate) & 0x0FF8; | |||||
else { | |||||
/* | |||||
** Disable DMA coalescing if interrupt moderation is | |||||
** disabled. | |||||
*/ | |||||
adapter->dmac = 0; | |||||
newitr = 0; | |||||
} | |||||
for (int i = 0; i < adapter->num_queues; i++, que++) { | |||||
struct rx_ring *rxr = &adapter->rx_rings[i]; | |||||
struct tx_ring *txr = &adapter->tx_rings[i]; | |||||
/* First the RX queue entry */ | |||||
ixgbe_set_ivar(adapter, rxr->me, que->msix, 0); | |||||
/* ... and the TX */ | |||||
ixgbe_set_ivar(adapter, txr->me, que->msix, 1); | |||||
/* Set an Initial EITR value */ | |||||
IXGBE_WRITE_REG(&adapter->hw, | |||||
IXGBE_EITR(que->msix), newitr); | |||||
} | |||||
/* For the Link interrupt */ | |||||
ixgbe_set_ivar(adapter, 1, adapter->vector, -1); | |||||
} | |||||
/* | |||||
** ixgbe_sfp_probe - called in the local timer to | |||||
** determine if a port had optics inserted. | |||||
*/ | |||||
static bool | |||||
ixgbe_sfp_probe(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
device_t dev = adapter->dev; | |||||
bool result = FALSE; | |||||
if ((hw->phy.type == ixgbe_phy_nl) && | |||||
(hw->phy.sfp_type == ixgbe_sfp_type_not_present)) { | |||||
s32 ret = hw->phy.ops.identify_sfp(hw); | |||||
if (ret) | |||||
goto out; | |||||
ret = hw->phy.ops.reset(hw); | |||||
if (ret == IXGBE_ERR_SFP_NOT_SUPPORTED) { | |||||
device_printf(dev, "Unsupported SFP+ module detected!"); | |||||
device_printf(dev, "Reload driver with supported module.\n"); | |||||
adapter->sfp_probe = FALSE; | |||||
goto out; | |||||
} else | |||||
device_printf(dev, "SFP+ module detected!\n"); | |||||
/* We now have supported optics */ | |||||
adapter->sfp_probe = FALSE; | |||||
/* Set the optics type so system reports correctly */ | |||||
ixgbe_setup_optics(adapter); | |||||
result = TRUE; | |||||
} | |||||
out: | |||||
return (result); | |||||
} | |||||
/* | |||||
** Tasklet handler for MSIX Link interrupts | |||||
** - do outside interrupt since it might sleep | |||||
*/ | |||||
static void | |||||
ixgbe_handle_link(void *context, int pending) | |||||
{ | |||||
struct adapter *adapter = context; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
ixgbe_check_link(hw, | |||||
&adapter->link_speed, &adapter->link_up, 0); | |||||
ixgbe_update_link_status(adapter); | |||||
/* Re-enable link interrupts */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_LSC); | |||||
} | |||||
/* | |||||
** Tasklet for handling SFP module interrupts | |||||
*/ | |||||
static void | |||||
ixgbe_handle_mod(void *context, int pending) | |||||
{ | |||||
struct adapter *adapter = context; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
enum ixgbe_phy_type orig_type = hw->phy.type; | |||||
device_t dev = adapter->dev; | |||||
u32 err; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
/* Check to see if the PHY type changed */ | |||||
if (hw->phy.ops.identify) { | |||||
hw->phy.type = ixgbe_phy_unknown; | |||||
hw->phy.ops.identify(hw); | |||||
} | |||||
if (hw->phy.type != orig_type) { | |||||
device_printf(dev, "Detected phy_type %d\n", hw->phy.type); | |||||
if (hw->phy.type == ixgbe_phy_none) { | |||||
hw->phy.sfp_type = ixgbe_sfp_type_unknown; | |||||
goto out; | |||||
} | |||||
/* Try to do the initialization that was skipped before */ | |||||
if (hw->phy.ops.init) | |||||
hw->phy.ops.init(hw); | |||||
if (hw->phy.ops.reset) | |||||
hw->phy.ops.reset(hw); | |||||
} | |||||
err = hw->phy.ops.identify_sfp(hw); | |||||
if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { | |||||
device_printf(dev, | |||||
"Unsupported SFP+ module type was detected.\n"); | |||||
goto out; | |||||
} | |||||
err = hw->mac.ops.setup_sfp(hw); | |||||
if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { | |||||
device_printf(dev, | |||||
"Setup failure - unsupported SFP+ module type.\n"); | |||||
goto out; | |||||
} | |||||
if (hw->phy.multispeed_fiber) | |||||
taskqueue_enqueue(adapter->tq, &adapter->msf_task); | |||||
out: | |||||
/* Update media type */ | |||||
switch (hw->mac.ops.get_media_type(hw)) { | |||||
case ixgbe_media_type_fiber: | |||||
adapter->optics = IFM_10G_SR; | |||||
break; | |||||
case ixgbe_media_type_copper: | |||||
adapter->optics = IFM_10G_TWINAX; | |||||
break; | |||||
case ixgbe_media_type_cx4: | |||||
adapter->optics = IFM_10G_CX4; | |||||
break; | |||||
default: | |||||
adapter->optics = 0; | |||||
break; | |||||
} | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return; | |||||
} | |||||
/* | |||||
** Tasklet for handling MSF (multispeed fiber) interrupts | |||||
*/ | |||||
static void | |||||
ixgbe_handle_msf(void *context, int pending) | |||||
{ | |||||
struct adapter *adapter = context; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 autoneg; | |||||
bool negotiate; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
/* get_supported_phy_layer will call hw->phy.ops.identify_sfp() */ | |||||
adapter->phy_layer = ixgbe_get_supported_physical_layer(hw); | |||||
autoneg = hw->phy.autoneg_advertised; | |||||
if ((!autoneg) && (hw->mac.ops.get_link_capabilities)) | |||||
hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiate); | |||||
if (hw->mac.ops.setup_link) | |||||
hw->mac.ops.setup_link(hw, autoneg, TRUE); | |||||
/* Adjust media types shown in ifconfig */ | |||||
ifmedia_removeall(&adapter->media); | |||||
ixgbe_add_media_types(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return; | |||||
} | |||||
/* | |||||
** Tasklet for handling interrupts from an external PHY | |||||
*/ | |||||
static void | |||||
ixgbe_handle_phy(void *context, int pending) | |||||
{ | |||||
struct adapter *adapter = context; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
int error; | |||||
error = hw->phy.ops.handle_lasi(hw); | |||||
if (error == IXGBE_ERR_OVERTEMP) | |||||
device_printf(adapter->dev, | |||||
"CRITICAL: EXTERNAL PHY OVER TEMP!! " | |||||
" PHY will downshift to lower power state!\n"); | |||||
else if (error) | |||||
device_printf(adapter->dev, | |||||
"Error handling LASI interrupt: %d\n", | |||||
error); | |||||
return; | |||||
} | |||||
#ifdef IXGBE_FDIR | |||||
/* | |||||
** Tasklet for reinitializing the Flow Director filter table | |||||
*/ | |||||
static void | |||||
ixgbe_reinit_fdir(void *context, int pending) | |||||
{ | |||||
struct adapter *adapter = context; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
if (adapter->fdir_reinit != 1) /* Shouldn't happen */ | |||||
return; | |||||
ixgbe_reinit_fdir_tables_82599(&adapter->hw); | |||||
adapter->fdir_reinit = 0; | |||||
/* re-enable flow director interrupts */ | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, IXGBE_EIMS_FLOW_DIR); | |||||
/* Restart the interface */ | |||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | |||||
return; | |||||
} | |||||
#endif | |||||
/********************************************************************* | |||||
* | |||||
* Configure DMA Coalescing | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_config_dmac(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ixgbe_dmac_config *dcfg = &hw->mac.dmac_config; | |||||
if (hw->mac.type < ixgbe_mac_X550 || | |||||
!hw->mac.ops.dmac_config) | |||||
return; | |||||
if (dcfg->watchdog_timer ^ adapter->dmac || | |||||
dcfg->link_speed ^ adapter->link_speed) { | |||||
dcfg->watchdog_timer = adapter->dmac; | |||||
dcfg->fcoe_en = false; | |||||
dcfg->link_speed = adapter->link_speed; | |||||
dcfg->num_tcs = 1; | |||||
INIT_DEBUGOUT2("dmac settings: watchdog %d, link speed %d\n", | |||||
dcfg->watchdog_timer, dcfg->link_speed); | |||||
hw->mac.ops.dmac_config(hw); | |||||
} | |||||
} | |||||
/* | |||||
* Checks whether the adapter's ports are capable of | |||||
* Wake On LAN by reading the adapter's NVM. | |||||
* | |||||
* Sets each port's hw->wol_enabled value depending | |||||
* on the value read here. | |||||
*/ | |||||
static void | |||||
ixgbe_check_wol_support(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u16 dev_caps = 0; | |||||
/* Find out WoL support for port */ | |||||
adapter->wol_support = hw->wol_enabled = 0; | |||||
ixgbe_get_device_caps(hw, &dev_caps); | |||||
if ((dev_caps & IXGBE_DEVICE_CAPS_WOL_PORT0_1) || | |||||
((dev_caps & IXGBE_DEVICE_CAPS_WOL_PORT0) && | |||||
hw->bus.func == 0)) | |||||
adapter->wol_support = hw->wol_enabled = 1; | |||||
/* Save initial wake up filter configuration */ | |||||
adapter->wufc = IXGBE_READ_REG(hw, IXGBE_WUFC); | |||||
return; | |||||
} | |||||
/* | |||||
* Prepare the adapter/port for LPLU and/or WoL | |||||
*/ | |||||
static int | |||||
ixgbe_setup_low_power_mode(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
device_t dev = adapter->dev; | |||||
s32 error = 0; | |||||
mtx_assert(&adapter->core_mtx, MA_OWNED); | |||||
if (!hw->wol_enabled) | |||||
ixgbe_set_phy_power(hw, FALSE); | |||||
/* Limit power management flow to X550EM baseT */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T | |||||
&& hw->phy.ops.enter_lplu) { | |||||
/* Turn off support for APM wakeup. (Using ACPI instead) */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_GRC, | |||||
IXGBE_READ_REG(hw, IXGBE_GRC) & ~(u32)2); | |||||
/* | |||||
* Clear Wake Up Status register to prevent any previous wakeup | |||||
* events from waking us up immediately after we suspend. | |||||
*/ | |||||
IXGBE_WRITE_REG(hw, IXGBE_WUS, 0xffffffff); | |||||
/* | |||||
* Program the Wakeup Filter Control register with user filter | |||||
* settings | |||||
*/ | |||||
IXGBE_WRITE_REG(hw, IXGBE_WUFC, adapter->wufc); | |||||
/* Enable wakeups and power management in Wakeup Control */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_WUC, | |||||
IXGBE_WUC_WKEN | IXGBE_WUC_PME_EN); | |||||
/* X550EM baseT adapters need a special LPLU flow */ | |||||
hw->phy.reset_disable = true; | |||||
ixgbe_stop(adapter); | |||||
error = hw->phy.ops.enter_lplu(hw); | |||||
if (error) | |||||
device_printf(dev, | |||||
"Error entering LPLU: %d\n", error); | |||||
hw->phy.reset_disable = false; | |||||
} else { | |||||
/* Just stop for other adapters */ | |||||
ixgbe_stop(adapter); | |||||
} | |||||
return error; | |||||
} | |||||
/********************************************************************** | |||||
* | |||||
* Update the board statistics counters. | |||||
* | |||||
**********************************************************************/ | |||||
static void | |||||
ixgbe_update_stats_counters(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 missed_rx = 0, bprc, lxon, lxoff, total; | |||||
u64 total_missed_rx = 0; | |||||
adapter->stats.pf.crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS); | |||||
adapter->stats.pf.illerrc += IXGBE_READ_REG(hw, IXGBE_ILLERRC); | |||||
adapter->stats.pf.errbc += IXGBE_READ_REG(hw, IXGBE_ERRBC); | |||||
adapter->stats.pf.mspdc += IXGBE_READ_REG(hw, IXGBE_MSPDC); | |||||
for (int i = 0; i < 16; i++) { | |||||
adapter->stats.pf.qprc[i] += IXGBE_READ_REG(hw, IXGBE_QPRC(i)); | |||||
adapter->stats.pf.qptc[i] += IXGBE_READ_REG(hw, IXGBE_QPTC(i)); | |||||
adapter->stats.pf.qprdc[i] += IXGBE_READ_REG(hw, IXGBE_QPRDC(i)); | |||||
} | |||||
adapter->stats.pf.mlfc += IXGBE_READ_REG(hw, IXGBE_MLFC); | |||||
adapter->stats.pf.mrfc += IXGBE_READ_REG(hw, IXGBE_MRFC); | |||||
adapter->stats.pf.rlec += IXGBE_READ_REG(hw, IXGBE_RLEC); | |||||
/* Hardware workaround, gprc counts missed packets */ | |||||
adapter->stats.pf.gprc += IXGBE_READ_REG(hw, IXGBE_GPRC); | |||||
adapter->stats.pf.gprc -= missed_rx; | |||||
if (hw->mac.type != ixgbe_mac_82598EB) { | |||||
adapter->stats.pf.gorc += IXGBE_READ_REG(hw, IXGBE_GORCL) + | |||||
((u64)IXGBE_READ_REG(hw, IXGBE_GORCH) << 32); | |||||
adapter->stats.pf.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCL) + | |||||
((u64)IXGBE_READ_REG(hw, IXGBE_GOTCH) << 32); | |||||
adapter->stats.pf.tor += IXGBE_READ_REG(hw, IXGBE_TORL) + | |||||
((u64)IXGBE_READ_REG(hw, IXGBE_TORH) << 32); | |||||
adapter->stats.pf.lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXCNT); | |||||
adapter->stats.pf.lxoffrxc += IXGBE_READ_REG(hw, IXGBE_LXOFFRXCNT); | |||||
} else { | |||||
adapter->stats.pf.lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXC); | |||||
adapter->stats.pf.lxoffrxc += IXGBE_READ_REG(hw, IXGBE_LXOFFRXC); | |||||
/* 82598 only has a counter in the high register */ | |||||
adapter->stats.pf.gorc += IXGBE_READ_REG(hw, IXGBE_GORCH); | |||||
adapter->stats.pf.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCH); | |||||
adapter->stats.pf.tor += IXGBE_READ_REG(hw, IXGBE_TORH); | |||||
} | |||||
/* | |||||
* Workaround: mprc hardware is incorrectly counting | |||||
* broadcasts, so for now we subtract those. | |||||
*/ | |||||
bprc = IXGBE_READ_REG(hw, IXGBE_BPRC); | |||||
adapter->stats.pf.bprc += bprc; | |||||
adapter->stats.pf.mprc += IXGBE_READ_REG(hw, IXGBE_MPRC); | |||||
if (hw->mac.type == ixgbe_mac_82598EB) | |||||
adapter->stats.pf.mprc -= bprc; | |||||
adapter->stats.pf.prc64 += IXGBE_READ_REG(hw, IXGBE_PRC64); | |||||
adapter->stats.pf.prc127 += IXGBE_READ_REG(hw, IXGBE_PRC127); | |||||
adapter->stats.pf.prc255 += IXGBE_READ_REG(hw, IXGBE_PRC255); | |||||
adapter->stats.pf.prc511 += IXGBE_READ_REG(hw, IXGBE_PRC511); | |||||
adapter->stats.pf.prc1023 += IXGBE_READ_REG(hw, IXGBE_PRC1023); | |||||
adapter->stats.pf.prc1522 += IXGBE_READ_REG(hw, IXGBE_PRC1522); | |||||
lxon = IXGBE_READ_REG(hw, IXGBE_LXONTXC); | |||||
adapter->stats.pf.lxontxc += lxon; | |||||
lxoff = IXGBE_READ_REG(hw, IXGBE_LXOFFTXC); | |||||
adapter->stats.pf.lxofftxc += lxoff; | |||||
total = lxon + lxoff; | |||||
adapter->stats.pf.gptc += IXGBE_READ_REG(hw, IXGBE_GPTC); | |||||
adapter->stats.pf.mptc += IXGBE_READ_REG(hw, IXGBE_MPTC); | |||||
adapter->stats.pf.ptc64 += IXGBE_READ_REG(hw, IXGBE_PTC64); | |||||
adapter->stats.pf.gptc -= total; | |||||
adapter->stats.pf.mptc -= total; | |||||
adapter->stats.pf.ptc64 -= total; | |||||
adapter->stats.pf.gotc -= total * ETHER_MIN_LEN; | |||||
adapter->stats.pf.ruc += IXGBE_READ_REG(hw, IXGBE_RUC); | |||||
adapter->stats.pf.rfc += IXGBE_READ_REG(hw, IXGBE_RFC); | |||||
adapter->stats.pf.roc += IXGBE_READ_REG(hw, IXGBE_ROC); | |||||
adapter->stats.pf.rjc += IXGBE_READ_REG(hw, IXGBE_RJC); | |||||
adapter->stats.pf.mngprc += IXGBE_READ_REG(hw, IXGBE_MNGPRC); | |||||
adapter->stats.pf.mngpdc += IXGBE_READ_REG(hw, IXGBE_MNGPDC); | |||||
adapter->stats.pf.mngptc += IXGBE_READ_REG(hw, IXGBE_MNGPTC); | |||||
adapter->stats.pf.tpr += IXGBE_READ_REG(hw, IXGBE_TPR); | |||||
adapter->stats.pf.tpt += IXGBE_READ_REG(hw, IXGBE_TPT); | |||||
adapter->stats.pf.ptc127 += IXGBE_READ_REG(hw, IXGBE_PTC127); | |||||
adapter->stats.pf.ptc255 += IXGBE_READ_REG(hw, IXGBE_PTC255); | |||||
adapter->stats.pf.ptc511 += IXGBE_READ_REG(hw, IXGBE_PTC511); | |||||
adapter->stats.pf.ptc1023 += IXGBE_READ_REG(hw, IXGBE_PTC1023); | |||||
adapter->stats.pf.ptc1522 += IXGBE_READ_REG(hw, IXGBE_PTC1522); | |||||
adapter->stats.pf.bptc += IXGBE_READ_REG(hw, IXGBE_BPTC); | |||||
adapter->stats.pf.xec += IXGBE_READ_REG(hw, IXGBE_XEC); | |||||
adapter->stats.pf.fccrc += IXGBE_READ_REG(hw, IXGBE_FCCRC); | |||||
adapter->stats.pf.fclast += IXGBE_READ_REG(hw, IXGBE_FCLAST); | |||||
/* Only read FCOE on 82599 */ | |||||
if (hw->mac.type != ixgbe_mac_82598EB) { | |||||
adapter->stats.pf.fcoerpdc += IXGBE_READ_REG(hw, IXGBE_FCOERPDC); | |||||
adapter->stats.pf.fcoeprc += IXGBE_READ_REG(hw, IXGBE_FCOEPRC); | |||||
adapter->stats.pf.fcoeptc += IXGBE_READ_REG(hw, IXGBE_FCOEPTC); | |||||
adapter->stats.pf.fcoedwrc += IXGBE_READ_REG(hw, IXGBE_FCOEDWRC); | |||||
adapter->stats.pf.fcoedwtc += IXGBE_READ_REG(hw, IXGBE_FCOEDWTC); | |||||
} | |||||
/* Fill out the OS statistics structure */ | |||||
IXGBE_SET_IPACKETS(adapter, adapter->stats.pf.gprc); | |||||
IXGBE_SET_OPACKETS(adapter, adapter->stats.pf.gptc); | |||||
IXGBE_SET_IBYTES(adapter, adapter->stats.pf.gorc); | |||||
IXGBE_SET_OBYTES(adapter, adapter->stats.pf.gotc); | |||||
IXGBE_SET_IMCASTS(adapter, adapter->stats.pf.mprc); | |||||
IXGBE_SET_OMCASTS(adapter, adapter->stats.pf.mptc); | |||||
IXGBE_SET_COLLISIONS(adapter, 0); | |||||
IXGBE_SET_IQDROPS(adapter, total_missed_rx); | |||||
IXGBE_SET_IERRORS(adapter, adapter->stats.pf.crcerrs | |||||
+ adapter->stats.pf.rlec); | |||||
} | |||||
#if __FreeBSD_version >= 1100036 | |||||
static uint64_t | |||||
ixgbe_get_counter(struct ifnet *ifp, ift_counter cnt) | |||||
{ | |||||
struct adapter *adapter; | |||||
struct tx_ring *txr; | |||||
uint64_t rv; | |||||
adapter = if_getsoftc(ifp); | |||||
switch (cnt) { | |||||
case IFCOUNTER_IPACKETS: | |||||
return (adapter->ipackets); | |||||
case IFCOUNTER_OPACKETS: | |||||
return (adapter->opackets); | |||||
case IFCOUNTER_IBYTES: | |||||
return (adapter->ibytes); | |||||
case IFCOUNTER_OBYTES: | |||||
return (adapter->obytes); | |||||
case IFCOUNTER_IMCASTS: | |||||
return (adapter->imcasts); | |||||
case IFCOUNTER_OMCASTS: | |||||
return (adapter->omcasts); | |||||
case IFCOUNTER_COLLISIONS: | |||||
return (0); | |||||
case IFCOUNTER_IQDROPS: | |||||
return (adapter->iqdrops); | |||||
case IFCOUNTER_OQDROPS: | |||||
rv = 0; | |||||
txr = adapter->tx_rings; | |||||
for (int i = 0; i < adapter->num_queues; i++, txr++) | |||||
rv += txr->br->br_drops; | |||||
return (rv); | |||||
case IFCOUNTER_IERRORS: | |||||
return (adapter->ierrors); | |||||
default: | |||||
return (if_get_counter_default(ifp, cnt)); | |||||
} | |||||
} | |||||
#endif | |||||
/** ixgbe_sysctl_tdh_handler - Handler function | |||||
* Retrieves the TDH value from the hardware | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_tdh_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error; | |||||
struct tx_ring *txr = ((struct tx_ring *)oidp->oid_arg1); | |||||
if (!txr) return 0; | |||||
unsigned val = IXGBE_READ_REG(&txr->adapter->hw, IXGBE_TDH(txr->me)); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
return 0; | |||||
} | |||||
/** ixgbe_sysctl_tdt_handler - Handler function | |||||
* Retrieves the TDT value from the hardware | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_tdt_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error; | |||||
struct tx_ring *txr = ((struct tx_ring *)oidp->oid_arg1); | |||||
if (!txr) return 0; | |||||
unsigned val = IXGBE_READ_REG(&txr->adapter->hw, IXGBE_TDT(txr->me)); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
return 0; | |||||
} | |||||
/** ixgbe_sysctl_rdh_handler - Handler function | |||||
* Retrieves the RDH value from the hardware | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_rdh_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error; | |||||
struct rx_ring *rxr = ((struct rx_ring *)oidp->oid_arg1); | |||||
if (!rxr) return 0; | |||||
unsigned val = IXGBE_READ_REG(&rxr->adapter->hw, IXGBE_RDH(rxr->me)); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
return 0; | |||||
} | |||||
/** ixgbe_sysctl_rdt_handler - Handler function | |||||
* Retrieves the RDT value from the hardware | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_rdt_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error; | |||||
struct rx_ring *rxr = ((struct rx_ring *)oidp->oid_arg1); | |||||
if (!rxr) return 0; | |||||
unsigned val = IXGBE_READ_REG(&rxr->adapter->hw, IXGBE_RDT(rxr->me)); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
return 0; | |||||
} | |||||
static int | |||||
ixgbe_sysctl_interrupt_rate_handler(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error; | |||||
struct ix_queue *que = ((struct ix_queue *)oidp->oid_arg1); | |||||
unsigned int reg, usec, rate; | |||||
reg = IXGBE_READ_REG(&que->adapter->hw, IXGBE_EITR(que->msix)); | |||||
usec = ((reg & 0x0FF8) >> 3); | |||||
if (usec > 0) | |||||
rate = 500000 / usec; | |||||
else | |||||
rate = 0; | |||||
error = sysctl_handle_int(oidp, &rate, 0, req); | |||||
if (error || !req->newptr) | |||||
return error; | |||||
reg &= ~0xfff; /* default, no limitation */ | |||||
ixgbe_max_interrupt_rate = 0; | |||||
if (rate > 0 && rate < 500000) { | |||||
if (rate < 1000) | |||||
rate = 1000; | |||||
ixgbe_max_interrupt_rate = rate; | |||||
reg |= ((4000000/rate) & 0xff8 ); | |||||
} | |||||
IXGBE_WRITE_REG(&que->adapter->hw, IXGBE_EITR(que->msix), reg); | |||||
return 0; | |||||
} | |||||
static void | |||||
ixgbe_add_device_sysctls(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct sysctl_oid_list *child; | |||||
struct sysctl_ctx_list *ctx; | |||||
ctx = device_get_sysctl_ctx(dev); | |||||
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | |||||
/* Sysctls for all devices */ | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fc", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_flowcntl, "I", IXGBE_SYSCTL_DESC_SET_FC); | |||||
SYSCTL_ADD_INT(ctx, child, OID_AUTO, "enable_aim", | |||||
CTLFLAG_RW, | |||||
&ixgbe_enable_aim, 1, "Interrupt Moderation"); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "advertise_speed", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_advertise, "I", IXGBE_SYSCTL_DESC_ADV_SPEED); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "thermal_test", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_thermal_test, "I", "Thermal Test"); | |||||
#ifdef IXGBE_DEBUG | |||||
/* testing sysctls (for all devices) */ | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "power_state", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_power_state, "I", "PCI Power State"); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "print_rss_config", | |||||
CTLTYPE_STRING | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_print_rss_config, "A", "Prints RSS Configuration"); | |||||
#endif | |||||
/* for X550 series devices */ | |||||
if (hw->mac.type >= ixgbe_mac_X550) | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "dmac", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_dmac, "I", "DMA Coalesce"); | |||||
/* for X552 backplane devices */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_KR) { | |||||
struct sysctl_oid *eee_node; | |||||
struct sysctl_oid_list *eee_list; | |||||
eee_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "eee", | |||||
CTLFLAG_RD, NULL, | |||||
"Energy Efficient Ethernet sysctls"); | |||||
eee_list = SYSCTL_CHILDREN(eee_node); | |||||
SYSCTL_ADD_PROC(ctx, eee_list, OID_AUTO, "enable", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_eee_enable, "I", | |||||
"Enable or Disable EEE"); | |||||
SYSCTL_ADD_PROC(ctx, eee_list, OID_AUTO, "negotiated", | |||||
CTLTYPE_INT | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_eee_negotiated, "I", | |||||
"EEE negotiated on link"); | |||||
SYSCTL_ADD_PROC(ctx, eee_list, OID_AUTO, "tx_lpi_status", | |||||
CTLTYPE_INT | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_eee_tx_lpi_status, "I", | |||||
"Whether or not TX link is in LPI state"); | |||||
SYSCTL_ADD_PROC(ctx, eee_list, OID_AUTO, "rx_lpi_status", | |||||
CTLTYPE_INT | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_eee_rx_lpi_status, "I", | |||||
"Whether or not RX link is in LPI state"); | |||||
SYSCTL_ADD_PROC(ctx, eee_list, OID_AUTO, "tx_lpi_delay", | |||||
CTLTYPE_INT | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_eee_tx_lpi_delay, "I", | |||||
"TX LPI entry delay in microseconds"); | |||||
} | |||||
/* for WoL-capable devices */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) { | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "wol_enable", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_wol_enable, "I", | |||||
"Enable/Disable Wake on LAN"); | |||||
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "wufc", | |||||
CTLTYPE_INT | CTLFLAG_RW, adapter, 0, | |||||
ixgbe_sysctl_wufc, "I", | |||||
"Enable/Disable Wake Up Filters"); | |||||
} | |||||
/* for X552/X557-AT devices */ | |||||
if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) { | |||||
struct sysctl_oid *phy_node; | |||||
struct sysctl_oid_list *phy_list; | |||||
phy_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "phy", | |||||
CTLFLAG_RD, NULL, | |||||
"External PHY sysctls"); | |||||
phy_list = SYSCTL_CHILDREN(phy_node); | |||||
SYSCTL_ADD_PROC(ctx, phy_list, OID_AUTO, "temp", | |||||
CTLTYPE_INT | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_phy_temp, "I", | |||||
"Current External PHY Temperature (Celsius)"); | |||||
SYSCTL_ADD_PROC(ctx, phy_list, OID_AUTO, "overtemp_occurred", | |||||
CTLTYPE_INT | CTLFLAG_RD, adapter, 0, | |||||
ixgbe_sysctl_phy_overtemp_occurred, "I", | |||||
"External PHY High Temperature Event Occurred"); | |||||
} | |||||
} | |||||
/* | |||||
* Add sysctl variables, one per statistic, to the system. | |||||
*/ | |||||
static void | |||||
ixgbe_add_hw_stats(struct adapter *adapter) | |||||
{ | |||||
device_t dev = adapter->dev; | |||||
struct tx_ring *txr = adapter->tx_rings; | |||||
struct rx_ring *rxr = adapter->rx_rings; | |||||
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); | |||||
struct sysctl_oid *tree = device_get_sysctl_tree(dev); | |||||
struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); | |||||
struct ixgbe_hw_stats *stats = &adapter->stats.pf; | |||||
struct sysctl_oid *stat_node, *queue_node; | |||||
struct sysctl_oid_list *stat_list, *queue_list; | |||||
#define QUEUE_NAME_LEN 32 | |||||
char namebuf[QUEUE_NAME_LEN]; | |||||
/* Driver Statistics */ | |||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "dropped", | |||||
CTLFLAG_RD, &adapter->dropped_pkts, | |||||
"Driver dropped packets"); | |||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "mbuf_defrag_failed", | |||||
CTLFLAG_RD, &adapter->mbuf_defrag_failed, | |||||
"m_defrag() failed"); | |||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_events", | |||||
CTLFLAG_RD, &adapter->watchdog_events, | |||||
"Watchdog timeouts"); | |||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "link_irq", | |||||
CTLFLAG_RD, &adapter->link_irq, | |||||
"Link MSIX IRQ Handled"); | |||||
for (int i = 0; i < adapter->num_queues; i++, txr++) { | |||||
snprintf(namebuf, QUEUE_NAME_LEN, "queue%d", i); | |||||
queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, | |||||
CTLFLAG_RD, NULL, "Queue Name"); | |||||
queue_list = SYSCTL_CHILDREN(queue_node); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "interrupt_rate", | |||||
CTLTYPE_UINT | CTLFLAG_RW, &adapter->queues[i], | |||||
sizeof(&adapter->queues[i]), | |||||
ixgbe_sysctl_interrupt_rate_handler, "IU", | |||||
"Interrupt Rate"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "irqs", | |||||
CTLFLAG_RD, &(adapter->queues[i].irqs), | |||||
"irqs on this queue"); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_head", | |||||
CTLTYPE_UINT | CTLFLAG_RD, txr, sizeof(txr), | |||||
ixgbe_sysctl_tdh_handler, "IU", | |||||
"Transmit Descriptor Head"); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_tail", | |||||
CTLTYPE_UINT | CTLFLAG_RD, txr, sizeof(txr), | |||||
ixgbe_sysctl_tdt_handler, "IU", | |||||
"Transmit Descriptor Tail"); | |||||
SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tso_tx", | |||||
CTLFLAG_RD, &txr->tso_tx, | |||||
"TSO"); | |||||
SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "no_tx_dma_setup", | |||||
CTLFLAG_RD, &txr->no_tx_dma_setup, | |||||
"Driver tx dma failure in xmit"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", | |||||
CTLFLAG_RD, &txr->no_desc_avail, | |||||
"Queue No Descriptor Available"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_packets", | |||||
CTLFLAG_RD, &txr->total_packets, | |||||
"Queue Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "br_drops", | |||||
CTLFLAG_RD, &txr->br->br_drops, | |||||
"Packets dropped in buf_ring"); | |||||
} | |||||
for (int i = 0; i < adapter->num_queues; i++, rxr++) { | |||||
snprintf(namebuf, QUEUE_NAME_LEN, "queue%d", i); | |||||
queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, | |||||
CTLFLAG_RD, NULL, "Queue Name"); | |||||
queue_list = SYSCTL_CHILDREN(queue_node); | |||||
struct lro_ctrl *lro = &rxr->lro; | |||||
snprintf(namebuf, QUEUE_NAME_LEN, "queue%d", i); | |||||
queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, | |||||
CTLFLAG_RD, NULL, "Queue Name"); | |||||
queue_list = SYSCTL_CHILDREN(queue_node); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_head", | |||||
CTLTYPE_UINT | CTLFLAG_RD, rxr, sizeof(rxr), | |||||
ixgbe_sysctl_rdh_handler, "IU", | |||||
"Receive Descriptor Head"); | |||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_tail", | |||||
CTLTYPE_UINT | CTLFLAG_RD, rxr, sizeof(rxr), | |||||
ixgbe_sysctl_rdt_handler, "IU", | |||||
"Receive Descriptor Tail"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_packets", | |||||
CTLFLAG_RD, &rxr->rx_packets, | |||||
"Queue Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_bytes", | |||||
CTLFLAG_RD, &rxr->rx_bytes, | |||||
"Queue Bytes Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_copies", | |||||
CTLFLAG_RD, &rxr->rx_copies, | |||||
"Copied RX Frames"); | |||||
SYSCTL_ADD_U64(ctx, queue_list, OID_AUTO, "lro_queued", | |||||
CTLFLAG_RD, &lro->lro_queued, 0, | |||||
"LRO Queued"); | |||||
SYSCTL_ADD_U64(ctx, queue_list, OID_AUTO, "lro_flushed", | |||||
CTLFLAG_RD, &lro->lro_flushed, 0, | |||||
"LRO Flushed"); | |||||
} | |||||
/* MAC stats get the own sub node */ | |||||
stat_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "mac_stats", | |||||
CTLFLAG_RD, NULL, "MAC Statistics"); | |||||
stat_list = SYSCTL_CHILDREN(stat_node); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "crc_errs", | |||||
CTLFLAG_RD, &stats->crcerrs, | |||||
"CRC Errors"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "ill_errs", | |||||
CTLFLAG_RD, &stats->illerrc, | |||||
"Illegal Byte Errors"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "byte_errs", | |||||
CTLFLAG_RD, &stats->errbc, | |||||
"Byte Errors"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "short_discards", | |||||
CTLFLAG_RD, &stats->mspdc, | |||||
"MAC Short Packets Discarded"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "local_faults", | |||||
CTLFLAG_RD, &stats->mlfc, | |||||
"MAC Local Faults"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "remote_faults", | |||||
CTLFLAG_RD, &stats->mrfc, | |||||
"MAC Remote Faults"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rec_len_errs", | |||||
CTLFLAG_RD, &stats->rlec, | |||||
"Receive Length Errors"); | |||||
/* Flow Control stats */ | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_txd", | |||||
CTLFLAG_RD, &stats->lxontxc, | |||||
"Link XON Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_recvd", | |||||
CTLFLAG_RD, &stats->lxonrxc, | |||||
"Link XON Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_txd", | |||||
CTLFLAG_RD, &stats->lxofftxc, | |||||
"Link XOFF Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_recvd", | |||||
CTLFLAG_RD, &stats->lxoffrxc, | |||||
"Link XOFF Received"); | |||||
/* Packet Reception Stats */ | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_octets_rcvd", | |||||
CTLFLAG_RD, &stats->tor, | |||||
"Total Octets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_rcvd", | |||||
CTLFLAG_RD, &stats->gorc, | |||||
"Good Octets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_rcvd", | |||||
CTLFLAG_RD, &stats->tpr, | |||||
"Total Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_rcvd", | |||||
CTLFLAG_RD, &stats->gprc, | |||||
"Good Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_rcvd", | |||||
CTLFLAG_RD, &stats->mprc, | |||||
"Multicast Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_rcvd", | |||||
CTLFLAG_RD, &stats->bprc, | |||||
"Broadcast Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_64", | |||||
CTLFLAG_RD, &stats->prc64, | |||||
"64 byte frames received "); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_65_127", | |||||
CTLFLAG_RD, &stats->prc127, | |||||
"65-127 byte frames received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_128_255", | |||||
CTLFLAG_RD, &stats->prc255, | |||||
"128-255 byte frames received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_256_511", | |||||
CTLFLAG_RD, &stats->prc511, | |||||
"256-511 byte frames received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_512_1023", | |||||
CTLFLAG_RD, &stats->prc1023, | |||||
"512-1023 byte frames received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_1024_1522", | |||||
CTLFLAG_RD, &stats->prc1522, | |||||
"1023-1522 byte frames received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_undersized", | |||||
CTLFLAG_RD, &stats->ruc, | |||||
"Receive Undersized"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_fragmented", | |||||
CTLFLAG_RD, &stats->rfc, | |||||
"Fragmented Packets Received "); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_oversized", | |||||
CTLFLAG_RD, &stats->roc, | |||||
"Oversized Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_jabberd", | |||||
CTLFLAG_RD, &stats->rjc, | |||||
"Received Jabber"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "management_pkts_rcvd", | |||||
CTLFLAG_RD, &stats->mngprc, | |||||
"Management Packets Received"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "management_pkts_drpd", | |||||
CTLFLAG_RD, &stats->mngptc, | |||||
"Management Packets Dropped"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "checksum_errs", | |||||
CTLFLAG_RD, &stats->xec, | |||||
"Checksum Errors"); | |||||
/* Packet Transmission Stats */ | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_txd", | |||||
CTLFLAG_RD, &stats->gotc, | |||||
"Good Octets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_txd", | |||||
CTLFLAG_RD, &stats->tpt, | |||||
"Total Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_txd", | |||||
CTLFLAG_RD, &stats->gptc, | |||||
"Good Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_txd", | |||||
CTLFLAG_RD, &stats->bptc, | |||||
"Broadcast Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_txd", | |||||
CTLFLAG_RD, &stats->mptc, | |||||
"Multicast Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "management_pkts_txd", | |||||
CTLFLAG_RD, &stats->mngptc, | |||||
"Management Packets Transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_64", | |||||
CTLFLAG_RD, &stats->ptc64, | |||||
"64 byte frames transmitted "); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_65_127", | |||||
CTLFLAG_RD, &stats->ptc127, | |||||
"65-127 byte frames transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_128_255", | |||||
CTLFLAG_RD, &stats->ptc255, | |||||
"128-255 byte frames transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_256_511", | |||||
CTLFLAG_RD, &stats->ptc511, | |||||
"256-511 byte frames transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_512_1023", | |||||
CTLFLAG_RD, &stats->ptc1023, | |||||
"512-1023 byte frames transmitted"); | |||||
SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_1024_1522", | |||||
CTLFLAG_RD, &stats->ptc1522, | |||||
"1024-1522 byte frames transmitted"); | |||||
} | |||||
static void | |||||
ixgbe_set_sysctl_value(struct adapter *adapter, const char *name, | |||||
const char *description, int *limit, int value) | |||||
{ | |||||
*limit = value; | |||||
SYSCTL_ADD_INT(device_get_sysctl_ctx(adapter->dev), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)), | |||||
OID_AUTO, name, CTLFLAG_RW, limit, value, description); | |||||
} | |||||
/* | |||||
** Set flow control using sysctl: | |||||
** Flow control values: | |||||
** 0 - off | |||||
** 1 - rx pause | |||||
** 2 - tx pause | |||||
** 3 - full | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_flowcntl(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error, fc; | |||||
struct adapter *adapter; | |||||
adapter = (struct adapter *) arg1; | |||||
fc = adapter->fc; | |||||
error = sysctl_handle_int(oidp, &fc, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
/* Don't bother if it's not changed */ | |||||
if (adapter->fc == fc) | |||||
return (0); | |||||
return ixgbe_set_flowcntl(adapter, fc); | |||||
} | |||||
static int | |||||
ixgbe_set_flowcntl(struct adapter *adapter, int fc) | |||||
{ | |||||
switch (fc) { | |||||
case ixgbe_fc_rx_pause: | |||||
case ixgbe_fc_tx_pause: | |||||
case ixgbe_fc_full: | |||||
adapter->hw.fc.requested_mode = adapter->fc; | |||||
if (adapter->num_queues > 1) | |||||
ixgbe_disable_rx_drop(adapter); | |||||
break; | |||||
case ixgbe_fc_none: | |||||
adapter->hw.fc.requested_mode = ixgbe_fc_none; | |||||
if (adapter->num_queues > 1) | |||||
ixgbe_enable_rx_drop(adapter); | |||||
break; | |||||
default: | |||||
return (EINVAL); | |||||
} | |||||
adapter->fc = fc; | |||||
/* Don't autoneg if forcing a value */ | |||||
adapter->hw.fc.disable_fc_autoneg = TRUE; | |||||
ixgbe_fc_enable(&adapter->hw); | |||||
return (0); | |||||
} | |||||
/* | |||||
** Control advertised link speed: | |||||
** Flags: | |||||
** 0x1 - advertise 100 Mb | |||||
** 0x2 - advertise 1G | |||||
** 0x4 - advertise 10G | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_advertise(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
int error, advertise; | |||||
struct adapter *adapter; | |||||
adapter = (struct adapter *) arg1; | |||||
advertise = adapter->advertise; | |||||
error = sysctl_handle_int(oidp, &advertise, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
return ixgbe_set_advertise(adapter, advertise); | |||||
} | |||||
static int | |||||
ixgbe_set_advertise(struct adapter *adapter, int advertise) | |||||
{ | |||||
device_t dev; | |||||
struct ixgbe_hw *hw; | |||||
ixgbe_link_speed speed; | |||||
/* Checks to validate new value */ | |||||
if (adapter->advertise == advertise) /* no change */ | |||||
return (0); | |||||
hw = &adapter->hw; | |||||
dev = adapter->dev; | |||||
/* No speed changes for backplane media */ | |||||
if (hw->phy.media_type == ixgbe_media_type_backplane) | |||||
return (ENODEV); | |||||
if (!((hw->phy.media_type == ixgbe_media_type_copper) || | |||||
(hw->phy.multispeed_fiber))) { | |||||
device_printf(dev, | |||||
"Advertised speed can only be set on copper or " | |||||
"multispeed fiber media types.\n"); | |||||
return (EINVAL); | |||||
} | |||||
if (advertise < 0x1 || advertise > 0x7) { | |||||
device_printf(dev, | |||||
"Invalid advertised speed; valid modes are 0x1 through 0x7\n"); | |||||
return (EINVAL); | |||||
} | |||||
if ((advertise & 0x1) | |||||
&& (hw->mac.type != ixgbe_mac_X540) | |||||
&& (hw->mac.type != ixgbe_mac_X550)) { | |||||
device_printf(dev, "Set Advertise: 100Mb on X540/X550 only\n"); | |||||
return (EINVAL); | |||||
} | |||||
/* Set new value and report new advertised mode */ | |||||
speed = 0; | |||||
if (advertise & 0x1) | |||||
speed |= IXGBE_LINK_SPEED_100_FULL; | |||||
if (advertise & 0x2) | |||||
speed |= IXGBE_LINK_SPEED_1GB_FULL; | |||||
if (advertise & 0x4) | |||||
speed |= IXGBE_LINK_SPEED_10GB_FULL; | |||||
adapter->advertise = advertise; | |||||
hw->mac.autotry_restart = TRUE; | |||||
hw->mac.ops.setup_link(hw, speed, TRUE); | |||||
return (0); | |||||
} | |||||
/* | |||||
* The following two sysctls are for X552/X557-AT devices; | |||||
* they deal with the external PHY used in them. | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_phy_temp(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u16 reg; | |||||
if (hw->device_id != IXGBE_DEV_ID_X550EM_X_10G_T) { | |||||
device_printf(adapter->dev, | |||||
"Device has no supported external thermal sensor.\n"); | |||||
return (ENODEV); | |||||
} | |||||
if (hw->phy.ops.read_reg(hw, IXGBE_PHY_CURRENT_TEMP, | |||||
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, | |||||
®)) { | |||||
device_printf(adapter->dev, | |||||
"Error reading from PHY's current temperature register\n"); | |||||
return (EAGAIN); | |||||
} | |||||
/* Shift temp for output */ | |||||
reg = reg >> 8; | |||||
return (sysctl_handle_int(oidp, NULL, reg, req)); | |||||
} | |||||
/* | |||||
* Reports whether the current PHY temperature is over | |||||
* the overtemp threshold. | |||||
* - This is reported directly from the PHY | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_phy_overtemp_occurred(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u16 reg; | |||||
if (hw->device_id != IXGBE_DEV_ID_X550EM_X_10G_T) { | |||||
device_printf(adapter->dev, | |||||
"Device has no supported external thermal sensor.\n"); | |||||
return (ENODEV); | |||||
} | |||||
if (hw->phy.ops.read_reg(hw, IXGBE_PHY_OVERTEMP_STATUS, | |||||
IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, | |||||
®)) { | |||||
device_printf(adapter->dev, | |||||
"Error reading from PHY's temperature status register\n"); | |||||
return (EAGAIN); | |||||
} | |||||
/* Get occurrence bit */ | |||||
reg = !!(reg & 0x4000); | |||||
return (sysctl_handle_int(oidp, 0, reg, req)); | |||||
} | |||||
/* | |||||
** Thermal Shutdown Trigger (internal MAC) | |||||
** - Set this to 1 to cause an overtemp event to occur | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_thermal_test(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
int error, fire = 0; | |||||
error = sysctl_handle_int(oidp, &fire, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
if (fire) { | |||||
u32 reg = IXGBE_READ_REG(hw, IXGBE_EICS); | |||||
reg |= IXGBE_EICR_TS; | |||||
IXGBE_WRITE_REG(hw, IXGBE_EICS, reg); | |||||
} | |||||
return (0); | |||||
} | |||||
/* | |||||
** Manage DMA Coalescing. | |||||
** Control values: | |||||
** 0/1 - off / on (use default value of 1000) | |||||
** | |||||
** Legal timer values are: | |||||
** 50,100,250,500,1000,2000,5000,10000 | |||||
** | |||||
** Turning off interrupt moderation will also turn this off. | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_dmac(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
int error; | |||||
u32 newval; | |||||
newval = adapter->dmac; | |||||
error = sysctl_handle_int(oidp, &newval, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
switch (newval) { | |||||
case 0: | |||||
/* Disabled */ | |||||
adapter->dmac = 0; | |||||
break; | |||||
case 1: | |||||
/* Enable and use default */ | |||||
adapter->dmac = 1000; | |||||
break; | |||||
case 50: | |||||
case 100: | |||||
case 250: | |||||
case 500: | |||||
case 1000: | |||||
case 2000: | |||||
case 5000: | |||||
case 10000: | |||||
/* Legal values - allow */ | |||||
adapter->dmac = newval; | |||||
break; | |||||
default: | |||||
/* Do nothing, illegal value */ | |||||
return (EINVAL); | |||||
} | |||||
/* Re-initialize hardware if it's already running */ | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | |||||
ixgbe_init(adapter); | |||||
return (0); | |||||
} | |||||
#ifdef IXGBE_DEBUG | |||||
/** | |||||
* Sysctl to test power states | |||||
* Values: | |||||
* 0 - set device to D0 | |||||
* 3 - set device to D3 | |||||
* (none) - get current device power state | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_power_state(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
device_t dev = adapter->dev; | |||||
int curr_ps, new_ps, error = 0; | |||||
curr_ps = new_ps = pci_get_powerstate(dev); | |||||
error = sysctl_handle_int(oidp, &new_ps, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
if (new_ps == curr_ps) | |||||
return (0); | |||||
if (new_ps == 3 && curr_ps == 0) | |||||
error = DEVICE_SUSPEND(dev); | |||||
else if (new_ps == 0 && curr_ps == 3) | |||||
error = DEVICE_RESUME(dev); | |||||
else | |||||
return (EINVAL); | |||||
device_printf(dev, "New state: %d\n", pci_get_powerstate(dev)); | |||||
return (error); | |||||
} | |||||
#endif | |||||
/* | |||||
* Sysctl to enable/disable the WoL capability, if supported by the adapter. | |||||
* Values: | |||||
* 0 - disabled | |||||
* 1 - enabled | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_wol_enable(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
int new_wol_enabled; | |||||
int error = 0; | |||||
new_wol_enabled = hw->wol_enabled; | |||||
error = sysctl_handle_int(oidp, &new_wol_enabled, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
new_wol_enabled = !!(new_wol_enabled); | |||||
if (new_wol_enabled == hw->wol_enabled) | |||||
return (0); | |||||
if (new_wol_enabled > 0 && !adapter->wol_support) | |||||
return (ENODEV); | |||||
else | |||||
hw->wol_enabled = new_wol_enabled; | |||||
return (0); | |||||
} | |||||
/* | |||||
* Sysctl to enable/disable the Energy Efficient Ethernet capability, | |||||
* if supported by the adapter. | |||||
* Values: | |||||
* 0 - disabled | |||||
* 1 - enabled | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_eee_enable(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
struct ifnet *ifp = adapter->ifp; | |||||
int new_eee_enabled, error = 0; | |||||
new_eee_enabled = adapter->eee_enabled; | |||||
error = sysctl_handle_int(oidp, &new_eee_enabled, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
new_eee_enabled = !!(new_eee_enabled); | |||||
if (new_eee_enabled == adapter->eee_enabled) | |||||
return (0); | |||||
if (new_eee_enabled > 0 && !hw->mac.ops.setup_eee) | |||||
return (ENODEV); | |||||
else | |||||
adapter->eee_enabled = new_eee_enabled; | |||||
/* Re-initialize hardware if it's already running */ | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) | |||||
ixgbe_init(adapter); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Read-only sysctl indicating whether EEE support was negotiated | |||||
* on the link. | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_eee_negotiated(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
bool status; | |||||
status = !!(IXGBE_READ_REG(hw, IXGBE_EEE_STAT) & IXGBE_EEE_STAT_NEG); | |||||
return (sysctl_handle_int(oidp, 0, status, req)); | |||||
} | |||||
/* | |||||
* Read-only sysctl indicating whether RX Link is in LPI state. | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_eee_rx_lpi_status(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
bool status; | |||||
status = !!(IXGBE_READ_REG(hw, IXGBE_EEE_STAT) & | |||||
IXGBE_EEE_RX_LPI_STATUS); | |||||
return (sysctl_handle_int(oidp, 0, status, req)); | |||||
} | |||||
/* | |||||
* Read-only sysctl indicating whether TX Link is in LPI state. | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_eee_tx_lpi_status(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
bool status; | |||||
status = !!(IXGBE_READ_REG(hw, IXGBE_EEE_STAT) & | |||||
IXGBE_EEE_TX_LPI_STATUS); | |||||
return (sysctl_handle_int(oidp, 0, status, req)); | |||||
} | |||||
/* | |||||
* Read-only sysctl indicating TX Link LPI delay | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_eee_tx_lpi_delay(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
u32 reg; | |||||
reg = IXGBE_READ_REG(hw, IXGBE_EEE_SU); | |||||
return (sysctl_handle_int(oidp, 0, reg >> 26, req)); | |||||
} | |||||
/* | |||||
* Sysctl to enable/disable the types of packets that the | |||||
* adapter will wake up on upon receipt. | |||||
* WUFC - Wake Up Filter Control | |||||
* Flags: | |||||
* 0x1 - Link Status Change | |||||
* 0x2 - Magic Packet | |||||
* 0x4 - Direct Exact | |||||
* 0x8 - Directed Multicast | |||||
* 0x10 - Broadcast | |||||
* 0x20 - ARP/IPv4 Request Packet | |||||
* 0x40 - Direct IPv4 Packet | |||||
* 0x80 - Direct IPv6 Packet | |||||
* | |||||
* Setting another flag will cause the sysctl to return an | |||||
* error. | |||||
*/ | |||||
static int | |||||
ixgbe_sysctl_wufc(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *) arg1; | |||||
int error = 0; | |||||
u32 new_wufc; | |||||
new_wufc = adapter->wufc; | |||||
error = sysctl_handle_int(oidp, &new_wufc, 0, req); | |||||
if ((error) || (req->newptr == NULL)) | |||||
return (error); | |||||
if (new_wufc == adapter->wufc) | |||||
return (0); | |||||
if (new_wufc & 0xffffff00) | |||||
return (EINVAL); | |||||
else { | |||||
new_wufc &= 0xff; | |||||
new_wufc |= (0xffffff & adapter->wufc); | |||||
adapter->wufc = new_wufc; | |||||
} | |||||
return (0); | |||||
} | |||||
#ifdef IXGBE_DEBUG | |||||
static int | |||||
ixgbe_sysctl_print_rss_config(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
struct adapter *adapter = (struct adapter *)arg1; | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
device_t dev = adapter->dev; | |||||
int error = 0, reta_size; | |||||
struct sbuf *buf; | |||||
u32 reg; | |||||
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); | |||||
if (!buf) { | |||||
device_printf(dev, "Could not allocate sbuf for output.\n"); | |||||
return (ENOMEM); | |||||
} | |||||
// TODO: use sbufs to make a string to print out | |||||
/* Set multiplier for RETA setup and table size based on MAC */ | |||||
switch (adapter->hw.mac.type) { | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
reta_size = 128; | |||||
break; | |||||
default: | |||||
reta_size = 32; | |||||
break; | |||||
} | |||||
/* Print out the redirection table */ | |||||
sbuf_cat(buf, "\n"); | |||||
for (int i = 0; i < reta_size; i++) { | |||||
if (i < 32) { | |||||
reg = IXGBE_READ_REG(hw, IXGBE_RETA(i)); | |||||
sbuf_printf(buf, "RETA(%2d): 0x%08x\n", i, reg); | |||||
} else { | |||||
reg = IXGBE_READ_REG(hw, IXGBE_ERETA(i - 32)); | |||||
sbuf_printf(buf, "ERETA(%2d): 0x%08x\n", i - 32, reg); | |||||
} | |||||
} | |||||
// TODO: print more config | |||||
error = sbuf_finish(buf); | |||||
if (error) | |||||
device_printf(dev, "Error finishing sbuf: %d\n", error); | |||||
sbuf_delete(buf); | |||||
return (0); | |||||
} | |||||
#endif /* IXGBE_DEBUG */ | |||||
/* | |||||
** Enable the hardware to drop packets when the buffer is | |||||
** full. This is useful when multiqueue,so that no single | |||||
** queue being full stalls the entire RX engine. We only | |||||
** enable this when Multiqueue AND when Flow Control is | |||||
** disabled. | |||||
*/ | |||||
static void | |||||
ixgbe_enable_rx_drop(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
for (int i = 0; i < adapter->num_queues; i++) { | |||||
struct rx_ring *rxr = &adapter->rx_rings[i]; | |||||
u32 srrctl = IXGBE_READ_REG(hw, IXGBE_SRRCTL(rxr->me)); | |||||
srrctl |= IXGBE_SRRCTL_DROP_EN; | |||||
IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(rxr->me), srrctl); | |||||
} | |||||
#ifdef PCI_IOV | |||||
/* enable drop for each vf */ | |||||
for (int i = 0; i < adapter->num_vfs; i++) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_QDE, | |||||
(IXGBE_QDE_WRITE | (i << IXGBE_QDE_IDX_SHIFT) | | |||||
IXGBE_QDE_ENABLE)); | |||||
} | |||||
#endif | |||||
} | |||||
static void | |||||
ixgbe_disable_rx_drop(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
for (int i = 0; i < adapter->num_queues; i++) { | |||||
struct rx_ring *rxr = &adapter->rx_rings[i]; | |||||
u32 srrctl = IXGBE_READ_REG(hw, IXGBE_SRRCTL(rxr->me)); | |||||
srrctl &= ~IXGBE_SRRCTL_DROP_EN; | |||||
IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(rxr->me), srrctl); | |||||
} | |||||
#ifdef PCI_IOV | |||||
/* disable drop for each vf */ | |||||
for (int i = 0; i < adapter->num_vfs; i++) { | |||||
IXGBE_WRITE_REG(hw, IXGBE_QDE, | |||||
(IXGBE_QDE_WRITE | (i << IXGBE_QDE_IDX_SHIFT))); | |||||
} | |||||
#endif | |||||
} | |||||
static void | |||||
ixgbe_rearm_queues(struct adapter *adapter, u64 queues) | |||||
{ | |||||
u32 mask; | |||||
switch (adapter->hw.mac.type) { | |||||
case ixgbe_mac_82598EB: | |||||
mask = (IXGBE_EIMS_RTX_QUEUE & queues); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, mask); | |||||
break; | |||||
case ixgbe_mac_82599EB: | |||||
case ixgbe_mac_X540: | |||||
case ixgbe_mac_X550: | |||||
case ixgbe_mac_X550EM_x: | |||||
mask = (queues & 0xFFFFFFFF); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS_EX(0), mask); | |||||
mask = (queues >> 32); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS_EX(1), mask); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
#ifdef PCI_IOV | |||||
/* | |||||
** Support functions for SRIOV/VF management | |||||
*/ | |||||
static void | |||||
ixgbe_ping_all_vfs(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_vf *vf; | |||||
for (int i = 0; i < adapter->num_vfs; i++) { | |||||
vf = &adapter->vfs[i]; | |||||
if (vf->flags & IXGBE_VF_ACTIVE) | |||||
ixgbe_send_vf_msg(adapter, vf, IXGBE_PF_CONTROL_MSG); | |||||
} | |||||
} | |||||
static void | |||||
ixgbe_vf_set_default_vlan(struct adapter *adapter, struct ixgbe_vf *vf, | |||||
uint16_t tag) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t vmolr, vmvir; | |||||
hw = &adapter->hw; | |||||
vf->vlan_tag = tag; | |||||
vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf->pool)); | |||||
/* Do not receive packets that pass inexact filters. */ | |||||
vmolr &= ~(IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_ROPE); | |||||
/* Disable Multicast Promicuous Mode. */ | |||||
vmolr &= ~IXGBE_VMOLR_MPE; | |||||
/* Accept broadcasts. */ | |||||
vmolr |= IXGBE_VMOLR_BAM; | |||||
if (tag == 0) { | |||||
/* Accept non-vlan tagged traffic. */ | |||||
//vmolr |= IXGBE_VMOLR_AUPE; | |||||
/* Allow VM to tag outgoing traffic; no default tag. */ | |||||
vmvir = 0; | |||||
} else { | |||||
/* Require vlan-tagged traffic. */ | |||||
vmolr &= ~IXGBE_VMOLR_AUPE; | |||||
/* Tag all traffic with provided vlan tag. */ | |||||
vmvir = (tag | IXGBE_VMVIR_VLANA_DEFAULT); | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf->pool), vmolr); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf->pool), vmvir); | |||||
} | |||||
static boolean_t | |||||
ixgbe_vf_frame_size_compatible(struct adapter *adapter, struct ixgbe_vf *vf) | |||||
{ | |||||
/* | |||||
* Frame size compatibility between PF and VF is only a problem on | |||||
* 82599-based cards. X540 and later support any combination of jumbo | |||||
* frames on PFs and VFs. | |||||
*/ | |||||
if (adapter->hw.mac.type != ixgbe_mac_82599EB) | |||||
return (TRUE); | |||||
switch (vf->api_ver) { | |||||
case IXGBE_API_VER_1_0: | |||||
case IXGBE_API_VER_UNKNOWN: | |||||
/* | |||||
* On legacy (1.0 and older) VF versions, we don't support jumbo | |||||
* frames on either the PF or the VF. | |||||
*/ | |||||
if (adapter->max_frame_size > ETHER_MAX_LEN || | |||||
vf->max_frame_size > ETHER_MAX_LEN) | |||||
return (FALSE); | |||||
return (TRUE); | |||||
break; | |||||
case IXGBE_API_VER_1_1: | |||||
default: | |||||
/* | |||||
* 1.1 or later VF versions always work if they aren't using | |||||
* jumbo frames. | |||||
*/ | |||||
if (vf->max_frame_size <= ETHER_MAX_LEN) | |||||
return (TRUE); | |||||
/* | |||||
* Jumbo frames only work with VFs if the PF is also using jumbo | |||||
* frames. | |||||
*/ | |||||
if (adapter->max_frame_size <= ETHER_MAX_LEN) | |||||
return (TRUE); | |||||
return (FALSE); | |||||
} | |||||
} | |||||
static void | |||||
ixgbe_process_vf_reset(struct adapter *adapter, struct ixgbe_vf *vf) | |||||
{ | |||||
ixgbe_vf_set_default_vlan(adapter, vf, vf->default_vlan); | |||||
// XXX clear multicast addresses | |||||
ixgbe_clear_rar(&adapter->hw, vf->rar_index); | |||||
vf->api_ver = IXGBE_API_VER_UNKNOWN; | |||||
} | |||||
static void | |||||
ixgbe_vf_enable_transmit(struct adapter *adapter, struct ixgbe_vf *vf) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t vf_index, vfte; | |||||
hw = &adapter->hw; | |||||
vf_index = IXGBE_VF_INDEX(vf->pool); | |||||
vfte = IXGBE_READ_REG(hw, IXGBE_VFTE(vf_index)); | |||||
vfte |= IXGBE_VF_BIT(vf->pool); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFTE(vf_index), vfte); | |||||
} | |||||
static void | |||||
ixgbe_vf_enable_receive(struct adapter *adapter, struct ixgbe_vf *vf) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t vf_index, vfre; | |||||
hw = &adapter->hw; | |||||
vf_index = IXGBE_VF_INDEX(vf->pool); | |||||
vfre = IXGBE_READ_REG(hw, IXGBE_VFRE(vf_index)); | |||||
if (ixgbe_vf_frame_size_compatible(adapter, vf)) | |||||
vfre |= IXGBE_VF_BIT(vf->pool); | |||||
else | |||||
vfre &= ~IXGBE_VF_BIT(vf->pool); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFRE(vf_index), vfre); | |||||
} | |||||
static void | |||||
ixgbe_vf_reset_msg(struct adapter *adapter, struct ixgbe_vf *vf, uint32_t *msg) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t ack; | |||||
uint32_t resp[IXGBE_VF_PERMADDR_MSG_LEN]; | |||||
hw = &adapter->hw; | |||||
ixgbe_process_vf_reset(adapter, vf); | |||||
if (ixgbe_validate_mac_addr(vf->ether_addr) == 0) { | |||||
ixgbe_set_rar(&adapter->hw, vf->rar_index, | |||||
vf->ether_addr, vf->pool, TRUE); | |||||
ack = IXGBE_VT_MSGTYPE_ACK; | |||||
} else | |||||
ack = IXGBE_VT_MSGTYPE_NACK; | |||||
ixgbe_vf_enable_transmit(adapter, vf); | |||||
ixgbe_vf_enable_receive(adapter, vf); | |||||
vf->flags |= IXGBE_VF_CTS; | |||||
resp[0] = IXGBE_VF_RESET | ack | IXGBE_VT_MSGTYPE_CTS; | |||||
bcopy(vf->ether_addr, &resp[1], ETHER_ADDR_LEN); | |||||
resp[3] = hw->mac.mc_filter_type; | |||||
ixgbe_write_mbx(hw, resp, IXGBE_VF_PERMADDR_MSG_LEN, vf->pool); | |||||
} | |||||
static void | |||||
ixgbe_vf_set_mac(struct adapter *adapter, struct ixgbe_vf *vf, uint32_t *msg) | |||||
{ | |||||
uint8_t *mac; | |||||
mac = (uint8_t*)&msg[1]; | |||||
/* Check that the VF has permission to change the MAC address. */ | |||||
if (!(vf->flags & IXGBE_VF_CAP_MAC) && ixgbe_vf_mac_changed(vf, mac)) { | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
if (ixgbe_validate_mac_addr(mac) != 0) { | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
bcopy(mac, vf->ether_addr, ETHER_ADDR_LEN); | |||||
ixgbe_set_rar(&adapter->hw, vf->rar_index, vf->ether_addr, | |||||
vf->pool, TRUE); | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
} | |||||
/* | |||||
** VF multicast addresses are set by using the appropriate bit in | |||||
** 1 of 128 32 bit addresses (4096 possible). | |||||
*/ | |||||
static void | |||||
ixgbe_vf_set_mc_addr(struct adapter *adapter, struct ixgbe_vf *vf, u32 *msg) | |||||
{ | |||||
u16 *list = (u16*)&msg[1]; | |||||
int entries; | |||||
u32 vmolr, vec_bit, vec_reg, mta_reg; | |||||
entries = (msg[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT; | |||||
entries = min(entries, IXGBE_MAX_VF_MC); | |||||
vmolr = IXGBE_READ_REG(&adapter->hw, IXGBE_VMOLR(vf->pool)); | |||||
vf->num_mc_hashes = entries; | |||||
/* Set the appropriate MTA bit */ | |||||
for (int i = 0; i < entries; i++) { | |||||
vf->mc_hash[i] = list[i]; | |||||
vec_reg = (vf->mc_hash[i] >> 5) & 0x7F; | |||||
vec_bit = vf->mc_hash[i] & 0x1F; | |||||
mta_reg = IXGBE_READ_REG(&adapter->hw, IXGBE_MTA(vec_reg)); | |||||
mta_reg |= (1 << vec_bit); | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_MTA(vec_reg), mta_reg); | |||||
} | |||||
vmolr |= IXGBE_VMOLR_ROMPE; | |||||
IXGBE_WRITE_REG(&adapter->hw, IXGBE_VMOLR(vf->pool), vmolr); | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
static void | |||||
ixgbe_vf_set_vlan(struct adapter *adapter, struct ixgbe_vf *vf, uint32_t *msg) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
int enable; | |||||
uint16_t tag; | |||||
hw = &adapter->hw; | |||||
enable = IXGBE_VT_MSGINFO(msg[0]); | |||||
tag = msg[1] & IXGBE_VLVF_VLANID_MASK; | |||||
if (!(vf->flags & IXGBE_VF_CAP_VLAN)) { | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
/* It is illegal to enable vlan tag 0. */ | |||||
if (tag == 0 && enable != 0){ | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
ixgbe_set_vfta(hw, tag, vf->pool, enable); | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
} | |||||
static void | |||||
ixgbe_vf_set_lpe(struct adapter *adapter, struct ixgbe_vf *vf, uint32_t *msg) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t vf_max_size, pf_max_size, mhadd; | |||||
hw = &adapter->hw; | |||||
vf_max_size = msg[1]; | |||||
if (vf_max_size < ETHER_CRC_LEN) { | |||||
/* We intentionally ACK invalid LPE requests. */ | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
vf_max_size -= ETHER_CRC_LEN; | |||||
if (vf_max_size > IXGBE_MAX_FRAME_SIZE) { | |||||
/* We intentionally ACK invalid LPE requests. */ | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
vf->max_frame_size = vf_max_size; | |||||
ixgbe_update_max_frame(adapter, vf->max_frame_size); | |||||
/* | |||||
* We might have to disable reception to this VF if the frame size is | |||||
* not compatible with the config on the PF. | |||||
*/ | |||||
ixgbe_vf_enable_receive(adapter, vf); | |||||
mhadd = IXGBE_READ_REG(hw, IXGBE_MHADD); | |||||
pf_max_size = (mhadd & IXGBE_MHADD_MFS_MASK) >> IXGBE_MHADD_MFS_SHIFT; | |||||
if (pf_max_size < adapter->max_frame_size) { | |||||
mhadd &= ~IXGBE_MHADD_MFS_MASK; | |||||
mhadd |= adapter->max_frame_size << IXGBE_MHADD_MFS_SHIFT; | |||||
IXGBE_WRITE_REG(hw, IXGBE_MHADD, mhadd); | |||||
} | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
} | |||||
static void | |||||
ixgbe_vf_set_macvlan(struct adapter *adapter, struct ixgbe_vf *vf, | |||||
uint32_t *msg) | |||||
{ | |||||
//XXX implement this | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
} | |||||
static void | |||||
ixgbe_vf_api_negotiate(struct adapter *adapter, struct ixgbe_vf *vf, | |||||
uint32_t *msg) | |||||
{ | |||||
switch (msg[1]) { | |||||
case IXGBE_API_VER_1_0: | |||||
case IXGBE_API_VER_1_1: | |||||
vf->api_ver = msg[1]; | |||||
ixgbe_send_vf_ack(adapter, vf, msg[0]); | |||||
break; | |||||
default: | |||||
vf->api_ver = IXGBE_API_VER_UNKNOWN; | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
break; | |||||
} | |||||
} | |||||
static void | |||||
ixgbe_vf_get_queues(struct adapter *adapter, struct ixgbe_vf *vf, | |||||
uint32_t *msg) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t resp[IXGBE_VF_GET_QUEUES_RESP_LEN]; | |||||
int num_queues; | |||||
hw = &adapter->hw; | |||||
/* GET_QUEUES is not supported on pre-1.1 APIs. */ | |||||
switch (msg[0]) { | |||||
case IXGBE_API_VER_1_0: | |||||
case IXGBE_API_VER_UNKNOWN: | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
resp[0] = IXGBE_VF_GET_QUEUES | IXGBE_VT_MSGTYPE_ACK | | |||||
IXGBE_VT_MSGTYPE_CTS; | |||||
num_queues = ixgbe_vf_queues(ixgbe_get_iov_mode(adapter)); | |||||
resp[IXGBE_VF_TX_QUEUES] = num_queues; | |||||
resp[IXGBE_VF_RX_QUEUES] = num_queues; | |||||
resp[IXGBE_VF_TRANS_VLAN] = (vf->default_vlan != 0); | |||||
resp[IXGBE_VF_DEF_QUEUE] = 0; | |||||
ixgbe_write_mbx(hw, resp, IXGBE_VF_GET_QUEUES_RESP_LEN, vf->pool); | |||||
} | |||||
static void | |||||
ixgbe_process_vf_msg(struct adapter *adapter, struct ixgbe_vf *vf) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t msg[IXGBE_VFMAILBOX_SIZE]; | |||||
int error; | |||||
hw = &adapter->hw; | |||||
error = ixgbe_read_mbx(hw, msg, IXGBE_VFMAILBOX_SIZE, vf->pool); | |||||
if (error != 0) | |||||
return; | |||||
CTR3(KTR_MALLOC, "%s: received msg %x from %d", | |||||
adapter->ifp->if_xname, msg[0], vf->pool); | |||||
if (msg[0] == IXGBE_VF_RESET) { | |||||
ixgbe_vf_reset_msg(adapter, vf, msg); | |||||
return; | |||||
} | |||||
if (!(vf->flags & IXGBE_VF_CTS)) { | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
return; | |||||
} | |||||
switch (msg[0] & IXGBE_VT_MSG_MASK) { | |||||
case IXGBE_VF_SET_MAC_ADDR: | |||||
ixgbe_vf_set_mac(adapter, vf, msg); | |||||
break; | |||||
case IXGBE_VF_SET_MULTICAST: | |||||
ixgbe_vf_set_mc_addr(adapter, vf, msg); | |||||
break; | |||||
case IXGBE_VF_SET_VLAN: | |||||
ixgbe_vf_set_vlan(adapter, vf, msg); | |||||
break; | |||||
case IXGBE_VF_SET_LPE: | |||||
ixgbe_vf_set_lpe(adapter, vf, msg); | |||||
break; | |||||
case IXGBE_VF_SET_MACVLAN: | |||||
ixgbe_vf_set_macvlan(adapter, vf, msg); | |||||
break; | |||||
case IXGBE_VF_API_NEGOTIATE: | |||||
ixgbe_vf_api_negotiate(adapter, vf, msg); | |||||
break; | |||||
case IXGBE_VF_GET_QUEUES: | |||||
ixgbe_vf_get_queues(adapter, vf, msg); | |||||
break; | |||||
default: | |||||
ixgbe_send_vf_nack(adapter, vf, msg[0]); | |||||
} | |||||
} | |||||
/* | |||||
* Tasklet for handling VF -> PF mailbox messages. | |||||
*/ | |||||
static void | |||||
ixgbe_handle_mbx(void *context, int pending) | |||||
{ | |||||
struct adapter *adapter; | |||||
struct ixgbe_hw *hw; | |||||
struct ixgbe_vf *vf; | |||||
int i; | |||||
adapter = context; | |||||
hw = &adapter->hw; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
for (i = 0; i < adapter->num_vfs; i++) { | |||||
vf = &adapter->vfs[i]; | |||||
if (vf->flags & IXGBE_VF_ACTIVE) { | |||||
if (ixgbe_check_for_rst(hw, vf->pool) == 0) | |||||
ixgbe_process_vf_reset(adapter, vf); | |||||
if (ixgbe_check_for_msg(hw, vf->pool) == 0) | |||||
ixgbe_process_vf_msg(adapter, vf); | |||||
if (ixgbe_check_for_ack(hw, vf->pool) == 0) | |||||
ixgbe_process_vf_ack(adapter, vf); | |||||
} | |||||
} | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
static int | |||||
ixgbe_init_iov(device_t dev, u16 num_vfs, const nvlist_t *config) | |||||
{ | |||||
struct adapter *adapter; | |||||
enum ixgbe_iov_mode mode; | |||||
adapter = device_get_softc(dev); | |||||
adapter->num_vfs = num_vfs; | |||||
mode = ixgbe_get_iov_mode(adapter); | |||||
if (num_vfs > ixgbe_max_vfs(mode)) { | |||||
adapter->num_vfs = 0; | |||||
return (ENOSPC); | |||||
} | |||||
IXGBE_CORE_LOCK(adapter); | |||||
adapter->vfs = malloc(sizeof(*adapter->vfs) * num_vfs, M_IXGBE, | |||||
M_NOWAIT | M_ZERO); | |||||
if (adapter->vfs == NULL) { | |||||
adapter->num_vfs = 0; | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return (ENOMEM); | |||||
} | |||||
ixgbe_init_locked(adapter); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return (0); | |||||
} | |||||
static void | |||||
ixgbe_uninit_iov(device_t dev) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
struct adapter *adapter; | |||||
uint32_t pf_reg, vf_reg; | |||||
adapter = device_get_softc(dev); | |||||
hw = &adapter->hw; | |||||
IXGBE_CORE_LOCK(adapter); | |||||
/* Enable rx/tx for the PF and disable it for all VFs. */ | |||||
pf_reg = IXGBE_VF_INDEX(adapter->pool); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFRE(pf_reg), | |||||
IXGBE_VF_BIT(adapter->pool)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFTE(pf_reg), | |||||
IXGBE_VF_BIT(adapter->pool)); | |||||
if (pf_reg == 0) | |||||
vf_reg = 1; | |||||
else | |||||
vf_reg = 0; | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFRE(vf_reg), 0); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFTE(vf_reg), 0); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, 0); | |||||
free(adapter->vfs, M_IXGBE); | |||||
adapter->vfs = NULL; | |||||
adapter->num_vfs = 0; | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
} | |||||
static void | |||||
ixgbe_initialize_iov(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_hw *hw = &adapter->hw; | |||||
uint32_t mrqc, mtqc, vt_ctl, vf_reg, gcr_ext, gpie; | |||||
enum ixgbe_iov_mode mode; | |||||
int i; | |||||
mode = ixgbe_get_iov_mode(adapter); | |||||
if (mode == IXGBE_NO_VM) | |||||
return; | |||||
IXGBE_CORE_LOCK_ASSERT(adapter); | |||||
mrqc = IXGBE_READ_REG(hw, IXGBE_MRQC); | |||||
mrqc &= ~IXGBE_MRQC_MRQE_MASK; | |||||
switch (mode) { | |||||
case IXGBE_64_VM: | |||||
mrqc |= IXGBE_MRQC_VMDQRSS64EN; | |||||
break; | |||||
case IXGBE_32_VM: | |||||
mrqc |= IXGBE_MRQC_VMDQRSS32EN; | |||||
break; | |||||
default: | |||||
panic("Unexpected SR-IOV mode %d", mode); | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc); | |||||
mtqc = IXGBE_MTQC_VT_ENA; | |||||
switch (mode) { | |||||
case IXGBE_64_VM: | |||||
mtqc |= IXGBE_MTQC_64VF; | |||||
break; | |||||
case IXGBE_32_VM: | |||||
mtqc |= IXGBE_MTQC_32VF; | |||||
break; | |||||
default: | |||||
panic("Unexpected SR-IOV mode %d", mode); | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_MTQC, mtqc); | |||||
gcr_ext = IXGBE_READ_REG(hw, IXGBE_GCR_EXT); | |||||
gcr_ext |= IXGBE_GCR_EXT_MSIX_EN; | |||||
gcr_ext &= ~IXGBE_GCR_EXT_VT_MODE_MASK; | |||||
switch (mode) { | |||||
case IXGBE_64_VM: | |||||
gcr_ext |= IXGBE_GCR_EXT_VT_MODE_64; | |||||
break; | |||||
case IXGBE_32_VM: | |||||
gcr_ext |= IXGBE_GCR_EXT_VT_MODE_32; | |||||
break; | |||||
default: | |||||
panic("Unexpected SR-IOV mode %d", mode); | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_GCR_EXT, gcr_ext); | |||||
gpie = IXGBE_READ_REG(hw, IXGBE_GPIE); | |||||
gcr_ext &= ~IXGBE_GPIE_VTMODE_MASK; | |||||
switch (mode) { | |||||
case IXGBE_64_VM: | |||||
gpie |= IXGBE_GPIE_VTMODE_64; | |||||
break; | |||||
case IXGBE_32_VM: | |||||
gpie |= IXGBE_GPIE_VTMODE_32; | |||||
break; | |||||
default: | |||||
panic("Unexpected SR-IOV mode %d", mode); | |||||
} | |||||
IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); | |||||
/* Enable rx/tx for the PF. */ | |||||
vf_reg = IXGBE_VF_INDEX(adapter->pool); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFRE(vf_reg), | |||||
IXGBE_VF_BIT(adapter->pool)); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VFTE(vf_reg), | |||||
IXGBE_VF_BIT(adapter->pool)); | |||||
/* Allow VM-to-VM communication. */ | |||||
IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); | |||||
vt_ctl = IXGBE_VT_CTL_VT_ENABLE | IXGBE_VT_CTL_REPLEN; | |||||
vt_ctl |= (adapter->pool << IXGBE_VT_CTL_POOL_SHIFT); | |||||
IXGBE_WRITE_REG(hw, IXGBE_VT_CTL, vt_ctl); | |||||
for (i = 0; i < adapter->num_vfs; i++) | |||||
ixgbe_init_vf(adapter, &adapter->vfs[i]); | |||||
} | |||||
/* | |||||
** Check the max frame setting of all active VF's | |||||
*/ | |||||
static void | |||||
ixgbe_recalculate_max_frame(struct adapter *adapter) | |||||
{ | |||||
struct ixgbe_vf *vf; | |||||
IXGBE_CORE_LOCK_ASSERT(adapter); | |||||
for (int i = 0; i < adapter->num_vfs; i++) { | |||||
vf = &adapter->vfs[i]; | |||||
if (vf->flags & IXGBE_VF_ACTIVE) | |||||
ixgbe_update_max_frame(adapter, vf->max_frame_size); | |||||
} | |||||
} | |||||
static void | |||||
ixgbe_init_vf(struct adapter *adapter, struct ixgbe_vf *vf) | |||||
{ | |||||
struct ixgbe_hw *hw; | |||||
uint32_t vf_index, pfmbimr; | |||||
IXGBE_CORE_LOCK_ASSERT(adapter); | |||||
hw = &adapter->hw; | |||||
if (!(vf->flags & IXGBE_VF_ACTIVE)) | |||||
return; | |||||
vf_index = IXGBE_VF_INDEX(vf->pool); | |||||
pfmbimr = IXGBE_READ_REG(hw, IXGBE_PFMBIMR(vf_index)); | |||||
pfmbimr |= IXGBE_VF_BIT(vf->pool); | |||||
IXGBE_WRITE_REG(hw, IXGBE_PFMBIMR(vf_index), pfmbimr); | |||||
ixgbe_vf_set_default_vlan(adapter, vf, vf->vlan_tag); | |||||
// XXX multicast addresses | |||||
if (ixgbe_validate_mac_addr(vf->ether_addr) == 0) { | |||||
ixgbe_set_rar(&adapter->hw, vf->rar_index, | |||||
vf->ether_addr, vf->pool, TRUE); | |||||
} | |||||
ixgbe_vf_enable_transmit(adapter, vf); | |||||
ixgbe_vf_enable_receive(adapter, vf); | |||||
ixgbe_send_vf_msg(adapter, vf, IXGBE_PF_CONTROL_MSG); | |||||
} | |||||
static int | |||||
ixgbe_add_vf(device_t dev, u16 vfnum, const nvlist_t *config) | |||||
{ | |||||
struct adapter *adapter; | |||||
struct ixgbe_vf *vf; | |||||
const void *mac; | |||||
adapter = device_get_softc(dev); | |||||
KASSERT(vfnum < adapter->num_vfs, ("VF index %d is out of range %d", | |||||
vfnum, adapter->num_vfs)); | |||||
IXGBE_CORE_LOCK(adapter); | |||||
vf = &adapter->vfs[vfnum]; | |||||
vf->pool= vfnum; | |||||
/* RAR[0] is used by the PF so use vfnum + 1 for VF RAR. */ | |||||
vf->rar_index = vfnum + 1; | |||||
vf->default_vlan = 0; | |||||
vf->max_frame_size = ETHER_MAX_LEN; | |||||
ixgbe_update_max_frame(adapter, vf->max_frame_size); | |||||
if (nvlist_exists_binary(config, "mac-addr")) { | |||||
mac = nvlist_get_binary(config, "mac-addr", NULL); | |||||
bcopy(mac, vf->ether_addr, ETHER_ADDR_LEN); | |||||
if (nvlist_get_bool(config, "allow-set-mac")) | |||||
vf->flags |= IXGBE_VF_CAP_MAC; | |||||
} else | |||||
/* | |||||
* If the administrator has not specified a MAC address then | |||||
* we must allow the VF to choose one. | |||||
*/ | |||||
vf->flags |= IXGBE_VF_CAP_MAC; | |||||
vf->flags = IXGBE_VF_ACTIVE; | |||||
ixgbe_init_vf(adapter, vf); | |||||
IXGBE_CORE_UNLOCK(adapter); | |||||
return (0); | |||||
} | |||||
#endif /* PCI_IOV */ | |||||
cramerj_intel.com: In iflib.c, the function parameter passed to legacy ISRs is the shared context pointer. In… | |||||
Not Done Inline ActionsWhy is this being commented out and not removed? sbruno: Why is this being commented out and not removed? |
In iflib.c, the function parameter passed to legacy ISRs is the shared context pointer. In this case, adapter. Changing the above two lines to...
struct adapter *adapter = arg;
struct ix_rx_queue *que = adapter->rx_queues;
...fixes a panic we encounter with legacy interrupts.