amd-xgbe: Add support for VXLAN offload capabilities
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Fri, 18 Aug 2017 14:04:04 +0000 (09:04 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 18 Aug 2017 23:30:17 +0000 (16:30 -0700)
The hardware has the capability to perform checksum offload support
(both Tx and Rx) and TSO support for VXLAN packets. Add the support
required to enable this.

The hardware can only support a single VXLAN port for offload. If more
than one VXLAN port is added then the offload capabilities have to be
disabled and can no longer be advertised.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/amd/xgbe/xgbe-common.h
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe.h

index d07edf9eaa697f4cbb309d2957d4d02be5e1be23..943133034879edb19f76b3537c36483b647f52bb 100644 (file)
 #define MAC_RWKPFR                     0x00c4
 #define MAC_LPICSR                     0x00d0
 #define MAC_LPITCR                     0x00d4
+#define MAC_TIR                                0x00e0
 #define MAC_VR                         0x0110
 #define MAC_DR                         0x0114
 #define MAC_HWF0R                      0x011c
 #define MAC_HWF0R_TXCOESEL_WIDTH       1
 #define MAC_HWF0R_VLHASH_INDEX         4
 #define MAC_HWF0R_VLHASH_WIDTH         1
+#define MAC_HWF0R_VXN_INDEX            29
+#define MAC_HWF0R_VXN_WIDTH            1
 #define MAC_HWF1R_ADDR64_INDEX         14
 #define MAC_HWF1R_ADDR64_WIDTH         2
 #define MAC_HWF1R_ADVTHWORD_INDEX      13
 #define MAC_PFR_PR_WIDTH               1
 #define MAC_PFR_VTFE_INDEX             16
 #define MAC_PFR_VTFE_WIDTH             1
+#define MAC_PFR_VUCC_INDEX             22
+#define MAC_PFR_VUCC_WIDTH             1
 #define MAC_PMTCSR_MGKPKTEN_INDEX      1
 #define MAC_PMTCSR_MGKPKTEN_WIDTH      1
 #define MAC_PMTCSR_PWRDWN_INDEX                0
 #define MAC_TCR_SS_WIDTH               2
 #define MAC_TCR_TE_INDEX               0
 #define MAC_TCR_TE_WIDTH               1
+#define MAC_TCR_VNE_INDEX              24
+#define MAC_TCR_VNE_WIDTH              1
+#define MAC_TCR_VNM_INDEX              25
+#define MAC_TCR_VNM_WIDTH              1
+#define MAC_TIR_TNID_INDEX             0
+#define MAC_TIR_TNID_WIDTH             16
 #define MAC_TSCR_AV8021ASMEN_INDEX     28
 #define MAC_TSCR_AV8021ASMEN_WIDTH     1
 #define MAC_TSCR_SNAPTYPSEL_INDEX      16
 #define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH    1
 #define RX_PACKET_ATTRIBUTES_FIRST_INDEX       7
 #define RX_PACKET_ATTRIBUTES_FIRST_WIDTH       1
+#define RX_PACKET_ATTRIBUTES_TNP_INDEX         8
+#define RX_PACKET_ATTRIBUTES_TNP_WIDTH         1
+#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_INDEX        9
+#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_WIDTH        1
 
 #define RX_NORMAL_DESC0_OVT_INDEX              0
 #define RX_NORMAL_DESC0_OVT_WIDTH              16
 #define RX_NORMAL_DESC2_HL_INDEX               0
 #define RX_NORMAL_DESC2_HL_WIDTH               10
+#define RX_NORMAL_DESC2_TNP_INDEX              11
+#define RX_NORMAL_DESC2_TNP_WIDTH              1
 #define RX_NORMAL_DESC3_CDA_INDEX              27
 #define RX_NORMAL_DESC3_CDA_WIDTH              1
 #define RX_NORMAL_DESC3_CTXT_INDEX             30
 #define RX_DESC3_L34T_IPV4_TCP                 1
 #define RX_DESC3_L34T_IPV4_UDP                 2
 #define RX_DESC3_L34T_IPV4_ICMP                        3
+#define RX_DESC3_L34T_IPV4_UNKNOWN             7
 #define RX_DESC3_L34T_IPV6_TCP                 9
 #define RX_DESC3_L34T_IPV6_UDP                 10
 #define RX_DESC3_L34T_IPV6_ICMP                        11
+#define RX_DESC3_L34T_IPV6_UNKNOWN             15
 
 #define RX_CONTEXT_DESC3_TSA_INDEX             4
 #define RX_CONTEXT_DESC3_TSA_WIDTH             1
 #define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH   1
 #define TX_PACKET_ATTRIBUTES_PTP_INDEX         3
 #define TX_PACKET_ATTRIBUTES_PTP_WIDTH         1
+#define TX_PACKET_ATTRIBUTES_VXLAN_INDEX       4
+#define TX_PACKET_ATTRIBUTES_VXLAN_WIDTH       1
 
 #define TX_CONTEXT_DESC2_MSS_INDEX             0
 #define TX_CONTEXT_DESC2_MSS_WIDTH             15
 #define TX_NORMAL_DESC3_TCPPL_WIDTH            18
 #define TX_NORMAL_DESC3_TSE_INDEX              18
 #define TX_NORMAL_DESC3_TSE_WIDTH              1
+#define TX_NORMAL_DESC3_VNP_INDEX              23
+#define TX_NORMAL_DESC3_VNP_WIDTH              3
 
 #define TX_NORMAL_DESC2_VLAN_INSERT            0x2
+#define TX_NORMAL_DESC3_VXLAN_PACKET           0x3
 
 /* MDIO undefined or vendor specific registers */
 #ifndef MDIO_PMA_10GBR_PMD_CTRL
index a9784084202adce7774cbc939146c2372ecdeff2..1bf671e02c0a7158e4f2ca65caae63466dbca483 100644 (file)
@@ -479,6 +479,50 @@ static bool xgbe_is_pfc_queue(struct xgbe_prv_data *pdata,
        return false;
 }
 
+static void xgbe_set_vxlan_id(struct xgbe_prv_data *pdata)
+{
+       /* Program the VXLAN port */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, pdata->vxlan_port);
+
+       netif_dbg(pdata, drv, pdata->netdev, "VXLAN tunnel id set to %hx\n",
+                 pdata->vxlan_port);
+}
+
+static void xgbe_enable_vxlan(struct xgbe_prv_data *pdata)
+{
+       if (!pdata->hw_feat.vxn)
+               return;
+
+       /* Program the VXLAN port */
+       xgbe_set_vxlan_id(pdata);
+
+       /* Allow for IPv6/UDP zero-checksum VXLAN packets */
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 1);
+
+       /* Enable VXLAN tunneling mode */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNM, 0);
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 1);
+
+       netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration enabled\n");
+}
+
+static void xgbe_disable_vxlan(struct xgbe_prv_data *pdata)
+{
+       if (!pdata->hw_feat.vxn)
+               return;
+
+       /* Disable tunneling mode */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 0);
+
+       /* Clear IPv6/UDP zero-checksum VXLAN packets setting */
+       XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 0);
+
+       /* Clear the VXLAN port */
+       XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, 0);
+
+       netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration disabled\n");
+}
+
 static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata)
 {
        unsigned int max_q_count, q_count;
@@ -1610,7 +1654,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
        struct xgbe_ring_desc *rdesc;
        struct xgbe_packet_data *packet = &ring->packet_data;
        unsigned int tx_packets, tx_bytes;
-       unsigned int csum, tso, vlan;
+       unsigned int csum, tso, vlan, vxlan;
        unsigned int tso_context, vlan_context;
        unsigned int tx_set_ic;
        int start_index = ring->cur;
@@ -1628,6 +1672,8 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
                             TSO_ENABLE);
        vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
                              VLAN_CTAG);
