static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, struct ethtool_rxnfc *nfc) { int rss_ipv4_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags); int rss_ipv6_udp = test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags); /* RSS does not support anything other than hashing * to queues on src and dst IPs and ports */ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL; switch (nfc->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || !(nfc->data & RXH_L4_B_0_1) || !(nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; case UDP_V4_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: clear_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): set_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags); break; default: return -EINVAL; } break; case UDP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: clear_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags); break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): set_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags); break; default: return -EINVAL; } break; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case SCTP_V4_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case SCTP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || (nfc->data & RXH_L4_B_0_1) || (nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; default: return -EINVAL; } /* If something changed we need to update the MRQC register. Note that * test_bit() is guaranteed to return strictly 0 or 1, so testing for * equality is safe. */ if ((rss_ipv4_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags)) || (rss_ipv6_udp != test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags))) { struct fm10k_hw *hw = &interface->hw; bool warn = false; u32 mrqc; /* Perform hash on these packet types */ mrqc = FM10K_MRQC_IPV4 | FM10K_MRQC_TCP_IPV4 | FM10K_MRQC_IPV6 | FM10K_MRQC_TCP_IPV6; if (test_bit(FM10K_FLAG_RSS_FIELD_IPV4_UDP, interface->flags)) { mrqc |= FM10K_MRQC_UDP_IPV4; warn = true; } if (test_bit(FM10K_FLAG_RSS_FIELD_IPV6_UDP, interface->flags)) { mrqc |= FM10K_MRQC_UDP_IPV6; warn = true; } /* If we enable UDP RSS display a warning that this may cause * fragmented UDP packets to arrive out of order. */ if (warn) netif_warn(interface, drv, interface->netdev, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); fm10k_write_reg(hw, FM10K_MRQC(0), mrqc); } return 0; }
static int fm10k_set_rss_hash_opt(struct fm10k_intfc *interface, struct ethtool_rxnfc *nfc) { u32 flags = interface->flags; /* RSS does not support anything other than hashing * to queues on src and dst IPs and ports */ if (nfc->data & ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) return -EINVAL; switch (nfc->flow_type) { case TCP_V4_FLOW: case TCP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || !(nfc->data & RXH_L4_B_0_1) || !(nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; case UDP_V4_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: flags &= ~FM10K_FLAG_RSS_FIELD_IPV4_UDP; break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): flags |= FM10K_FLAG_RSS_FIELD_IPV4_UDP; break; default: return -EINVAL; } break; case UDP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) return -EINVAL; switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { case 0: flags &= ~FM10K_FLAG_RSS_FIELD_IPV6_UDP; break; case (RXH_L4_B_0_1 | RXH_L4_B_2_3): flags |= FM10K_FLAG_RSS_FIELD_IPV6_UDP; break; default: return -EINVAL; } break; case AH_ESP_V4_FLOW: case AH_V4_FLOW: case ESP_V4_FLOW: case SCTP_V4_FLOW: case AH_ESP_V6_FLOW: case AH_V6_FLOW: case ESP_V6_FLOW: case SCTP_V6_FLOW: if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || (nfc->data & RXH_L4_B_0_1) || (nfc->data & RXH_L4_B_2_3)) return -EINVAL; break; default: return -EINVAL; } /* if we changed something we need to update flags */ if (flags != interface->flags) { struct fm10k_hw *hw = &interface->hw; u32 mrqc; if ((flags & UDP_RSS_FLAGS) && !(interface->flags & UDP_RSS_FLAGS)) netif_warn(interface, drv, interface->netdev, "enabling UDP RSS: fragmented packets may arrive out of order to the stack above\n"); interface->flags = flags; /* Perform hash on these packet types */ mrqc = FM10K_MRQC_IPV4 | FM10K_MRQC_TCP_IPV4 | FM10K_MRQC_IPV6 | FM10K_MRQC_TCP_IPV6; if (flags & FM10K_FLAG_RSS_FIELD_IPV4_UDP) mrqc |= FM10K_MRQC_UDP_IPV4; if (flags & FM10K_FLAG_RSS_FIELD_IPV6_UDP) mrqc |= FM10K_MRQC_UDP_IPV6; fm10k_write_reg(hw, FM10K_MRQC(0), mrqc); } return 0; }
int efx_ef10_sriov_set_vf_vlan(struct efx_nic *efx, int vf_i, u16 vlan, u8 qos) { struct efx_ef10_nic_data *nic_data = efx->nic_data; struct ef10_vf *vf; u16 old_vlan, new_vlan; int rc = 0, rc2 = 0; if (vf_i >= efx->vf_count) return -EINVAL; if (qos != 0) return -EINVAL; vf = nic_data->vf + vf_i; new_vlan = (vlan == 0) ? EFX_EF10_NO_VLAN : vlan; if (new_vlan == vf->vlan) return 0; if (vf->efx) { efx_device_detach_sync(vf->efx); efx_net_stop(vf->efx->net_dev); down_write(&vf->efx->filter_sem); vf->efx->type->filter_table_remove(vf->efx); rc = efx_ef10_vadaptor_free(vf->efx, EVB_PORT_ID_ASSIGNED); if (rc) goto restore_filters; } if (vf->vport_assigned) { rc = efx_ef10_evb_port_assign(efx, EVB_PORT_ID_NULL, vf_i); if (rc) { netif_warn(efx, drv, efx->net_dev, "Failed to change vlan on VF %d.\n", vf_i); netif_warn(efx, drv, efx->net_dev, "This is likely because the VF is bound to a driver in a VM.\n"); netif_warn(efx, drv, efx->net_dev, "Please unload the driver in the VM.\n"); goto restore_vadaptor; } vf->vport_assigned = 0; } if (!is_zero_ether_addr(vf->mac)) { rc = efx_ef10_vport_del_mac(efx, vf->vport_id, vf->mac); if (rc) goto restore_evb_port; } if (vf->vport_id) { rc = efx_ef10_vport_free(efx, vf->vport_id); if (rc) goto restore_mac; vf->vport_id = 0; } /* Do the actual vlan change */ old_vlan = vf->vlan; vf->vlan = new_vlan; /* Restore everything in reverse order */ rc = efx_ef10_vport_alloc(efx, EVB_PORT_ID_ASSIGNED, MC_CMD_VPORT_ALLOC_IN_VPORT_TYPE_NORMAL, vf->vlan, &vf->vport_id); if (rc) goto reset_nic; restore_mac: if (!is_zero_ether_addr(vf->mac)) { rc2 = efx_ef10_vport_add_mac(efx, vf->vport_id, vf->mac); if (rc2) { eth_zero_addr(vf->mac); goto reset_nic; } } restore_evb_port: rc2 = efx_ef10_evb_port_assign(efx, vf->vport_id, vf_i); if (rc2) goto reset_nic; else vf->vport_assigned = 1; restore_vadaptor: if (vf->efx) { rc2 = efx_ef10_vadaptor_alloc(vf->efx, EVB_PORT_ID_ASSIGNED); if (rc2) goto reset_nic; } restore_filters: if (vf->efx) { rc2 = vf->efx->type->filter_table_probe(vf->efx); if (rc2) goto reset_nic; rc2 = efx_net_open(vf->efx->net_dev); if (rc2) goto reset_nic; up_write(&vf->efx->filter_sem); netif_device_attach(vf->efx->net_dev); } return rc; reset_nic: if (vf->efx) { up_write(&vf->efx->filter_sem); netif_err(efx, drv, efx->net_dev, "Failed to restore the VF - scheduling reset.\n"); efx_schedule_reset(vf->efx, RESET_TYPE_DATAPATH); } else { netif_err(efx, drv, efx->net_dev, "Failed to restore the VF and cannot reset the VF " "- VF is not functional.\n"); netif_err(efx, drv, efx->net_dev, "Please reload the driver attached to the VF.\n"); } return rc ? rc : rc2; }