diff options
Diffstat (limited to 'drivers/net/ethernet/amd')
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-common.h | 24 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-dev.c | 92 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 365 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-main.c | 23 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe.h | 22 |
5 files changed, 520 insertions, 6 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index d07edf9eaa69..943133034879 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -298,6 +298,7 @@ #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 @@ -364,6 +365,8 @@ #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 @@ -448,6 +451,8 @@ #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 @@ -510,6 +515,12 @@ #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 @@ -1153,11 +1164,17 @@ #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 @@ -1184,9 +1201,11 @@ #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 @@ -1201,6 +1220,8 @@ #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 @@ -1241,8 +1262,11 @@ #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 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index a9784084202a..1bf671e02c0a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -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"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 7498bb81f918..608693d11bd7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -124,6 +124,7 @@ #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) @@ -2422,6 +2774,15 @@ skip_data: 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), packet->vlan_ctag); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index c5ff385d51ae..d91fa595be98 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -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 | diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 48a46a70bf5e..db155fe7f74d 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -132,6 +132,7 @@ #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; |