+       vxlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              VXLAN);
 
        if (tso && (packet->mss != ring->tx.cur_mss))
                tso_context = 1;
@@ -1759,6 +1805,10 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
                                  packet->length);
        }
 
+       if (vxlan)
+               XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, VNP,
+                                 TX_NORMAL_DESC3_VXLAN_PACKET);
+
        for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) {
                cur_index++;
                rdata = XGBE_GET_DESC_DATA(ring, cur_index);
@@ -1920,9 +1970,27 @@ static int xgbe_dev_read(struct xgbe_channel *channel)
        rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL);
 
        /* Set checksum done indicator as appropriate */
-       if (netdev->features & NETIF_F_RXCSUM)
+       if (netdev->features & NETIF_F_RXCSUM) {
                XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
                               CSUM_DONE, 1);
+               XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                              TNPCSUM_DONE, 1);
+       }
+
+       /* Set the tunneled packet indicator */
+       if (XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, TNP)) {
+               XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                              TNP, 1);
+
+               l34t = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, L34T);
+               switch (l34t) {
+               case RX_DESC3_L34T_IPV4_UNKNOWN:
+               case RX_DESC3_L34T_IPV6_UNKNOWN:
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      TNPCSUM_DONE, 0);
+                       break;
+               }
+       }
 
        /* Check for errors (only valid in last descriptor) */
        err = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ES);
