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,
-                                 __sum16 check);
+                                 netdev_features_t features);
 
 static inline struct udphdr *udp_gro_udphdr(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,
-                                 __sum16 check)
+                                 netdev_features_t features)
 {
        struct sock *sk = gso_skb->sk;
        unsigned int sum_truesize = 0;
        unsigned int hdrlen;
        struct udphdr *uh;
        unsigned int mss;
+       __sum16 check;
+       __be16 newlen;
 
        mss = skb_shinfo(gso_skb)->gso_size;
        if (gso_skb->len <= sizeof(*uh) + mss)
                return segs;
        }
 
+       uh = udp_hdr(segs);
+
+       /* 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 (seg = segs; seg; seg = seg->next) {
                uh = udp_hdr(seg);
-               uh->len = htons(seg->len - hdrlen);
-               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->next) {
+                       newlen = htons(seg->len - hdrlen);
+                       check = csum16_add(csum16_sub(uh->check, uh->len),
+                                          newlen);
+               }
+
+               uh->len = newlen;
+               uh->check = check;
 
-               uh->check = ~uh->check;
                seg->destructor = sock_wfree;
                seg->sk = sk;
                sum_truesize += seg->truesize;
 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,
-                                udp_v4_check(sizeof(struct udphdr) + mss,
-                                             iph->saddr, iph->daddr, 0));
+       return __udp_gso_segment(gso_skb, features);
 }
 
 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
 
 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,
-                                udp_v6_check(sizeof(struct udphdr) + mss,
-                                             &ip6h->saddr, &ip6h->daddr, 0));
+       return __udp_gso_segment(gso_skb, features);
 }
 
 static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,