]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/ipv4/tcp.c
tcp: remove empty skb from write queue in error cases
[mirror_ubuntu-bionic-kernel.git] / net / ipv4 / tcp.c
index f08eebe60446e2e99d19bd0ea51e5fafb96aca63..d409bbd1248e221aaecae39e21e2b119bbfdb15b 100644 (file)
@@ -692,7 +692,7 @@ static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb,
 {
        return skb->len < size_goal &&
               sock_net(sk)->ipv4.sysctl_tcp_autocorking &&
-              skb != tcp_write_queue_head(sk) &&
+              !tcp_rtx_queue_empty(sk) &&
               refcount_read(&sk->sk_wmem_alloc) > skb->truesize;
 }
 
@@ -930,6 +930,22 @@ static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
        return mss_now;
 }
 
+/* In some cases, both sendpage() and sendmsg() could have added
+ * an skb to the write queue, but failed adding payload on it.
+ * We need to remove it to consume less memory, but more
+ * importantly be able to generate EPOLLOUT for Edge Trigger epoll()
+ * users.
+ */
+static void tcp_remove_empty_skb(struct sock *sk, struct sk_buff *skb)
+{
+       if (skb && !skb->len) {
+               tcp_unlink_write_queue(skb, sk);
+               if (tcp_write_queue_empty(sk))
+                       tcp_chrono_stop(sk, TCP_CHRONO_BUSY);
+               sk_wmem_free_skb(sk, skb);
+       }
+}
+
 ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
                         size_t size, int flags)
 {
@@ -1050,6 +1066,7 @@ out:
        return copied;
 
 do_error:
+       tcp_remove_empty_skb(sk, tcp_write_queue_tail(sk));
        if (copied)
                goto out;
 out_err:
@@ -1193,8 +1210,8 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
 
        flags = msg->msg_flags;
 
-       if (flags & MSG_ZEROCOPY && size) {
-               if (sk->sk_state != TCP_ESTABLISHED) {
+       if (flags & MSG_ZEROCOPY && size && sock_flag(sk, SOCK_ZEROCOPY)) {
+               if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
                        err = -EINVAL;
                        goto out_err;
                }
@@ -1210,7 +1227,8 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
                        uarg->zerocopy = 0;
        }
 
-       if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) {
+       if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect) &&
+           !tp->repair) {
                err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
                if (err == -EINPROGRESS && copied_syn > 0)
                        goto out;
@@ -1427,17 +1445,11 @@ out_nopush:
        sock_zerocopy_put(uarg);
        return copied + copied_syn;
 
+do_error:
+       skb = tcp_write_queue_tail(sk);
 do_fault:
-       if (!skb->len) {
-               tcp_unlink_write_queue(skb, sk);
-               /* It is the one place in all of TCP, except connection
-                * reset, where we can be unlinking the send_head.
-                */
-               tcp_check_send_head(sk, skb);
-               sk_wmem_free_skb(sk, skb);
-       }
+       tcp_remove_empty_skb(sk, skb);
 
-do_error:
        if (copied + copied_syn)
                goto out;
 out_err:
@@ -1859,7 +1871,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
                         * shouldn't happen.
                         */
                        if (WARN(before(*seq, TCP_SKB_CB(skb)->seq),
-                                "recvmsg bug: copied %X seq %X rcvnxt %X fl %X\n",
+                                "TCP recvmsg seq # bug: copied %X, seq %X, rcvnxt %X, fl %X\n",
                                 *seq, TCP_SKB_CB(skb)->seq, tp->rcv_nxt,
                                 flags))
                                break;
@@ -1874,7 +1886,7 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
                        if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
                                goto found_fin_ok;
                        WARN(!(flags & MSG_PEEK),
-                            "recvmsg bug 2: copied %X seq %X rcvnxt %X fl %X\n",
+                            "TCP recvmsg seq # bug 2: copied %X, seq %X, rcvnxt %X, fl %X\n",
                             *seq, TCP_SKB_CB(skb)->seq, tp->rcv_nxt, flags);
                }
 
@@ -2241,16 +2253,10 @@ adjudge_to_death:
        sock_hold(sk);
        sock_orphan(sk);
 
-       /* It is the last release_sock in its life. It will remove backlog. */
-       release_sock(sk);
-
-
-       /* Now socket is owned by kernel and we acquire BH lock
-        *  to finish close. No need to check for user refs.
-        */
        local_bh_disable();
        bh_lock_sock(sk);
-       WARN_ON(sock_owned_by_user(sk));
+       /* remove backlog if any, without releasing ownership. */
+       __release_sock(sk);
 
        percpu_counter_inc(sk->sk_prot->orphan_count);
 
