__be16 protocol = skb->protocol;
        u16 mac_len = skb->mac_len;
        int udp_offset, outer_hlen;
-       u32 partial;
+       __wsum partial;
 
        if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
                goto out;
 
-       /* adjust partial header checksum to negate old length */
-       partial = (__force u32)uh->check + (__force u16)~uh->len;
+       /* Adjust partial header checksum to negate old length.
+        * We cannot rely on the value contained in uh->len as it is
+        * possible that the actual value exceeds the boundaries of the
+        * 16 bit length field due to the header being added outside of an
+        * IP or IPv6 frame that was already limited to 64K - 1.
+        */
+       partial = csum_sub(csum_unfold(uh->check),
+                          (__force __wsum)htonl(skb->len));
 
        /* setup inner skb. */
        skb->encapsulation = 0;
                if (!need_csum)
                        continue;
 
-               uh->check = ~csum_fold((__force __wsum)
-                                      ((__force u32)len + partial));
+               uh->check = ~csum_fold(csum_add(partial, (__force __wsum)len));
 
                if (skb->encapsulation || !offload_csum) {
                        uh->check = gso_make_checksum(skb, ~uh->check);