]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/intel/i40e/i40e_main.c
i40e: Look up MAC address in Open Firmware or IDPROM
[karo-tx-linux.git] / drivers / net / ethernet / intel / i40e / i40e_main.c
index b118deb08ce60f48f6a306d7c86bc7621c5786e8..291480650d113bf2e8a42fde45c10e04fb26b21e 100644 (file)
  *
  ******************************************************************************/
 
+#include <linux/etherdevice.h>
+#include <linux/of_net.h>
+#include <linux/pci.h>
+
+#ifdef CONFIG_SPARC
+#include <asm/idprom.h>
+#include <asm/prom.h>
+#endif
+
 /* Local includes */
 #include "i40e.h"
 #include "i40e_diag.h"
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN)
 #include <net/vxlan.h>
 #endif
+#if IS_ENABLED(CONFIG_GENEVE)
+#include <net/geneve.h>
+#endif
 
 const char i40e_driver_name[] = "i40e";
 static const char i40e_driver_string[] =
@@ -5336,6 +5348,9 @@ int i40e_open(struct net_device *netdev)
 #ifdef CONFIG_I40E_VXLAN
        vxlan_get_rx_port(netdev);
 #endif
+#ifdef CONFIG_I40E_GENEVE
+       geneve_get_rx_port(netdev);
+#endif
 
        return 0;
 }
@@ -7036,30 +7051,30 @@ 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
+ * i40e_sync_udp_filters_subtask - Sync the VSI filter list with HW
  * @pf: board private structure
  **/
-static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
+static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf)
 {
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
        struct i40e_hw *hw = &pf->hw;
        i40e_status ret;
        __be16 port;
        int i;
 
-       if (!(pf->flags & I40E_FLAG_VXLAN_FILTER_SYNC))
+       if (!(pf->flags & I40E_FLAG_UDP_FILTER_SYNC))
                return;
 
-       pf->flags &= ~I40E_FLAG_VXLAN_FILTER_SYNC;
+       pf->flags &= ~I40E_FLAG_UDP_FILTER_SYNC;
 
        for (i = 0; i < I40E_MAX_PF_UDP_OFFLOAD_PORTS; i++) {
-               if (pf->pending_vxlan_bitmap & BIT_ULL(i)) {
-                       pf->pending_vxlan_bitmap &= ~BIT_ULL(i);
-                       port = pf->vxlan_ports[i];
+               if (pf->pending_udp_bitmap & BIT_ULL(i)) {
+                       pf->pending_udp_bitmap &= ~BIT_ULL(i);
+                       port = pf->udp_ports[i].index;
                        if (port)
                                ret = i40e_aq_add_udp_tunnel(hw, ntohs(port),
-                                                    I40E_AQC_TUNNEL_TYPE_VXLAN,
+                                                    pf->udp_ports[i].type,
                                                     NULL, NULL);
                        else
                                ret = i40e_aq_del_udp_tunnel(hw, i, NULL);
@@ -7072,13 +7087,13 @@ static void i40e_sync_vxlan_filters_subtask(struct i40e_pf *pf)
                                         i40e_stat_str(&pf->hw, ret),
                                         i40e_aq_str(&pf->hw,
                                                    pf->hw.aq.asq_last_status));
-                               pf->vxlan_ports[i] = 0;
+                               pf->udp_ports[i].index = 0;
                        }
                }
        }
+#endif
 }
 
-#endif
 /**
  * i40e_service_task - Run the driver's async subtasks
  * @work: pointer to work_struct containing our data
@@ -7103,8 +7118,8 @@ static void i40e_service_task(struct work_struct *work)
        i40e_watchdog_subtask(pf);
        i40e_fdir_reinit_subtask(pf);
        i40e_sync_filters_subtask(pf);
-#ifdef CONFIG_I40E_VXLAN
-       i40e_sync_vxlan_filters_subtask(pf);
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
+       i40e_sync_udp_filters_subtask(pf);
 #endif
        i40e_clean_adminq_subtask(pf);
 
@@ -8380,7 +8395,8 @@ static int i40e_sw_init(struct i40e_pf *pf)
                             I40E_FLAG_HW_ATR_EVICT_CAPABLE |
                             I40E_FLAG_OUTER_UDP_CSUM_CAPABLE |
                             I40E_FLAG_WB_ON_ITR_CAPABLE |
-                            I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE;
+                            I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE |
+                            I40E_FLAG_GENEVE_OFFLOAD_CAPABLE;
        }
        pf->eeprom_version = 0xDEAD;
        pf->lan_veb = I40E_NO_VEB;
@@ -8479,26 +8495,27 @@ static int i40e_set_features(struct net_device *netdev,
        return 0;
 }
 
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN) || IS_ENABLED(CONFIG_GENEVE)
 /**
- * i40e_get_vxlan_port_idx - Lookup a possibly offloaded for Rx UDP port
+ * i40e_get_udp_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)
+static u8 i40e_get_udp_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)
+               if (pf->udp_ports[i].index == port)
                        return i;
        }
 
        return i;
 }
 
+#endif
 /**
  * i40e_add_vxlan_port - Get notifications about VXLAN ports that come up
  * @netdev: This physical port's netdev
@@ -8508,6 +8525,7 @@ static u8 i40e_get_vxlan_port_idx(struct i40e_pf *pf, __be16 port)
 static void i40e_add_vxlan_port(struct net_device *netdev,
                                sa_family_t sa_family, __be16 port)
 {
+#if IS_ENABLED(CONFIG_VXLAN)
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
        struct i40e_pf *pf = vsi->back;
@@ -8517,7 +8535,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
        if (sa_family == AF_INET6)
                return;
 
-       idx = i40e_get_vxlan_port_idx(pf, port);
+       idx = i40e_get_udp_port_idx(pf, port);
 
        /* Check if port already exists */
        if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
