bool zero_okay,
                                                      __sum16 check)
 {
-       return (skb->ip_summed != CHECKSUM_PARTIAL &&
+       return ((skb->ip_summed != CHECKSUM_PARTIAL ||
+               skb_checksum_start_offset(skb) <
+                skb_gro_offset(skb)) &&
                NAPI_GRO_CB(skb)->csum_cnt == 0 &&
                (!zero_okay || check));
 }
 
  *
  * CHECKSUM_PARTIAL:
  *
- *   This is identical to the case for output below. This may occur on a packet
+ *   A checksum is set up to be offloaded to a device as described in the
+ *   output description for CHECKSUM_PARTIAL. This may occur on a packet
  *   received directly from another Linux OS, e.g., a virtualized Linux kernel
- *   on the same host. The packet can be treated in the same way as
- *   CHECKSUM_UNNECESSARY, except that on output (i.e., forwarding) the
- *   checksum must be filled in by the OS or the hardware.
+ *   on the same host, or it may be set in the input path in GRO or remote
+ *   checksum offload. For the purposes of checksum verification, the checksum
+ *   referred to by skb->csum_start + skb->csum_offset and any preceding
+ *   checksums in the packet are considered verified. Any checksums in the
+ *   packet that are after the checksum being offloaded are not considered to
+ *   be verified.
  *
  * B. Checksumming on output.
  *
 
 static inline int skb_csum_unnecessary(const struct sk_buff *skb)
 {
-       return ((skb->ip_summed & CHECKSUM_UNNECESSARY) || skb->csum_valid);
+       return ((skb->ip_summed == CHECKSUM_UNNECESSARY) ||
+               skb->csum_valid ||
+               (skb->ip_summed == CHECKSUM_PARTIAL &&
+                skb_checksum_start_offset(skb) >= 0));
 }
 
 /**