]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - net/ipv4/tcp.c
tcp: gso: do not generate out of order packets
[mirror_ubuntu-jammy-kernel.git] / net / ipv4 / tcp.c
index 10c93930abda80de2d49822fa9b93de06e9414a5..ab450c099aa49a3d4b68ca531e7f5426a8eabaf1 100644 (file)
@@ -2887,6 +2887,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        unsigned int mss;
        struct sk_buff *gso_skb = skb;
        __sum16 newcheck;
+       bool ooo_okay, copy_destructor;
 
        if (!pskb_may_pull(skb, sizeof(*th)))
                goto out;
@@ -2927,10 +2928,18 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                goto out;
        }
 
+       copy_destructor = gso_skb->destructor == tcp_wfree;
+       ooo_okay = gso_skb->ooo_okay;
+       /* All segments but the first should have ooo_okay cleared */
+       skb->ooo_okay = 0;
+
        segs = skb_segment(skb, features);
        if (IS_ERR(segs))
                goto out;
 
+       /* Only first segment might have ooo_okay set */
+       segs->ooo_okay = ooo_okay;
+
        delta = htonl(oldlen + (thlen + mss));
 
        skb = segs;
@@ -2950,6 +2959,17 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                                                    thlen, skb->csum));
 
                seq += mss;
+               if (copy_destructor) {
+                       skb->destructor = gso_skb->destructor;
+                       skb->sk = gso_skb->sk;
+                       /* {tcp|sock}_wfree() use exact truesize accounting :
+                        * sum(skb->truesize) MUST be exactly be gso_skb->truesize
+                        * So we account mss bytes of 'true size' for each segment.
+                        * The last segment will contain the remaining.
+                        */
+                       skb->truesize = mss;
+                       gso_skb->truesize -= mss;
+               }
                skb = skb->next;
                th = tcp_hdr(skb);
 
@@ -2962,7 +2982,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
         * is freed at TX completion, and not right now when gso_skb
         * is freed by GSO engine
         */
-       if (gso_skb->destructor == tcp_wfree) {
+       if (copy_destructor) {
                swap(gso_skb->sk, skb->sk);
                swap(gso_skb->destructor, skb->destructor);
                swap(gso_skb->truesize, skb->truesize);