tcp: TCP Small Queues and strange attractors
[pandora-kernel.git] / net / ipv4 / tcp_output.c
index a462fb1..e13d778 100644 (file)
@@ -318,36 +318,47 @@ static u16 tcp_select_window(struct sock *sk)
 }
 
 /* Packet ECN state for a SYN-ACK */
-static inline void TCP_ECN_send_synack(const struct tcp_sock *tp, struct sk_buff *skb)
+static void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb)
 {
+       const struct tcp_sock *tp = tcp_sk(sk);
+
        TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_CWR;
        if (!(tp->ecn_flags & TCP_ECN_OK))
                TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_ECE;
+       else if (tcp_ca_needs_ecn(sk))
+               INET_ECN_xmit(sk);
 }
 
 /* Packet ECN state for a SYN.  */
-static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb)
+static void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)
 {
        struct tcp_sock *tp = tcp_sk(sk);
 
        tp->ecn_flags = 0;
-       if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1) {
+       if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
+           tcp_ca_needs_ecn(sk)) {
                TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR;
                tp->ecn_flags = TCP_ECN_OK;
+               if (tcp_ca_needs_ecn(sk))
+                       INET_ECN_xmit(sk);
        }
 }
 
-static __inline__ void
-TCP_ECN_make_synack(const struct request_sock *req, struct tcphdr *th)
+static void
+tcp_ecn_make_synack(const struct request_sock *req, struct tcphdr *th,
+                   struct sock *sk)
 {
-       if (inet_rsk(req)->ecn_ok)
+       if (inet_rsk(req)->ecn_ok) {
                th->ece = 1;
+               if (tcp_ca_needs_ecn(sk))
+                       INET_ECN_xmit(sk);
+       }
 }
 
 /* Set up ECN state for a packet on a ESTABLISHED socket that is about to
  * be sent.
  */
-static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
+static void tcp_ecn_send(struct sock *sk, struct sk_buff *skb,
                                int tcp_header_len)
 {
        struct tcp_sock *tp = tcp_sk(sk);
@@ -362,7 +373,7 @@ static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
                                tcp_hdr(skb)->cwr = 1;
                                skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
                        }
-               } else {
+               } else if (!tcp_ca_needs_ecn(sk)) {
                        /* ACK or retransmitted segment: clear ECT|CE */
                        INET_ECN_dontxmit(sk);
                }
@@ -384,7 +395,7 @@ static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)
        TCP_SKB_CB(skb)->tcp_flags = flags;
        TCP_SKB_CB(skb)->sacked = 0;
 
-       shinfo->gso_segs = 1;
+       tcp_skb_pcount_set(skb, 1);
        shinfo->gso_size = 0;
        shinfo->gso_type = 0;
 
@@ -828,26 +839,38 @@ void tcp_wfree(struct sk_buff *skb)
 {
        struct sock *sk = skb->sk;
        struct tcp_sock *tp = tcp_sk(sk);
+       int wmem;
+
+       /* Keep one reference on sk_wmem_alloc.
+        * Will be released by sk_free() from here or tcp_tasklet_func()
+        */
+       wmem = atomic_sub_return(skb->truesize - 1, &sk->sk_wmem_alloc);
+
+       /* If this softirq is serviced by ksoftirqd, we are likely under stress.
+        * Wait until our queues (qdisc + devices) are drained.
+        * This gives :
+        * - less callbacks to tcp_write_xmit(), reducing stress (batches)
+        * - chance for incoming ACK (processed by another cpu maybe)
+        *   to migrate this flow (skb->ooo_okay will be eventually set)
+        */
+       if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current)
+               goto out;
 
        if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) &&
            !test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) {
                unsigned long flags;
                struct tsq_tasklet *tsq;
 
-               /* Keep a ref on socket.
-                * This last ref will be released in tcp_tasklet_func()
-                */
-               atomic_sub(skb->truesize - 1, &sk->sk_wmem_alloc);
-
                /* queue this socket to tasklet queue */
                local_irq_save(flags);
                tsq = &__get_cpu_var(tsq_tasklet);
                list_add(&tp->tsq_node, &tsq->head);
                tasklet_schedule(&tsq->tasklet);
                local_irq_restore(flags);
-       } else {
-               sock_wfree(skb);
+               return;
        }
+out:
+       sk_free(sk);
 }
 
 /* This routine actually transmits TCP packets queued in by
@@ -903,9 +926,13 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
                tcp_ca_event(sk, CA_EVENT_TX_START);
 
        /* if no packet is in qdisc/device queue, then allow XPS to select
-        * another queue.
+        * another queue. We can be called from tcp_tsq_handler()
+        * which holds one reference to sk_wmem_alloc.
+        *
+        * TODO: Ideally, in-flight pure ACK packets should not matter here.
+        * One way to get this would be to set skb->truesize = 2 on them.
         */