@@ -8527,7 +8545,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
        }
 
        /* Now check if there is space to add the new port */
-       next_idx = i40e_get_vxlan_port_idx(pf, 0);
+       next_idx = i40e_get_udp_port_idx(pf, 0);
 
        if (next_idx == I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
                netdev_info(netdev, "maximum number of vxlan UDP ports reached, not adding port %d\n",
@@ -8536,9 +8554,11 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
        }
 
        /* New port: add it and mark its index in the bitmap */
-       pf->vxlan_ports[next_idx] = port;
-       pf->pending_vxlan_bitmap |= BIT_ULL(next_idx);
-       pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+       pf->udp_ports[next_idx].index = port;
+       pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN;
+       pf->pending_udp_bitmap |= BIT_ULL(next_idx);
+       pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+#endif
 }
 
 /**
@@ -8550,6 +8570,7 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
 static void i40e_del_vxlan_port(struct net_device *netdev,
                                sa_family_t sa_family, __be16 port)
 {
+#if IS_ENABLED(CONFIG_VXLAN)
        struct i40e_netdev_priv *np = netdev_priv(netdev);
        struct i40e_vsi *vsi = np->vsi;
        struct i40e_pf *pf = vsi->back;
@@ -8558,23 +8579,108 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
        if (sa_family == AF_INET6)
                return;
 
-       idx = i40e_get_vxlan_port_idx(pf, port);
+       idx = i40e_get_udp_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 |= BIT_ULL(idx);
-               pf->flags |= I40E_FLAG_VXLAN_FILTER_SYNC;
+               pf->udp_ports[idx].index = 0;
+               pf->pending_udp_bitmap |= BIT_ULL(idx);
+               pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
        } else {
                netdev_warn(netdev, "vxlan port %d was not found, not deleting\n",
                            ntohs(port));
        }
+#endif
+}
+
+/**
+ * i40e_add_geneve_port - Get notifications about GENEVE ports that come up
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that GENEVE is notifying us about
+ * @port: New UDP port number that GENEVE started listening to
+ **/
+static void i40e_add_geneve_port(struct net_device *netdev,
+                                sa_family_t sa_family, __be16 port)
+{
+#if IS_ENABLED(CONFIG_GENEVE)
+       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_udp_port_idx(pf, port);
+
+       /* Check if port already exists */
+       if (idx < I40E_MAX_PF_UDP_OFFLOAD_PORTS) {
+               netdev_info(netdev, "udp port %d already offloaded\n",
+                           ntohs(port));
+               return;
+       }
+
+       /* Now check if there is space to add the new port */
+       next_idx = i40e_get_udp_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->udp_ports[next_idx].index = port;
+       pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE;
+       pf->pending_udp_bitmap |= BIT_ULL(next_idx);
+       pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+
+       dev_info(&pf->pdev->dev, "adding geneve port %d\n", ntohs(port));
+#endif
 }
 
+/**
+ * i40e_del_geneve_port - Get notifications about GENEVE ports that go away
+ * @netdev: This physical port's netdev
+ * @sa_family: Socket Family that GENEVE is notifying us about
+ * @port: UDP port number that GENEVE stopped listening to
+ **/
+static void i40e_del_geneve_port(struct net_device *netdev,
+                                sa_family_t sa_family, __be16 port)
+{
+#if IS_ENABLED(CONFIG_GENEVE)
+       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_udp_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->udp_ports[idx].index = 0;
+               pf->pending_udp_bitmap |= BIT_ULL(idx);
+               pf->flags |= I40E_FLAG_UDP_FILTER_SYNC;
+
+               dev_info(&pf->pdev->dev, "deleting geneve port %d\n",
+                        ntohs(port));
+       } else {
+               netdev_warn(netdev, "geneve port %d was not found, not deleting\n",
+                           ntohs(port));
+       }
 #endif