@@ -1942,12 +2010,23 @@ static int xgbe_dev_read(struct xgbe_channel *channel)
                                  packet->vlan_ctag);
                }
        } else {
-               if ((etlt == 0x05) || (etlt == 0x06))
+               unsigned int tnp = XGMAC_GET_BITS(packet->attributes,
+                                                 RX_PACKET_ATTRIBUTES, TNP);
+
+               if ((etlt == 0x05) || (etlt == 0x06)) {
                        XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
                                       CSUM_DONE, 0);
-               else
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      TNPCSUM_DONE, 0);
+               } else if (tnp && ((etlt == 0x09) || (etlt == 0x0a))) {
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      CSUM_DONE, 0);
+                       XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES,
+                                      TNPCSUM_DONE, 0);
+               } else {
                        XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS,
                                       FRAME, 1);
+               }
        }
 
        pdata->ext_stats.rxq_packets[channel->queue_index]++;
@@ -3536,5 +3615,10 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
        hw_if->disable_ecc_ded = xgbe_disable_ecc_ded;
        hw_if->disable_ecc_sec = xgbe_disable_ecc_sec;
 
+       /* For VXLAN */
+       hw_if->enable_vxlan = xgbe_enable_vxlan;
+       hw_if->disable_vxlan = xgbe_disable_vxlan;
+       hw_if->set_vxlan_id = xgbe_set_vxlan_id;
+
        DBGPR("<--xgbe_init_function_ptrs\n");
 }
index 7498bb81f9184b781b243c3234a8aefd4613c8b0..608693d11bd7f7f2a7b6c3eeaeaf20dbacf859e4 100644 (file)
 #include <linux/if_ether.h>
 #include <linux/net_tstamp.h>
 #include <linux/phy.h>
+#include <net/vxlan.h>
 
 #include "xgbe.h"
 #include "xgbe-common.h"
@@ -756,6 +757,7 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
                                              ADDMACADRSEL);
        hw_feat->ts_src      = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSTSSEL);
        hw_feat->sa_vlan_ins = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SAVLANINS);
+       hw_feat->vxn         = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VXN);
 
        /* Hardware feature register 1 */
        hw_feat->rx_fifo_size  = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R,
@@ -860,6 +862,8 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
                        (hw_feat->ts_src == 3) ? "internal/external" : "n/a");
                dev_dbg(pdata->dev, "  SA/VLAN insertion         : %s\n",
                        hw_feat->sa_vlan_ins ? "yes" : "no");
+               dev_dbg(pdata->dev, "  VXLAN/NVGRE support       : %s\n",
+                       hw_feat->vxn ? "yes" : "no");
 
                /* Hardware feature register 1 */
                dev_dbg(pdata->dev, "  RX fifo size              : %u\n",
@@ -903,6 +907,116 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata)
        }
 }
 
