mirror of https://gitee.com/openkylin/linux.git
Merge branch 'udp-gso-cleanups'
Alexander Duyck says: ==================== UDP GSO Segmentation clean-ups This patch set addresses a number of issues I found while sorting out enabling UDP GSO Segmentation support for ixgbe/ixgbevf. Specifically there were a number of issues related to the checksum and such that seemed to cause either minor irregularities or kernel panics in the case of the offload request being allowed to traverse between name spaces. With this set applied I am was able to get UDP GSO traffic to pass over vxlan tunnels in both offloaded modes and non-offloaded modes for ixgbe and ixgbevf. I submitted the driver specific patches earlier as an RFC: https://patchwork.ozlabs.org/project/netdev/list/?series=42477&archive=both&state=* v2: Updated patches based on feedback from Eric Dumazet Split first patch into several patches based on feedback from Eric v3: Drop patch that was calling pskb_may_pull as it was redundant. Added code to use MANGLED_0 in case of UDP checksum Drop patch adding NETIF_F_GSO_UDP_L4 to list of GSO software offloads Added Acked-by for patches reviewed by Willem and not changed ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
0e3e466b7c
|
@ -175,8 +175,7 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
|
|||
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
|
||||
|
||||
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
|
||||
netdev_features_t features,
|
||||
unsigned int mss, __sum16 check);
|
||||
netdev_features_t features);
|
||||
|
||||
static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
|
||||
{
|
||||
|
|
|
@ -793,6 +793,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
|
|||
|
||||
skb_shinfo(skb)->gso_size = cork->gso_size;
|
||||
skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4;
|
||||
skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(len - sizeof(uh),
|
||||
cork->gso_size);
|
||||
goto csum_partial;
|
||||
}
|
||||
|
||||
|
|
|
@ -188,67 +188,93 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
|
|||
EXPORT_SYMBOL(skb_udp_tunnel_segment);
|
||||
|
||||
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
|
||||
netdev_features_t features,
|
||||
unsigned int mss, __sum16 check)
|
||||
netdev_features_t features)
|
||||
{
|
||||
struct sock *sk = gso_skb->sk;
|
||||
unsigned int sum_truesize = 0;
|
||||
struct sk_buff *segs, *seg;
|
||||
unsigned int hdrlen;
|
||||
struct udphdr *uh;
|
||||
unsigned int mss;
|
||||
bool copy_dtor;
|
||||
__sum16 check;
|
||||
__be16 newlen;
|
||||
|
||||
mss = skb_shinfo(gso_skb)->gso_size;
|
||||
if (gso_skb->len <= sizeof(*uh) + mss)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
hdrlen = gso_skb->data - skb_mac_header(gso_skb);
|
||||
skb_pull(gso_skb, sizeof(*uh));
|
||||
|
||||
/* clear destructor to avoid skb_segment assigning it to tail */
|
||||
WARN_ON_ONCE(gso_skb->destructor != sock_wfree);
|
||||
gso_skb->destructor = NULL;
|
||||
copy_dtor = gso_skb->destructor == sock_wfree;
|
||||
if (copy_dtor)
|
||||
gso_skb->destructor = NULL;
|
||||
|
||||
segs = skb_segment(gso_skb, features);
|
||||
if (unlikely(IS_ERR_OR_NULL(segs))) {
|
||||
gso_skb->destructor = sock_wfree;
|
||||
if (copy_dtor)
|
||||
gso_skb->destructor = sock_wfree;
|
||||
return segs;
|
||||
}
|
||||
|
||||
for (seg = segs; seg; seg = seg->next) {
|
||||
uh = udp_hdr(seg);
|
||||
uh->len = htons(seg->len - hdrlen);
|
||||
/* GSO partial and frag_list segmentation only requires splitting
|
||||
* the frame into an MSS multiple and possibly a remainder, both
|
||||
* cases return a GSO skb. So update the mss now.
|
||||
*/
|
||||
if (skb_is_gso(segs))
|
||||
mss *= skb_shinfo(segs)->gso_segs;
|
||||
|
||||
seg = segs;
|
||||
uh = udp_hdr(seg);
|
||||
|
||||
/* compute checksum adjustment based on old length versus new */
|
||||
newlen = htons(sizeof(*uh) + mss);
|
||||
check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
|
||||
|
||||
for (;;) {
|
||||
if (copy_dtor) {
|
||||
seg->destructor = sock_wfree;
|
||||
seg->sk = sk;
|
||||
sum_truesize += seg->truesize;
|
||||
}
|
||||
|
||||
if (!seg->next)
|
||||
break;
|
||||
|
||||
uh->len = newlen;
|
||||
uh->check = check;
|
||||
|
||||
/* last packet can be partial gso_size */
|
||||
if (!seg->next)
|
||||
csum_replace2(&uh->check, htons(mss),
|
||||
htons(seg->len - hdrlen - sizeof(*uh)));
|
||||
if (seg->ip_summed == CHECKSUM_PARTIAL)
|
||||
gso_reset_checksum(seg, ~check);
|
||||
else
|
||||
uh->check = gso_make_checksum(seg, ~check) ? :
|
||||
CSUM_MANGLED_0;
|
||||
|
||||
uh->check = ~uh->check;
|
||||
seg->destructor = sock_wfree;
|
||||
seg->sk = sk;
|
||||
sum_truesize += seg->truesize;
|
||||
seg = seg->next;
|
||||
uh = udp_hdr(seg);
|
||||
}
|
||||
|
||||
refcount_add(sum_truesize - gso_skb->truesize, &sk->sk_wmem_alloc);
|
||||
/* last packet can be partial gso_size, account for that in checksum */
|
||||
newlen = htons(skb_tail_pointer(seg) - skb_transport_header(seg) +
|
||||
seg->data_len);
|
||||
check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
|
||||
|
||||
uh->len = newlen;
|
||||
uh->check = check;
|
||||
|
||||
if (seg->ip_summed == CHECKSUM_PARTIAL)
|
||||
gso_reset_checksum(seg, ~check);
|
||||
else
|
||||
uh->check = gso_make_checksum(seg, ~check) ? : CSUM_MANGLED_0;
|
||||
|
||||
/* update refcount for the packet */
|
||||
if (copy_dtor)
|
||||
refcount_add(sum_truesize - gso_skb->truesize,
|
||||
&sk->sk_wmem_alloc);
|
||||
return segs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__udp_gso_segment);
|
||||
|
||||
static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
|
||||
netdev_features_t features)
|
||||
{
|
||||
const struct iphdr *iph = ip_hdr(gso_skb);
|
||||
unsigned int mss = skb_shinfo(gso_skb)->gso_size;
|
||||
|
||||
if (!can_checksum_protocol(features, htons(ETH_P_IP)))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
return __udp_gso_segment(gso_skb, features, mss,
|
||||
udp_v4_check(sizeof(struct udphdr) + mss,
|
||||
iph->saddr, iph->daddr, 0));
|
||||
}
|
||||
|
||||
static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
|
||||
netdev_features_t features)
|
||||
{
|
||||
|
@ -272,7 +298,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
|
|||
goto out;
|
||||
|
||||
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
|
||||
return __udp4_gso_segment(skb, features);
|
||||
return __udp_gso_segment(skb, features);
|
||||
|
||||
mss = skb_shinfo(skb)->gso_size;
|
||||
if (unlikely(skb->len <= mss))
|
||||
|
|
|
@ -17,20 +17,6 @@
|
|||
#include <net/ip6_checksum.h>
|
||||
#include "ip6_offload.h"
|
||||
|
||||
static struct sk_buff *__udp6_gso_segment(struct sk_buff *gso_skb,
|
||||
netdev_features_t features)
|
||||
{
|
||||
const struct ipv6hdr *ip6h = ipv6_hdr(gso_skb);
|
||||
unsigned int mss = skb_shinfo(gso_skb)->gso_size;
|
||||
|
||||
if (!can_checksum_protocol(features, htons(ETH_P_IPV6)))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
return __udp_gso_segment(gso_skb, features, mss,
|
||||
udp_v6_check(sizeof(struct udphdr) + mss,
|
||||
&ip6h->saddr, &ip6h->daddr, 0));
|
||||
}
|
||||
|
||||
static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
|
||||
netdev_features_t features)
|
||||
{
|
||||
|
@ -63,7 +49,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
|
|||
goto out;
|
||||
|
||||
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
|
||||
return __udp6_gso_segment(skb, features);
|
||||
return __udp_gso_segment(skb, features);
|
||||
|
||||
/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
|
||||
* do checksum of UDP packets sent as multiple IP fragments.
|
||||
|
|
Loading…
Reference in New Issue