Commit d289cbed authored by David S. Miller's avatar David S. Miller

Merge branch '40GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue

Jeff Kirsher says:

====================
40GbE Intel Wired LAN Driver Updates 2016-02-18

This series contains updates to i40e and i40evf only.

Alex Duyck provides all the patches in the series to update and fix the
drivers.  Fixed the driver to drop the outer checksum offload on UDP
tunnels, since the issue is that the upper levels of the stack never
requested such an offload and it results in possible errors.  Updates the
TSO function to just use u64 values, so we do not have to end up casting
u32 values.  In the TSO path, factored out the L4 header offsets allowing
us to ignore the L4 header offsets when dealing with the L3 checksum and
length update.  Consolidates all of the spots where we were updating
either the TCP or IP checksums in the TSO and checksum path into the TSO
function.  Fixed two issues by adding support for IPv4 encapsulated in
IPv6, first issue was the fact that iphdr(skb)->protocol was being used to
test for the outer transport protocol which breaks IPv6 support.  The second
was that we cleared the flag for v4 going to v6, but we did not take care
of txflags going the other way.  Added support for IPv6 extension headers
in setting up the Tx checksum.  Added exception handling to the Tx
checksum path so that we can handle cases of TSO where the frame is bad,
or Tx checksum where we did not recognize a protocol.  Fixed a number of
issues to make certain that we are using the correct protocols when
parsing both the inner and outer headers of a frame that is mixed between
IPv4 and IPv6 for inner and outer.  Updated the feature flags to reflect
the newly enabled/added features.