+static void xgbe_disable_vxlan_offloads(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+
+       if (!pdata->vxlan_offloads_set)
+               return;
+
+       netdev_info(netdev, "disabling VXLAN offloads\n");
+
+       netdev->hw_enc_features &= ~(NETIF_F_SG |
+                                    NETIF_F_IP_CSUM |
+                                    NETIF_F_IPV6_CSUM |
+                                    NETIF_F_RXCSUM |
+                                    NETIF_F_TSO |
+                                    NETIF_F_TSO6 |
+                                    NETIF_F_GRO |
+                                    NETIF_F_GSO_UDP_TUNNEL |
+                                    NETIF_F_GSO_UDP_TUNNEL_CSUM);
+
+       netdev->features &= ~(NETIF_F_GSO_UDP_TUNNEL |
+                             NETIF_F_GSO_UDP_TUNNEL_CSUM);
+
+       pdata->vxlan_offloads_set = 0;
+}
+
+static void xgbe_disable_vxlan_hw(struct xgbe_prv_data *pdata)
+{
+       if (!pdata->vxlan_port_set)
+               return;
+
+       pdata->hw_if.disable_vxlan(pdata);
+
+       pdata->vxlan_port_set = 0;
+       pdata->vxlan_port = 0;
+}
+
+static void xgbe_disable_vxlan_accel(struct xgbe_prv_data *pdata)
+{
+       xgbe_disable_vxlan_offloads(pdata);
+
+       xgbe_disable_vxlan_hw(pdata);
+}
+
+static void xgbe_enable_vxlan_offloads(struct xgbe_prv_data *pdata)
+{
+       struct net_device *netdev = pdata->netdev;
+
+       if (pdata->vxlan_offloads_set)
+               return;
+
+       netdev_info(netdev, "enabling VXLAN offloads\n");
+
+       netdev->hw_enc_features |= NETIF_F_SG |
+                                  NETIF_F_IP_CSUM |
+                                  NETIF_F_IPV6_CSUM |
+                                  NETIF_F_RXCSUM |
+                                  NETIF_F_TSO |
+                                  NETIF_F_TSO6 |
+                                  NETIF_F_GRO |
+                                  pdata->vxlan_features;
+
+       netdev->features |= pdata->vxlan_features;
+
+       pdata->vxlan_offloads_set = 1;
+}
+
+static void xgbe_enable_vxlan_hw(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_vxlan_data *vdata;
+
+       if (pdata->vxlan_port_set)
+               return;
+
+       if (list_empty(&pdata->vxlan_ports))
+               return;
+
+       vdata = list_first_entry(&pdata->vxlan_ports,
+                                struct xgbe_vxlan_data, list);
+
+       pdata->vxlan_port_set = 1;
+       pdata->vxlan_port = be16_to_cpu(vdata->port);
+
+       pdata->hw_if.enable_vxlan(pdata);
+}
+
+static void xgbe_enable_vxlan_accel(struct xgbe_prv_data *pdata)
+{
+       /* VXLAN acceleration desired? */
+       if (!pdata->vxlan_features)
+               return;
+
+       /* VXLAN acceleration possible? */
+       if (pdata->vxlan_force_disable)
+               return;
+
+       xgbe_enable_vxlan_hw(pdata);
+
+       xgbe_enable_vxlan_offloads(pdata);
+}
+
+static void xgbe_reset_vxlan_accel(struct xgbe_prv_data *pdata)
+{
+       xgbe_disable_vxlan_hw(pdata);
+
+       if (pdata->vxlan_features)
+               xgbe_enable_vxlan_offloads(pdata);
+
+       pdata->vxlan_force_disable = 0;
+}
+
 static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add)
 {
        struct xgbe_channel *channel;
@@ -1226,6 +1340,8 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
        hw_if->enable_tx(pdata);
        hw_if->enable_rx(pdata);
 
+       udp_tunnel_get_rx_info(netdev);
+
        netif_tx_start_all_queues(netdev);
 
        xgbe_start_timers(pdata);
@@ -1267,6 +1383,8 @@ static void xgbe_stop(struct xgbe_prv_data *pdata)
        xgbe_stop_timers(pdata);
        flush_workqueue(pdata->dev_workqueue);
 
+       xgbe_reset_vxlan_accel(pdata);
+
        hw_if->disable_tx(pdata);
        hw_if->disable_rx(pdata);
 
@@ -1555,10 +1673,18 @@ static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet)
        if (ret)
                return ret;
 
-       packet->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
-       packet->tcp_header_len = tcp_hdrlen(skb);
+       if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, VXLAN)) {
+               packet->header_len = skb_inner_transport_offset(skb) +
+                                    inner_tcp_hdrlen(skb);
+               packet->tcp_header_len = inner_tcp_hdrlen(skb);
+       } else {
+               packet->header_len = skb_transport_offset(skb) +
+                                    tcp_hdrlen(skb);
+               packet->tcp_header_len = tcp_hdrlen(skb);
+       }
        packet->tcp_payload_len = skb->len - packet->header_len;
        packet->mss = skb_shinfo(skb)->gso_size;
+
        DBGPR("  packet->header_len=%u\n", packet->header_len);
        DBGPR("  packet->tcp_header_len=%u, packet->tcp_payload_len=%u\n",
              packet->tcp_header_len, packet->tcp_payload_len);
@@ -1573,6 +1699,49 @@ static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet)
        return 0;
 }
 
+static bool xgbe_is_vxlan(struct xgbe_prv_data *pdata, struct sk_buff *skb)
+{
+       struct xgbe_vxlan_data *vdata;
+
+       if (pdata->vxlan_force_disable)
+               return false;
+
+       if (!skb->encapsulation)
+               return false;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return false;
+
+       switch (skb->protocol) {
+       case htons(ETH_P_IP):
+               if (ip_hdr(skb)->protocol != IPPROTO_UDP)
+                       return false;
+               break;
+
+       case htons(ETH_P_IPV6):
+               if (ipv6_hdr(skb)->nexthdr != IPPROTO_UDP)
+                       return false;
+               break;
+
+       default:
+               return false;
+       }
+
+       /* See if we have the UDP port in our list */
+       list_for_each_entry(vdata, &pdata->vxlan_ports, list) {
+               if ((skb->protocol == htons(ETH_P_IP)) &&
+                   (vdata->sa_family == AF_INET) &&
+                   (vdata->port == udp_hdr(skb)->dest))
+                       return true;
+               else if ((skb->protocol == htons(ETH_P_IPV6)) &&
+                        (vdata->sa_family == AF_INET6) &&
+                        (vdata->port == udp_hdr(skb)->dest))
+                       return true;
+       }
+
+       return false;
+}
+
 static int xgbe_is_tso(struct sk_buff *skb)
 {
        if (skb->ip_summed != CHECKSUM_PARTIAL)
@@ -1621,6 +1790,10 @@ static void xgbe_packet_info(struct xgbe_prv_data *pdata,
                XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
                               CSUM_ENABLE, 1);
 
+       if (xgbe_is_vxlan(pdata, skb))
+               XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
+                              VXLAN, 1);
+
        if (skb_vlan_tag_present(skb)) {
                /* VLAN requires an extra descriptor if tag is different */
                if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag)
@@ -2050,18 +2223,83 @@ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type,
        return 0;
 }
 