+}
+
 static int i40e_get_phys_port_id(struct net_device *netdev,
                                 struct netdev_phys_item_id *ppid)
 {
@@ -8752,7 +8858,10 @@ static int i40e_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
                                       nlflags, 0, 0, filter_mask, NULL);
 }
 
-#define I40E_MAX_TUNNEL_HDR_LEN 80
+/* Hardware supports L4 tunnel length of 128B (=2^7) which includes
+ * inner mac plus all inner ethertypes.
+ */
+#define I40E_MAX_TUNNEL_HDR_LEN 128
 /**
  * i40e_features_check - Validate encapsulated packet conforms to limits
  * @skb: skb buff
@@ -8764,9 +8873,9 @@ static netdev_features_t i40e_features_check(struct sk_buff *skb,
                                             netdev_features_t features)
 {
        if (skb->encapsulation &&
-           (skb_inner_mac_header(skb) - skb_transport_header(skb) >
+           ((skb_inner_network_header(skb) - skb_transport_header(skb)) >
             I40E_MAX_TUNNEL_HDR_LEN))
-               return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK);
+               return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
 
        return features;
 }
@@ -8799,9 +8908,13 @@ static const struct net_device_ops i40e_netdev_ops = {
        .ndo_get_vf_config      = i40e_ndo_get_vf_config,
        .ndo_set_vf_link_state  = i40e_ndo_set_vf_link_state,
        .ndo_set_vf_spoofchk    = i40e_ndo_set_vf_spoofchk,
-#ifdef CONFIG_I40E_VXLAN
+#if IS_ENABLED(CONFIG_VXLAN)
        .ndo_add_vxlan_port     = i40e_add_vxlan_port,
        .ndo_del_vxlan_port     = i40e_del_vxlan_port,
+#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+       .ndo_add_geneve_port    = i40e_add_geneve_port,
+       .ndo_del_geneve_port    = i40e_del_geneve_port,
 #endif
        .ndo_get_phys_port_id   = i40e_get_phys_port_id,
        .ndo_fdb_add            = i40e_ndo_fdb_add,
@@ -8836,13 +8949,14 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
        np->vsi = vsi;
 
        netdev->hw_enc_features |= NETIF_F_IP_CSUM       |
+                                 NETIF_F_RXCSUM         |
                                  NETIF_F_GSO_UDP_TUNNEL |
                                  NETIF_F_GSO_GRE        |
                                  NETIF_F_TSO;
 
        netdev->features = NETIF_F_SG                  |
                           NETIF_F_IP_CSUM             |
-                          NETIF_F_SCTP_CSUM           |
+                          NETIF_F_SCTP_CRC            |
                           NETIF_F_HIGHDMA             |
                           NETIF_F_GSO_UDP_TUNNEL      |
                           NETIF_F_GSO_GRE             |
@@ -9416,6 +9530,44 @@ err_vsi:
        return NULL;
 }
 
+/**
+ * i40e_macaddr_init - explicitly write the mac address filters.
+ *
+ * @vsi: pointer to the vsi.
+ * @macaddr: the MAC address
+ *
+ * This is needed when the macaddr has been obtained by other
+ * means than the default, e.g., from Open Firmware or IDPROM.
+ * Returns 0 on success, negative on failure
+ **/
+static int i40e_macaddr_init(struct i40e_vsi *vsi, u8 *macaddr)
+{
+       int ret;
+       struct i40e_aqc_add_macvlan_element_data element;
+
+       ret = i40e_aq_mac_address_write(&vsi->back->hw,
+                                       I40E_AQC_WRITE_TYPE_LAA_WOL,
+                                       macaddr, NULL);
+       if (ret) {
+               dev_info(&vsi->back->pdev->dev,
+                        "Addr change for VSI failed: %d\n", ret);
+               return -EADDRNOTAVAIL;
+       }
+
+       memset(&element, 0, sizeof(element));
+       ether_addr_copy(element.mac_addr, macaddr);
+       element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH);
+       ret = i40e_aq_add_macvlan(&vsi->back->hw, vsi->seid, &element, 1, NULL);
+       if (ret) {
+               dev_info(&vsi->back->pdev->dev,
+                        "add filter failed err %s aq_err %s\n",
+                        i40e_stat_str(&vsi->back->hw, ret),
+                        i40e_aq_str(&vsi->back->hw,
+                                    vsi->back->hw.aq.asq_last_status));
+       }
+       return ret;
+}
+
 /**
  * i40e_vsi_setup - Set up a VSI by a given type
  * @pf: board private structure
@@ -9540,6 +9692,17 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type,
        switch (vsi->type) {
        /* setup the netdev if needed */
        case I40E_VSI_MAIN:
