]> git.karo-electronics.de Git - linux-beck.git/blobdiff - net/ipv4/tcp_output.c
tcp: make connect() mem charging friendly
[linux-beck.git] / net / ipv4 / tcp_output.c
index 3af21296d96788b899daaa25562301e38036e802..f5bd4bd3f7e669b3fd48a843d55e7313a30a3409 100644 (file)
@@ -333,10 +333,19 @@ static void tcp_ecn_send_synack(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);
+       bool use_ecn = sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
+                      tcp_ca_needs_ecn(sk);
+
+       if (!use_ecn) {
+               const struct dst_entry *dst = __sk_dst_get(sk);
+
+               if (dst && dst_feature(dst, RTAX_FEATURE_ECN))
+                       use_ecn = true;
+       }
 
        tp->ecn_flags = 0;
-       if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
-           tcp_ca_needs_ecn(sk)) {
+
+       if (use_ecn) {
                TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR;
                tp->ecn_flags = TCP_ECN_OK;
                if (tcp_ca_needs_ecn(sk))
@@ -1553,7 +1562,7 @@ static unsigned int tcp_mss_split_point(const struct sock *sk,
 static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp,
                                         const struct sk_buff *skb)
 {
-       u32 in_flight, cwnd;
+       u32 in_flight, cwnd, halfcwnd;
 
        /* Don't be strict about the congestion window for the final FIN.  */
        if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) &&
@@ -1562,10 +1571,14 @@ static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp,
 
        in_flight = tcp_packets_in_flight(tp);
        cwnd = tp->snd_cwnd;
-       if (in_flight < cwnd)
-               return (cwnd - in_flight);
+       if (in_flight >= cwnd)
+               return 0;
 
-       return 0;
+       /* For better scheduling, ensure we have at least
+        * 2 GSO packets in flight.
+        */
+       halfcwnd = max(cwnd >> 1, 1U);
+       return min(halfcwnd, cwnd - in_flight);
 }
 
 /* Initialize TSO state of a skb.
@@ -2126,7 +2139,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)
 {
-       if (unlikely(skb_fclone_busy(skb))) {
+       if (unlikely(skb_fclone_busy(sk, skb))) {
                NET_INC_STATS_BH(sock_net(sk),
                                 LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES);
                return true;
@@ -2998,9 +3011,9 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        struct tcp_fastopen_request *fo = tp->fastopen_req;
-       int syn_loss = 0, space, i, err = 0, iovlen = fo->data->msg_iovlen;
-       struct sk_buff *syn_data = NULL, *data;
+       int syn_loss = 0, space, err = 0;
        unsigned long last_syn_loss = 0;
+       struct sk_buff *syn_data;
 
        tp->rx_opt.mss_clamp = tp->advmss;  /* If MSS is not cached */
        tcp_fastopen_cache_get(sk, &tp->rx_opt.mss_clamp, &fo->cookie,
@@ -3031,48 +3044,40 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
        /* limit to order-0 allocations */
        space = min_t(size_t, space, SKB_MAX_HEAD(MAX_TCP_HEADER));
 
-       syn_data = skb_copy_expand(syn, MAX_TCP_HEADER, space,
-                                  sk->sk_allocation);
-       if (syn_data == NULL)
+       syn_data = sk_stream_alloc_skb(sk, space, sk->sk_allocation);
+       if (!syn_data)
+               goto fallback;
+       syn_data->ip_summed = CHECKSUM_PARTIAL;
+       memcpy(syn_data->cb, syn->cb, sizeof(syn->cb));
+       if (unlikely(memcpy_fromiovecend(skb_put(syn_data, space),
+                                        fo->data->msg_iov, 0, space))) {
+               kfree_skb(syn_data);
                goto fallback;
+       }
 
-       for (i = 0; i < iovlen && syn_data->len < space; ++i) {
-               struct iovec *iov = &fo->data->msg_iov[i];
-               unsigned char __user *from = iov->iov_base;
-               int len = iov->iov_len;
+       /* No more data pending in inet_wait_for_connect() */
+       if (space == fo->size)
+               fo->data = NULL;
+       fo->copied = space;
 
-               if (syn_data->len + len > space)
-                       len = space - syn_data->len;
-               else if (i + 1 == iovlen)
-                       /* No more data pending in inet_wait_for_connect() */
-                       fo->data = NULL;
+       tcp_connect_queue_skb(sk, syn_data);
 
-               if (skb_add_data(syn_data, from, len))
-                       goto fallback;
-       }
+       err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation);
 
-       /* Queue a data-only packet after the regular SYN for retransmission */
-       data = pskb_copy(syn_data, sk->sk_allocation);
-       if (data == NULL)
-               goto fallback;
-       TCP_SKB_CB(data)->seq++;
-       TCP_SKB_CB(data)->tcp_flags &= ~TCPHDR_SYN;
-       TCP_SKB_CB(data)->tcp_flags = (TCPHDR_ACK|TCPHDR_PSH);
-       tcp_connect_queue_skb(sk, data);
-       fo->copied = data->len;
-
-       /* syn_data is about to be sent, we need to take current time stamps
-        * for the packets that are in write queue : SYN packet and DATA
-        */
-       skb_mstamp_get(&syn->skb_mstamp);
-       data->skb_mstamp = syn->skb_mstamp;
+       syn->skb_mstamp = syn_data->skb_mstamp;
 
-       if (tcp_transmit_skb(sk, syn_data, 0, sk->sk_allocation) == 0) {
+       /* Now full SYN+DATA was cloned and sent (or not),
+        * remove the SYN from the original skb (syn_data)
+        * we keep in write queue in case of a retransmit, as we
+        * also have the SYN packet (with no data) in the same queue.
+        */
+       TCP_SKB_CB(syn_data)->seq++;
+       TCP_SKB_CB(syn_data)->tcp_flags = TCPHDR_ACK | TCPHDR_PSH;
+       if (!err) {
                tp->syn_data = (fo->copied > 0);
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT);
                goto done;
        }
-       syn_data = NULL;
 
 fallback:
        /* Send a regular SYN with Fast Open cookie request option */
@@ -3081,7 +3086,6 @@ fallback:
        err = tcp_transmit_skb(sk, syn, 1, sk->sk_allocation);
        if (err)
                tp->syn_fastopen = 0;
-       kfree_skb(syn_data);
 done:
        fo->cookie.len = -1;  /* Exclude Fast Open option for SYN retries */
        return err;
@@ -3101,13 +3105,10 @@ int tcp_connect(struct sock *sk)
                return 0;
        }
 
-       buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
-       if (unlikely(buff == NULL))
+       buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
+       if (unlikely(!buff))
                return -ENOBUFS;
 
-       /* Reserve space for headers. */
-       skb_reserve(buff, MAX_TCP_HEADER);
-
        tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
        tp->retrans_stamp = tcp_time_stamp;
        tcp_connect_queue_skb(sk, buff);