summaryrefslogtreecommitdiff
path: root/net/ipv4/tcp_output.c
diff options
context:
space:
mode:
authorJakub Kicinski <jakub.kicinski@netronome.com>2019-12-14 20:57:36 +0300
committerJakub Kicinski <jakub.kicinski@netronome.com>2019-12-14 20:57:36 +0300
commitcd1263b6dcfdf0e938946441295d8fcfe1654d3a (patch)
tree99e290567b1943af5cf51b3cdfbf9f8f3752b6e8 /net/ipv4/tcp_output.c
parent5c9934b6767b16ba60be22ec3cbd4379ad64170d (diff)
parent216808c6ba6d00169fd2aa928ec3c0e63bef254f (diff)
downloadlinux-cd1263b6dcfdf0e938946441295d8fcfe1654d3a.tar.xz
Merge branch 'tcp-take-care-of-empty-skbs-in-write-queue'
Eric Dumazet says: ==================== tcp: take care of empty skbs in write queue We understood recently that TCP sockets could have an empty skb at the tail of the write queue, leading to various problems. This patch series : 1) Make sure we do not send an empty packet since this was unintended and causing crashes in old kernels. 2) Change tcp_write_queue_empty() to not be fooled by the presence of an empty skb. 3) Fix a bug that could trigger suboptimal epoll() application behavior under memory pressure. ==================== Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Diffstat (limited to 'net/ipv4/tcp_output.c')
-rw-r--r--net/ipv4/tcp_output.c13
1 files changed, 11 insertions, 2 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index b184f03d7437..36902d08473e 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2438,6 +2438,14 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
if (tcp_small_queue_check(sk, skb, 0))
break;
+ /* Argh, we hit an empty skb(), presumably a thread
+ * is sleeping in sendmsg()/sk_stream_wait_memory().
+ * We do not want to send a pure-ack packet and have
+ * a strange looking rtx queue with empty packet(s).
+ */
+ if (TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq)
+ break;
+
if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
break;
@@ -3121,7 +3129,7 @@ void sk_forced_mem_schedule(struct sock *sk, int size)
*/
void tcp_send_fin(struct sock *sk)
{
- struct sk_buff *skb, *tskb = tcp_write_queue_tail(sk);
+ struct sk_buff *skb, *tskb, *tail = tcp_write_queue_tail(sk);
struct tcp_sock *tp = tcp_sk(sk);
/* Optimization, tack on the FIN if we have one skb in write queue and
@@ -3129,6 +3137,7 @@ void tcp_send_fin(struct sock *sk)
* Note: in the latter case, FIN packet will be sent after a timeout,
* as TCP stack thinks it has already been transmitted.
*/
+ tskb = tail;
if (!tskb && tcp_under_memory_pressure(sk))
tskb = skb_rb_last(&sk->tcp_rtx_queue);
@@ -3136,7 +3145,7 @@ void tcp_send_fin(struct sock *sk)
TCP_SKB_CB(tskb)->tcp_flags |= TCPHDR_FIN;
TCP_SKB_CB(tskb)->end_seq++;
tp->write_seq++;
- if (tcp_write_queue_empty(sk)) {
+ if (!tail) {
/* This means tskb was already sent.
* Pretend we included the FIN on previous transmit.
* We need to set tp->snd_nxt to the value it would have