@@ -2298,6 +2304,9 @@ adjudge_to_death:
                        tcp_send_active_reset(sk, GFP_ATOMIC);
                        __NET_INC_STATS(sock_net(sk),
                                        LINUX_MIB_TCPABORTONMEMORY);
+               } else if (!check_net(sock_net(sk))) {
+                       /* Not possible to send reset; just close */
+                       tcp_set_state(sk, TCP_CLOSE);
                }
        }
 
@@ -2316,6 +2325,7 @@ adjudge_to_death:
 out:
        bh_unlock_sock(sk);
        local_bh_enable();
+       release_sock(sk);
        sock_put(sk);
 }
 EXPORT_SYMBOL(tcp_close);
@@ -2358,6 +2368,8 @@ void tcp_write_queue_purge(struct sock *sk)
        INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue);
        sk_mem_reclaim(sk);
        tcp_clear_all_retrans_hints(tcp_sk(sk));
+       tcp_sk(sk)->packets_out = 0;
+       inet_csk(sk)->icsk_backoff = 0;
 }
 
 int tcp_disconnect(struct sock *sk, int flags)
@@ -2404,10 +2416,8 @@ int tcp_disconnect(struct sock *sk, int flags)
        tp->write_seq += tp->max_window + 2;
        if (tp->write_seq == 0)
                tp->write_seq = 1;
-       icsk->icsk_backoff = 0;
        tp->snd_cwnd = 2;
        icsk->icsk_probes_out = 0;
-       tp->packets_out = 0;
        tp->snd_ssthresh = TCP_INFINITE_SSTHRESH;
        tp->snd_cwnd_cnt = 0;
        tp->window_clamp = 0;
@@ -2424,6 +2434,8 @@ int tcp_disconnect(struct sock *sk, int flags)
        dst_release(sk->sk_rx_dst);
        sk->sk_rx_dst = NULL;
        tcp_saved_syn_free(tp);
+       tp->bytes_acked = 0;
+       tp->bytes_received = 0;
 
        /* Clean up fastopen related fields */
        tcp_free_fastopen_req(tp);
@@ -2431,6 +2443,12 @@ int tcp_disconnect(struct sock *sk, int flags)
 
        WARN_ON(inet->inet_num && !icsk->icsk_bind_hash);
 
+       if (sk->sk_frag.page) {
+               put_page(sk->sk_frag.page);
+               sk->sk_frag.page = NULL;
+               sk->sk_frag.offset = 0;
+       }
+
        sk->sk_error_report(sk);
        return err;
 }
@@ -2550,7 +2568,9 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
                name[val] = 0;
 
                lock_sock(sk);
-               err = tcp_set_congestion_control(sk, name, true, true);
+               err = tcp_set_congestion_control(sk, name, true, true,
+                                                ns_capable(sock_net(sk)->user_ns,
+                                                           CAP_NET_ADMIN));
                release_sock(sk);
                return err;
        }
@@ -2657,7 +2677,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
        case TCP_REPAIR_QUEUE:
                if (!tp->repair)
                        err = -EPERM;
-               else if (val < TCP_QUEUES_NR)
+               else if ((unsigned int)val < TCP_QUEUES_NR)
                        tp->repair_queue = val;
                else
                        err = -EINVAL;
@@ -2797,8 +2817,10 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
 #ifdef CONFIG_TCP_MD5SIG
        case TCP_MD5SIG:
        case TCP_MD5SIG_EXT:
-               /* Read the IP->Key mappings from userspace */
-               err = tp->af_specific->md5_parse(sk, optname, optval, optlen);
+               if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))
+                       err = tp->af_specific->md5_parse(sk, optname, optval, optlen);
+               else
+                       err = -EINVAL;
                break;
 #endif
        case TCP_USER_TIMEOUT:
@@ -3501,8 +3523,7 @@ int tcp_abort(struct sock *sk, int err)
                        struct request_sock *req = inet_reqsk(sk);
 
                        local_bh_disable();
-                       inet_csk_reqsk_queue_drop_and_put(req->rsk_listener,
-                                                         req);
+                       inet_csk_reqsk_queue_drop(req->rsk_listener, req);
                        local_bh_enable();
                        return 0;
                }
@@ -3533,6 +3554,7 @@ int tcp_abort(struct sock *sk, int err)
 
        bh_unlock_sock(sk);
        local_bh_enable();
+       tcp_write_queue_purge(sk);
        release_sock(sk);
        return 0;
 }
@@ -3572,6 +3594,7 @@ void __init tcp_init(void)
        unsigned long limit;
        unsigned int i;
 
+       BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE);
        BUILD_BUG_ON(sizeof(struct tcp_skb_cb) >
                     FIELD_SIZEOF(struct sk_buff, cb));