+static netdev_features_t xgbe_fix_features(struct net_device *netdev,
+                                          netdev_features_t features)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       netdev_features_t vxlan_base, vxlan_mask;
+
+       vxlan_base = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RX_UDP_TUNNEL_PORT;
+       vxlan_mask = vxlan_base | NETIF_F_GSO_UDP_TUNNEL_CSUM;
+
+       pdata->vxlan_features = features & vxlan_mask;
+
+       /* Only fix VXLAN-related features */
+       if (!pdata->vxlan_features)
+               return features;
+
+       /* If VXLAN isn't supported then clear any features:
+        *   This is needed because NETIF_F_RX_UDP_TUNNEL_PORT gets
+        *   automatically set if ndo_udp_tunnel_add is set.
+        */
+       if (!pdata->hw_feat.vxn)
+               return features & ~vxlan_mask;
+
+       /* VXLAN CSUM requires VXLAN base */
+       if ((features & NETIF_F_GSO_UDP_TUNNEL_CSUM) &&
+           !(features & NETIF_F_GSO_UDP_TUNNEL)) {
+               netdev_notice(netdev,
+                             "forcing tx udp tunnel support\n");
+               features |= NETIF_F_GSO_UDP_TUNNEL;
+       }
+
+       /* Can't do one without doing the other */
+       if ((features & vxlan_base) != vxlan_base) {
+               netdev_notice(netdev,
+                             "forcing both tx and rx udp tunnel support\n");
+               features |= vxlan_base;
+       }
+
+       if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
+               if (!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM)) {
+                       netdev_notice(netdev,
+                                     "forcing tx udp tunnel checksumming on\n");
+                       features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
+               }
+       } else {
+               if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM) {
+                       netdev_notice(netdev,
+                                     "forcing tx udp tunnel checksumming off\n");
+                       features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM;
+               }
+       }
+
+       pdata->vxlan_features = features & vxlan_mask;
+
+       /* Adjust UDP Tunnel based on current state */
+       if (pdata->vxlan_force_disable) {
+               netdev_notice(netdev,
+                             "VXLAN acceleration disabled, turning off udp tunnel features\n");
+               features &= ~vxlan_mask;
+       }
+
+       return features;
+}
+
 static int xgbe_set_features(struct net_device *netdev,
                             netdev_features_t features)
 {
        struct xgbe_prv_data *pdata = netdev_priv(netdev);
        struct xgbe_hw_if *hw_if = &pdata->hw_if;
        netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter;
+       netdev_features_t udp_tunnel;
        int ret = 0;
 
        rxhash = pdata->netdev_features & NETIF_F_RXHASH;
        rxcsum = pdata->netdev_features & NETIF_F_RXCSUM;
        rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX;
        rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER;
+       udp_tunnel = pdata->netdev_features & NETIF_F_GSO_UDP_TUNNEL;
 
        if ((features & NETIF_F_RXHASH) && !rxhash)
                ret = hw_if->enable_rss(pdata);
@@ -2085,6 +2323,11 @@ static int xgbe_set_features(struct net_device *netdev,
        else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter)
                hw_if->disable_rx_vlan_filtering(pdata);
 
+       if ((features & NETIF_F_GSO_UDP_TUNNEL) && !udp_tunnel)
+               xgbe_enable_vxlan_accel(pdata);
+       else if (!(features & NETIF_F_GSO_UDP_TUNNEL) && udp_tunnel)
+               xgbe_disable_vxlan_accel(pdata);
+
        pdata->netdev_features = features;
 
        DBGPR("<--xgbe_set_features\n");
@@ -2092,6 +2335,111 @@ static int xgbe_set_features(struct net_device *netdev,
        return 0;
 }
 
+static void xgbe_udp_tunnel_add(struct net_device *netdev,
+                               struct udp_tunnel_info *ti)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_vxlan_data *vdata;
+
+       if (!pdata->hw_feat.vxn)
+               return;
+
+       if (ti->type != UDP_TUNNEL_TYPE_VXLAN)
+               return;
+
+       pdata->vxlan_port_count++;
+
+       netif_dbg(pdata, drv, netdev,
+                 "adding VXLAN tunnel, family=%hx/port=%hx\n",
+                 ti->sa_family, be16_to_cpu(ti->port));
+
+       if (pdata->vxlan_force_disable)
+               return;
+
+       vdata = kzalloc(sizeof(*vdata), GFP_ATOMIC);
+       if (!vdata) {
+               /* Can no longer properly track VXLAN ports */
+               pdata->vxlan_force_disable = 1;
+               netif_dbg(pdata, drv, netdev,
+                         "internal error, disabling VXLAN accelerations\n");
+
+               xgbe_disable_vxlan_accel(pdata);
+
+               return;
+       }
+       vdata->sa_family = ti->sa_family;
+       vdata->port = ti->port;
+
+       list_add_tail(&vdata->list, &pdata->vxlan_ports);
+
+       /* First port added? */
+       if (pdata->vxlan_port_count == 1) {
+               xgbe_enable_vxlan_accel(pdata);
+
+               return;
+       }
+}
+
+static void xgbe_udp_tunnel_del(struct net_device *netdev,
+                               struct udp_tunnel_info *ti)
+{
+       struct xgbe_prv_data *pdata = netdev_priv(netdev);
+       struct xgbe_vxlan_data *vdata;
+
+       if (!pdata->hw_feat.vxn)
+               return;
+
+       if (ti->type != UDP_TUNNEL_TYPE_VXLAN)
+               return;
+
+       netif_dbg(pdata, drv, netdev,
+                 "deleting VXLAN tunnel, family=%hx/port=%hx\n",
+                 ti->sa_family, be16_to_cpu(ti->port));
+
+       /* Don't need safe version since loop terminates with deletion */
+       list_for_each_entry(vdata, &pdata->vxlan_ports, list) {
+               if (vdata->sa_family != ti->sa_family)
+                       continue;
+
+               if (vdata->port != ti->port)
+                       continue;
+
+               list_del(&vdata->list);
+               kfree(vdata);
+
+               break;
+       }
+
+       pdata->vxlan_port_count--;
+       if (!pdata->vxlan_port_count) {
+               xgbe_reset_vxlan_accel(pdata);
+
+               return;
+       }
+
+       if (pdata->vxlan_force_disable)
+               return;
+
+       /* See if VXLAN tunnel id needs to be changed */
+       vdata = list_first_entry(&pdata->vxlan_ports,
+                                struct xgbe_vxlan_data, list);
+       if (pdata->vxlan_port == be16_to_cpu(vdata->port))
+               return;
+
+       pdata->vxlan_port = be16_to_cpu(vdata->port);
+       pdata->hw_if.set_vxlan_id(pdata);
+}
+
+static netdev_features_t xgbe_features_check(struct sk_buff *skb,
+                                            struct net_device *netdev,
+                                            netdev_features_t features)
+{
+       features = vlan_features_check(skb, features);
+       features = vxlan_features_check(skb, features);
+
+       return features;
+}
+
 static const struct net_device_ops xgbe_netdev_ops = {
        .ndo_open               = xgbe_open,
        .ndo_stop               = xgbe_close,
@@ -2109,7 +2457,11 @@ static const struct net_device_ops xgbe_netdev_ops = {
        .ndo_poll_controller    = xgbe_poll_controller,
 #endif
        .ndo_setup_tc           = xgbe_setup_tc,
+       .ndo_fix_features       = xgbe_fix_features,
        .ndo_set_features       = xgbe_set_features,
+       .ndo_udp_tunnel_add     = xgbe_udp_tunnel_add,
+       .ndo_udp_tunnel_del     = xgbe_udp_tunnel_del,
+       .ndo_features_check     = xgbe_features_check,
 };
 
 const struct net_device_ops *xgbe_get_netdev_ops(void)
@@ -2421,6 +2773,15 @@ skip_data:
                                   RX_PACKET_ATTRIBUTES, CSUM_DONE))
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
 
