diff options
Diffstat (limited to 'drivers/net/ethernet/intel/i40e/i40e_main.c')
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_main.c | 264 |
1 files changed, 213 insertions, 51 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index efdf8a261b9a..d5719bd9d5ee 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -27,6 +27,9 @@ /* Local includes */ #include "i40e.h" +#ifdef CONFIG_I40E_VXLAN +#include <net/vxlan.h> +#endif const char i40e_driver_name[] = "i40e"; static const char i40e_driver_string[] = @@ -2814,6 +2817,11 @@ static irqreturn_t i40e_intr(int irq, void *data) pf->empr_count++; } + if (icr0 & I40E_PFINT_ICR0_HMC_ERR_MASK) { + icr0 &= ~I40E_PFINT_ICR0_HMC_ERR_MASK; + dev_info(&pf->pdev->dev, "HMC error interrupt\n"); + } + /* If a critical error is pending we have no choice but to reset the * device. * Report and mask out any remaining unexpected interrupts. @@ -2822,18 +2830,13 @@ static irqreturn_t i40e_intr(int irq, void *data) if (icr0_remaining) { dev_info(&pf->pdev->dev, "unhandled interrupt icr0=0x%08x\n", icr0_remaining); - if ((icr0_remaining & I40E_PFINT_ICR0_HMC_ERR_MASK) || - (icr0_remaining & I40E_PFINT_ICR0_PE_CRITERR_MASK) || + if ((icr0_remaining & I40E_PFINT_ICR0_PE_CRITERR_MASK) || (icr0_remaining & I40E_PFINT_ICR0_PCI_EXCEPTION_MASK) || (icr0_remaining & I40E_PFINT_ICR0_ECC_ERR_MASK) || (icr0_remaining & I40E_PFINT_ICR0_MAL_DETECT_MASK)) { - if (icr0 & I40E_PFINT_ICR0_HMC_ERR_MASK) { - dev_info(&pf->pdev->dev, "HMC error interrupt\n"); - } else { - dev_info(&pf->pdev->dev, "device will be reset\n"); - set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); - i40e_service_event_schedule(pf); - } + dev_info(&pf->pdev->dev, "device will be reset\n"); + set_bit(__I40E_PF_RESET_REQUESTED, &pf->state); + i40e_service_event_schedule(pf); } ena_mask &= ~icr0_remaining; } @@ -2987,21 +2990,11 @@ static int i40e_vsi_control_tx(struct i40e_vsi *vsi, bool enable) } while (j-- && ((tx_reg >> I40E_QTX_ENA_QENA_REQ_SHIFT) ^ (tx_reg >> I40E_QTX_ENA_QENA_STAT_SHIFT)) & 1); - if (enable) { - /* is STAT set ? */ - if ((tx_reg & I40E_QTX_ENA_QENA_STAT_MASK)) { - dev_info(&pf->pdev->dev, - "Tx %d already enabled\n", i); - continue; - } - } else { - /* is !STAT set ? */ - if (!(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK)) { - dev_info(&pf->pdev->dev, - "Tx %d already disabled\n", i); - continue; - } - } + /* Skip if the queue is already in the requested state */ + if (enable && (tx_reg & I40E_QTX_ENA_QENA_STAT_MASK)) + continue; + if (!enable && !(tx_reg & I40E_QTX_ENA_QENA_STAT_MASK)) + continue; /* turn on/off the queue */ if (enable) @@ -3107,7 +3100,7 @@ static int i40e_vsi_control_rx(struct i40e_vsi *vsi, bool enable) * @vsi: the VSI being configured * @enable: start or stop the rings **/ -static int i40e_vsi_control_rings(struct i40e_vsi *vsi, bool request) +int i40e_vsi_control_rings(struct i40e_vsi *vsi, bool request) { int ret; @@ -3993,6 +3986,9 @@ static int i40e_open(struct net_device *netdev) "couldn't set broadcast err %d aq_err %d\n", err, pf->hw.aq.asq_last_status); } +#ifdef CONFIG_I40E_VXLAN + vxlan_get_rx_port(netdev); +#endif return 0; @@ -5016,6 +5012,52 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } +#ifdef CONFIG_I40E_VXLAN +/** + * i40e_sync_vxlan_filters_subtask - Sync the VSI filter list with HW + * @pf: board private structure + **/ +static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf) +{ + const int vxlan_hdr_qwords = 4; + struct i40e_hw *hw = &pf->hw; + i40e_status ret; + u8 filter_index; + __be16 port; + int i; + + if (!(pf->flags & I40E_FLAG_VXLAN_FILTER_SYNC)) + return; + + pf->flags &= ~I40E_FLAG_VXLAN_FILTER_SYNC; + + for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { + if (pf->pending_vxlan_bitmap & (1 << i)) { + pf->pending_vxlan_bitmap &= ~(1 << i); + port = pf->vxlan_ports[i]; + ret = port ? + i40e_aq_add_udp_tunnel(hw, ntohs(port), + vxlan_hdr_qwords, + I40E_AQC_TUNNEL_TYPE_VXLAN, + &filter_index, NULL) + : i40e_aq_del_udp_tunnel(hw, i, NULL); + + if (ret) { + dev_info(&pf->pdev->dev, "Failed to execute AQ command for %s port %d with index %d\n", + port ? "adding" : "deleting", + ntohs(port), port ? i : i); + + pf->vxlan_ports[i] = 0; + } else { + dev_info(&pf->pdev->dev, "%s port %d with AQ command with index %d\n", + port ? "Added" : "Deleted", + ntohs(port), port ? i : filter_index); + } + } + } +} + +#endif /** * i40e_service_task - Run the driver's async subtasks * @work: pointer to work_struct containing our data @@ -5034,6 +5076,9 @@ static void i40e_service_task(struct work_struct *work) i40e_fdir_reinit_subtask(pf); i40e_check_hang_subtask(pf); i40e_sync_filters_subtask(pf); +#ifdef CONFIG_I40E_VXLAN + i40e_sync_vxlan_filters_subtask(pf); +#endif i40e_clean_adminq_subtask(pf); i40e_service_event_complete(pf); @@ -5300,18 +5345,17 @@ free_vsi: * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI * @vsi: the VSI being cleaned **/ -static s32 i40e_vsi_clear_rings(struct i40e_vsi *vsi) +static void i40e_vsi_clear_rings(struct i40e_vsi *vsi) { int i; - if (vsi->tx_rings[0]) + if (vsi->tx_rings[0]) { for (i = 0; i < vsi->num_queue_pairs; i++) { kfree_rcu(vsi->tx_rings[i], rcu); vsi->tx_rings[i] = NULL; vsi->rx_rings[i] = NULL; } - - return 0; + } } /** @@ -5659,25 +5703,6 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf) **/ static int i40e_config_rss(struct i40e_pf *pf) { - const u64 default_hena = - ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_SCTP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_TCP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV4_OTHER) | - ((u64)1 << I40E_FILTER_PCTYPE_FRAG_IPV4) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_UDP) | - ((u64)1 << I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN) | - ((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); - /* Set of random keys generated using kernel random number generator */ static const u32 seed[I40E_PFQF_HKEY_MAX_INDEX + 1] = {0x41b01687, 0x183cfd8c, 0xce880440, 0x580cbc3c, 0x35897377, @@ -5695,7 +5720,7 @@ static int i40e_config_rss(struct i40e_pf *pf) /* By default we enable TCP/UDP with IPv4/IPv6 ptypes */ hena = (u64)rd32(hw, I40E_PFQF_HENA(0)) | ((u64)rd32(hw, I40E_PFQF_HENA(1)) << 32); - hena |= default_hena; + hena |= I40E_DEFAULT_RSS_HENA; wr32(hw, I40E_PFQF_HENA(0), (u32)hena); wr32(hw, I40E_PFQF_HENA(1), (u32)(hena >> 32)); @@ -5901,6 +5926,104 @@ static int i40e_set_features(struct net_device *netdev, return 0; } +#ifdef CONFIG_I40E_VXLAN +/** + * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port + * @pf: board private structure + * @port: The UDP port to look up + * + * Returns the index number or I40E_MAX_PF_UDP_OFFLOAD_PORTS if port not found + **/ +static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port) +{ + u8 i; + + for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) { + if (pf->vxlan_ports[i] == port) + return i; + } + + return i; +} + +/** + * i40e_add_vxlan_port - Get notifications about VXLAN ports that come up + * @netdev: This physical port's netdev + * @sa_family: Socket Family that VXLAN is notifying us about + * @port: New UDP port number that VXLAN started listening to + **/ +static void i40e_add_vxlan_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u8 next_idx; + u8 idx; + + if (sa_family == AF_INET6) + return; + + idx = i40e_get_vxlan_port_idx(pf, port); + + /* Check if port already exists */ + if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + netdev_info(netdev, "Port %d already offloaded\n", ntohs(port)); + return; + } + + /* Now check if there is space to add the new port */ + next_idx = i40e_get_vxlan_port_idx(pf, 0); + + if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + netdev_info(netdev, "Maximum number of UDP ports reached, not adding port %d\n", + ntohs(port)); + return; + } + + /* New port: add it and mark its index in the bitmap */ + pf->vxlan_ports[next_idx] = port; + pf->pending_vxlan_bitmap |= (1 << next_idx); + + pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; +} + +/** + * i40e_del_vxlan_port - Get notifications about VXLAN ports that go away + * @netdev: This physical port's netdev + * @sa_family: Socket Family that VXLAN is notifying us about + * @port: UDP port number that VXLAN stopped listening to + **/ +static void i40e_del_vxlan_port(struct net_device *netdev, + sa_family_t sa_family, __be16 port) +{ + struct i40e_netdev_priv *np = netdev_priv(netdev); + struct i40e_vsi *vsi = np->vsi; + struct i40e_pf *pf = vsi->back; + u8 idx; + + if (sa_family == AF_INET6) + return; + + idx = i40e_get_vxlan_port_idx(pf, port); + + /* Check if port already exists */ + if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) { + /* if port exists, set it to 0 (mark for deletion) + * and make it pending + */ + pf->vxlan_ports[idx] = 0; + + pf->pending_vxlan_bitmap |= (1 << idx); + + pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC; + } else { + netdev_warn(netdev, "Port %d was not found, not deleting\n", + ntohs(port)); + } +} + +#endif static const struct net_device_ops i40e_netdev_ops = { .ndo_open = i40e_open, .ndo_stop = i40e_close, @@ -5922,6 +6045,10 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_set_vf_vlan = i40e_ndo_set_vf_port_vlan, .ndo_set_vf_tx_rate = i40e_ndo_set_vf_bw, .ndo_get_vf_config = i40e_ndo_get_vf_config, +#ifdef CONFIG_I40E_VXLAN + .ndo_add_vxlan_port = i40e_add_vxlan_port, + .ndo_del_vxlan_port = i40e_del_vxlan_port, +#endif }; /** @@ -7369,6 +7496,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct i40e_pf *pf; struct i40e_hw *hw; static u16 pfs_found; + u16 link_status; int err = 0; u32 len; @@ -7536,6 +7664,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pf->flags |= I40E_FLAG_NEED_LINK_UPDATE; pf->link_check_timeout = jiffies; + /* WoL defaults to disabled */ + pf->wol_en = false; + device_set_wakeup_enable(&pf->pdev->dev, pf->wol_en); + /* set up the main switch operations */ i40e_determine_queue_usage(pf); i40e_init_interrupt_scheme(pf); @@ -7603,6 +7735,28 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mod_timer(&pf->service_timer, round_jiffies(jiffies + pf->service_timer_period)); + /* Get the negotiated link width and speed from PCI config space */ + pcie_capability_read_word(pf->pdev, PCI_EXP_LNKSTA, &link_status); + + i40e_set_pci_config_data(hw, link_status); + + dev_info(&pdev->dev, "PCI Express: %s %s\n", + (hw->bus.speed == i40e_bus_speed_8000 ? "Speed 8.0GT/s" : + hw->bus.speed == i40e_bus_speed_5000 ? "Speed 5.0GT/s" : + hw->bus.speed == i40e_bus_speed_2500 ? "Speed 2.5GT/s" : + "Unknown"), + (hw->bus.width == i40e_bus_width_pcie_x8 ? "Width x8" : + hw->bus.width == i40e_bus_width_pcie_x4 ? "Width x4" : + hw->bus.width == i40e_bus_width_pcie_x2 ? "Width x2" : + hw->bus.width == i40e_bus_width_pcie_x1 ? "Width x1" : + "Unknown")); + + if (hw->bus.width < i40e_bus_width_pcie_x8 || + hw->bus.speed < i40e_bus_speed_8000) { + dev_warn(&pdev->dev, "PCI-Express bandwidth available for this device may be insufficient for optimal performance.\n"); + dev_warn(&pdev->dev, "Please move the device to a different PCI-e link with more lanes and/or higher transfer rate.\n"); + } + return 0; /* Unwind what we've done if something failed in the setup */ @@ -7835,6 +7989,7 @@ static void i40e_pci_error_resume(struct pci_dev *pdev) static void i40e_shutdown(struct pci_dev *pdev) { struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_hw *hw = &pf->hw; set_bit(__I40E_SUSPENDED, &pf->state); set_bit(__I40E_DOWN, &pf->state); @@ -7842,8 +7997,11 @@ static void i40e_shutdown(struct pci_dev *pdev) i40e_prep_for_reset(pf); rtnl_unlock(); + wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); + wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); + if (system_state == SYSTEM_POWER_OFF) { - pci_wake_from_d3(pdev, false); /* No WoL support yet */ + pci_wake_from_d3(pdev, pf->wol_en); pci_set_power_state(pdev, PCI_D3hot); } } @@ -7856,6 +8014,7 @@ static void i40e_shutdown(struct pci_dev *pdev) static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) { struct i40e_pf *pf = pci_get_drvdata(pdev); + struct i40e_hw *hw = &pf->hw; set_bit(__I40E_SUSPENDED, &pf->state); set_bit(__I40E_DOWN, &pf->state); @@ -7863,7 +8022,10 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) i40e_prep_for_reset(pf); rtnl_unlock(); - pci_wake_from_d3(pdev, false); /* No WoL support yet */ + wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0)); + wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); + + pci_wake_from_d3(pdev, pf->wol_en); pci_set_power_state(pdev, PCI_D3hot); return 0; |