#include "iavf_iflib.h"
#include "iavf_vc_common.h"
static void iavf_init_hw(struct iavf_hw *hw, device_t dev);
static u_int iavf_mc_filter_apply(void *arg, struct sockaddr_dl *sdl, u_int cnt);
void
iavf_msec_pause(int msecs)
{
pause("iavf_msec_pause", MSEC_2_TICKS(msecs));
}
void
iavf_get_default_rss_key(u32 *key)
{
MPASS(key != NULL);
u32 rss_seed[IAVF_RSS_KEY_SIZE_REG] = {0x41b01687,
0x183cfd8c, 0xce880440, 0x580cbc3c,
0x35897377, 0x328b25e1, 0x4fa98922,
0xb7d90c14, 0xd5bad70d, 0xcd15a2c1,
0x0, 0x0, 0x0};
bcopy(rss_seed, key, IAVF_RSS_KEY_SIZE);
}
int
iavf_allocate_pci_resources_common(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
int rid;
rid = PCIR_BAR(0);
sc->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&rid, RF_ACTIVE);
if (!(sc->pci_mem)) {
device_printf(dev, "Unable to allocate bus resource: PCI memory\n");
return (ENXIO);
}
iavf_init_hw(hw, dev);
sc->osdep.mem_bus_space_tag =
rman_get_bustag(sc->pci_mem);
sc->osdep.mem_bus_space_handle =
rman_get_bushandle(sc->pci_mem);
sc->osdep.mem_bus_space_size = rman_get_size(sc->pci_mem);
sc->osdep.flush_reg = IAVF_VFGEN_RSTAT;
sc->osdep.dev = dev;
sc->hw.hw_addr = (u8 *)&sc->osdep.mem_bus_space_handle;
sc->hw.back = &sc->osdep;
return (0);
}
static void
iavf_init_hw(struct iavf_hw *hw, device_t dev)
{
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);
hw->bus.device = pci_get_slot(dev);
hw->bus.func = pci_get_function(dev);
}
int
iavf_sysctl_current_speed(SYSCTL_HANDLER_ARGS)
{
struct iavf_sc *sc = (struct iavf_sc *)arg1;
int error = 0;
UNREFERENCED_PARAMETER(arg2);
if (iavf_driver_is_detaching(sc))
return (ESHUTDOWN);
if (IAVF_CAP_ADV_LINK_SPEED(sc))
error = sysctl_handle_string(oidp,
__DECONST(char *, iavf_ext_speed_to_str(iavf_adv_speed_to_ext_speed(sc->link_speed_adv))),
8, req);
else
error = sysctl_handle_string(oidp,
__DECONST(char *, iavf_vc_speed_to_string(sc->link_speed)),
8, req);
return (error);
}
int
iavf_reset_complete(struct iavf_hw *hw)
{
u32 reg;
for (int i = 0; i < 100; i++) {
reg = rd32(hw, IAVF_VFGEN_RSTAT) &
IAVF_VFGEN_RSTAT_VFR_STATE_MASK;
if ((reg == VIRTCHNL_VFR_VFACTIVE) ||
(reg == VIRTCHNL_VFR_COMPLETED))
return (0);
iavf_msec_pause(100);
}
return (EBUSY);
}
int
iavf_setup_vc(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
int error = 0, ret_error = 0, asq_retries = 0;
bool send_api_ver_retried = 0;
hw->aq.num_arq_entries = IAVF_AQ_LEN;
hw->aq.num_asq_entries = IAVF_AQ_LEN;
hw->aq.arq_buf_size = IAVF_AQ_BUF_SZ;
hw->aq.asq_buf_size = IAVF_AQ_BUF_SZ;
for (int i = 0; i < IAVF_AQ_MAX_ERR; i++) {
error = iavf_init_adminq(hw);
if (error) {
device_printf(dev, "%s: init_adminq failed: %d\n",
__func__, error);
ret_error = 1;
continue;
}
iavf_dbg_init(sc, "Initialized Admin Queue; starting"
" send_api_ver attempt %d", i+1);
retry_send:
error = iavf_send_api_ver(sc);
if (error) {
iavf_shutdown_adminq(hw);
ret_error = 2;
device_printf(dev, "%s: unable to send api"
" version to PF on attempt %d, error %d\n",
__func__, i+1, error);
}
asq_retries = 0;
while (!iavf_asq_done(hw)) {
if (++asq_retries > IAVF_AQ_MAX_ERR) {
iavf_shutdown_adminq(hw);
device_printf(dev, "Admin Queue timeout "
"(waiting for send_api_ver), %d more tries...\n",
IAVF_AQ_MAX_ERR - (i + 1));
ret_error = 3;
break;
}
iavf_msec_pause(10);
}
if (asq_retries > IAVF_AQ_MAX_ERR)
continue;
iavf_dbg_init(sc, "Sent API version message to PF");
error = iavf_verify_api_ver(sc);
if (error == ETIMEDOUT) {
if (!send_api_ver_retried) {
send_api_ver_retried = true;
device_printf(dev,
"%s: Timeout while verifying API version on first"
" try!\n", __func__);
goto retry_send;
} else {
device_printf(dev,
"%s: Timeout while verifying API version on second"
" try!\n", __func__);
ret_error = 4;
break;
}
}
if (error) {
device_printf(dev,
"%s: Unable to verify API version,"
" error %d\n", __func__, error);
ret_error = 5;
}
break;
}
if (ret_error >= 4)
iavf_shutdown_adminq(hw);
return (ret_error);
}
int
iavf_reset(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
int error = 0;
if (!iavf_test_state(&sc->state, IAVF_STATE_RESET_PENDING))
iavf_request_reset(sc);
iavf_msec_pause(100);
error = iavf_reset_complete(hw);
if (error) {
device_printf(dev, "%s: VF reset failed\n",
__func__);
return (error);
}
pci_enable_busmaster(dev);
error = iavf_shutdown_adminq(hw);
if (error) {
device_printf(dev, "%s: shutdown_adminq failed: %d\n",
__func__, error);
return (error);
}
error = iavf_init_adminq(hw);
if (error) {
device_printf(dev, "%s: init_adminq failed: %d\n",
__func__, error);
return (error);
}
iavf_enable_adminq_irq(hw);
return (0);
}
void
iavf_enable_adminq_irq(struct iavf_hw *hw)
{
wr32(hw, IAVF_VFINT_DYN_CTL01,
IAVF_VFINT_DYN_CTL01_INTENA_MASK |
IAVF_VFINT_DYN_CTL01_CLEARPBA_MASK |
IAVF_VFINT_DYN_CTL01_ITR_INDX_MASK);
wr32(hw, IAVF_VFINT_ICR0_ENA1, IAVF_VFINT_ICR0_ENA1_ADMINQ_MASK);
rd32(hw, IAVF_VFGEN_RSTAT);
}
void
iavf_disable_adminq_irq(struct iavf_hw *hw)
{
wr32(hw, IAVF_VFINT_DYN_CTL01, 0);
wr32(hw, IAVF_VFINT_ICR0_ENA1, 0);
iavf_flush(hw);
}
int
iavf_vf_config(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
int bufsz, error = 0, ret_error = 0;
int asq_retries, retried = 0;
retry_config:
error = iavf_send_vf_config_msg(sc);
if (error) {
device_printf(dev,
"%s: Unable to send VF config request, attempt %d,"
" error %d\n", __func__, retried + 1, error);
ret_error = 2;
}
asq_retries = 0;
while (!iavf_asq_done(hw)) {
if (++asq_retries > IAVF_AQ_MAX_ERR) {
device_printf(dev, "%s: Admin Queue timeout "
"(waiting for send_vf_config_msg), attempt %d\n",
__func__, retried + 1);
ret_error = 3;
goto fail;
}
iavf_msec_pause(10);
}
iavf_dbg_init(sc, "Sent VF config message to PF, attempt %d\n",
retried + 1);
if (!sc->vf_res) {
bufsz = sizeof(struct virtchnl_vf_resource) +
(IAVF_MAX_VF_VSI * sizeof(struct virtchnl_vsi_resource));
sc->vf_res = (struct virtchnl_vf_resource *)malloc(bufsz, M_IAVF, M_NOWAIT);
if (!sc->vf_res) {
device_printf(dev,
"%s: Unable to allocate memory for VF configuration"
" message from PF on attempt %d\n", __func__, retried + 1);
ret_error = 1;
goto fail;
}
}
error = iavf_get_vf_config(sc);
if (error == ETIMEDOUT) {
if (!retried) {
retried++;
goto retry_config;
}
device_printf(dev,
"%s: iavf_get_vf_config() timed out waiting for a response\n",
__func__);
}
if (error) {
device_printf(dev,
"%s: Unable to get VF configuration from PF after %d tries!\n",
__func__, retried + 1);
ret_error = 4;
}
goto done;
fail:
free(sc->vf_res, M_IAVF);
done:
return (ret_error);
}
void
iavf_print_device_info(struct iavf_sc *sc)
{
device_t dev = sc->dev;
device_printf(dev,
"VSIs %d, QPs %d, MSI-X %d, RSS sizes: key %d lut %d\n",
sc->vf_res->num_vsis,
sc->vf_res->num_queue_pairs,
sc->vf_res->max_vectors,
sc->vf_res->rss_key_size,
sc->vf_res->rss_lut_size);
iavf_dbg_info(sc, "Capabilities=%b\n",
sc->vf_res->vf_cap_flags, IAVF_PRINTF_VF_OFFLOAD_FLAGS);
}
int
iavf_get_vsi_res_from_vf_res(struct iavf_sc *sc)
{
struct iavf_vsi *vsi = &sc->vsi;
device_t dev = sc->dev;
sc->vsi_res = NULL;
for (int i = 0; i < sc->vf_res->num_vsis; i++) {
if (sc->vf_res->vsi_res[i].vsi_type == VIRTCHNL_VSI_SRIOV)
sc->vsi_res = &sc->vf_res->vsi_res[i];
}
if (!sc->vsi_res) {
device_printf(dev, "%s: no LAN VSI found\n", __func__);
return (EIO);
}
vsi->id = sc->vsi_res->vsi_id;
return (0);
}
void
iavf_set_mac_addresses(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
device_t dev = sc->dev;
u8 addr[ETHER_ADDR_LEN];
if (ETHER_IS_ZERO(hw->mac.addr)) {
arc4rand(&addr, sizeof(addr), 0);
addr[0] &= 0xFE;
addr[0] |= 0x02;
memcpy(hw->mac.addr, addr, sizeof(addr));
device_printf(dev, "Generated random MAC address\n");
}
memcpy(hw->mac.perm_addr, hw->mac.addr, ETHER_ADDR_LEN);
}
void
iavf_init_filters(struct iavf_sc *sc)
{
sc->mac_filters = (struct mac_list *)malloc(sizeof(struct iavf_mac_filter),
M_IAVF, M_WAITOK | M_ZERO);
SLIST_INIT(sc->mac_filters);
sc->vlan_filters = (struct vlan_list *)malloc(sizeof(struct iavf_vlan_filter),
M_IAVF, M_WAITOK | M_ZERO);
SLIST_INIT(sc->vlan_filters);
}
void
iavf_free_filters(struct iavf_sc *sc)
{
struct iavf_mac_filter *f;
struct iavf_vlan_filter *v;
while (!SLIST_EMPTY(sc->mac_filters)) {
f = SLIST_FIRST(sc->mac_filters);
SLIST_REMOVE_HEAD(sc->mac_filters, next);
free(f, M_IAVF);
}
free(sc->mac_filters, M_IAVF);
while (!SLIST_EMPTY(sc->vlan_filters)) {
v = SLIST_FIRST(sc->vlan_filters);
SLIST_REMOVE_HEAD(sc->vlan_filters, next);
free(v, M_IAVF);
}
free(sc->vlan_filters, M_IAVF);
}
void
iavf_add_device_sysctls_common(struct iavf_sc *sc)
{
device_t dev = sc->dev;
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
struct sysctl_oid_list *ctx_list =
SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "current_speed", CTLTYPE_STRING | CTLFLAG_RD,
sc, 0, iavf_sysctl_current_speed, "A", "Current Port Speed");
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "tx_itr", CTLTYPE_INT | CTLFLAG_RW,
sc, 0, iavf_sysctl_tx_itr, "I",
"Immediately set TX ITR value for all queues");
SYSCTL_ADD_PROC(ctx, ctx_list,
OID_AUTO, "rx_itr", CTLTYPE_INT | CTLFLAG_RW,
sc, 0, iavf_sysctl_rx_itr, "I",
"Immediately set RX ITR value for all queues");
SYSCTL_ADD_UQUAD(ctx, ctx_list,
OID_AUTO, "admin_irq", CTLFLAG_RD,
&sc->admin_irq, "Admin Queue IRQ Handled");
}
void
iavf_add_debug_sysctls_common(struct iavf_sc *sc, struct sysctl_oid_list *debug_list)
{
device_t dev = sc->dev;
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
SYSCTL_ADD_UINT(ctx, debug_list,
OID_AUTO, "shared_debug_mask", CTLFLAG_RW,
&sc->hw.debug_mask, 0, "Shared code debug message level");
SYSCTL_ADD_UINT(ctx, debug_list,
OID_AUTO, "core_debug_mask", CTLFLAG_RW,
(unsigned int *)&sc->dbg_mask, 0, "Non-shared code debug message level");
SYSCTL_ADD_PROC(ctx, debug_list,
OID_AUTO, "filter_list", CTLTYPE_STRING | CTLFLAG_RD,
sc, 0, iavf_sysctl_sw_filter_list, "A", "SW Filter List");
}
int
iavf_sysctl_tx_itr(SYSCTL_HANDLER_ARGS)
{
struct iavf_sc *sc = (struct iavf_sc *)arg1;
device_t dev = sc->dev;
int requested_tx_itr;
int error = 0;
UNREFERENCED_PARAMETER(arg2);
if (iavf_driver_is_detaching(sc))
return (ESHUTDOWN);
requested_tx_itr = sc->tx_itr;
error = sysctl_handle_int(oidp, &requested_tx_itr, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (requested_tx_itr < 0 || requested_tx_itr > IAVF_MAX_ITR) {
device_printf(dev,
"Invalid TX itr value; value must be between 0 and %d\n",
IAVF_MAX_ITR);
return (EINVAL);
}
sc->tx_itr = requested_tx_itr;
iavf_configure_tx_itr(sc);
return (error);
}
int
iavf_sysctl_rx_itr(SYSCTL_HANDLER_ARGS)
{
struct iavf_sc *sc = (struct iavf_sc *)arg1;
device_t dev = sc->dev;
int requested_rx_itr;
int error = 0;
UNREFERENCED_PARAMETER(arg2);
if (iavf_driver_is_detaching(sc))
return (ESHUTDOWN);
requested_rx_itr = sc->rx_itr;
error = sysctl_handle_int(oidp, &requested_rx_itr, 0, req);
if ((error) || (req->newptr == NULL))
return (error);
if (requested_rx_itr < 0 || requested_rx_itr > IAVF_MAX_ITR) {
device_printf(dev,
"Invalid RX itr value; value must be between 0 and %d\n",
IAVF_MAX_ITR);
return (EINVAL);
}
sc->rx_itr = requested_rx_itr;
iavf_configure_rx_itr(sc);
return (error);
}
void
iavf_configure_tx_itr(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
struct iavf_vsi *vsi = &sc->vsi;
struct iavf_tx_queue *que = vsi->tx_queues;
vsi->tx_itr_setting = sc->tx_itr;
for (int i = 0; i < IAVF_NTXQS(vsi); i++, que++) {
struct tx_ring *txr = &que->txr;
wr32(hw, IAVF_VFINT_ITRN1(IAVF_TX_ITR, i),
vsi->tx_itr_setting);
txr->itr = vsi->tx_itr_setting;
txr->latency = IAVF_AVE_LATENCY;
}
}
void
iavf_configure_rx_itr(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
struct iavf_vsi *vsi = &sc->vsi;
struct iavf_rx_queue *que = vsi->rx_queues;
vsi->rx_itr_setting = sc->rx_itr;
for (int i = 0; i < IAVF_NRXQS(vsi); i++, que++) {
struct rx_ring *rxr = &que->rxr;
wr32(hw, IAVF_VFINT_ITRN1(IAVF_RX_ITR, i),
vsi->rx_itr_setting);
rxr->itr = vsi->rx_itr_setting;
rxr->latency = IAVF_AVE_LATENCY;
}
}
struct sysctl_oid_list *
iavf_create_debug_sysctl_tree(struct iavf_sc *sc)
{
device_t dev = sc->dev;
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
struct sysctl_oid_list *ctx_list =
SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
struct sysctl_oid *debug_node;
debug_node = SYSCTL_ADD_NODE(ctx, ctx_list,
OID_AUTO, "debug", CTLFLAG_RD | CTLFLAG_SKIP, NULL, "Debug Sysctls");
return (SYSCTL_CHILDREN(debug_node));
}
void
iavf_add_vsi_sysctls(device_t dev, struct iavf_vsi *vsi,
struct sysctl_ctx_list *ctx, const char *sysctl_name)
{
struct sysctl_oid *tree;
struct sysctl_oid_list *child;
struct sysctl_oid_list *vsi_list;
tree = device_get_sysctl_tree(dev);
child = SYSCTL_CHILDREN(tree);
vsi->vsi_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, sysctl_name,
CTLFLAG_RD, NULL, "VSI Number");
vsi_list = SYSCTL_CHILDREN(vsi->vsi_node);
iavf_add_sysctls_eth_stats(ctx, vsi_list, &vsi->eth_stats);
}
int
iavf_sysctl_sw_filter_list(SYSCTL_HANDLER_ARGS)
{
struct iavf_sc *sc = (struct iavf_sc *)arg1;
struct iavf_mac_filter *f;
struct iavf_vlan_filter *v;
device_t dev = sc->dev;
int ftl_len, ftl_counter = 0, error = 0;
struct sbuf *buf;
UNREFERENCED_2PARAMETER(arg2, oidp);
if (iavf_driver_is_detaching(sc))
return (ESHUTDOWN);
buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
if (!buf) {
device_printf(dev, "Could not allocate sbuf for output.\n");
return (ENOMEM);
}
sbuf_printf(buf, "\n");
sbuf_printf(buf, "MAC Filters:\n");
ftl_len = 0;
SLIST_FOREACH(f, sc->mac_filters, next)
ftl_len++;
if (ftl_len < 1)
sbuf_printf(buf, "(none)\n");
else {
SLIST_FOREACH(f, sc->mac_filters, next) {
sbuf_printf(buf,
MAC_FORMAT ", flags %#06x\n",
MAC_FORMAT_ARGS(f->macaddr), f->flags);
}
}
sbuf_printf(buf, "VLAN Filters:\n");
ftl_len = 0;
SLIST_FOREACH(v, sc->vlan_filters, next)
ftl_len++;
if (ftl_len < 1)
sbuf_printf(buf, "(none)");
else {
SLIST_FOREACH(v, sc->vlan_filters, next) {
sbuf_printf(buf,
"%d, flags %#06x",
v->vlan, v->flags);
if (++ftl_counter != ftl_len)
sbuf_printf(buf, "\n");
}
}
error = sbuf_finish(buf);
if (error)
device_printf(dev, "Error finishing sbuf: %d\n", error);
sbuf_delete(buf);
return (error);
}
void
iavf_media_status_common(struct iavf_sc *sc, struct ifmediareq *ifmr)
{
enum iavf_ext_link_speed ext_speed;
iavf_update_link_status(sc);
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
if (!sc->link_up)
return;
ifmr->ifm_status |= IFM_ACTIVE;
ifmr->ifm_active |= IFM_FDX;
if (IAVF_CAP_ADV_LINK_SPEED(sc))
ext_speed = iavf_adv_speed_to_ext_speed(sc->link_speed_adv);
else
ext_speed = iavf_vc_speed_to_ext_speed(sc->link_speed);
ifmr->ifm_active |= iavf_ext_speed_to_ifmedia(ext_speed);
}
int
iavf_media_change_common(if_t ifp)
{
if_printf(ifp, "Changing speed is not supported\n");
return (ENODEV);
}
void
iavf_set_initial_baudrate(if_t ifp)
{
if_setbaudrate(ifp, IF_Gbps(40));
}
void
iavf_add_sysctls_eth_stats(struct sysctl_ctx_list *ctx,
struct sysctl_oid_list *child,
struct iavf_eth_stats *eth_stats)
{
struct iavf_sysctl_info ctls[] =
{
{ð_stats->rx_bytes, "good_octets_rcvd", "Good Octets Received"},
{ð_stats->rx_unicast, "ucast_pkts_rcvd",
"Unicast Packets Received"},
{ð_stats->rx_multicast, "mcast_pkts_rcvd",
"Multicast Packets Received"},
{ð_stats->rx_broadcast, "bcast_pkts_rcvd",
"Broadcast Packets Received"},
{ð_stats->rx_discards, "rx_discards", "Discarded RX packets"},
{ð_stats->rx_unknown_protocol, "rx_unknown_proto",
"RX unknown protocol packets"},
{ð_stats->tx_bytes, "good_octets_txd", "Good Octets Transmitted"},
{ð_stats->tx_unicast, "ucast_pkts_txd", "Unicast Packets Transmitted"},
{ð_stats->tx_multicast, "mcast_pkts_txd",
"Multicast Packets Transmitted"},
{ð_stats->tx_broadcast, "bcast_pkts_txd",
"Broadcast Packets Transmitted"},
{ð_stats->tx_errors, "tx_errors", "TX packet errors"},
{0,0,0}
};
struct iavf_sysctl_info *entry = ctls;
while (entry->stat != 0)
{
SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, entry->name,
CTLFLAG_RD, entry->stat,
entry->description);
entry++;
}
}
u64
iavf_max_vc_speed_to_value(u8 link_speeds)
{
if (link_speeds & VIRTCHNL_LINK_SPEED_40GB)
return IF_Gbps(40);
if (link_speeds & VIRTCHNL_LINK_SPEED_25GB)
return IF_Gbps(25);
if (link_speeds & VIRTCHNL_LINK_SPEED_20GB)
return IF_Gbps(20);
if (link_speeds & VIRTCHNL_LINK_SPEED_10GB)
return IF_Gbps(10);
if (link_speeds & VIRTCHNL_LINK_SPEED_1GB)
return IF_Gbps(1);
if (link_speeds & VIRTCHNL_LINK_SPEED_100MB)
return IF_Mbps(100);
else
return IF_Mbps(100);
}
void
iavf_config_rss_reg(struct iavf_sc *sc)
{
struct iavf_hw *hw = &sc->hw;
struct iavf_vsi *vsi = &sc->vsi;
u32 lut = 0;
u64 set_hena = 0, hena;
int i, j, que_id;
u32 rss_seed[IAVF_RSS_KEY_SIZE_REG];
u32 rss_hash_config;
if (IAVF_NRXQS(vsi) == 1) {
wr32(hw, IAVF_VFQF_HENA(0), 0);
wr32(hw, IAVF_VFQF_HENA(1), 0);
iavf_flush(hw);
return;
}
rss_getkey((uint8_t *) &rss_seed);
for (i = 0; i < IAVF_RSS_KEY_SIZE_REG; i++)
wr32(hw, IAVF_VFQF_HKEY(i), rss_seed[i]);
rss_hash_config = rss_gethashconfig();
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_NONF_IPV4_OTHER);
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_NONF_IPV4_TCP);
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_NONF_IPV4_UDP);
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_NONF_IPV6_OTHER);
if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_FRAG_IPV6);
if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_NONF_IPV6_TCP);
if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6)
set_hena |= ((u64)1 << IAVF_FILTER_PCTYPE_NONF_IPV6_UDP);
hena = (u64)rd32(hw, IAVF_VFQF_HENA(0)) |
((u64)rd32(hw, IAVF_VFQF_HENA(1)) << 32);
hena |= set_hena;
wr32(hw, IAVF_VFQF_HENA(0), (u32)hena);
wr32(hw, IAVF_VFQF_HENA(1), (u32)(hena >> 32));
for (i = 0, j = 0; i < IAVF_RSS_VSI_LUT_SIZE; i++, j++) {
if (j == IAVF_NRXQS(vsi))
j = 0;
#ifdef RSS
que_id = rss_get_indirection_to_bucket(i);
que_id = que_id % IAVF_NRXQS(vsi);
#else
que_id = j;
#endif
lut = (lut << 8) | (que_id & IAVF_RSS_VF_LUT_ENTRY_MASK);
if ((i & 3) == 3) {
wr32(hw, IAVF_VFQF_HLUT(i >> 2), lut);
iavf_dbg_rss(sc, "%s: HLUT(%2d): %#010x", __func__,
i, lut);
}
}
iavf_flush(hw);
}
void
iavf_config_rss_pf(struct iavf_sc *sc)
{
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_CONFIG_RSS_KEY);
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_SET_RSS_HENA);
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_CONFIG_RSS_LUT);
}
void
iavf_config_rss(struct iavf_sc *sc)
{
if (sc->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_REG) {
iavf_dbg_info(sc, "Setting up RSS using VF registers...\n");
iavf_config_rss_reg(sc);
} else if (sc->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_PF) {
iavf_dbg_info(sc, "Setting up RSS using messages to PF...\n");
iavf_config_rss_pf(sc);
} else
device_printf(sc->dev, "VF does not support RSS capability sent by PF.\n");
}
int
iavf_config_promisc(struct iavf_sc *sc, int flags)
{
if_t ifp = sc->vsi.ifp;
sc->promisc_flags = 0;
if (flags & IFF_ALLMULTI ||
if_llmaddr_count(ifp) == MAX_MULTICAST_ADDR)
sc->promisc_flags |= FLAG_VF_MULTICAST_PROMISC;
if (flags & IFF_PROMISC)
sc->promisc_flags |= FLAG_VF_UNICAST_PROMISC;
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_CONFIGURE_PROMISC);
return (0);
}
static u_int
iavf_mc_filter_apply(void *arg, struct sockaddr_dl *sdl, u_int cnt __unused)
{
struct iavf_sc *sc = (struct iavf_sc *)arg;
int error;
error = iavf_add_mac_filter(sc, (u8*)LLADDR(sdl), IAVF_FILTER_MC);
return (!error);
}
void
iavf_init_multi(struct iavf_sc *sc)
{
struct iavf_mac_filter *f;
int mcnt = 0;
SLIST_FOREACH(f, sc->mac_filters, next) {
if ((f->flags & IAVF_FILTER_USED)
&& (f->flags & IAVF_FILTER_MC)) {
f->flags |= IAVF_FILTER_DEL;
mcnt++;
}
}
if (mcnt > 0)
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_DEL_MAC_FILTER);
}
void
iavf_multi_set(struct iavf_sc *sc)
{
if_t ifp = sc->vsi.ifp;
int mcnt = 0;
IOCTL_DEBUGOUT("iavf_multi_set: begin");
mcnt = if_llmaddr_count(ifp);
if (__predict_false(mcnt == MAX_MULTICAST_ADDR)) {
iavf_init_multi(sc);
sc->promisc_flags |= FLAG_VF_MULTICAST_PROMISC;
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_CONFIGURE_PROMISC);
return;
}
iavf_init_multi(sc);
mcnt = if_foreach_llmaddr(ifp, iavf_mc_filter_apply, sc);
if (mcnt > 0)
iavf_send_vc_msg(sc, IAVF_FLAG_AQ_ADD_MAC_FILTER);
}
int
iavf_add_mac_filter(struct iavf_sc *sc, u8 *macaddr, u16 flags)
{
struct iavf_mac_filter *f;
f = iavf_find_mac_filter(sc, macaddr);
if (f != NULL) {
iavf_dbg_filter(sc, "exists: " MAC_FORMAT "\n",
MAC_FORMAT_ARGS(macaddr));
return (EEXIST);
}
f = iavf_get_mac_filter(sc);
if (f == NULL) {
device_printf(sc->dev, "%s: no filters available!!\n",
__func__);
return (ENOMEM);
}
iavf_dbg_filter(sc, "marked: " MAC_FORMAT "\n",
MAC_FORMAT_ARGS(macaddr));
bcopy(macaddr, f->macaddr, ETHER_ADDR_LEN);
f->flags |= (IAVF_FILTER_ADD | IAVF_FILTER_USED);
f->flags |= flags;
return (0);
}
struct iavf_mac_filter *
iavf_find_mac_filter(struct iavf_sc *sc, u8 *macaddr)
{
struct iavf_mac_filter *f;
bool match = FALSE;
SLIST_FOREACH(f, sc->mac_filters, next) {
if (cmp_etheraddr(f->macaddr, macaddr)) {
match = TRUE;
break;
}
}
if (!match)
f = NULL;
return (f);
}
struct iavf_mac_filter *
iavf_get_mac_filter(struct iavf_sc *sc)
{
struct iavf_mac_filter *f;
f = (struct iavf_mac_filter *)malloc(sizeof(struct iavf_mac_filter),
M_IAVF, M_NOWAIT | M_ZERO);
if (f)
SLIST_INSERT_HEAD(sc->mac_filters, f, next);
return (f);
}
u64
iavf_baudrate_from_link_speed(struct iavf_sc *sc)
{
if (sc->vf_res->vf_cap_flags & VIRTCHNL_VF_CAP_ADV_LINK_SPEED)
return (sc->link_speed_adv * IAVF_ADV_LINK_SPEED_SCALE);
else
return iavf_max_vc_speed_to_value(sc->link_speed);
}
void
iavf_add_vlan_filter(struct iavf_sc *sc, u16 vtag)
{
struct iavf_vlan_filter *v;
v = (struct iavf_vlan_filter *)malloc(sizeof(struct iavf_vlan_filter),
M_IAVF, M_WAITOK | M_ZERO);
SLIST_INSERT_HEAD(sc->vlan_filters, v, next);
v->vlan = vtag;
v->flags = IAVF_FILTER_ADD;
}
int
iavf_mark_del_vlan_filter(struct iavf_sc *sc, u16 vtag)
{
struct iavf_vlan_filter *v;
int i = 0;
SLIST_FOREACH(v, sc->vlan_filters, next) {
if (v->vlan == vtag) {
v->flags = IAVF_FILTER_DEL;
++i;
}
}
return (i);
}
void
iavf_disable_queues_with_retries(struct iavf_sc *sc)
{
bool in_detach = iavf_driver_is_detaching(sc);
int max_attempts = IAVF_MAX_DIS_Q_RETRY;
int msg_count = 0;
if (in_detach)
max_attempts = 1;
while ((msg_count < max_attempts) &&
atomic_load_acq_32(&sc->queues_enabled)) {
msg_count++;
iavf_send_vc_msg_sleep(sc, IAVF_FLAG_AQ_DISABLE_QUEUES);
}
if (msg_count > 1)
iavf_dbg_vc(sc, "DISABLE_QUEUES messages sent: %d\n",
msg_count);
if (!in_detach && msg_count >= max_attempts)
device_printf(sc->dev, "%s: DISABLE_QUEUES may have failed\n",
__func__);
}