+               if (XGMAC_GET_BITS(packet->attributes,
+                                  RX_PACKET_ATTRIBUTES, TNP)) {
+                       skb->encapsulation = 1;
+
+                       if (XGMAC_GET_BITS(packet->attributes,
+                                          RX_PACKET_ATTRIBUTES, TNPCSUM_DONE))
+                               skb->csum_level = 1;
+               }
+
                if (XGMAC_GET_BITS(packet->attributes,
                                   RX_PACKET_ATTRIBUTES, VLAN_CTAG))
                        __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
index c5ff385d51ae03a766c0ea938ff66de116ab3a37..d91fa595be9835318ce15a0b6b8ef6ef58bdf56a 100644 (file)
@@ -193,6 +193,7 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev)
        mutex_init(&pdata->i2c_mutex);
        init_completion(&pdata->i2c_complete);
        init_completion(&pdata->mdio_complete);
+       INIT_LIST_HEAD(&pdata->vxlan_ports);
 
        pdata->msg_enable = netif_msg_init(debug, default_msg_level);
 
@@ -374,6 +375,28 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
        if (pdata->hw_feat.rss)
                netdev->hw_features |= NETIF_F_RXHASH;
 
+       if (pdata->hw_feat.vxn) {
+               netdev->hw_enc_features = NETIF_F_SG |
+                                         NETIF_F_IP_CSUM |
+                                         NETIF_F_IPV6_CSUM |
+                                         NETIF_F_RXCSUM |
+                                         NETIF_F_TSO |
+                                         NETIF_F_TSO6 |
+                                         NETIF_F_GRO |
+                                         NETIF_F_GSO_UDP_TUNNEL |
+                                         NETIF_F_GSO_UDP_TUNNEL_CSUM |
+                                         NETIF_F_RX_UDP_TUNNEL_PORT;
+
+               netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL |
+                                      NETIF_F_GSO_UDP_TUNNEL_CSUM |
+                                      NETIF_F_RX_UDP_TUNNEL_PORT;
+
+               pdata->vxlan_offloads_set = 1;
+               pdata->vxlan_features = NETIF_F_GSO_UDP_TUNNEL |
+                                       NETIF_F_GSO_UDP_TUNNEL_CSUM |
+                                       NETIF_F_RX_UDP_TUNNEL_PORT;
+       }
+
        netdev->vlan_features |= NETIF_F_SG |
                                 NETIF_F_IP_CSUM |
                                 NETIF_F_IPV6_CSUM |
