return skb->csum_start - skb_headroom(skb);
 }
 
+static inline unsigned char *skb_checksum_start(const struct sk_buff *skb)
+{
+       return skb->head + skb->csum_start;
+}
+
 static inline int skb_transport_offset(const struct sk_buff *skb)
 {
        return skb_transport_header(skb) - skb->data;
        return 0;
 }
 
+static inline void gso_reset_checksum(struct sk_buff *skb, __wsum res)
+{
+       /* Do not update partial checksums if remote checksum is enabled. */
+       if (skb->remcsum_offload)
+               return;
+
+       SKB_GSO_CB(skb)->csum = res;
+       SKB_GSO_CB(skb)->csum_start = skb_checksum_start(skb) - skb->head;
+}
+
 /* Compute the checksum for a gso segment. First compute the checksum value
  * from the start of transport header to SKB_GSO_CB(skb)->csum_start, and
  * then add in skb->csum (checksum from csum_start to end of packet).
 
                th->fin = th->psh = 0;
                th->check = newcheck;
 
-               if (skb->ip_summed != CHECKSUM_PARTIAL)
+               if (skb->ip_summed == CHECKSUM_PARTIAL)
+                       gso_reset_checksum(skb, ~th->check);
+               else
                        th->check = gso_make_checksum(skb, ~th->check);
 
                seq += mss;
                      skb->data_len);
        th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
                                (__force u32)delta));
-       if (skb->ip_summed != CHECKSUM_PARTIAL)
+       if (skb->ip_summed == CHECKSUM_PARTIAL)
+               gso_reset_checksum(skb, ~th->check);
+       else
                th->check = gso_make_checksum(skb, ~th->check);
 out:
        return segs;