-       skb->ooo_okay = sk_wmem_alloc_get(sk) == 0;
+       skb->ooo_okay = sk_wmem_alloc_get(sk) < SKB_TRUESIZE(1);
 
        skb_push(skb, tcp_header_size);
        skb_reset_transport_header(skb);
@@ -949,7 +976,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
 
        tcp_options_write((__be32 *)(th + 1), tp, &opts);
        if (likely((tcb->tcp_flags & TCPHDR_SYN) == 0))
-               TCP_ECN_send(sk, skb, tcp_header_size);
+               tcp_ecn_send(sk, skb, tcp_header_size);
 
 #ifdef CONFIG_TCP_MD5SIG
        /* Calculate the MD5 hash, as we have all we need now */
@@ -972,6 +999,9 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
                TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS,
                              tcp_skb_pcount(skb));
 
+       /* OK, its time to fill skb_shinfo(skb)->gso_segs */
+       skb_shinfo(skb)->gso_segs = tcp_skb_pcount(skb);
+
        /* Our usage of tstamp should remain private */
        skb->tstamp.tv64 = 0;
 
@@ -1019,11 +1049,11 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
                /* Avoid the costly divide in the normal
                 * non-TSO case.
                 */
-               shinfo->gso_segs = 1;
+               tcp_skb_pcount_set(skb, 1);
                shinfo->gso_size = 0;
                shinfo->gso_type = 0;
        } else {
-               shinfo->gso_segs = DIV_ROUND_UP(skb->len, mss_now);
+               tcp_skb_pcount_set(skb, DIV_ROUND_UP(skb->len, mss_now));
                shinfo->gso_size = mss_now;
                shinfo->gso_type = sk->sk_gso_type;
        }
@@ -2096,10 +2126,7 @@ bool tcp_schedule_loss_probe(struct sock *sk)
 static bool skb_still_in_host_queue(const struct sock *sk,
                                    const struct sk_buff *skb)
 {
-       const struct sk_buff *fclone = skb + 1;
-
-       if (unlikely(skb->fclone == SKB_FCLONE_ORIG &&
-                    fclone->fclone == SKB_FCLONE_CLONE)) {
+       if (unlikely(skb_fclone_busy(skb))) {
                NET_INC_STATS_BH(sock_net(sk),
                                 LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
                return true;
@@ -2786,7 +2813,7 @@ int tcp_send_synack(struct sock *sk)
                }
 
                TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ACK;
-               TCP_ECN_send_synack(tcp_sk(sk), skb);
+               tcp_ecn_send_synack(sk, skb);
        }
        return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
 }
@@ -2845,7 +2872,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
        memset(th, 0, sizeof(struct tcphdr));
        th->syn = 1;
        th->ack = 1;
-       TCP_ECN_make_synack(req, th);
+       tcp_ecn_make_synack(req, th, sk);
        th->source = htons(ireq->ir_num);
        th->dest = ireq->ir_rmt_port;
        /* Setting of flags are superfluous here for callers (and ECE is
@@ -3084,7 +3111,7 @@ int tcp_connect(struct sock *sk)
        tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
        tp->retrans_stamp = tcp_time_stamp;
        tcp_connect_queue_skb(sk, buff);
-       TCP_ECN_send_syn(sk, buff);
+       tcp_ecn_send_syn(sk, buff);
 
        /* Send off SYN; include data in Fast Open. */
        err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) :
@@ -3116,6 +3143,8 @@ void tcp_send_delayed_ack(struct sock *sk)
        int ato = icsk->icsk_ack.ato;
        unsigned long timeout;
 
+       tcp_ca_event(sk, CA_EVENT_DELAYED_ACK);
+
        if (ato > TCP_DELACK_MIN) {
                const struct tcp_sock *tp = tcp_sk(sk);
                int max_ato = HZ / 2;
@@ -3172,6 +3201,8 @@ void tcp_send_ack(struct sock *sk)
        if (sk->sk_state == TCP_CLOSE)
                return;
 
+       tcp_ca_event(sk, CA_EVENT_NON_DELAYED_ACK);
+
        /* We are not putting this on the write queue, so
         * tcp_transmit_skb() will set the ownership to this
         * sock.
@@ -3193,6 +3224,7 @@ void tcp_send_ack(struct sock *sk)
        skb_mstamp_get(&buff->skb_mstamp);
        tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
 }
+EXPORT_SYMBOL_GPL(tcp_send_ack);
 
 /* This routine sends a packet with an out of date sequence
  * number. It assumes the other end will try to ack it.