index 48a46a70bf5e338afcf0d262cbe2f25be443ad57..db155fe7f74d3154cc665c8f65afabc530d84b3a 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/dcache.h>
 #include <linux/ethtool.h>
+#include <linux/list.h>
 
 #define XGBE_DRV_NAME          "amd-xgbe"
 #define XGBE_DRV_VERSION       "1.0.3"
@@ -817,6 +818,11 @@ struct xgbe_hw_if {
        /* For ECC */
        void (*disable_ecc_ded)(struct xgbe_prv_data *);
        void (*disable_ecc_sec)(struct xgbe_prv_data *, enum xgbe_ecc_sec);
+
+       /* For VXLAN */
+       void (*enable_vxlan)(struct xgbe_prv_data *);
+       void (*disable_vxlan)(struct xgbe_prv_data *);
+       void (*set_vxlan_id)(struct xgbe_prv_data *);
 };
 
 /* This structure represents implementation specific routines for an
@@ -941,6 +947,7 @@ struct xgbe_hw_features {
        unsigned int addn_mac;          /* Additional MAC Addresses */
        unsigned int ts_src;            /* Timestamp Source */
        unsigned int sa_vlan_ins;       /* Source Address or VLAN Insertion */
+       unsigned int vxn;               /* VXLAN/NVGRE */
 
        /* HW Feature Register1 */
        unsigned int rx_fifo_size;      /* MTL Receive FIFO Size */
@@ -979,6 +986,12 @@ struct xgbe_version_data {
        unsigned int rx_desc_prefetch;
 };
 
+struct xgbe_vxlan_data {
+       struct list_head list;
+       sa_family_t sa_family;
+       __be16 port;
+};
+
 struct xgbe_prv_data {
        struct net_device *netdev;
        struct pci_dev *pcidev;
@@ -1120,6 +1133,15 @@ struct xgbe_prv_data {
        u32 rss_table[XGBE_RSS_MAX_TABLE_SIZE];
        u32 rss_options;
 
+       /* VXLAN settings */
+       unsigned int vxlan_port_set;
+       unsigned int vxlan_offloads_set;
+       unsigned int vxlan_force_disable;
+       unsigned int vxlan_port_count;
+       struct list_head vxlan_ports;
+       u16 vxlan_port;
+       netdev_features_t vxlan_features;
+
        /* Netdev related settings */
        unsigned char mac_addr[ETH_ALEN];
        netdev_features_t netdev_features;