Sorry, no witty patch descriptions this time around, probably should
let Mitch help in writing patch descriptions for Alex. :-)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 376471a7 ffcc55c0
...@@ -7474,8 +7474,6 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) ...@@ -7474,8 +7474,6 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi)
tx_ring->dcb_tc = 0; tx_ring->dcb_tc = 0;
if (vsi->back->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) if (vsi->back->flags & I40E_FLAG_WB_ON_ITR_CAPABLE)
tx_ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; tx_ring->flags = I40E_TXR_FLAGS_WB_ON_ITR;
if (vsi->back->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE)
tx_ring->flags |= I40E_TXR_FLAGS_OUTER_UDP_CSUM;
vsi->tx_rings[i] = tx_ring; vsi->tx_rings[i] = tx_ring;
rx_ring = &tx_ring[1]; rx_ring = &tx_ring[1];
...@@ -8628,9 +8626,6 @@ static void i40e_add_vxlan_port(struct net_device *netdev, ...@@ -8628,9 +8626,6 @@ static void i40e_add_vxlan_port(struct net_device *netdev,
u8 next_idx; u8 next_idx;
u8 idx; u8 idx;
if (sa_family == AF_INET6)
return;
idx = i40e_get_udp_port_idx(pf, port); idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */ /* Check if port already exists */
...@@ -8670,9 +8665,6 @@ static void i40e_del_vxlan_port(struct net_device *netdev, ...@@ -8670,9 +8665,6 @@ static void i40e_del_vxlan_port(struct net_device *netdev,
struct i40e_pf *pf = vsi->back; struct i40e_pf *pf = vsi->back;
u8 idx; u8 idx;
if (sa_family == AF_INET6)
return;
idx = i40e_get_udp_port_idx(pf, port); idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */ /* Check if port already exists */
...@@ -8709,9 +8701,6 @@ static void i40e_add_geneve_port(struct net_device *netdev, ...@@ -8709,9 +8701,6 @@ static void i40e_add_geneve_port(struct net_device *netdev,
if (!(pf->flags & I40E_FLAG_GENEVE_OFFLOAD_CAPABLE)) if (!(pf->flags & I40E_FLAG_GENEVE_OFFLOAD_CAPABLE))
return; return;
if (sa_family == AF_INET6)
return;
idx = i40e_get_udp_port_idx(pf, port); idx = i40e_get_udp_port_idx(pf, port);
/* Check if port already exists */ /* Check if port already exists */
...@@ -8753,9 +8742,6 @@ static void i40e_del_geneve_port(struct net_device *netdev, ...@@ -8753,9 +8742,6 @@ static void i40e_del_geneve_port(struct net_device *netdev,
struct i40e_pf *pf = vsi->back; struct i40e_pf *pf = vsi->back;
u8 idx; u8 idx;
if (sa_family == AF_INET6)
return;
if (!(pf->flags & I40E_FLAG_GENEVE_OFFLOAD_CAPABLE)) if (!(pf->flags & I40E_FLAG_GENEVE_OFFLOAD_CAPABLE))
return; return;
...@@ -9046,10 +9032,14 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) ...@@ -9046,10 +9032,14 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
np = netdev_priv(netdev); np = netdev_priv(netdev);
np->vsi = vsi; np->vsi = vsi;
netdev->hw_enc_features |= NETIF_F_IP_CSUM | netdev->hw_enc_features |= NETIF_F_IP_CSUM |
NETIF_F_GSO_UDP_TUNNEL | NETIF_F_IPV6_CSUM |
NETIF_F_GSO_GRE | NETIF_F_TSO |
NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_TSO_ECN |
NETIF_F_GSO_GRE |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_UDP_TUNNEL_CSUM |
0; 0;
netdev->features = NETIF_F_SG | netdev->features = NETIF_F_SG |
...@@ -9071,6 +9061,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) ...@@ -9071,6 +9061,8 @@ static int i40e_config_netdev(struct i40e_vsi *vsi)
if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) if (!(pf->flags & I40E_FLAG_MFP_ENABLED))
netdev->features |= NETIF_F_NTUPLE; netdev->features |= NETIF_F_NTUPLE;
if (pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE)
netdev->features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
/* copy netdev features into list of user selectable features */ /* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features; netdev->hw_features |= netdev->features;
......
...@@ -1389,16 +1389,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -1389,16 +1389,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
u16 rx_ptype) u16 rx_ptype)
{ {
struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype); struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
bool ipv4 = false, ipv6 = false; bool ipv4, ipv6, ipv4_tunnel, ipv6_tunnel;
bool ipv4_tunnel, ipv6_tunnel;
__wsum rx_udp_csum;
struct iphdr *iph;
__sum16 csum;
ipv4_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
(rx_ptype <= I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
ipv6_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT6_MAC_PAY3) &&
(rx_ptype <= I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4);
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
...@@ -1414,12 +1405,10 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -1414,12 +1405,10 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
if (!(decoded.known && decoded.outer_ip)) if (!(decoded.known && decoded.outer_ip))
return; return;
if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP && ipv4 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) &&
decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4) (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4);
ipv4 = true; ipv6 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) &&
else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP && (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6);
decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
ipv6 = true;
if (ipv4 && if (ipv4 &&
(rx_error & (BIT(I40E_RX_DESC_ERROR_IPE_SHIFT) | (rx_error & (BIT(I40E_RX_DESC_ERROR_IPE_SHIFT) |
...@@ -1443,37 +1432,17 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -1443,37 +1432,17 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT)) if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
return; return;
/* If VXLAN/GENEVE traffic has an outer UDPv4 checksum we need to check /* The hardware supported by this driver does not validate outer
* it in the driver, hardware does not do it for us. * checksums for tunneled VXLAN or GENEVE frames. I don't agree
* Since L3L4P bit was set we assume a valid IHL value (>=5) * with it but the specification states that you "MAY validate", it
* so the total length of IPv4 header is IHL*4 bytes * doesn't make it a hard requirement so if we have validated the
* The UDP_0 bit *may* bet set if the *inner* header is UDP * inner checksum report CHECKSUM_UNNECESSARY.
*/ */
if (!(vsi->back->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE) &&
(ipv4_tunnel)) { ipv4_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
skb->transport_header = skb->mac_header + (rx_ptype <= I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
sizeof(struct ethhdr) + ipv6_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT6_MAC_PAY3) &&
(ip_hdr(skb)->ihl * 4); (rx_ptype <= I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4);
/* Add 4 bytes for VLAN tagged packets */
skb->transport_header += (skb->protocol == htons(ETH_P_8021Q) ||
skb->protocol == htons(ETH_P_8021AD))
? VLAN_HLEN : 0;
if ((ip_hdr(skb)->protocol == IPPROTO_UDP) &&
(udp_hdr(skb)->check != 0)) {
rx_udp_csum = udp_csum(skb);
iph = ip_hdr(skb);
csum = csum_tcpudp_magic(
iph->saddr, iph->daddr,
(skb->len - skb_transport_offset(skb)),
IPPROTO_UDP, rx_udp_csum);
if (udp_hdr(skb)->check != csum)
goto checksum_fail;
} /* else its GRE and so no outer UDP header */
}
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum_level = ipv4_tunnel || ipv6_tunnel; skb->csum_level = ipv4_tunnel || ipv6_tunnel;
...@@ -2061,10 +2030,9 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) ...@@ -2061,10 +2030,9 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
* @tx_ring: ring to add programming descriptor to * @tx_ring: ring to add programming descriptor to
* @skb: send buffer * @skb: send buffer
* @tx_flags: send tx flags * @tx_flags: send tx flags
* @protocol: wire protocol
**/ **/
static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
u32 tx_flags, __be16 protocol) u32 tx_flags)
{ {
struct i40e_filter_program_desc *fdir_desc; struct i40e_filter_program_desc *fdir_desc;
struct i40e_pf *pf = tx_ring->vsi->back; struct i40e_pf *pf = tx_ring->vsi->back;
...@@ -2076,6 +2044,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2076,6 +2044,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
struct tcphdr *th; struct tcphdr *th;
unsigned int hlen; unsigned int hlen;
u32 flex_ptype, dtype_cmd; u32 flex_ptype, dtype_cmd;
int l4_proto;
u16 i; u16 i;
/* make sure ATR is enabled */ /* make sure ATR is enabled */
...@@ -2089,36 +2058,28 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2089,36 +2058,28 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (!tx_ring->atr_sample_rate) if (!tx_ring->atr_sample_rate)
return; return;
/* Currently only IPv4/IPv6 with TCP is supported */
if (!(tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6))) if (!(tx_flags & (I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6)))
return; return;
if (!(tx_flags & I40E_TX_FLAGS_UDP_TUNNEL)) { /* snag network header to get L4 type and address */
/* snag network header to get L4 type and address */ hdr.network = (tx_flags & I40E_TX_FLAGS_UDP_TUNNEL) ?
hdr.network = skb_network_header(skb); skb_inner_network_header(skb) : skb_network_header(skb);
/* Currently only IPv4/IPv6 with TCP is supported /* Note: tx_flags gets modified to reflect inner protocols in
* access ihl as u8 to avoid unaligned access on ia64 * tx_enable_csum function if encap is enabled.
*/ */
if (tx_flags & I40E_TX_FLAGS_IPV4) if (tx_flags & I40E_TX_FLAGS_IPV4) {
hlen = (hdr.network[0] & 0x0F) << 2; /* access ihl as u8 to avoid unaligned access on ia64 */
else if (protocol == htons(ETH_P_IPV6)) hlen = (hdr.network[0] & 0x0F) << 2;
hlen = sizeof(struct ipv6hdr); l4_proto = hdr.ipv4->protocol;
else
return;
} else { } else {
hdr.network = skb_inner_network_header(skb); hlen = hdr.network - skb->data;
hlen = skb_inner_network_header_len(skb); l4_proto = ipv6_find_hdr(skb, &hlen, IPPROTO_TCP, NULL, NULL);
hlen -= hdr.network - skb->data;
} }
/* Currently only IPv4/IPv6 with TCP is supported if (l4_proto != IPPROTO_TCP)
* Note: tx_flags gets modified to reflect inner protocols in
* tx_enable_csum function if encap is enabled.
*/
if ((tx_flags & I40E_TX_FLAGS_IPV4) &&
(hdr.ipv4->protocol != IPPROTO_TCP))
return;
else if ((tx_flags & I40E_TX_FLAGS_IPV6) &&
(hdr.ipv6->nexthdr != IPPROTO_TCP))
return; return;
th = (struct tcphdr *)(hdr.network + hlen); th = (struct tcphdr *)(hdr.network + hlen);
...@@ -2155,7 +2116,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2155,7 +2116,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb,
flex_ptype = (tx_ring->queue_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) & flex_ptype = (tx_ring->queue_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) &
I40E_TXD_FLTR_QW0_QINDEX_MASK; I40E_TXD_FLTR_QW0_QINDEX_MASK;
flex_ptype |= (protocol == htons(ETH_P_IP)) ? flex_ptype |= (tx_flags & I40E_TX_FLAGS_IPV4) ?
(I40E_FILTER_PCTYPE_NONF_IPV4_TCP << (I40E_FILTER_PCTYPE_NONF_IPV4_TCP <<
I40E_TXD_FLTR_QW0_PCTYPE_SHIFT) : I40E_TXD_FLTR_QW0_PCTYPE_SHIFT) :
(I40E_FILTER_PCTYPE_NONF_IPV6_TCP << (I40E_FILTER_PCTYPE_NONF_IPV6_TCP <<
...@@ -2295,11 +2256,18 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, ...@@ -2295,11 +2256,18 @@ static inline int i40e_tx_prepare_vlan_flags(struct sk_buff *skb,
static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
u8 *hdr_len, u64 *cd_type_cmd_tso_mss) u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
{ {
u32 cd_cmd, cd_tso_len, cd_mss; u64 cd_cmd, cd_tso_len, cd_mss;
struct ipv6hdr *ipv6h; union {
struct tcphdr *tcph; struct iphdr *v4;
struct iphdr *iph; struct ipv6hdr *v6;
u32 l4len; unsigned char *hdr;
} ip;
union {
struct tcphdr *tcp;
struct udphdr *udp;
unsigned char *hdr;
} l4;
u32 paylen, l4_offset;
int err; int err;
if (skb->ip_summed != CHECKSUM_PARTIAL) if (skb->ip_summed != CHECKSUM_PARTIAL)
...@@ -2312,35 +2280,60 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2312,35 +2280,60 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (err < 0) if (err < 0)
return err; return err;
iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); ip.hdr = skb_network_header(skb);
ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); l4.hdr = skb_transport_header(skb);
if (iph->version == 4) { /* initialize outer IP header fields */
tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb); if (ip.v4->version == 4) {
iph->tot_len = 0; ip.v4->tot_len = 0;
iph->check = 0; ip.v4->check = 0;
tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, } else {
0, IPPROTO_TCP, 0); ip.v6->payload_len = 0;
} else if (ipv6h->version == 6) { }
tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb);
ipv6h->payload_len = 0; if (skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE |
tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, SKB_GSO_UDP_TUNNEL_CSUM)) {
0, IPPROTO_TCP, 0); if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) {
/* determine offset of outer transport header */
l4_offset = l4.hdr - skb->data;
/* remove payload length from outer checksum */
paylen = (__force u16)l4.udp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset);
l4.udp->check = ~csum_fold((__force __wsum)paylen);
}
/* reset pointers to inner headers */
ip.hdr = skb_inner_network_header(skb);
l4.hdr = skb_inner_transport_header(skb);
/* initialize inner IP header fields */
if (ip.v4->version == 4) {
ip.v4->tot_len = 0;
ip.v4->check = 0;
} else {
ip.v6->payload_len = 0;
}
} }
l4len = skb->encapsulation ? inner_tcp_hdrlen(skb) : tcp_hdrlen(skb); /* determine offset of inner transport header */
*hdr_len = (skb->encapsulation l4_offset = l4.hdr - skb->data;
? (skb_inner_transport_header(skb) - skb->data)
: skb_transport_offset(skb)) + l4len; /* remove payload length from inner checksum */
paylen = (__force u16)l4.tcp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset);
l4.tcp->check = ~csum_fold((__force __wsum)paylen);
/* compute length of segmentation header */
*hdr_len = (l4.tcp->doff * 4) + l4_offset;
/* find the field values */ /* find the field values */
cd_cmd = I40E_TX_CTX_DESC_TSO; cd_cmd = I40E_TX_CTX_DESC_TSO;
cd_tso_len = skb->len - *hdr_len; cd_tso_len = skb->len - *hdr_len;
cd_mss = skb_shinfo(skb)->gso_size; cd_mss = skb_shinfo(skb)->gso_size;
*cd_type_cmd_tso_mss |= ((u64)cd_cmd << I40E_TXD_CTX_QW1_CMD_SHIFT) | *cd_type_cmd_tso_mss |= (cd_cmd << I40E_TXD_CTX_QW1_CMD_SHIFT) |
((u64)cd_tso_len << (cd_tso_len << I40E_TXD_CTX_QW1_TSO_LEN_SHIFT) |
I40E_TXD_CTX_QW1_TSO_LEN_SHIFT) | (cd_mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
((u64)cd_mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
return 1; return 1;
} }
...@@ -2395,129 +2388,154 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -2395,129 +2388,154 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb,
* @tx_ring: Tx descriptor ring * @tx_ring: Tx descriptor ring
* @cd_tunneling: ptr to context desc bits * @cd_tunneling: ptr to context desc bits
**/ **/
static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
u32 *td_cmd, u32 *td_offset, u32 *td_cmd, u32 *td_offset,
struct i40e_ring *tx_ring, struct i40e_ring *tx_ring,
u32 *cd_tunneling) u32 *cd_tunneling)
{ {
struct ipv6hdr *this_ipv6_hdr; union {
unsigned int this_tcp_hdrlen; struct iphdr *v4;
struct iphdr *this_ip_hdr; struct ipv6hdr *v6;
u32 network_hdr_len; unsigned char *hdr;
u8 l4_hdr = 0; } ip;
struct udphdr *oudph = NULL; union {
struct iphdr *oiph = NULL; struct tcphdr *tcp;
u32 l4_tunnel = 0; struct udphdr *udp;
unsigned char *hdr;
} l4;
unsigned char *exthdr;
u32 offset, cmd = 0, tunnel = 0;
__be16 frag_off;
u8 l4_proto = 0;
if (skb->ip_summed != CHECKSUM_PARTIAL)
return 0;
ip.hdr = skb_network_header(skb);
l4.hdr = skb_transport_header(skb);
/* compute outer L2 header size */
offset = ((ip.hdr - skb->data) / 2) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT;
if (skb->encapsulation) { if (skb->encapsulation) {
switch (ip_hdr(skb)->protocol) { /* define outer network header type */
if (*tx_flags & I40E_TX_FLAGS_IPV4) {
tunnel |= (*tx_flags & I40E_TX_FLAGS_TSO) ?
I40E_TX_CTX_EXT_IP_IPV4 :
I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM;
l4_proto = ip.v4->protocol;
} else if (*tx_flags & I40E_TX_FLAGS_IPV6) {
tunnel |= I40E_TX_CTX_EXT_IP_IPV6;
exthdr = ip.hdr + sizeof(*ip.v6);
l4_proto = ip.v6->nexthdr;
if (l4.hdr != exthdr)
ipv6_skip_exthdr(skb, exthdr - skb->data,
&l4_proto, &frag_off);
}
/* compute outer L3 header size */
tunnel |= ((l4.hdr - ip.hdr) / 4) <<
I40E_TXD_CTX_QW0_EXT_IPLEN_SHIFT;
/* switch IP header pointer from outer to inner header */
ip.hdr = skb_inner_network_header(skb);
/* define outer transport */
switch (l4_proto) {
case IPPROTO_UDP: case IPPROTO_UDP:
oudph = udp_hdr(skb); tunnel |= I40E_TXD_CTX_UDP_TUNNELING;
oiph = ip_hdr(skb);
l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING;
*tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL; *tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL;
break; break;
case IPPROTO_GRE: case IPPROTO_GRE:
l4_tunnel = I40E_TXD_CTX_GRE_TUNNELING; tunnel |= I40E_TXD_CTX_GRE_TUNNELING;
*tx_flags |= I40E_TX_FLAGS_UDP_TUNNEL;
break; break;
default: default:
return;
}
network_hdr_len = skb_inner_network_header_len(skb);
this_ip_hdr = inner_ip_hdr(skb);
this_ipv6_hdr = inner_ipv6_hdr(skb);
this_tcp_hdrlen = inner_tcp_hdrlen(skb);
if (*tx_flags & I40E_TX_FLAGS_IPV4) {
if (*tx_flags & I40E_TX_FLAGS_TSO) {
*cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV4;
ip_hdr(skb)->check = 0;
} else {
*cd_tunneling |=
I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM;
}
} else if (*tx_flags & I40E_TX_FLAGS_IPV6) {
*cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6;
if (*tx_flags & I40E_TX_FLAGS_TSO) if (*tx_flags & I40E_TX_FLAGS_TSO)
ip_hdr(skb)->check = 0; return -1;
skb_checksum_help(skb);
return 0;
} }
/* Now set the ctx descriptor fields */ /* compute tunnel header size */
*cd_tunneling |= (skb_network_header_len(skb) >> 2) << tunnel |= ((ip.hdr - l4.hdr) / 2) <<
I40E_TXD_CTX_QW0_EXT_IPLEN_SHIFT | I40E_TXD_CTX_QW0_NATLEN_SHIFT;
l4_tunnel |
((skb_inner_network_offset(skb) - /* indicate if we need to offload outer UDP header */
skb_transport_offset(skb)) >> 1) << if ((*tx_flags & I40E_TX_FLAGS_TSO) &&
I40E_TXD_CTX_QW0_NATLEN_SHIFT; (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM))
if (this_ip_hdr->version == 6) { tunnel |= I40E_TXD_CTX_QW0_L4T_CS_MASK;
*tx_flags &= ~I40E_TX_FLAGS_IPV4;
/* record tunnel offload values */
*cd_tunneling |= tunnel;
/* switch L4 header pointer from outer to inner */
l4.hdr = skb_inner_transport_header(skb);
l4_proto = 0;
/* reset type as we transition from outer to inner headers */
*tx_flags &= ~(I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6);
if (ip.v4->version == 4)
*tx_flags |= I40E_TX_FLAGS_IPV4;
if (ip.v6->version == 6)
*tx_flags |= I40E_TX_FLAGS_IPV6; *tx_flags |= I40E_TX_FLAGS_IPV6;
}
if ((tx_ring->flags & I40E_TXR_FLAGS_OUTER_UDP_CSUM) &&
(l4_tunnel == I40E_TXD_CTX_UDP_TUNNELING) &&
(*cd_tunneling & I40E_TXD_CTX_QW0_EXT_IP_MASK)) {
oudph->check = ~csum_tcpudp_magic(oiph->saddr,
oiph->daddr,
(skb->len - skb_transport_offset(skb)),
IPPROTO_UDP, 0);
*cd_tunneling |= I40E_TXD_CTX_QW0_L4T_CS_MASK;
}
} else {
network_hdr_len = skb_network_header_len(skb);
this_ip_hdr = ip_hdr(skb);
this_ipv6_hdr = ipv6_hdr(skb);
this_tcp_hdrlen = tcp_hdrlen(skb);
} }
/* Enable IP checksum offloads */ /* Enable IP checksum offloads */
if (*tx_flags & I40E_TX_FLAGS_IPV4) { if (*tx_flags & I40E_TX_FLAGS_IPV4) {
l4_hdr = this_ip_hdr->protocol; l4_proto = ip.v4->protocol;
/* the stack computes the IP header already, the only time we /* the stack computes the IP header already, the only time we
* need the hardware to recompute it is in the case of TSO. * need the hardware to recompute it is in the case of TSO.
*/ */
if (*tx_flags & I40E_TX_FLAGS_TSO) { cmd |= (*tx_flags & I40E_TX_FLAGS_TSO) ?
*td_cmd |= I40E_TX_DESC_CMD_IIPT_IPV4_CSUM; I40E_TX_DESC_CMD_IIPT_IPV4_CSUM :
this_ip_hdr->check = 0; I40E_TX_DESC_CMD_IIPT_IPV4;
} else {
*td_cmd |= I40E_TX_DESC_CMD_IIPT_IPV4;
}
/* Now set the td_offset for IP header length */
*td_offset = (network_hdr_len >> 2) <<
I40E_TX_DESC_LENGTH_IPLEN_SHIFT;
} else if (*tx_flags & I40E_TX_FLAGS_IPV6) { } else if (*tx_flags & I40E_TX_FLAGS_IPV6) {
l4_hdr = this_ipv6_hdr->nexthdr; cmd |= I40E_TX_DESC_CMD_IIPT_IPV6;
*td_cmd |= I40E_TX_DESC_CMD_IIPT_IPV6;
/* Now set the td_offset for IP header length */ exthdr = ip.hdr + sizeof(*ip.v6);
*td_offset = (network_hdr_len >> 2) << l4_proto = ip.v6->nexthdr;
I40E_TX_DESC_LENGTH_IPLEN_SHIFT; if (l4.hdr != exthdr)
ipv6_skip_exthdr(skb, exthdr - skb->data,
&l4_proto, &frag_off);
} }
/* words in MACLEN + dwords in IPLEN + dwords in L4Len */
*td_offset |= (skb_network_offset(skb) >> 1) << /* compute inner L3 header size */
I40E_TX_DESC_LENGTH_MACLEN_SHIFT; offset |= ((l4.hdr - ip.hdr) / 4) << I40E_TX_DESC_LENGTH_IPLEN_SHIFT;
/* Enable L4 checksum offloads */ /* Enable L4 checksum offloads */
switch (l4_hdr) { switch (l4_proto) {
case IPPROTO_TCP: case IPPROTO_TCP:
/* enable checksum offloads */ /* enable checksum offloads */
*td_cmd |= I40E_TX_DESC_CMD_L4T_EOFT_TCP; cmd |= I40E_TX_DESC_CMD_L4T_EOFT_TCP;
*td_offset |= (this_tcp_hdrlen >> 2) << offset |= l4.tcp->doff << I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
break; break;
case IPPROTO_SCTP: case IPPROTO_SCTP:
/* enable SCTP checksum offload */ /* enable SCTP checksum offload */
*td_cmd |= I40E_TX_DESC_CMD_L4T_EOFT_SCTP; cmd |= I40E_TX_DESC_CMD_L4T_EOFT_SCTP;
*td_offset |= (sizeof(struct sctphdr) >> 2) << offset |= (sizeof(struct sctphdr) >> 2) <<
I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
/* enable UDP checksum offload */ /* enable UDP checksum offload */
*td_cmd |= I40E_TX_DESC_CMD_L4T_EOFT_UDP; cmd |= I40E_TX_DESC_CMD_L4T_EOFT_UDP;
*td_offset |= (sizeof(struct udphdr) >> 2) << offset |= (sizeof(struct udphdr) >> 2) <<
I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
break; break;
default: default:
break; if (*tx_flags & I40E_TX_FLAGS_TSO)
return -1;
skb_checksum_help(skb);
return 0;
} }
*td_cmd |= cmd;
*td_offset |= offset;
return 1;
} }
/** /**
...@@ -2954,12 +2972,10 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, ...@@ -2954,12 +2972,10 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
td_cmd |= I40E_TX_DESC_CMD_ICRC; td_cmd |= I40E_TX_DESC_CMD_ICRC;
/* Always offload the checksum, since it's in the data descriptor */ /* Always offload the checksum, since it's in the data descriptor */
if (skb->ip_summed == CHECKSUM_PARTIAL) { tso = i40e_tx_enable_csum(skb, &tx_flags, &td_cmd, &td_offset,
tx_flags |= I40E_TX_FLAGS_CSUM; tx_ring, &cd_tunneling);
if (tso < 0)
i40e_tx_enable_csum(skb, &tx_flags, &td_cmd, &td_offset, goto out_drop;
tx_ring, &cd_tunneling);
}
i40e_create_tx_ctx(tx_ring, cd_type_cmd_tso_mss, i40e_create_tx_ctx(tx_ring, cd_type_cmd_tso_mss,
cd_tunneling, cd_l2tag2); cd_tunneling, cd_l2tag2);
...@@ -2968,7 +2984,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, ...@@ -2968,7 +2984,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
* *
* NOTE: this must always be directly before the data descriptor. * NOTE: this must always be directly before the data descriptor.
*/ */
i40e_atr(tx_ring, skb, tx_flags, protocol); i40e_atr(tx_ring, skb, tx_flags);
i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len, i40e_tx_map(tx_ring, skb, first, tx_flags, hdr_len,
td_cmd, td_offset); td_cmd, td_offset);
......
...@@ -153,7 +153,6 @@ enum i40e_dyn_idx_t { ...@@ -153,7 +153,6 @@ enum i40e_dyn_idx_t {
#define DESC_NEEDED (MAX_SKB_FRAGS + 4) #define DESC_NEEDED (MAX_SKB_FRAGS + 4)
#define I40E_MIN_DESC_PENDING 4 #define I40E_MIN_DESC_PENDING 4
#define I40E_TX_FLAGS_CSUM BIT(0)
#define I40E_TX_FLAGS_HW_VLAN BIT(1) #define I40E_TX_FLAGS_HW_VLAN BIT(1)
#define I40E_TX_FLAGS_SW_VLAN BIT(2) #define I40E_TX_FLAGS_SW_VLAN BIT(2)
#define I40E_TX_FLAGS_TSO BIT(3) #define I40E_TX_FLAGS_TSO BIT(3)
...@@ -277,7 +276,6 @@ struct i40e_ring { ...@@ -277,7 +276,6 @@ struct i40e_ring {
u16 flags; u16 flags;
#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0)
#define I40E_TXR_FLAGS_OUTER_UDP_CSUM BIT(1)
#define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2) #define I40E_TXR_FLAGS_LAST_XMIT_MORE_SET BIT(2)
/* stats structs */ /* stats structs */
......
...@@ -861,16 +861,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -861,16 +861,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
u16 rx_ptype) u16 rx_ptype)
{ {
struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype); struct i40e_rx_ptype_decoded decoded = decode_rx_desc_ptype(rx_ptype);
bool ipv4 = false, ipv6 = false; bool ipv4, ipv6, ipv4_tunnel, ipv6_tunnel;
bool ipv4_tunnel, ipv6_tunnel;
__wsum rx_udp_csum;
struct iphdr *iph;
__sum16 csum;
ipv4_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
(rx_ptype <= I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
ipv6_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT6_MAC_PAY3) &&
(rx_ptype <= I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4);
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
...@@ -886,12 +877,10 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -886,12 +877,10 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
if (!(decoded.known && decoded.outer_ip)) if (!(decoded.known && decoded.outer_ip))
return; return;
if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP && ipv4 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) &&
decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4) (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV4);
ipv4 = true; ipv6 = (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP) &&
else if (decoded.outer_ip == I40E_RX_PTYPE_OUTER_IP && (decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6);
decoded.outer_ip_ver == I40E_RX_PTYPE_OUTER_IPV6)
ipv6 = true;
if (ipv4 && if (ipv4 &&
(rx_error & (BIT(I40E_RX_DESC_ERROR_IPE_SHIFT) | (rx_error & (BIT(I40E_RX_DESC_ERROR_IPE_SHIFT) |
...@@ -915,36 +904,17 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, ...@@ -915,36 +904,17 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT)) if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
return; return;
/* If VXLAN traffic has an outer UDPv4 checksum we need to check /* The hardware supported by this driver does not validate outer
* it in the driver, hardware does not do it for us. * checksums for tunneled VXLAN or GENEVE frames. I don't agree
* Since L3L4P bit was set we assume a valid IHL value (>=5) * with it but the specification states that you "MAY validate", it
* so the total length of IPv4 header is IHL*4 bytes * doesn't make it a hard requirement so if we have validated the
* The UDP_0 bit *may* bet set if the *inner* header is UDP * inner checksum report CHECKSUM_UNNECESSARY.
*/ */
if (ipv4_tunnel) {
skb->transport_header = skb->mac_header + ipv4_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT4_MAC_PAY3) &&
sizeof(struct ethhdr) + (rx_ptype <= I40E_RX_PTYPE_GRENAT4_MACVLAN_IPV6_ICMP_PAY4);
(ip_hdr(skb)->ihl * 4); ipv6_tunnel = (rx_ptype >= I40E_RX_PTYPE_GRENAT6_MAC_PAY3) &&
(rx_ptype <= I40E_RX_PTYPE_GRENAT6_MACVLAN_IPV6_ICMP_PAY4);
/* Add 4 bytes for VLAN tagged packets */
skb->transport_header += (skb->protocol == htons(ETH_P_8021Q) ||
skb->protocol == htons(ETH_P_8021AD))
? VLAN_HLEN : 0;
if ((ip_hdr(skb)->protocol == IPPROTO_UDP) &&
(udp_hdr(skb)->check != 0)) {
rx_udp_csum = udp_csum(skb);
iph = ip_hdr(skb);
csum = csum_tcpudp_magic(iph->saddr, iph->daddr,
(skb->len -
skb_transport_offset(skb)),
IPPROTO_UDP, rx_udp_csum);
if (udp_hdr(skb)->check != csum)
goto checksum_fail;
} /* else its GRE and so no outer UDP header */
}
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
skb->csum_level = ipv4_tunnel || ipv6_tunnel; skb->csum_level = ipv4_tunnel || ipv6_tunnel;
...@@ -1554,11 +1524,18 @@ static inline int i40evf_tx_prepare_vlan_flags(struct sk_buff *skb, ...@@ -1554,11 +1524,18 @@ static inline int i40evf_tx_prepare_vlan_flags(struct sk_buff *skb,
static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
u8 *hdr_len, u64 *cd_type_cmd_tso_mss) u8 *hdr_len, u64 *cd_type_cmd_tso_mss)
{ {
u32 cd_cmd, cd_tso_len, cd_mss; u64 cd_cmd, cd_tso_len, cd_mss;
struct ipv6hdr *ipv6h; union {
struct tcphdr *tcph; struct iphdr *v4;
struct iphdr *iph; struct ipv6hdr *v6;
u32 l4len; unsigned char *hdr;
} ip;
union {
struct tcphdr *tcp;
struct udphdr *udp;
unsigned char *hdr;
} l4;
u32 paylen, l4_offset;
int err; int err;
if (skb->ip_summed != CHECKSUM_PARTIAL) if (skb->ip_summed != CHECKSUM_PARTIAL)
...@@ -1571,35 +1548,60 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1571,35 +1548,60 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
if (err < 0) if (err < 0)
return err; return err;
iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); ip.hdr = skb_network_header(skb);
ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); l4.hdr = skb_transport_header(skb);
if (iph->version == 4) { /* initialize outer IP header fields */
tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb); if (ip.v4->version == 4) {
iph->tot_len = 0; ip.v4->tot_len = 0;
iph->check = 0; ip.v4->check = 0;
tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, } else {
0, IPPROTO_TCP, 0); ip.v6->payload_len = 0;
} else if (ipv6h->version == 6) { }
tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb);
ipv6h->payload_len = 0; if (skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE |
tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, SKB_GSO_UDP_TUNNEL_CSUM)) {
0, IPPROTO_TCP, 0); if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM) {
/* determine offset of outer transport header */
l4_offset = l4.hdr - skb->data;
/* remove payload length from outer checksum */
paylen = (__force u16)l4.udp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset);
l4.udp->check = ~csum_fold((__force __wsum)paylen);
}
/* reset pointers to inner headers */
ip.hdr = skb_inner_network_header(skb);
l4.hdr = skb_inner_transport_header(skb);
/* initialize inner IP header fields */
if (ip.v4->version == 4) {
ip.v4->tot_len = 0;
ip.v4->check = 0;
} else {
ip.v6->payload_len = 0;
}
} }
l4len = skb->encapsulation ? inner_tcp_hdrlen(skb) : tcp_hdrlen(skb); /* determine offset of inner transport header */
*hdr_len = (skb->encapsulation l4_offset = l4.hdr - skb->data;
? (skb_inner_transport_header(skb) - skb->data)
: skb_transport_offset(skb)) + l4len; /* remove payload length from inner checksum */
paylen = (__force u16)l4.tcp->check;
paylen += ntohs(1) * (u16)~(skb->len - l4_offset);
l4.tcp->check = ~csum_fold((__force __wsum)paylen);
/* compute length of segmentation header */
*hdr_len = (l4.tcp->doff * 4) + l4_offset;
/* find the field values */ /* find the field values */
cd_cmd = I40E_TX_CTX_DESC_TSO; cd_cmd = I40E_TX_CTX_DESC_TSO;
cd_tso_len = skb->len - *hdr_len; cd_tso_len = skb->len - *hdr_len;
cd_mss = skb_shinfo(skb)->gso_size; cd_mss = skb_shinfo(skb)->gso_size;
*cd_type_cmd_tso_mss |= ((u64)cd_cmd << I40E_TXD_CTX_QW1_CMD_SHIFT) | *cd_type_cmd_tso_mss |= (cd_cmd << I40E_TXD_CTX_QW1_CMD_SHIFT) |
((u64)cd_tso_len << (cd_tso_len << I40E_TXD_CTX_QW1_TSO_LEN_SHIFT) |
I40E_TXD_CTX_QW1_TSO_LEN_SHIFT) | (cd_mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
((u64)cd_mss << I40E_TXD_CTX_QW1_MSS_SHIFT);
return 1; return 1;
} }
...@@ -1609,129 +1611,157 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, ...@@ -1609,129 +1611,157 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb,
* @tx_flags: pointer to Tx flags currently set * @tx_flags: pointer to Tx flags currently set
* @td_cmd: Tx descriptor command bits to set * @td_cmd: Tx descriptor command bits to set
* @td_offset: Tx descriptor header offsets to set * @td_offset: Tx descriptor header offsets to set
* @tx_ring: Tx descriptor ring
* @cd_tunneling: ptr to context desc bits * @cd_tunneling: ptr to context desc bits
**/ **/
static void i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags, static int i40e_tx_enable_csum(struct sk_buff *skb, u32 *tx_flags,
u32 *td_cmd, u32 *td_offset, u32 *td_cmd, u32 *td_offset,
struct i40e_ring *tx_ring, struct i40e_ring *tx_ring,
u32 *cd_tunneling) u32 *cd_tunneling)
{ {
struct ipv6hdr *this_ipv6_hdr; union {
unsigned int this_tcp_hdrlen; struct iphdr *v4;
struct iphdr *this_ip_hdr; struct ipv6hdr *v6;
u32 network_hdr_len; unsigned char *hdr;
u8 l4_hdr = 0; } ip;
struct udphdr *oudph; union {
struct iphdr *oiph; struct tcphdr *tcp;
u32 l4_tunnel = 0; struct udphdr *udp;
unsigned char *hdr;
} l4;
unsigned char *exthdr;
u32 offset, cmd = 0, tunnel = 0;
__be16 frag_off;
u8 l4_proto = 0;
if (skb->ip_summed != CHECKSUM_PARTIAL)
return 0;
ip.hdr = skb_network_header(skb);
l4.hdr = skb_transport_header(skb);
/* compute outer L2 header size */
offset = ((ip.hdr - skb->data) / 2) << I40E_TX_DESC_LENGTH_MACLEN_SHIFT;
if (skb->encapsulation) { if (skb->encapsulation) {
switch (ip_hdr(skb)->protocol) { /* define outer network header type */
if (*tx_flags & I40E_TX_FLAGS_IPV4) {
tunnel |= (*tx_flags & I40E_TX_FLAGS_TSO) ?
I40E_TX_CTX_EXT_IP_IPV4 :
I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM;
l4_proto = ip.v4->protocol;
} else if (*tx_flags & I40E_TX_FLAGS_IPV6) {
tunnel |= I40E_TX_CTX_EXT_IP_IPV6;
exthdr = ip.hdr + sizeof(*ip.v6);
l4_proto = ip.v6->nexthdr;
if (l4.hdr != exthdr)
ipv6_skip_exthdr(skb, exthdr - skb->data,
&l4_proto, &frag_off);
}
/* compute outer L3 header size */
tunnel |= ((l4.hdr - ip.hdr) / 4) <<
I40E_TXD_CTX_QW0_EXT_IPLEN_SHIFT;
/* switch IP header pointer from outer to inner header */
ip.hdr = skb_inner_network_header(skb);
/* define outer transport */
switch (l4_proto) {
case IPPROTO_UDP: case IPPROTO_UDP:
oudph = udp_hdr(skb); tunnel |= I40E_TXD_CTX_UDP_TUNNELING;
oiph = ip_hdr(skb); *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL;
l4_tunnel = I40E_TXD_CTX_UDP_TUNNELING; break;
case IPPROTO_GRE:
tunnel |= I40E_TXD_CTX_GRE_TUNNELING;
*tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL; *tx_flags |= I40E_TX_FLAGS_VXLAN_TUNNEL;
break; break;
default: default:
return;
}
network_hdr_len = skb_inner_network_header_len(skb);
this_ip_hdr = inner_ip_hdr(skb);
this_ipv6_hdr = inner_ipv6_hdr(skb);
this_tcp_hdrlen = inner_tcp_hdrlen(skb);
if (*tx_flags & I40E_TX_FLAGS_IPV4) {
if (*tx_flags & I40E_TX_FLAGS_TSO) {
*cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV4;
ip_hdr(skb)->check = 0;
} else {
*cd_tunneling |=
I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM;
}
} else if (*tx_flags & I40E_TX_FLAGS_IPV6) {
*cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6;
if (*tx_flags & I40E_TX_FLAGS_TSO) if (*tx_flags & I40E_TX_FLAGS_TSO)
ip_hdr(skb)->check = 0; return -1;
}
/* Now set the ctx descriptor fields */ skb_checksum_help(skb);
*cd_tunneling |= (skb_network_header_len(skb) >> 2) << return 0;
I40E_TXD_CTX_QW0_EXT_IPLEN_SHIFT |
l4_tunnel |
((skb_inner_network_offset(skb) -
skb_transport_offset(skb)) >> 1) <<
I40E_TXD_CTX_QW0_NATLEN_SHIFT;
if (this_ip_hdr->version == 6) {
*tx_flags &= ~I40E_TX_FLAGS_IPV4;
*tx_flags |= I40E_TX_FLAGS_IPV6;
} }
if ((tx_ring->flags & I40E_TXR_FLAGS_OUTER_UDP_CSUM) && /* compute tunnel header size */
(l4_tunnel == I40E_TXD_CTX_UDP_TUNNELING) && tunnel |= ((ip.hdr - l4.hdr) / 2) <<
(*cd_tunneling & I40E_TXD_CTX_QW0_EXT_IP_MASK)) { I40E_TXD_CTX_QW0_NATLEN_SHIFT;
oudph->check = ~csum_tcpudp_magic(oiph->saddr,
oiph->daddr, /* indicate if we need to offload outer UDP header */
(skb->len - skb_transport_offset(skb)), if ((*tx_flags & I40E_TX_FLAGS_TSO) &&
IPPROTO_UDP, 0); (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM))
*cd_tunneling |= I40E_TXD_CTX_QW0_L4T_CS_MASK; tunnel |= I40E_TXD_CTX_QW0_L4T_CS_MASK;
}
} else { /* record tunnel offload values */
network_hdr_len = skb_network_header_len(skb); *cd_tunneling |= tunnel;
this_ip_hdr = ip_hdr(skb);
this_ipv6_hdr = ipv6_hdr(skb); /* switch L4 header pointer from outer to inner */
this_tcp_hdrlen = tcp_hdrlen(skb); l4.hdr = skb_inner_transport_header(skb);
l4_proto = 0;
/* reset type as we transition from outer to inner headers */
*tx_flags &= ~(I40E_TX_FLAGS_IPV4 | I40E_TX_FLAGS_IPV6);
if (ip.v4->version == 4)
*tx_flags |= I40E_TX_FLAGS_IPV4;
if (ip.v6->version == 6)
*tx_flags |= I40E_TX_FLAGS_IPV6;
} }
/* Enable IP checksum offloads */ /* Enable IP checksum offloads */
if (*tx_flags & I40E_TX_FLAGS_IPV4) { if (*tx_flags & I40E_TX_FLAGS_IPV4) {
l4_hdr = this_ip_hdr->protocol; l4_proto = ip.v4->protocol;
/* the stack computes the IP header already, the only time we /* the stack computes the IP header already, the only time we
* need the hardware to recompute it is in the case of TSO. * need the hardware to recompute it is in the case of TSO.
*/ */
if (*tx_flags & I40E_TX_FLAGS_TSO) { cmd |= (*tx_flags & I40E_TX_FLAGS_TSO) ?
*td_cmd |= I40E_TX_DESC_CMD_IIPT_IPV4_CSUM; I40E_TX_DESC_CMD_IIPT_IPV4_CSUM :
this_ip_hdr->check = 0; I40E_TX_DESC_CMD_IIPT_IPV4;
} else {
*td_cmd |= I40E_TX_DESC_CMD_IIPT_IPV4;
}
/* Now set the td_offset for IP header length */
*td_offset = (network_hdr_len >> 2) <<
I40E_TX_DESC_LENGTH_IPLEN_SHIFT;
} else if (*tx_flags & I40E_TX_FLAGS_IPV6) { } else if (*tx_flags & I40E_TX_FLAGS_IPV6) {
l4_hdr = this_ipv6_hdr->nexthdr; cmd |= I40E_TX_DESC_CMD_IIPT_IPV6;
*td_cmd |= I40E_TX_DESC_CMD_IIPT_IPV6;
/* Now set the td_offset for IP header length */ exthdr = ip.hdr + sizeof(*ip.v6);
*td_offset = (network_hdr_len >> 2) << l4_proto = ip.v6->nexthdr;
I40E_TX_DESC_LENGTH_IPLEN_SHIFT; if (l4.hdr != exthdr)
ipv6_skip_exthdr(skb, exthdr - skb->data,
&l4_proto, &frag_off);
} }
/* words in MACLEN + dwords in IPLEN + dwords in L4Len */
*td_offset |= (skb_network_offset(skb) >> 1) << /* compute inner L3 header size */
I40E_TX_DESC_LENGTH_MACLEN_SHIFT; offset |= ((l4.hdr - ip.hdr) / 4) << I40E_TX_DESC_LENGTH_IPLEN_SHIFT;
/* Enable L4 checksum offloads */ /* Enable L4 checksum offloads */
switch (l4_hdr) { switch (l4_proto) {
case IPPROTO_TCP: case IPPROTO_TCP:
/* enable checksum offloads */ /* enable checksum offloads */
*td_cmd |= I40E_TX_DESC_CMD_L4T_EOFT_TCP; cmd |= I40E_TX_DESC_CMD_L4T_EOFT_TCP;
*td_offset |= (this_tcp_hdrlen >> 2) << offset |= l4.tcp->doff << I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
break; break;
case IPPROTO_SCTP: case IPPROTO_SCTP:
/* enable SCTP checksum offload */ /* enable SCTP checksum offload */
*td_cmd |= I40E_TX_DESC_CMD_L4T_EOFT_SCTP; cmd |= I40E_TX_DESC_CMD_L4T_EOFT_SCTP;
*td_offset |= (sizeof(struct sctphdr) >> 2) << offset |= (sizeof(struct sctphdr) >> 2) <<
I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
/* enable UDP checksum offload */ /* enable UDP checksum offload */
*td_cmd |= I40E_TX_DESC_CMD_L4T_EOFT_UDP; cmd |= I40E_TX_DESC_CMD_L4T_EOFT_UDP;
*td_offset |= (sizeof(struct udphdr) >> 2) << offset |= (sizeof(struct udphdr) >> 2) <<
I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT; I40E_TX_DESC_LENGTH_L4_FC_LEN_SHIFT;
break; break;
default: default:
break; if (*tx_flags & I40E_TX_FLAGS_TSO)
return -1;
skb_checksum_help(skb);
return 0;
} }
*td_cmd |= cmd;
*td_offset |= offset;
return 1;
} }
/** /**
...@@ -2147,12 +2177,10 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, ...@@ -2147,12 +2177,10 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
td_cmd |= I40E_TX_DESC_CMD_ICRC; td_cmd |= I40E_TX_DESC_CMD_ICRC;
/* Always offload the checksum, since it's in the data descriptor */ /* Always offload the checksum, since it's in the data descriptor */
if (skb->ip_summed == CHECKSUM_PARTIAL) { tso = i40e_tx_enable_csum(skb, &tx_flags, &td_cmd, &td_offset,
tx_flags |= I40E_TX_FLAGS_CSUM; tx_ring, &cd_tunneling);
if (tso < 0)
i40e_tx_enable_csum(skb, &tx_flags, &td_cmd, &td_offset, goto out_drop;
tx_ring, &cd_tunneling);
}
i40e_create_tx_ctx(tx_ring, cd_type_cmd_tso_mss, i40e_create_tx_ctx(tx_ring, cd_type_cmd_tso_mss,
cd_tunneling, cd_l2tag2); cd_tunneling, cd_l2tag2);
......
...@@ -153,7 +153,6 @@ enum i40e_dyn_idx_t { ...@@ -153,7 +153,6 @@ enum i40e_dyn_idx_t {
#define DESC_NEEDED (MAX_SKB_FRAGS + 4) #define DESC_NEEDED (MAX_SKB_FRAGS + 4)
#define I40E_MIN_DESC_PENDING 4 #define I40E_MIN_DESC_PENDING 4
#define I40E_TX_FLAGS_CSUM BIT(0)
#define I40E_TX_FLAGS_HW_VLAN BIT(1) #define I40E_TX_FLAGS_HW_VLAN BIT(1)
#define I40E_TX_FLAGS_SW_VLAN BIT(2) #define I40E_TX_FLAGS_SW_VLAN BIT(2)
#define I40E_TX_FLAGS_TSO BIT(3) #define I40E_TX_FLAGS_TSO BIT(3)
...@@ -275,7 +274,6 @@ struct i40e_ring { ...@@ -275,7 +274,6 @@ struct i40e_ring {
u16 flags; u16 flags;
#define I40E_TXR_FLAGS_WB_ON_ITR BIT(0) #define I40E_TXR_FLAGS_WB_ON_ITR BIT(0)
#define I40E_TXR_FLAGS_OUTER_UDP_CSUM BIT(1)
/* stats structs */ /* stats structs */
struct i40e_queue_stats stats; struct i40e_queue_stats stats;
......
...@@ -2337,9 +2337,24 @@ int i40evf_process_config(struct i40evf_adapter *adapter) ...@@ -2337,9 +2337,24 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
NETIF_F_IPV6_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_TSO | NETIF_F_TSO |
NETIF_F_TSO6 | NETIF_F_TSO6 |
NETIF_F_TSO_ECN |
NETIF_F_GSO_GRE |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_RXCSUM | NETIF_F_RXCSUM |
NETIF_F_GRO; NETIF_F_GRO;
netdev->hw_enc_features |= NETIF_F_IP_CSUM |
NETIF_F_IPV6_CSUM |
NETIF_F_TSO |
NETIF_F_TSO6 |
NETIF_F_TSO_ECN |
NETIF_F_GSO_GRE |
NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_UDP_TUNNEL_CSUM;
if (adapter->flags & I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE)
netdev->features |= NETIF_F_GSO_UDP_TUNNEL_CSUM;
/* copy netdev features into list of user selectable features */ /* copy netdev features into list of user selectable features */
netdev->hw_features |= netdev->features; netdev->hw_features |= netdev->features;
netdev->hw_features &= ~NETIF_F_RXCSUM; netdev->hw_features &= ~NETIF_F_RXCSUM;
...@@ -2478,6 +2493,10 @@ static void i40evf_init_task(struct work_struct *work) ...@@ -2478,6 +2493,10 @@ static void i40evf_init_task(struct work_struct *work)
default: default:
goto err_alloc; goto err_alloc;
} }
if (hw->mac.type == I40E_MAC_X722_VF)
adapter->flags |= I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE;
if (i40evf_process_config(adapter)) if (i40evf_process_config(adapter))
goto err_alloc; goto err_alloc;
adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN; adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN;
...@@ -2518,10 +2537,6 @@ static void i40evf_init_task(struct work_struct *work) ...@@ -2518,10 +2537,6 @@ static void i40evf_init_task(struct work_struct *work)
if (err) if (err)
goto err_sw_init; goto err_sw_init;
i40evf_map_rings_to_vectors(adapter); i40evf_map_rings_to_vectors(adapter);
if (adapter->vf_res->vf_offload_flags &
I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE;
if (adapter->vf_res->vf_offload_flags & if (adapter->vf_res->vf_offload_flags &
I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE; adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment