Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ixl/if_ixlv.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#ifdef RSS | #ifdef RSS | ||||
#include <net/rss_config.h> | #include <net/rss_config.h> | ||||
#endif | #endif | ||||
/********************************************************************* | /********************************************************************* | ||||
* Driver version | * Driver version | ||||
*********************************************************************/ | *********************************************************************/ | ||||
char ixlv_driver_version[] = "1.2.11-k"; | char ixlv_driver_version[] = "1.4.6-k"; | ||||
/********************************************************************* | /********************************************************************* | ||||
* PCI Device ID Table | * PCI Device ID Table | ||||
* | * | ||||
* Used by probe to select devices to load on | * Used by probe to select devices to load on | ||||
* Last field stores an index into ixlv_strings | * Last field stores an index into ixlv_strings | ||||
* Last entry must be all 0s | * Last entry must be all 0s | ||||
* | * | ||||
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static ixl_vendor_info_t ixlv_vendor_info_array[] = | static ixl_vendor_info_t ixlv_vendor_info_array[] = | ||||
{ | { | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF_HV, 0, 0, 0}, | {I40E_INTEL_VENDOR_ID, I40E_DEV_ID_VF_HV, 0, 0, 0}, | ||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF, 0, 0, 0}, | |||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_A0_VF, 0, 0, 0}, | |||||
{I40E_INTEL_VENDOR_ID, I40E_DEV_ID_X722_VF_HV, 0, 0, 0}, | |||||
/* required last entry */ | /* required last entry */ | ||||
{0, 0, 0, 0, 0} | {0, 0, 0, 0, 0} | ||||
}; | }; | ||||
/********************************************************************* | /********************************************************************* | ||||
* Table of branding strings | * Table of branding strings | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static char *ixlv_strings[] = { | static char *ixlv_strings[] = { | ||||
"Intel(R) Ethernet Connection XL710 VF Driver" | "Intel(R) Ethernet Connection XL710/X722 VF Driver" | ||||
}; | }; | ||||
/********************************************************************* | /********************************************************************* | ||||
* Function prototypes | * Function prototypes | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static int ixlv_probe(device_t); | static int ixlv_probe(device_t); | ||||
static int ixlv_attach(device_t); | static int ixlv_attach(device_t); | ||||
Show All 29 Lines | |||||
static void ixlv_do_adminq_locked(struct ixlv_sc *sc); | static void ixlv_do_adminq_locked(struct ixlv_sc *sc); | ||||
static void ixlv_handle_que(void *, int); | static void ixlv_handle_que(void *, int); | ||||
static int ixlv_reset(struct ixlv_sc *); | static int ixlv_reset(struct ixlv_sc *); | ||||
static int ixlv_reset_complete(struct i40e_hw *); | static int ixlv_reset_complete(struct i40e_hw *); | ||||
static void ixlv_set_queue_rx_itr(struct ixl_queue *); | static void ixlv_set_queue_rx_itr(struct ixl_queue *); | ||||
static void ixlv_set_queue_tx_itr(struct ixl_queue *); | static void ixlv_set_queue_tx_itr(struct ixl_queue *); | ||||
static void ixl_init_cmd_complete(struct ixl_vc_cmd *, void *, | static void ixl_init_cmd_complete(struct ixl_vc_cmd *, void *, | ||||
enum i40e_status_code); | enum i40e_status_code); | ||||
static void ixlv_configure_itr(struct ixlv_sc *); | |||||
static void ixlv_enable_adminq_irq(struct i40e_hw *); | static void ixlv_enable_adminq_irq(struct i40e_hw *); | ||||
static void ixlv_disable_adminq_irq(struct i40e_hw *); | static void ixlv_disable_adminq_irq(struct i40e_hw *); | ||||
static void ixlv_enable_queue_irq(struct i40e_hw *, int); | static void ixlv_enable_queue_irq(struct i40e_hw *, int); | ||||
static void ixlv_disable_queue_irq(struct i40e_hw *, int); | static void ixlv_disable_queue_irq(struct i40e_hw *, int); | ||||
static void ixlv_setup_vlan_filters(struct ixlv_sc *); | static void ixlv_setup_vlan_filters(struct ixlv_sc *); | ||||
static void ixlv_register_vlan(void *, struct ifnet *, u16); | static void ixlv_register_vlan(void *, struct ifnet *, u16); | ||||
static void ixlv_unregister_vlan(void *, struct ifnet *, u16); | static void ixlv_unregister_vlan(void *, struct ifnet *, u16); | ||||
static void ixlv_init_hw(struct ixlv_sc *); | static void ixlv_init_hw(struct ixlv_sc *); | ||||
static int ixlv_setup_vc(struct ixlv_sc *); | static int ixlv_setup_vc(struct ixlv_sc *); | ||||
static int ixlv_vf_config(struct ixlv_sc *); | static int ixlv_vf_config(struct ixlv_sc *); | ||||
static void ixlv_cap_txcsum_tso(struct ixl_vsi *, | static void ixlv_cap_txcsum_tso(struct ixl_vsi *, | ||||
struct ifnet *, int); | struct ifnet *, int); | ||||
static void ixlv_add_sysctls(struct ixlv_sc *); | static void ixlv_add_sysctls(struct ixlv_sc *); | ||||
#ifdef IXL_DEBUG | |||||
static int ixlv_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS); | static int ixlv_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS); | ||||
static int ixlv_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS); | static int ixlv_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS); | ||||
#endif | |||||
/********************************************************************* | /********************************************************************* | ||||
* FreeBSD Device Interface Entry Points | * FreeBSD Device Interface Entry Points | ||||
*********************************************************************/ | *********************************************************************/ | ||||
static device_method_t ixlv_methods[] = { | static device_method_t ixlv_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, ixlv_probe), | DEVMETHOD(device_probe, ixlv_probe), | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | ixlv_attach(device_t dev) | ||||
vsi->dev = dev; | vsi->dev = dev; | ||||
/* Initialize hw struct */ | /* Initialize hw struct */ | ||||
ixlv_init_hw(sc); | ixlv_init_hw(sc); | ||||
/* Allocate filter lists */ | /* Allocate filter lists */ | ||||
ixlv_init_filters(sc); | ixlv_init_filters(sc); | ||||
/* Core Lock Init*/ | /* Core Lock Init */ | ||||
mtx_init(&sc->mtx, device_get_nameunit(dev), | mtx_init(&sc->mtx, device_get_nameunit(dev), | ||||
"IXL SC Lock", MTX_DEF); | "IXL SC Lock", MTX_DEF); | ||||
/* Set up the timer callout */ | /* Set up the timer callout */ | ||||
callout_init_mtx(&sc->timer, &sc->mtx, 0); | callout_init_mtx(&sc->timer, &sc->mtx, 0); | ||||
/* Do PCI setup - map BAR0, etc */ | /* Do PCI setup - map BAR0, etc */ | ||||
if (ixlv_allocate_pci_resources(sc)) { | if (ixlv_allocate_pci_resources(sc)) { | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | ixlv_attach(device_t dev) | ||||
/* Ask for VF config from PF */ | /* Ask for VF config from PF */ | ||||
error = ixlv_vf_config(sc); | error = ixlv_vf_config(sc); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "Error getting configuration from PF: %d\n", | device_printf(dev, "Error getting configuration from PF: %d\n", | ||||
error); | error); | ||||
goto err_aq; | goto err_aq; | ||||
} | } | ||||
INIT_DBG_DEV(dev, "VF config from PF:"); | device_printf(dev, "VSIs %d, QPs %d, MSIX %d, RSS sizes: key %d lut %d\n", | ||||
INIT_DBG_DEV(dev, "VSIs %d, Queues %d, Max Vectors %d, Max MTU %d", | |||||
sc->vf_res->num_vsis, | sc->vf_res->num_vsis, | ||||
sc->vf_res->num_queue_pairs, | sc->vf_res->num_queue_pairs, | ||||
sc->vf_res->max_vectors, | sc->vf_res->max_vectors, | ||||
sc->vf_res->max_mtu); | sc->vf_res->rss_key_size, | ||||
INIT_DBG_DEV(dev, "Offload flags: %#010x", | sc->vf_res->rss_lut_size); | ||||
sc->vf_res->vf_offload_flags); | #ifdef IXL_DEBUG | ||||
device_printf(dev, "Offload flags: 0x%b\n", | |||||
sc->vf_res->vf_offload_flags, IXLV_PRINTF_VF_OFFLOAD_FLAGS); | |||||
#endif | |||||
/* got VF config message back from PF, now we can parse it */ | /* got VF config message back from PF, now we can parse it */ | ||||
for (int i = 0; i < sc->vf_res->num_vsis; i++) { | for (int i = 0; i < sc->vf_res->num_vsis; i++) { | ||||
if (sc->vf_res->vsi_res[i].vsi_type == I40E_VSI_SRIOV) | if (sc->vf_res->vsi_res[i].vsi_type == I40E_VSI_SRIOV) | ||||
sc->vsi_res = &sc->vf_res->vsi_res[i]; | sc->vsi_res = &sc->vf_res->vsi_res[i]; | ||||
} | } | ||||
if (!sc->vsi_res) { | if (!sc->vsi_res) { | ||||
device_printf(dev, "%s: no LAN VSI found\n", __func__); | device_printf(dev, "%s: no LAN VSI found\n", __func__); | ||||
error = EIO; | error = EIO; | ||||
goto err_res_buf; | goto err_res_buf; | ||||
} | } | ||||
INIT_DBG_DEV(dev, "Resource Acquisition complete"); | INIT_DBG_DEV(dev, "Resource Acquisition complete"); | ||||
/* If no mac address was assigned just make a random one */ | /* If no mac address was assigned just make a random one */ | ||||
if (!ixlv_check_ether_addr(hw->mac.addr)) { | if (!ixlv_check_ether_addr(hw->mac.addr)) { | ||||
u8 addr[ETHER_ADDR_LEN]; | u8 addr[ETHER_ADDR_LEN]; | ||||
arc4rand(&addr, sizeof(addr), 0); | arc4rand(&addr, sizeof(addr), 0); | ||||
addr[0] &= 0xFE; | addr[0] &= 0xFE; | ||||
addr[0] |= 0x02; | addr[0] |= 0x02; | ||||
bcopy(addr, hw->mac.addr, sizeof(addr)); | bcopy(addr, hw->mac.addr, sizeof(addr)); | ||||
} | } | ||||
/* Now that the number of queues for this VF is known, set up interrupts */ | |||||
sc->msix = ixlv_init_msix(sc); | |||||
/* We fail without MSIX support */ | |||||
if (sc->msix == 0) { | |||||
error = ENXIO; | |||||
goto err_res_buf; | |||||
} | |||||
vsi->id = sc->vsi_res->vsi_id; | vsi->id = sc->vsi_res->vsi_id; | ||||
vsi->back = (void *)sc; | vsi->back = (void *)sc; | ||||
sc->link_up = TRUE; | sc->link_up = TRUE; | ||||
/* This allocates the memory and early settings */ | /* This allocates the memory and early settings */ | ||||
if (ixlv_setup_queues(sc) != 0) { | if (ixlv_setup_queues(sc) != 0) { | ||||
device_printf(dev, "%s: setup queues failed!\n", | device_printf(dev, "%s: setup queues failed!\n", | ||||
__func__); | __func__); | ||||
error = EIO; | error = EIO; | ||||
goto out; | goto out; | ||||
} | } | ||||
/* Setup the stack interface */ | /* Setup the stack interface */ | ||||
if (ixlv_setup_interface(dev, sc) != 0) { | if (ixlv_setup_interface(dev, sc) != 0) { | ||||
device_printf(dev, "%s: setup interface failed!\n", | device_printf(dev, "%s: setup interface failed!\n", | ||||
__func__); | __func__); | ||||
error = EIO; | error = EIO; | ||||
goto out; | goto out; | ||||
} | } | ||||
INIT_DBG_DEV(dev, "Queue memory and interface setup"); | INIT_DBG_DEV(dev, "Queue memory and interface setup"); | ||||
/* Do queue interrupt setup */ | /* Do queue interrupt setup */ | ||||
ixlv_assign_msix(sc); | if (ixlv_assign_msix(sc) != 0) { | ||||
device_printf(dev, "%s: allocating queue interrupts failed!\n", | |||||
__func__); | |||||
error = ENXIO; | |||||
goto out; | |||||
} | |||||
/* Start AdminQ taskqueue */ | /* Start AdminQ taskqueue */ | ||||
ixlv_init_taskqueue(sc); | ixlv_init_taskqueue(sc); | ||||
/* Initialize stats */ | /* Initialize stats */ | ||||
bzero(&sc->vsi.eth_stats, sizeof(struct i40e_eth_stats)); | bzero(&sc->vsi.eth_stats, sizeof(struct i40e_eth_stats)); | ||||
ixlv_add_sysctls(sc); | ixlv_add_sysctls(sc); | ||||
▲ Show 20 Lines • Show All 393 Lines • ▼ Show 20 Lines | ixl_init_cmd_complete(struct ixl_vc_cmd *cmd, void *arg, | ||||
/* | /* | ||||
* Ignore "Adapter Stopped" message as that happens if an ifconfig down | * Ignore "Adapter Stopped" message as that happens if an ifconfig down | ||||
* happens while a command is in progress, so we don't print an error | * happens while a command is in progress, so we don't print an error | ||||
* in that case. | * in that case. | ||||
*/ | */ | ||||
if (code != I40E_SUCCESS && code != I40E_ERR_ADAPTER_STOPPED) { | if (code != I40E_SUCCESS && code != I40E_ERR_ADAPTER_STOPPED) { | ||||
if_printf(sc->vsi.ifp, | if_printf(sc->vsi.ifp, | ||||
"Error %d waiting for PF to complete operation %d\n", | "Error %s waiting for PF to complete operation %d\n", | ||||
code, cmd->request); | i40e_stat_str(&sc->hw, code), cmd->request); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
ixlv_init_locked(struct ixlv_sc *sc) | ixlv_init_locked(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
if (vsi->max_frame_size <= MCLBYTES) | if (vsi->max_frame_size <= MCLBYTES) | ||||
rxr->mbuf_sz = MCLBYTES; | rxr->mbuf_sz = MCLBYTES; | ||||
else | else | ||||
rxr->mbuf_sz = MJUMPAGESIZE; | rxr->mbuf_sz = MJUMPAGESIZE; | ||||
ixl_init_rx_ring(que); | ixl_init_rx_ring(que); | ||||
} | } | ||||
/* Set initial ITR values */ | |||||
ixlv_configure_itr(sc); | |||||
/* Configure queues */ | /* Configure queues */ | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->config_queues_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->config_queues_cmd, | ||||
IXLV_FLAG_AQ_CONFIGURE_QUEUES, ixl_init_cmd_complete, sc); | IXLV_FLAG_AQ_CONFIGURE_QUEUES, ixl_init_cmd_complete, sc); | ||||
/* Set up RSS */ | /* Set up RSS */ | ||||
ixlv_config_rss(sc); | ixlv_config_rss(sc); | ||||
/* Map vectors */ | /* Map vectors */ | ||||
Show All 19 Lines | |||||
*/ | */ | ||||
void | void | ||||
ixlv_init(void *arg) | ixlv_init(void *arg) | ||||
{ | { | ||||
struct ixl_vsi *vsi = (struct ixl_vsi *)arg; | struct ixl_vsi *vsi = (struct ixl_vsi *)arg; | ||||
struct ixlv_sc *sc = vsi->back; | struct ixlv_sc *sc = vsi->back; | ||||
int retries = 0; | int retries = 0; | ||||
/* Prevent init from running again while waiting for AQ calls | |||||
* made in init_locked() to complete. */ | |||||
mtx_lock(&sc->mtx); | mtx_lock(&sc->mtx); | ||||
if (sc->init_in_progress) { | |||||
mtx_unlock(&sc->mtx); | |||||
return; | |||||
} else | |||||
sc->init_in_progress = true; | |||||
ixlv_init_locked(sc); | ixlv_init_locked(sc); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
/* Wait for init_locked to finish */ | /* Wait for init_locked to finish */ | ||||
while (!(vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) | while (!(vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) | ||||
&& ++retries < IXLV_AQ_MAX_ERR) { | && ++retries < IXLV_AQ_MAX_ERR) { | ||||
i40e_msec_delay(25); | i40e_msec_pause(25); | ||||
} | } | ||||
if (retries >= IXLV_AQ_MAX_ERR) | if (retries >= IXLV_AQ_MAX_ERR) { | ||||
if_printf(vsi->ifp, | if_printf(vsi->ifp, | ||||
"Init failed to complete in allotted time!\n"); | "Init failed to complete in allotted time!\n"); | ||||
} | } | ||||
mtx_lock(&sc->mtx); | |||||
sc->init_in_progress = false; | |||||
mtx_unlock(&sc->mtx); | |||||
} | |||||
/* | /* | ||||
* ixlv_attach() helper function; gathers information about | * ixlv_attach() helper function; gathers information about | ||||
* the (virtual) hardware for use elsewhere in the driver. | * the (virtual) hardware for use elsewhere in the driver. | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_init_hw(struct ixlv_sc *sc) | ixlv_init_hw(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
Show All 24 Lines | ixlv_setup_vc(struct ixlv_sc *sc) | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
int error = 0, ret_error = 0, asq_retries = 0; | int error = 0, ret_error = 0, asq_retries = 0; | ||||
bool send_api_ver_retried = 0; | bool send_api_ver_retried = 0; | ||||
/* Need to set these AQ paramters before initializing AQ */ | /* Need to set these AQ paramters before initializing AQ */ | ||||
hw->aq.num_arq_entries = IXL_AQ_LEN; | hw->aq.num_arq_entries = IXL_AQ_LEN; | ||||
hw->aq.num_asq_entries = IXL_AQ_LEN; | hw->aq.num_asq_entries = IXL_AQ_LEN; | ||||
hw->aq.arq_buf_size = IXL_AQ_BUFSZ; | hw->aq.arq_buf_size = IXL_AQ_BUF_SZ; | ||||
hw->aq.asq_buf_size = IXL_AQ_BUFSZ; | hw->aq.asq_buf_size = IXL_AQ_BUF_SZ; | ||||
for (int i = 0; i < IXLV_AQ_MAX_ERR; i++) { | for (int i = 0; i < IXLV_AQ_MAX_ERR; i++) { | ||||
/* Initialize admin queue */ | /* Initialize admin queue */ | ||||
error = i40e_init_adminq(hw); | error = i40e_init_adminq(hw); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "%s: init_adminq failed: %d\n", | device_printf(dev, "%s: init_adminq failed: %d\n", | ||||
__func__, error); | __func__, error); | ||||
ret_error = 1; | ret_error = 1; | ||||
Show All 13 Lines | if (error) { | ||||
" version to PF on attempt %d, error %d\n", | " version to PF on attempt %d, error %d\n", | ||||
__func__, i+1, error); | __func__, i+1, error); | ||||
} | } | ||||
asq_retries = 0; | asq_retries = 0; | ||||
while (!i40e_asq_done(hw)) { | while (!i40e_asq_done(hw)) { | ||||
if (++asq_retries > IXLV_AQ_MAX_ERR) { | if (++asq_retries > IXLV_AQ_MAX_ERR) { | ||||
i40e_shutdown_adminq(hw); | i40e_shutdown_adminq(hw); | ||||
DDPRINTF(dev, "Admin Queue timeout " | device_printf(dev, "Admin Queue timeout " | ||||
"(waiting for send_api_ver), %d more retries...", | "(waiting for send_api_ver), %d more tries...\n", | ||||
IXLV_AQ_MAX_ERR - (i + 1)); | IXLV_AQ_MAX_ERR - (i + 1)); | ||||
ret_error = 3; | ret_error = 3; | ||||
break; | break; | ||||
} | } | ||||
i40e_msec_delay(10); | i40e_msec_pause(10); | ||||
} | } | ||||
if (asq_retries > IXLV_AQ_MAX_ERR) | if (asq_retries > IXLV_AQ_MAX_ERR) | ||||
continue; | continue; | ||||
INIT_DBG_DEV(dev, "Sent API version message to PF"); | INIT_DBG_DEV(dev, "Sent API version message to PF"); | ||||
/* Verify that the VF accepts the PF's API version */ | /* Verify that the VF accepts the PF's API version */ | ||||
error = ixlv_verify_api_ver(sc); | error = ixlv_verify_api_ver(sc); | ||||
Show All 11 Lines | if (error == ETIMEDOUT) { | ||||
" try!\n", __func__); | " try!\n", __func__); | ||||
ret_error = 4; | ret_error = 4; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (error) { | if (error) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"%s: Unable to verify API version," | "%s: Unable to verify API version," | ||||
" error %d\n", __func__, error); | " error %s\n", __func__, i40e_stat_str(hw, error)); | ||||
ret_error = 5; | ret_error = 5; | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
if (ret_error >= 4) | if (ret_error >= 4) | ||||
i40e_shutdown_adminq(hw); | i40e_shutdown_adminq(hw); | ||||
return (ret_error); | return (ret_error); | ||||
Show All 24 Lines | retry_config: | ||||
while (!i40e_asq_done(hw)) { | while (!i40e_asq_done(hw)) { | ||||
if (++asq_retries > IXLV_AQ_MAX_ERR) { | if (++asq_retries > IXLV_AQ_MAX_ERR) { | ||||
device_printf(dev, "%s: Admin Queue timeout " | device_printf(dev, "%s: Admin Queue timeout " | ||||
"(waiting for send_vf_config_msg), attempt %d\n", | "(waiting for send_vf_config_msg), attempt %d\n", | ||||
__func__, retried + 1); | __func__, retried + 1); | ||||
ret_error = 3; | ret_error = 3; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
i40e_msec_delay(10); | i40e_msec_pause(10); | ||||
} | } | ||||
INIT_DBG_DEV(dev, "Sent VF config message to PF, attempt %d", | INIT_DBG_DEV(dev, "Sent VF config message to PF, attempt %d", | ||||
retried + 1); | retried + 1); | ||||
if (!sc->vf_res) { | if (!sc->vf_res) { | ||||
bufsz = sizeof(struct i40e_virtchnl_vf_resource) + | bufsz = sizeof(struct i40e_virtchnl_vf_resource) + | ||||
(I40E_MAX_VF_VSI * sizeof(struct i40e_virtchnl_vsi_resource)); | (I40E_MAX_VF_VSI * sizeof(struct i40e_virtchnl_vsi_resource)); | ||||
Show All 36 Lines | |||||
/* | /* | ||||
* Allocate MSI/X vectors, setup the AQ vector early | * Allocate MSI/X vectors, setup the AQ vector early | ||||
*/ | */ | ||||
static int | static int | ||||
ixlv_init_msix(struct ixlv_sc *sc) | ixlv_init_msix(struct ixlv_sc *sc) | ||||
{ | { | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
int rid, want, vectors, queues, available; | int rid, want, vectors, queues, available; | ||||
int auto_max_queues; | |||||
rid = PCIR_BAR(IXL_BAR); | rid = PCIR_BAR(IXL_BAR); | ||||
sc->msix_mem = bus_alloc_resource_any(dev, | sc->msix_mem = bus_alloc_resource_any(dev, | ||||
SYS_RES_MEMORY, &rid, RF_ACTIVE); | SYS_RES_MEMORY, &rid, RF_ACTIVE); | ||||
if (!sc->msix_mem) { | if (!sc->msix_mem) { | ||||
/* May not be enabled */ | /* May not be enabled */ | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"Unable to map MSIX table \n"); | "Unable to map MSIX table\n"); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
available = pci_msix_count(dev); | available = pci_msix_count(dev); | ||||
if (available == 0) { /* system has msix disabled */ | if (available == 0) { /* system has msix disabled */ | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
rid, sc->msix_mem); | rid, sc->msix_mem); | ||||
sc->msix_mem = NULL; | sc->msix_mem = NULL; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
/* Figure out a reasonable auto config value */ | /* Clamp queues to number of CPUs and # of MSI-X vectors available */ | ||||
queues = (mp_ncpus > (available - 1)) ? (available - 1) : mp_ncpus; | auto_max_queues = min(mp_ncpus, available - 1); | ||||
/* Clamp queues to # assigned to VF by PF */ | |||||
auto_max_queues = min(auto_max_queues, sc->vf_res->num_queue_pairs); | |||||
/* Override with hardcoded value if sane */ | /* Override with tunable value if tunable is less than autoconfig count */ | ||||
if ((ixlv_max_queues != 0) && (ixlv_max_queues <= queues)) | if ((ixlv_max_queues != 0) && (ixlv_max_queues <= auto_max_queues)) | ||||
queues = ixlv_max_queues; | queues = ixlv_max_queues; | ||||
/* Use autoconfig amount if that's lower */ | |||||
else if ((ixlv_max_queues != 0) && (ixlv_max_queues > auto_max_queues)) { | |||||
device_printf(dev, "ixlv_max_queues (%d) is too large, using " | |||||
"autoconfig amount (%d)...\n", | |||||
ixlv_max_queues, auto_max_queues); | |||||
queues = auto_max_queues; | |||||
} | |||||
/* Limit maximum auto-configured queues to 8 if no user value is set */ | |||||
else | |||||
queues = min(auto_max_queues, 8); | |||||
#ifdef RSS | #ifdef RSS | ||||
/* If we're doing RSS, clamp at the number of RSS buckets */ | /* If we're doing RSS, clamp at the number of RSS buckets */ | ||||
if (queues > rss_getnumbuckets()) | if (queues > rss_getnumbuckets()) | ||||
queues = rss_getnumbuckets(); | queues = rss_getnumbuckets(); | ||||
#endif | #endif | ||||
/* Enforce the VF max value */ | |||||
if (queues > IXLV_MAX_QUEUES) | |||||
queues = IXLV_MAX_QUEUES; | |||||
/* | /* | ||||
** Want one vector (RX/TX pair) per queue | ** Want one vector (RX/TX pair) per queue | ||||
** plus an additional for the admin queue. | ** plus an additional for the admin queue. | ||||
*/ | */ | ||||
want = queues + 1; | want = queues + 1; | ||||
if (want <= available) /* Have enough */ | if (want <= available) /* Have enough */ | ||||
vectors = want; | vectors = want; | ||||
Show All 27 Lines | #endif | ||||
if (pci_alloc_msix(dev, &vectors) == 0) { | if (pci_alloc_msix(dev, &vectors) == 0) { | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"Using MSIX interrupts with %d vectors\n", vectors); | "Using MSIX interrupts with %d vectors\n", vectors); | ||||
sc->msix = vectors; | sc->msix = vectors; | ||||
sc->vsi.num_queues = queues; | sc->vsi.num_queues = queues; | ||||
} | } | ||||
/* | |||||
** Explicitly set the guest PCI BUSMASTER capability | |||||
** and we must rewrite the ENABLE in the MSIX control | |||||
** register again at this point to cause the host to | |||||
** successfully initialize us. | |||||
*/ | |||||
{ | |||||
u16 pci_cmd_word; | |||||
int msix_ctrl; | |||||
pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); | |||||
pci_cmd_word |= PCIM_CMD_BUSMASTEREN; | |||||
pci_write_config(dev, PCIR_COMMAND, pci_cmd_word, 2); | |||||
pci_find_cap(dev, PCIY_MSIX, &rid); | |||||
rid += PCIR_MSIX_CTRL; | |||||
msix_ctrl = pci_read_config(dev, rid, 2); | |||||
msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE; | |||||
pci_write_config(dev, rid, msix_ctrl, 2); | |||||
} | |||||
/* Next we need to setup the vector for the Admin Queue */ | /* Next we need to setup the vector for the Admin Queue */ | ||||
rid = 1; // zero vector + 1 | rid = 1; // zero vector + 1 | ||||
sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, | ||||
&rid, RF_SHAREABLE | RF_ACTIVE); | &rid, RF_SHAREABLE | RF_ACTIVE); | ||||
if (sc->res == NULL) { | if (sc->res == NULL) { | ||||
device_printf(dev,"Unable to allocate" | device_printf(dev,"Unable to allocate" | ||||
" bus resource: AQ interrupt \n"); | " bus resource: AQ interrupt \n"); | ||||
goto fail; | goto fail; | ||||
Show All 20 Lines | ixlv_allocate_pci_resources(struct ixlv_sc *sc) | ||||
int rid; | int rid; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
rid = PCIR_BAR(0); | rid = PCIR_BAR(0); | ||||
sc->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | sc->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | ||||
&rid, RF_ACTIVE); | &rid, RF_ACTIVE); | ||||
if (!(sc->pci_mem)) { | if (!(sc->pci_mem)) { | ||||
device_printf(dev,"Unable to allocate bus resource: memory\n"); | device_printf(dev, "Unable to allocate bus resource: memory\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
sc->osdep.mem_bus_space_tag = | sc->osdep.mem_bus_space_tag = | ||||
rman_get_bustag(sc->pci_mem); | rman_get_bustag(sc->pci_mem); | ||||
sc->osdep.mem_bus_space_handle = | sc->osdep.mem_bus_space_handle = | ||||
rman_get_bushandle(sc->pci_mem); | rman_get_bushandle(sc->pci_mem); | ||||
sc->osdep.mem_bus_space_size = rman_get_size(sc->pci_mem); | sc->osdep.mem_bus_space_size = rman_get_size(sc->pci_mem); | ||||
sc->osdep.flush_reg = I40E_VFGEN_RSTAT; | sc->osdep.flush_reg = I40E_VFGEN_RSTAT; | ||||
sc->hw.hw_addr = (u8 *) &sc->osdep.mem_bus_space_handle; | sc->hw.hw_addr = (u8 *) &sc->osdep.mem_bus_space_handle; | ||||
sc->hw.back = &sc->osdep; | sc->hw.back = &sc->osdep; | ||||
/* Disable adminq interrupts */ | |||||
ixlv_disable_adminq_irq(&sc->hw); | |||||
/* | /* | ||||
** Now setup MSI/X, it will return | ** Explicitly set the guest PCI BUSMASTER capability | ||||
** us the number of supported vectors | ** and we must rewrite the ENABLE in the MSIX control | ||||
** register again at this point to cause the host to | |||||
** successfully initialize us. | |||||
** | |||||
** This must be set before accessing any registers. | |||||
*/ | */ | ||||
sc->msix = ixlv_init_msix(sc); | { | ||||
u16 pci_cmd_word; | |||||
int msix_ctrl; | |||||
pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); | |||||
pci_cmd_word |= PCIM_CMD_BUSMASTEREN; | |||||
pci_write_config(dev, PCIR_COMMAND, pci_cmd_word, 2); | |||||
pci_find_cap(dev, PCIY_MSIX, &rid); | |||||
rid += PCIR_MSIX_CTRL; | |||||
msix_ctrl = pci_read_config(dev, rid, 2); | |||||
msix_ctrl |= PCIM_MSIXCTRL_MSIX_ENABLE; | |||||
pci_write_config(dev, rid, msix_ctrl, 2); | |||||
} | |||||
/* We fail without MSIX support */ | /* Disable adminq interrupts (just in case) */ | ||||
if (sc->msix == 0) | ixlv_disable_adminq_irq(&sc->hw); | ||||
return (ENXIO); | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
ixlv_free_pci_resources(struct ixlv_sc *sc) | ixlv_free_pci_resources(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct ixl_queue *que = vsi->queues; | struct ixl_queue *que = vsi->queues; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
/* We may get here before stations are setup */ | /* We may get here before stations are setup */ | ||||
if (que == NULL) | if (que == NULL) | ||||
goto early; | goto early; | ||||
/* | /* | ||||
** Release all msix queue resources: | ** Release all msix queue resources: | ||||
*/ | */ | ||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | for (int i = 0; i < vsi->num_queues; i++, que++) { | ||||
int rid = que->msix + 1; | int rid = que->msix + 1; | ||||
if (que->tag != NULL) { | if (que->tag != NULL) { | ||||
bus_teardown_intr(dev, que->res, que->tag); | bus_teardown_intr(dev, que->res, que->tag); | ||||
que->tag = NULL; | que->tag = NULL; | ||||
} | } | ||||
if (que->res != NULL) | if (que->res != NULL) { | ||||
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | bus_release_resource(dev, SYS_RES_IRQ, rid, que->res); | ||||
que->res = NULL; | |||||
} | } | ||||
} | |||||
early: | early: | ||||
/* Clean the AdminQ interrupt */ | /* Clean the AdminQ interrupt */ | ||||
if (sc->tag != NULL) { | if (sc->tag != NULL) { | ||||
bus_teardown_intr(dev, sc->res, sc->tag); | bus_teardown_intr(dev, sc->res, sc->tag); | ||||
sc->tag = NULL; | sc->tag = NULL; | ||||
} | } | ||||
if (sc->res != NULL) | if (sc->res != NULL) { | ||||
bus_release_resource(dev, SYS_RES_IRQ, 1, sc->res); | bus_release_resource(dev, SYS_RES_IRQ, 1, sc->res); | ||||
sc->res = NULL; | |||||
} | |||||
pci_release_msi(dev); | pci_release_msi(dev); | ||||
if (sc->msix_mem != NULL) | if (sc->msix_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
PCIR_BAR(IXL_BAR), sc->msix_mem); | PCIR_BAR(IXL_BAR), sc->msix_mem); | ||||
if (sc->pci_mem != NULL) | if (sc->pci_mem != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, | bus_release_resource(dev, SYS_RES_MEMORY, | ||||
PCIR_BAR(0), sc->pci_mem); | PCIR_BAR(0), sc->pci_mem); | ||||
return; | |||||
} | } | ||||
/* | /* | ||||
* Create taskqueue and tasklet for Admin Queue interrupts. | * Create taskqueue and tasklet for Admin Queue interrupts. | ||||
*/ | */ | ||||
static int | static int | ||||
ixlv_init_taskqueue(struct ixlv_sc *sc) | ixlv_init_taskqueue(struct ixlv_sc *sc) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | for (int i = 0; i < vsi->num_queues; i++, vector++, que++) { | ||||
} | } | ||||
bus_describe_intr(dev, que->res, que->tag, "que %d", i); | bus_describe_intr(dev, que->res, que->tag, "que %d", i); | ||||
/* Bind the vector to a CPU */ | /* Bind the vector to a CPU */ | ||||
#ifdef RSS | #ifdef RSS | ||||
cpu_id = rss_getcpu(i % rss_getnumbuckets()); | cpu_id = rss_getcpu(i % rss_getnumbuckets()); | ||||
#endif | #endif | ||||
bus_bind_intr(dev, que->res, cpu_id); | bus_bind_intr(dev, que->res, cpu_id); | ||||
que->msix = vector; | que->msix = vector; | ||||
vsi->que_mask |= (u64)(1 << que->msix); | |||||
TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | TASK_INIT(&que->tx_task, 0, ixl_deferred_mq_start, que); | ||||
TASK_INIT(&que->task, 0, ixlv_handle_que, que); | TASK_INIT(&que->task, 0, ixlv_handle_que, que); | ||||
que->tq = taskqueue_create_fast("ixlv_que", M_NOWAIT, | que->tq = taskqueue_create_fast("ixlv_que", M_NOWAIT, | ||||
taskqueue_thread_enqueue, &que->tq); | taskqueue_thread_enqueue, &que->tq); | ||||
#ifdef RSS | #ifdef RSS | ||||
CPU_SETOF(cpu_id, &cpu_mask); | CPU_SETOF(cpu_id, &cpu_mask); | ||||
taskqueue_start_threads_cpuset(&que->tq, 1, PI_NET, | taskqueue_start_threads_cpuset(&que->tq, 1, PI_NET, | ||||
&cpu_mask, "%s (bucket %d)", | &cpu_mask, "%s (bucket %d)", | ||||
Show All 19 Lines | ixlv_reset(struct ixlv_sc *sc) | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
device_t dev = sc->dev; | device_t dev = sc->dev; | ||||
int error = 0; | int error = 0; | ||||
/* Ask the PF to reset us if we are initiating */ | /* Ask the PF to reset us if we are initiating */ | ||||
if (sc->init_state != IXLV_RESET_PENDING) | if (sc->init_state != IXLV_RESET_PENDING) | ||||
ixlv_request_reset(sc); | ixlv_request_reset(sc); | ||||
i40e_msec_delay(100); | i40e_msec_pause(100); | ||||
error = ixlv_reset_complete(hw); | error = ixlv_reset_complete(hw); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "%s: VF reset failed\n", | device_printf(dev, "%s: VF reset failed\n", | ||||
__func__); | __func__); | ||||
return (error); | return (error); | ||||
} | } | ||||
error = i40e_shutdown_adminq(hw); | error = i40e_shutdown_adminq(hw); | ||||
Show All 13 Lines | ixlv_reset(struct ixlv_sc *sc) | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
ixlv_reset_complete(struct i40e_hw *hw) | ixlv_reset_complete(struct i40e_hw *hw) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
/* Wait up to ~10 seconds */ | |||||
for (int i = 0; i < 100; i++) { | for (int i = 0; i < 100; i++) { | ||||
reg = rd32(hw, I40E_VFGEN_RSTAT) & | reg = rd32(hw, I40E_VFGEN_RSTAT) & | ||||
I40E_VFGEN_RSTAT_VFR_STATE_MASK; | I40E_VFGEN_RSTAT_VFR_STATE_MASK; | ||||
if ((reg == I40E_VFR_VFACTIVE) || | if ((reg == I40E_VFR_VFACTIVE) || | ||||
(reg == I40E_VFR_COMPLETED)) | (reg == I40E_VFR_COMPLETED)) | ||||
return (0); | return (0); | ||||
i40e_msec_delay(100); | i40e_msec_pause(100); | ||||
} | } | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
/********************************************************************* | /********************************************************************* | ||||
* | * | ||||
Show All 14 Lines | if (ifp == NULL) { | ||||
device_printf(dev, "%s: could not allocate ifnet" | device_printf(dev, "%s: could not allocate ifnet" | ||||
" structure!\n", __func__); | " structure!\n", __func__); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | ||||
ifp->if_mtu = ETHERMTU; | ifp->if_mtu = ETHERMTU; | ||||
ifp->if_baudrate = 4000000000; // ?? | ifp->if_baudrate = IF_Gbps(40); | ||||
ifp->if_init = ixlv_init; | ifp->if_init = ixlv_init; | ||||
ifp->if_softc = vsi; | ifp->if_softc = vsi; | ||||
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | ||||
ifp->if_ioctl = ixlv_ioctl; | ifp->if_ioctl = ixlv_ioctl; | ||||
#if __FreeBSD_version >= 1100000 | #if __FreeBSD_version >= 1100000 | ||||
if_setgetcounterfn(ifp, ixl_get_counter); | if_setgetcounterfn(ifp, ixl_get_counter); | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Lines | if (i40e_allocate_dma_mem(&sc->hw, | ||||
device_printf(dev, | device_printf(dev, | ||||
"Unable to allocate RX Descriptor memory\n"); | "Unable to allocate RX Descriptor memory\n"); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
rxr->base = (union i40e_rx_desc *)rxr->dma.va; | rxr->base = (union i40e_rx_desc *)rxr->dma.va; | ||||
bzero((void *)rxr->base, rsize); | bzero((void *)rxr->base, rsize); | ||||
/* Allocate receive soft structs for the ring*/ | /* Allocate receive soft structs for the ring */ | ||||
if (ixl_allocate_rx_data(que)) { | if (ixl_allocate_rx_data(que)) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"Critical Failure setting up receive structs\n"); | "Critical Failure setting up receive structs\n"); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static void | static void | ||||
ixlv_enable_queue_irq(struct i40e_hw *hw, int id) | ixlv_enable_queue_irq(struct i40e_hw *hw, int id) | ||||
{ | { | ||||
u32 reg; | u32 reg; | ||||
reg = I40E_VFINT_DYN_CTLN1_INTENA_MASK | | reg = I40E_VFINT_DYN_CTLN1_INTENA_MASK | | ||||
I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK; | I40E_VFINT_DYN_CTLN1_CLEARPBA_MASK | | ||||
I40E_VFINT_DYN_CTLN1_ITR_INDX_MASK; | |||||
wr32(hw, I40E_VFINT_DYN_CTLN1(id), reg); | wr32(hw, I40E_VFINT_DYN_CTLN1(id), reg); | ||||
} | } | ||||
static void | static void | ||||
ixlv_disable_queue_irq(struct i40e_hw *hw, int id) | ixlv_disable_queue_irq(struct i40e_hw *hw, int id) | ||||
{ | { | ||||
wr32(hw, I40E_VFINT_DYN_CTLN1(id), 0); | wr32(hw, I40E_VFINT_DYN_CTLN1(id), | ||||
I40E_VFINT_DYN_CTLN1_ITR_INDX_MASK); | |||||
rd32(hw, I40E_VFGEN_RSTAT); | rd32(hw, I40E_VFGEN_RSTAT); | ||||
return; | return; | ||||
} | } | ||||
/* | |||||
* Get initial ITR values from tunable values. | |||||
*/ | |||||
static void | |||||
ixlv_configure_itr(struct ixlv_sc *sc) | |||||
{ | |||||
struct i40e_hw *hw = &sc->hw; | |||||
struct ixl_vsi *vsi = &sc->vsi; | |||||
struct ixl_queue *que = vsi->queues; | |||||
vsi->rx_itr_setting = ixlv_rx_itr; | |||||
vsi->tx_itr_setting = ixlv_tx_itr; | |||||
for (int i = 0; i < vsi->num_queues; i++, que++) { | |||||
struct tx_ring *txr = &que->txr; | |||||
struct rx_ring *rxr = &que->rxr; | |||||
wr32(hw, I40E_VFINT_ITRN1(IXL_RX_ITR, i), | |||||
vsi->rx_itr_setting); | |||||
rxr->itr = vsi->rx_itr_setting; | |||||
rxr->latency = IXL_AVE_LATENCY; | |||||
wr32(hw, I40E_VFINT_ITRN1(IXL_TX_ITR, i), | |||||
vsi->tx_itr_setting); | |||||
txr->itr = vsi->tx_itr_setting; | |||||
txr->latency = IXL_AVE_LATENCY; | |||||
} | |||||
} | |||||
/* | /* | ||||
** Provide a update to the queue RX | ** Provide a update to the queue RX | ||||
** interrupt moderation value. | ** interrupt moderation value. | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_set_queue_rx_itr(struct ixl_queue *que) | ixlv_set_queue_rx_itr(struct ixl_queue *que) | ||||
{ | { | ||||
struct ixl_vsi *vsi = que->vsi; | struct ixl_vsi *vsi = que->vsi; | ||||
▲ Show 20 Lines • Show All 326 Lines • ▼ Show 20 Lines | ixlv_add_multi(struct ixl_vsi *vsi) | ||||
*/ | */ | ||||
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | ||||
if (ifma->ifma_addr->sa_family != AF_LINK) | if (ifma->ifma_addr->sa_family != AF_LINK) | ||||
continue; | continue; | ||||
mcnt++; | mcnt++; | ||||
} | } | ||||
if_maddr_runlock(ifp); | if_maddr_runlock(ifp); | ||||
// TODO: Remove -- cannot set promiscuous mode in a VF | /* TODO: Remove -- cannot set promiscuous mode in a VF */ | ||||
if (__predict_false(mcnt >= MAX_MULTICAST_ADDR)) { | if (__predict_false(mcnt >= MAX_MULTICAST_ADDR)) { | ||||
/* delete all multicast filters */ | /* delete all multicast filters */ | ||||
ixlv_init_multi(vsi); | ixlv_init_multi(vsi); | ||||
sc->promiscuous_flags |= I40E_FLAG_VF_MULTICAST_PROMISC; | sc->promiscuous_flags |= I40E_FLAG_VF_MULTICAST_PROMISC; | ||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->add_multi_cmd, | ixl_vc_enqueue(&sc->vc_mgr, &sc->add_multi_cmd, | ||||
IXLV_FLAG_AQ_CONFIGURE_PROMISC, ixl_init_cmd_complete, | IXLV_FLAG_AQ_CONFIGURE_PROMISC, ixl_init_cmd_complete, | ||||
sc); | sc); | ||||
IOCTL_DEBUGOUT("%s: end: too many filters", __func__); | IOCTL_DEBUGOUT("%s: end: too many filters", __func__); | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | ixlv_local_timer(void *arg) | ||||
/* clean and process any events */ | /* clean and process any events */ | ||||
taskqueue_enqueue(sc->tq, &sc->aq_irq); | taskqueue_enqueue(sc->tq, &sc->aq_irq); | ||||
/* | /* | ||||
** Check status on the queues for a hang | ** Check status on the queues for a hang | ||||
*/ | */ | ||||
mask = (I40E_VFINT_DYN_CTLN1_INTENA_MASK | | mask = (I40E_VFINT_DYN_CTLN1_INTENA_MASK | | ||||
I40E_VFINT_DYN_CTLN1_SWINT_TRIG_MASK); | I40E_VFINT_DYN_CTLN1_SWINT_TRIG_MASK | | ||||
I40E_VFINT_DYN_CTLN1_ITR_INDX_MASK); | |||||
for (int i = 0; i < vsi->num_queues; i++,que++) { | for (int i = 0; i < vsi->num_queues; i++,que++) { | ||||
/* Any queues with outstanding work get a sw irq */ | /* Any queues with outstanding work get a sw irq */ | ||||
if (que->busy) | if (que->busy) | ||||
wr32(hw, I40E_VFINT_DYN_CTLN1(que->me), mask); | wr32(hw, I40E_VFINT_DYN_CTLN1(que->me), mask); | ||||
/* | /* | ||||
** Each time txeof runs without cleaning, but there | ** Each time txeof runs without cleaning, but there | ||||
** are uncleaned descriptors it increments busy. If | ** are uncleaned descriptors it increments busy. If | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | if (rxr->base) | ||||
i40e_free_dma_mem(&sc->hw, &rxr->dma); | i40e_free_dma_mem(&sc->hw, &rxr->dma); | ||||
IXL_RX_UNLOCK(rxr); | IXL_RX_UNLOCK(rxr); | ||||
IXL_RX_LOCK_DESTROY(rxr); | IXL_RX_LOCK_DESTROY(rxr); | ||||
} | } | ||||
free(vsi->queues, M_DEVBUF); | free(vsi->queues, M_DEVBUF); | ||||
} | } | ||||
/* | |||||
** ixlv_config_rss - setup RSS | |||||
** | |||||
** RSS keys and table are cleared on VF reset. | |||||
*/ | |||||
static void | static void | ||||
ixlv_config_rss(struct ixlv_sc *sc) | ixlv_config_rss_reg(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct i40e_hw *hw = &sc->hw; | struct i40e_hw *hw = &sc->hw; | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
u32 lut = 0; | u32 lut = 0; | ||||
u64 set_hena = 0, hena; | u64 set_hena = 0, hena; | ||||
int i, j, que_id; | int i, j, que_id; | ||||
u32 rss_seed[IXL_RSS_KEY_SIZE_REG]; | |||||
#ifdef RSS | #ifdef RSS | ||||
u32 rss_hash_config; | u32 rss_hash_config; | ||||
u32 rss_seed[IXL_KEYSZ]; | |||||
#else | |||||
u32 rss_seed[IXL_KEYSZ] = {0x41b01687, | |||||
0x183cfd8c, 0xce880440, 0x580cbc3c, | |||||
0x35897377, 0x328b25e1, 0x4fa98922, | |||||
0xb7d90c14, 0xd5bad70d, 0xcd15a2c1}; | |||||
#endif | #endif | ||||
/* Don't set up RSS if using a single queue */ | /* Don't set up RSS if using a single queue */ | ||||
if (vsi->num_queues == 1) { | if (vsi->num_queues == 1) { | ||||
wr32(hw, I40E_VFQF_HENA(0), 0); | wr32(hw, I40E_VFQF_HENA(0), 0); | ||||
wr32(hw, I40E_VFQF_HENA(1), 0); | wr32(hw, I40E_VFQF_HENA(1), 0); | ||||
ixl_flush(hw); | ixl_flush(hw); | ||||
return; | return; | ||||
} | } | ||||
#ifdef RSS | #ifdef RSS | ||||
/* Fetch the configured RSS key */ | /* Fetch the configured RSS key */ | ||||
rss_getkey((uint8_t *) &rss_seed); | rss_getkey((uint8_t *) &rss_seed); | ||||
#else | |||||
ixl_get_default_rss_key(rss_seed); | |||||
#endif | #endif | ||||
/* Fill out hash function seed */ | /* Fill out hash function seed */ | ||||
for (i = 0; i <= IXL_KEYSZ; i++) | for (i = 0; i < IXL_RSS_KEY_SIZE_REG; i++) | ||||
wr32(hw, I40E_VFQF_HKEY(i), rss_seed[i]); | wr32(hw, I40E_VFQF_HKEY(i), rss_seed[i]); | ||||
/* Enable PCTYPES for RSS: */ | /* Enable PCTYPES for RSS: */ | ||||
#ifdef RSS | #ifdef RSS | ||||
rss_hash_config = rss_gethashconfig(); | rss_hash_config = rss_gethashconfig(); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4) | if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4) | if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4) | if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6) | if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX) | if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6) | if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP); | ||||
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6) | if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6) | ||||
set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP); | set_hena |= ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP); | ||||
#else | #else | ||||
set_hena = | set_hena = IXL_DEFAULT_RSS_HENA; | ||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_SCTP) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_OTHER) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV6) | | |||||
((u64)1 << I40E_FILTER_PCTYPE_L2_PAYLOAD); | |||||
#endif | #endif | ||||
hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | hena = (u64)rd32(hw, I40E_VFQF_HENA(0)) | | ||||
((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | ((u64)rd32(hw, I40E_VFQF_HENA(1)) << 32); | ||||
hena |= set_hena; | hena |= set_hena; | ||||
wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | wr32(hw, I40E_VFQF_HENA(0), (u32)hena); | ||||
wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32)); | ||||
// TODO: Fix -- only 3,7,11,15 are filled out, instead of all 16 registers | |||||
/* Populate the LUT with max no. of queues in round robin fashion */ | /* Populate the LUT with max no. of queues in round robin fashion */ | ||||
for (i = 0, j = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++, j++) { | for (i = 0, j = 0; i < IXL_RSS_VSI_LUT_SIZE; i++, j++) { | ||||
if (j == vsi->num_queues) | if (j == vsi->num_queues) | ||||
j = 0; | j = 0; | ||||
#ifdef RSS | #ifdef RSS | ||||
/* | /* | ||||
* Fetch the RSS bucket id for the given indirection entry. | * Fetch the RSS bucket id for the given indirection entry. | ||||
* Cap it at the number of configured buckets (which is | * Cap it at the number of configured buckets (which is | ||||
* num_queues.) | * num_queues.) | ||||
*/ | */ | ||||
que_id = rss_get_indirection_to_bucket(i); | que_id = rss_get_indirection_to_bucket(i); | ||||
que_id = que_id % vsi->num_queues; | que_id = que_id % vsi->num_queues; | ||||
#else | #else | ||||
que_id = j; | que_id = j; | ||||
#endif | #endif | ||||
/* lut = 4-byte sliding window of 4 lut entries */ | /* lut = 4-byte sliding window of 4 lut entries */ | ||||
lut = (lut << 8) | (que_id & 0xF); | lut = (lut << 8) | (que_id & IXL_RSS_VF_LUT_ENTRY_MASK); | ||||
/* On i = 3, we have 4 entries in lut; write to the register */ | /* On i = 3, we have 4 entries in lut; write to the register */ | ||||
if ((i & 3) == 3) { | if ((i & 3) == 3) { | ||||
wr32(hw, I40E_VFQF_HLUT(i), lut); | wr32(hw, I40E_VFQF_HLUT(i >> 2), lut); | ||||
DDPRINTF(sc->dev, "HLUT(%2d): %#010x", i, lut); | DDPRINTF(sc->dev, "HLUT(%2d): %#010x", i, lut); | ||||
} | } | ||||
} | } | ||||
ixl_flush(hw); | ixl_flush(hw); | ||||
} | } | ||||
static void | |||||
ixlv_config_rss_pf(struct ixlv_sc *sc) | |||||
{ | |||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->config_rss_key_cmd, | |||||
IXLV_FLAG_AQ_CONFIG_RSS_KEY, ixl_init_cmd_complete, sc); | |||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->set_rss_hena_cmd, | |||||
IXLV_FLAG_AQ_SET_RSS_HENA, ixl_init_cmd_complete, sc); | |||||
ixl_vc_enqueue(&sc->vc_mgr, &sc->config_rss_lut_cmd, | |||||
IXLV_FLAG_AQ_CONFIG_RSS_LUT, ixl_init_cmd_complete, sc); | |||||
} | |||||
/* | /* | ||||
** ixlv_config_rss - setup RSS | |||||
** | |||||
** RSS keys and table are cleared on VF reset. | |||||
*/ | |||||
static void | |||||
ixlv_config_rss(struct ixlv_sc *sc) | |||||
{ | |||||
if (sc->vf_res->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_RSS_REG) { | |||||
DDPRINTF(sc->dev, "Setting up RSS using VF registers..."); | |||||
ixlv_config_rss_reg(sc); | |||||
} else if (sc->vf_res->vf_offload_flags & I40E_VIRTCHNL_VF_OFFLOAD_RSS_PF) { | |||||
DDPRINTF(sc->dev, "Setting up RSS using messages to PF..."); | |||||
ixlv_config_rss_pf(sc); | |||||
} else | |||||
device_printf(sc->dev, "VF does not support RSS capability sent by PF.\n"); | |||||
} | |||||
/* | |||||
** This routine refreshes vlan filters, called by init | ** This routine refreshes vlan filters, called by init | ||||
** it scans the filter table and then updates the AQ | ** it scans the filter table and then updates the AQ | ||||
*/ | */ | ||||
static void | static void | ||||
ixlv_setup_vlan_filters(struct ixlv_sc *sc) | ixlv_setup_vlan_filters(struct ixlv_sc *sc) | ||||
{ | { | ||||
struct ixl_vsi *vsi = &sc->vsi; | struct ixl_vsi *vsi = &sc->vsi; | ||||
struct ixlv_vlan_filter *f; | struct ixlv_vlan_filter *f; | ||||
▲ Show 20 Lines • Show All 224 Lines • ▼ Show 20 Lines | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "dropped", | ||||
CTLFLAG_RD, &(queues[q].dropped_pkts), | CTLFLAG_RD, &(queues[q].dropped_pkts), | ||||
"Driver dropped packets"); | "Driver dropped packets"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "irqs", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "irqs", | ||||
CTLFLAG_RD, &(queues[q].irqs), | CTLFLAG_RD, &(queues[q].irqs), | ||||
"irqs on this queue"); | "irqs on this queue"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tso_tx", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tso_tx", | ||||
CTLFLAG_RD, &(queues[q].tso), | CTLFLAG_RD, &(queues[q].tso), | ||||
"TSO"); | "TSO"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_dma_setup", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_dmamap_failed", | ||||
CTLFLAG_RD, &(queues[q].tx_dma_setup), | CTLFLAG_RD, &(queues[q].tx_dmamap_failed), | ||||
"Driver tx dma failure in xmit"); | "Driver tx dma failure in xmit"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "no_desc_avail", | ||||
CTLFLAG_RD, &(txr->no_desc), | CTLFLAG_RD, &(txr->no_desc), | ||||
"Queue No Descriptor Available"); | "Queue No Descriptor Available"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_packets", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_packets", | ||||
CTLFLAG_RD, &(txr->total_packets), | CTLFLAG_RD, &(txr->total_packets), | ||||
"Queue Packets Transmitted"); | "Queue Packets Transmitted"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_bytes", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "tx_bytes", | ||||
CTLFLAG_RD, &(txr->tx_bytes), | CTLFLAG_RD, &(txr->tx_bytes), | ||||
"Queue Bytes Transmitted"); | "Queue Bytes Transmitted"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "rx_packets", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "rx_packets", | ||||
CTLFLAG_RD, &(rxr->rx_packets), | CTLFLAG_RD, &(rxr->rx_packets), | ||||
"Queue Packets Received"); | "Queue Packets Received"); | ||||
SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "rx_bytes", | SYSCTL_ADD_QUAD(ctx, queue_list, OID_AUTO, "rx_bytes", | ||||
CTLFLAG_RD, &(rxr->rx_bytes), | CTLFLAG_RD, &(rxr->rx_bytes), | ||||
"Queue Bytes Received"); | "Queue Bytes Received"); | ||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "rx_itr", | |||||
CTLFLAG_RD, &(rxr->itr), 0, | |||||
"Queue Rx ITR Interval"); | |||||
SYSCTL_ADD_UINT(ctx, queue_list, OID_AUTO, "tx_itr", | |||||
CTLFLAG_RD, &(txr->itr), 0, | |||||
"Queue Tx ITR Interval"); | |||||
#ifdef IXL_DEBUG | |||||
/* Examine queue state */ | /* Examine queue state */ | ||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qtx_head", | SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qtx_head", | ||||
CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | ||||
sizeof(struct ixl_queue), | sizeof(struct ixl_queue), | ||||
ixlv_sysctl_qtx_tail_handler, "IU", | ixlv_sysctl_qtx_tail_handler, "IU", | ||||
"Queue Transmit Descriptor Tail"); | "Queue Transmit Descriptor Tail"); | ||||
SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_head", | SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "qrx_head", | ||||
CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | CTLTYPE_UINT | CTLFLAG_RD, &queues[q], | ||||
sizeof(struct ixl_queue), | sizeof(struct ixl_queue), | ||||
ixlv_sysctl_qrx_tail_handler, "IU", | ixlv_sysctl_qrx_tail_handler, "IU", | ||||
"Queue Receive Descriptor Tail"); | "Queue Receive Descriptor Tail"); | ||||
#endif | |||||
} | } | ||||
} | } | ||||
static void | static void | ||||
ixlv_init_filters(struct ixlv_sc *sc) | ixlv_init_filters(struct ixlv_sc *sc) | ||||
{ | { | ||||
sc->mac_filters = malloc(sizeof(struct ixlv_mac_filter), | sc->mac_filters = malloc(sizeof(struct ixlv_mac_filter), | ||||
M_DEVBUF, M_NOWAIT | M_ZERO); | M_DEVBUF, M_NOWAIT | M_ZERO); | ||||
Show All 18 Lines | ixlv_free_filters(struct ixlv_sc *sc) | ||||
while (!SLIST_EMPTY(sc->vlan_filters)) { | while (!SLIST_EMPTY(sc->vlan_filters)) { | ||||
v = SLIST_FIRST(sc->vlan_filters); | v = SLIST_FIRST(sc->vlan_filters); | ||||
SLIST_REMOVE_HEAD(sc->vlan_filters, next); | SLIST_REMOVE_HEAD(sc->vlan_filters, next); | ||||
free(v, M_DEVBUF); | free(v, M_DEVBUF); | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
#ifdef IXL_DEBUG | |||||
/** | /** | ||||
* ixlv_sysctl_qtx_tail_handler | * ixlv_sysctl_qtx_tail_handler | ||||
* Retrieves I40E_QTX_TAIL1 value from hardware | * Retrieves I40E_QTX_TAIL1 value from hardware | ||||
* for a sysctl. | * for a sysctl. | ||||
*/ | */ | ||||
static int | static int | ||||
ixlv_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS) | ixlv_sysctl_qtx_tail_handler(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
Show All 27 Lines | ixlv_sysctl_qrx_tail_handler(SYSCTL_HANDLER_ARGS) | ||||
if (!que) return 0; | if (!que) return 0; | ||||
val = rd32(que->vsi->hw, que->rxr.tail); | val = rd32(que->vsi->hw, que->rxr.tail); | ||||
error = sysctl_handle_int(oidp, &val, 0, req); | error = sysctl_handle_int(oidp, &val, 0, req); | ||||
if (error || !req->newptr) | if (error || !req->newptr) | ||||
return error; | return error; | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | |||||