tcp: GSO should be TSQ friendly
authorEric Dumazet <edumazet@google.com>
Fri, 12 Apr 2013 11:31:52 +0000 (11:31 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 12 Apr 2013 22:17:06 +0000 (18:17 -0400)
I noticed that TSQ (TCP Small queues) was less effective when TSO is
turned off, and GSO is on. If BQL is not enabled, TSQ has then no
effect.

It turns out the GSO engine frees the original gso_skb at the time the
fragments are generated and queued to the NIC.

We should instead call the tcp_wfree() destructor for the last fragment,
to keep the flow control as intended in TSQ. This effectively limits
the number of queued packets on qdisc + NIC layers.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Tom Herbert <therbert@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Nandita Dukkipati <nanditad@google.com>
Cc: Neal Cardwell <ncardwell@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/tcp.h
net/ipv4/tcp.c
net/ipv4/tcp_output.c

index 4475aaf0af577cd5c778f65c4348380c6219742e..5bba80fbd1d9d92738d115cbc870bf0612cb0305 100644 (file)
@@ -370,6 +370,7 @@ extern int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 extern int tcp_sendpage(struct sock *sk, struct page *page, int offset,
                        size_t size, int flags);
 extern void tcp_release_cb(struct sock *sk);
+extern void tcp_wfree(struct sk_buff *skb);
 extern void tcp_write_timer_handler(struct sock *sk);
 extern void tcp_delack_timer_handler(struct sock *sk);
 extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
index a96f7b586277ee4a2eb7e903874a62ca83701606..963bda18486f956a738abd33dfcbb4380eef0363 100644 (file)
@@ -2885,6 +2885,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
        __be32 delta;
        unsigned int oldlen;
        unsigned int mss;
+       struct sk_buff *gso_skb = skb;
 
        if (!pskb_may_pull(skb, sizeof(*th)))
                goto out;
@@ -2953,6 +2954,17 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
                th->cwr = 0;
        } while (skb->next);
 
+       /* Following permits TCP Small Queues to work well with GSO :
+        * The callback to TCP stack will be called at the time last frag
+        * is freed at TX completion, and not right now when gso_skb
+        * is freed by GSO engine
+        */
+       if (gso_skb->destructor == tcp_wfree) {
+               swap(gso_skb->sk, skb->sk);
+               swap(gso_skb->destructor, skb->destructor);
+               swap(gso_skb->truesize, skb->truesize);
+       }
+
        delta = htonl(oldlen + (skb->tail - skb->transport_header) +
                      skb->data_len);
        th->check = ~csum_fold((__force __wsum)((__force u32)th->check +
index af354c98fdb56609a351b32c35422b57c9639f4e..d12694353540547f63a207f8176f1e8d6fb485c2 100644 (file)
@@ -787,7 +787,7 @@ void __init tcp_tasklet_init(void)
  * We cant xmit new skbs from this context, as we might already
  * hold qdisc lock.
  */
-static void tcp_wfree(struct sk_buff *skb)
+void tcp_wfree(struct sk_buff *skb)
 {
        struct sock *sk = skb->sk;
        struct tcp_sock *tp = tcp_sk(sk);