]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/ipv4/tcp.c
Merge tag 'iio-fixes-for-5.4a' of https://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / net / ipv4 / tcp.c
index 77b485d60b9d0e00edc4e2f0d6c5bb3a9460b23b..79c325a07ba5dc7cfad0a846d1f03bf1787f840b 100644 (file)
@@ -935,6 +935,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)
 {
@@ -1064,6 +1080,7 @@ ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
        return copied;
 
 do_error:
+       tcp_remove_empty_skb(sk, tcp_write_queue_tail(sk));
        if (copied)
                goto out;
 out_err:
@@ -1165,7 +1182,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
        struct sockcm_cookie sockc;
        int flags, err, copied = 0;
        int mss_now = 0, size_goal, copied_syn = 0;
-       bool process_backlog = false;
+       int process_backlog = 0;
        bool zc = false;
        long timeo;
 
@@ -1257,9 +1274,10 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
                        if (!sk_stream_memory_free(sk))
                                goto wait_for_sndbuf;
 
-                       if (process_backlog && sk_flush_backlog(sk)) {
-                               process_backlog = false;
-                               goto restart;
+                       if (unlikely(process_backlog >= 16)) {
+                               process_backlog = 0;
+                               if (sk_flush_backlog(sk))
+                                       goto restart;
                        }
                        first_skb = tcp_rtx_and_write_queues_empty(sk);
                        skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation,
@@ -1267,7 +1285,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
                        if (!skb)
                                goto wait_for_memory;
 
-                       process_backlog = true;
+                       process_backlog++;
                        skb->ip_summed = CHECKSUM_PARTIAL;
 
                        skb_entail(sk, skb);
@@ -1388,18 +1406,11 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
        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.
-                */
-               if (tcp_write_queue_empty(sk))
-                       tcp_chrono_stop(sk, TCP_CHRONO_BUSY);
-               sk_wmem_free_skb(sk, skb);
-       }
+       tcp_remove_empty_skb(sk, skb);
 
-do_error:
        if (copied + copied_syn)
                goto out;
 out_err:
@@ -1779,19 +1790,21 @@ static int tcp_zerocopy_receive(struct sock *sk,
                                break;
                        frags = skb_shinfo(skb)->frags;
                        while (offset) {
-                               if (frags->size > offset)
+                               if (skb_frag_size(frags) > offset)
                                        goto out;
-                               offset -= frags->size;
+                               offset -= skb_frag_size(frags);
                                frags++;
                        }
                }
-               if (frags->size != PAGE_SIZE || frags->page_offset) {
+               if (skb_frag_size(frags) != PAGE_SIZE || skb_frag_off(frags)) {
                        int remaining = zc->recv_skip_hint;
+                       int size = skb_frag_size(frags);
 
-                       while (remaining && (frags->size != PAGE_SIZE ||
-                                            frags->page_offset)) {
-                               remaining -= frags->size;
+                       while (remaining && (size != PAGE_SIZE ||
+                                            skb_frag_off(frags))) {
+                               remaining -= size;
                                frags++;
+                               size = skb_frag_size(frags);
                        }
                        zc->recv_skip_hint -= remaining;
                        break;
@@ -2640,6 +2653,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        tp->rx_opt.saw_tstamp = 0;
        tp->rx_opt.dsack = 0;
        tp->rx_opt.num_sacks = 0;
+       tp->rcv_ooopack = 0;
 
 
        /* Clean up fastopen related fields */
@@ -3282,6 +3296,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)
        info->tcpi_bytes_retrans = tp->bytes_retrans;
        info->tcpi_dsack_dups = tp->dsack_dups;
        info->tcpi_reord_seen = tp->reord_seen;
+       info->tcpi_rcv_ooopack = tp->rcv_ooopack;
+       info->tcpi_snd_wnd = tp->snd_wnd;
        unlock_sock_fast(sk, slow);
 }
 EXPORT_SYMBOL_GPL(tcp_get_info);
@@ -3784,8 +3800,8 @@ int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *hp,
                return 1;
 
        for (i = 0; i < shi->nr_frags; ++i) {
-               const struct skb_frag_struct *f = &shi->frags[i];
-               unsigned int offset = f->page_offset;
+               const skb_frag_t *f = &shi->frags[i];
+               unsigned int offset = skb_frag_off(f);
                struct page *page = skb_frag_page(f) + (offset >> PAGE_SHIFT);
 
                sg_set_page(&sg, page, skb_frag_size(f),