+               /* Apply relevant filters if a platform-specific mac
+                * address was selected.
+                */
+               if (!!(pf->flags & I40E_FLAG_PF_MAC)) {
+                       ret = i40e_macaddr_init(vsi, pf->hw.mac.addr);
+                       if (ret) {
+                               dev_warn(&pf->pdev->dev,
+                                        "could not set up macaddr; err %d\n",
+                                        ret);
+                       }
+               }
        case I40E_VSI_VMDQ2:
        case I40E_VSI_FCOE:
                ret = i40e_config_netdev(vsi);
@@ -10348,6 +10511,9 @@ static void i40e_print_features(struct i40e_pf *pf)
                i += snprintf(&buf[i], REMAIN(i), " DCB");
 #if IS_ENABLED(CONFIG_VXLAN)
        i += snprintf(&buf[i], REMAIN(i), " VxLAN");
+#endif
+#if IS_ENABLED(CONFIG_GENEVE)
+       i += snprintf(&buf[i], REMAIN(i), " Geneve");
 #endif
        if (pf->flags & I40E_FLAG_PTP)
                i += snprintf(&buf[i], REMAIN(i), " PTP");
@@ -10365,6 +10531,36 @@ static void i40e_print_features(struct i40e_pf *pf)
        WARN_ON(i > INFO_STRING_LEN);
 }
 
+/**
+ * i40e_get_platform_mac_addr - get platform-specific MAC address
+ *
+ * @pdev: PCI device information struct
+ * @pf: board private structure
+ *
+ * Look up the MAC address in Open Firmware  on systems that support it,
+ * and use IDPROM on SPARC if no OF address is found. On return, the
+ * I40E_FLAG_PF_MAC will be wset in pf->flags if a platform-specific value
+ * has been selected.
+ **/
+static void i40e_get_platform_mac_addr(struct pci_dev *pdev, struct i40e_pf *pf)
+{
+       struct device_node *dp = pci_device_to_OF_node(pdev);
+       const unsigned char *addr;
+       u8 *mac_addr = pf->hw.mac.addr;
+
+       pf->flags &= ~I40E_FLAG_PF_MAC;
+       addr = of_get_mac_address(dp);
+       if (addr) {
+               ether_addr_copy(mac_addr, addr);
+               pf->flags |= I40E_FLAG_PF_MAC;
+#ifdef CONFIG_SPARC
+       } else {
+               ether_addr_copy(mac_addr, idprom->id_ethaddr);
+               pf->flags |= I40E_FLAG_PF_MAC;
+#endif /* CONFIG_SPARC */
+       }
+}
+
 /**
  * i40e_probe - Device initialization routine
  * @pdev: PCI device information struct
@@ -10499,6 +10695,12 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* set up a default setting for link flow control */
        pf->hw.fc.requested_mode = I40E_FC_NONE;
 
+       /* set up the locks for the AQ, do this only once in probe
+        * and destroy them only once in remove
+        */
+       mutex_init(&hw->aq.asq_mutex);
+       mutex_init(&hw->aq.arq_mutex);
+
        err = i40e_init_adminq(hw);
        if (err) {
                if (err == I40E_ERR_FIRMWARE_API_VERSION)
@@ -10569,6 +10771,8 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        }
 
        i40e_get_mac_addr(hw, hw->mac.addr);
+       /* allow a platform config to override the HW addr */
+       i40e_get_platform_mac_addr(pdev, pf);
        if (!is_valid_ether_addr(hw->mac.addr)) {
                dev_info(&pdev->dev, "invalid MAC address %pM\n", hw->mac.addr);
                err = -EIO;
@@ -10916,7 +11120,6 @@ static void i40e_remove(struct pci_dev *pdev)
        set_bit(__I40E_DOWN, &pf->state);
        del_timer_sync(&pf->service_timer);
        cancel_work_sync(&pf->service_task);
-       i40e_fdir_teardown(pf);
 
        if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
                i40e_free_vfs(pf);
@@ -10959,6 +11162,10 @@ static void i40e_remove(struct pci_dev *pdev)
                         "Failed to destroy the Admin Queue resources: %d\n",
                         ret_code);
 
+       /* destroy the locks only once, here */
+       mutex_destroy(&hw->aq.arq_mutex);
+       mutex_destroy(&hw->aq.asq_mutex);
+
        /* Clear all dynamic memory lists of rings, q_vectors, and VSIs */
        i40e_clear_interrupt_scheme(pf);
        for (i = 0; i < pf->num_alloc_vsi; i++) {