]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
tcp: avoid order-1 allocations on wifi and tx path
authorEric Dumazet <eric.dumazet@gmail.com>
Wed, 25 Apr 2012 03:01:22 +0000 (23:01 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Apr 2012 17:17:06 +0000 (10:17 -0700)
[ This combines upstream commit
  a21d45726acacc963d8baddf74607d9b74e2b723 and the follow-on bug fix
  commit a21d45726acacc963d8baddf74607d9b74e2b723 ]

Marc Merlin reported many order-1 allocations failures in TX path on its
wireless setup, that dont make any sense with MTU=1500 network, and non
SG capable hardware.

After investigation, it turns out TCP uses sk_stream_alloc_skb() and
used as a convention skb_tailroom(skb) to know how many bytes of data
payload could be put in this skb (for non SG capable devices)

Note : these skb used kmalloc-4096 (MTU=1500 + MAX_HEADER +
sizeof(struct skb_shared_info) being above 2048)

Later, mac80211 layer need to add some bytes at the tail of skb
(IEEE80211_ENCRYPT_TAILROOM = 18 bytes) and since no more tailroom is
available has to call pskb_expand_head() and request order-1
allocations.

This patch changes sk_stream_alloc_skb() so that only
sk->sk_prot->max_header bytes of headroom are reserved, and use a new
skb field, avail_size to hold the data payload limit.

This way, order-0 allocations done by TCP stack can leave more than 2 KB
of tailroom and no more allocation is performed in mac80211 layer (or
any layer needing some tailroom)

avail_size is unioned with mark/dropcount, since mark will be set later
in IP stack for output packets. Therefore, skb size is unchanged.

Reported-by: Marc MERLIN <marc@merlins.org>
Tested-by: Marc MERLIN <marc@merlins.org>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/skbuff.h
net/ipv4/tcp.c
net/ipv4/tcp_output.c

index ae86adee3746aec6661ef35e2b18cf62e0d17780..42854cee6fc19856145e0910db4fa8fb770b8d93 100644 (file)
@@ -468,6 +468,7 @@ struct sk_buff {
        union {
                __u32           mark;
                __u32           dropcount;
+               __u32           avail_size;
        };
 
        __u16                   vlan_tci;
@@ -1337,6 +1338,18 @@ static inline int skb_tailroom(const struct sk_buff *skb)
        return skb_is_nonlinear(skb) ? 0 : skb->end - skb->tail;
 }
 
+/**
+ *     skb_availroom - bytes at buffer end
+ *     @skb: buffer to check
+ *
+ *     Return the number of bytes of free space at the tail of an sk_buff
+ *     allocated by sk_stream_alloc()
+ */
+static inline int skb_availroom(const struct sk_buff *skb)
+{
+       return skb_is_nonlinear(skb) ? 0 : skb->avail_size - skb->len;
+}
+
 /**
  *     skb_reserve - adjust headroom
  *     @skb: buffer to alter
index 86014bbd3e34d7b5f46110bdcfbd5568b68bd519..8c850211916131b663de25628b6fa51d49513516 100644 (file)
@@ -699,11 +699,12 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp)
        skb = alloc_skb_fclone(size + sk->sk_prot->max_header, gfp);
        if (skb) {
                if (sk_wmem_schedule(sk, skb->truesize)) {
+                       skb_reserve(skb, sk->sk_prot->max_header);
                        /*
                         * Make sure that we have exactly size bytes
                         * available to the caller, no more, no less.
                         */
-                       skb_reserve(skb, skb_tailroom(skb) - size);
+                       skb->avail_size = size;
                        return skb;
                }
                __kfree_skb(skb);
@@ -993,10 +994,9 @@ new_segment:
                                copy = seglen;
 
                        /* Where to copy to? */
-                       if (skb_tailroom(skb) > 0) {
+                       if (skb_availroom(skb) > 0) {
                                /* We have some space in skb head. Superb! */
-                               if (copy > skb_tailroom(skb))
-                                       copy = skb_tailroom(skb);
+                               copy = min_t(int, copy, skb_availroom(skb));
                                err = skb_add_data_nocache(sk, skb, from, copy);
                                if (err)
                                        goto do_fault;
index 4ff3b6dc74fc013b00720443587e2f40d2f1bbbf..e2c6722f7170e81a81dc9c0d1eb27963fadf2ac4 100644 (file)
@@ -1096,6 +1096,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)
        eat = min_t(int, len, skb_headlen(skb));
        if (eat) {
                __skb_pull(skb, eat);
+               skb->avail_size -= eat;
                len -= eat;
                if (!len)
                        return;
@@ -2060,7 +2061,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
                /* Punt if not enough space exists in the first SKB for
                 * the data in the second
                 */
-               if (skb->len > skb_tailroom(to))
+               if (skb->len > skb_availroom(to))
                        break;
 
                if (after(TCP_SKB_CB(skb)->end_seq, tcp_wnd_end(tp)))