diff options
Diffstat (limited to 'net/ipv4/tcp.c')
| -rw-r--r-- | net/ipv4/tcp.c | 15 | 
1 files changed, 11 insertions, 4 deletions
| diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1ca253635f7a..2aa69c8ae60c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1428,12 +1428,12 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)  }  #endif -static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) +static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)  {  	struct sk_buff *skb;  	u32 offset; -	skb_queue_walk(&sk->sk_receive_queue, skb) { +	while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {  		offset = seq - TCP_SKB_CB(skb)->seq;  		if (tcp_hdr(skb)->syn)  			offset--; @@ -1441,6 +1441,11 @@ static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)  			*off = offset;  			return skb;  		} +		/* This looks weird, but this can happen if TCP collapsing +		 * splitted a fat GRO packet, while we released socket lock +		 * in skb_splice_bits() +		 */ +		sk_eat_skb(sk, skb, false);  	}  	return NULL;  } @@ -1482,7 +1487,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,  					break;  			}  			used = recv_actor(desc, skb, offset, len); -			if (used < 0) { +			if (used <= 0) {  				if (!copied)  					copied = used;  				break; @@ -1520,8 +1525,10 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,  	tcp_rcv_space_adjust(sk);  	/* Clean up data we have read: This will do ACK frames. */ -	if (copied > 0) +	if (copied > 0) { +		tcp_recv_skb(sk, seq, &offset);  		tcp_cleanup_rbuf(sk, copied); +	}  	return copied;  }  EXPORT_SYMBOL(tcp_read_sock); |