i40e: geneve tunnel offload support
authorSinghai, Anjali <anjali.singhai@intel.com>
Mon, 14 Dec 2015 20:21:18 +0000 (12:21 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Dec 2015 15:58:56 +0000 (10:58 -0500)
This patch adds driver hooks to implement ndo_ops to add/del
udp port in the HW to identify GENEVE tunnels.

Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com>
Signed-off-by: Kiran Patil <kiran.patil@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_txrx.h

index b7bc014..c202f9b 100644 (file)
@@ -245,6 +245,11 @@ struct i40e_tc_configuration {
        struct i40e_tc_info tc_info[I40E_MAX_TRAFFIC_CLASS];
 };
 
+struct i40e_udp_port_config {
+       __be16 index;
+       u8 type;
+};
+
 /* struct that defines the Ethernet device */
 struct i40e_pf {
        struct pci_dev *pdev;
@@ -281,11 +286,9 @@ struct i40e_pf {
        u32 fd_atr_cnt;
        u32 fd_tcp_rule;
 
-#ifdef CONFIG_I40E_VXLAN
-       __be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
-       u16 pending_vxlan_bitmap;
+       struct i40e_udp_port_config udp_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
+       u16 pending_udp_bitmap;
 
-#endif
        enum i40e_interrupt_policy int_policy;
        u16 rx_itr_default;
        u16 tx_itr_default;
@@ -322,9 +325,7 @@ struct i40e_pf {
 #define I40E_FLAG_FD_ATR_ENABLED               BIT_ULL(22)
 #define I40E_FLAG_PTP                          BIT_ULL(25)
 #define I40E_FLAG_MFP_ENABLED                  BIT_ULL(26)
-#ifdef CONFIG_I40E_VXLAN
-#define I40E_FLAG_VXLAN_FILTER_SYNC            BIT_ULL(27)
-#endif
+#define I40E_FLAG_UDP_FILTER_SYNC              BIT_ULL(27)
 #define I40E_FLAG_PORT_ID_VALID                        BIT_ULL(28)
 #define I40E_FLAG_DCB_CAPABLE                  BIT_ULL(29)
 #define I40E_FLAG_RSS_AQ_CAPABLE               BIT_ULL(31)
@@ -336,6 +337,7 @@ struct i40e_pf {
 #define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE  BIT_ULL(38)
 #define I40E_FLAG_LINK_POLLING_ENABLED         BIT_ULL(39)
 #define I40E_FLAG_VEB_MODE_ENABLED             BIT_ULL(40)
+#define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE       BIT_ULL(41)
 #define I40E_FLAG_NO_PCI_LINK_CHECK            BIT_ULL(42)
 
        /* tracks features that get auto disabled by errors */
index c284e43..c69b1bb 100644 (file)
 /* 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[] =
@@ -7036,30 +7039,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 +7075,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 +7106,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 +8383,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 +8483,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 +8513,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 +8523,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 +8533,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 +8542,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 +8558,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 +8567,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 +8846,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,7 +8861,7 @@ 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_CSUM_MASK | NETIF_F_GSO_MASK);
 
@@ -8799,10 +8896,14 @@ 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,
        .ndo_features_check     = i40e_features_check,
@@ -8836,6 +8937,7 @@ 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;
@@ -10349,6 +10451,9 @@ static void i40e_print_features(struct i40e_pf *pf)
 #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");
 #ifdef I40E_FCOE
index b0ae3e6..e9e9a37 100644 (file)
@@ -1380,7 +1380,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
 
-       /* If VXLAN traffic has an outer UDPv4 checksum we need to check
+       /* If VXLAN/GENEVE traffic has an outer UDPv4 checksum we need to check
         * it in the driver, hardware does not do it for us.
         * Since L3L4P bit was set we assume a valid IHL value (>=5)
         * so the total length of IPv4 header is IHL*4 bytes
@@ -2001,7 +2001,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
        if (!(tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6)))
                return;
 
-       if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL)) {
+       if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) {
                /* snag network header to get L4 type and address */
                hdr.network = skb_network_header(skb);
 
@@ -2086,7 +2086,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
                     I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT;
 
        dtype_cmd |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK;
-       if (!(tx_flags & I40E_TX_FLAGS_VXLAN_TUNNEL))
+       if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL))
                dtype_cmd |=
                        ((u32)I40E_FD_ATR_STAT_IDX(pf->hw.pf_id) <<
                        I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) &
@@ -2319,7 +2319,7 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
                        oudph = udp_hdr(skb);
                        oiph = ip_hdr(skb);
                        l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING;
-                       *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL;
+                       *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL;
                        break;
                case IPPROTO_GRE:
                        l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING;
index dccc1eb..3f081e2 100644 (file)
@@ -163,7 +163,7 @@ enum i40e_dyn_idx_t {
 #define I40E_TX_FLAGS_FSO              BIT(7)
 #define I40E_TX_FLAGS_TSYN             BIT(8)
 #define I40E_TX_FLAGS_FD_SB            BIT(9)
-#define I40E_TX_FLAGS_VXLAN_TUNNEL     BIT(10)
+#define I40E_TX_FLAGS_UDP_TUNNEL       BIT(10)
 #define I40E_TX_FLAGS_VLAN_MASK                0xffff0000
 #define I40E_TX_FLAGS_VLAN_PRIO_MASK   0xe0000000
 #define I40E_TX_FLAGS_VLAN_PRIO_SHIFT  29