diff options
Diffstat (limited to 'net/ipv4/tcp.c')
| -rw-r--r-- | net/ipv4/tcp.c | 78 | 
1 files changed, 62 insertions, 16 deletions
| diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0cfa7c0c1e80..7bb1b091efd1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -451,11 +451,14 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)  	unsigned int mask;  	struct sock *sk = sock->sk;  	const struct tcp_sock *tp = tcp_sk(sk); +	int state;  	sock_rps_record_flow(sk);  	sock_poll_wait(file, sk_sleep(sk), wait); -	if (sk->sk_state == TCP_LISTEN) + +	state = sk_state_load(sk); +	if (state == TCP_LISTEN)  		return inet_csk_listen_poll(sk);  	/* Socket is not locked. We are protected from async events @@ -492,14 +495,14 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)  	 * NOTE. Check for TCP_CLOSE is added. The goal is to prevent  	 * blocking on fresh not-connected or disconnected socket. --ANK  	 */ -	if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE) +	if (sk->sk_shutdown == SHUTDOWN_MASK || state == TCP_CLOSE)  		mask |= POLLHUP;  	if (sk->sk_shutdown & RCV_SHUTDOWN)  		mask |= POLLIN | POLLRDNORM | POLLRDHUP;  	/* Connected or passive Fast Open socket? */ -	if (sk->sk_state != TCP_SYN_SENT && -	    (sk->sk_state != TCP_SYN_RECV || tp->fastopen_rsk)) { +	if (state != TCP_SYN_SENT && +	    (state != TCP_SYN_RECV || tp->fastopen_rsk)) {  		int target = sock_rcvlowat(sk, 0, INT_MAX);  		if (tp->urg_seq == tp->copied_seq && @@ -507,9 +510,6 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)  		    tp->urg_data)  			target++; -		/* Potential race condition. If read of tp below will -		 * escape above sk->sk_state, we can be illegally awaken -		 * in SYN_* states. */  		if (tp->rcv_nxt - tp->copied_seq >= target)  			mask |= POLLIN | POLLRDNORM; @@ -517,8 +517,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)  			if (sk_stream_is_writeable(sk)) {  				mask |= POLLOUT | POLLWRNORM;  			} else {  /* send SIGIO later */ -				set_bit(SOCK_ASYNC_NOSPACE, -					&sk->sk_socket->flags); +				sk_set_bit(SOCKWQ_ASYNC_NOSPACE, sk);  				set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);  				/* Race breaker. If space is freed after @@ -906,7 +905,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,  			goto out_err;  	} -	clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); +	sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);  	mss_now = tcp_send_mss(sk, &size_goal, flags);  	copied = 0; @@ -1019,7 +1018,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset,  	ssize_t res;  	if (!(sk->sk_route_caps & NETIF_F_SG) || -	    !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) +	    !sk_check_csum_caps(sk))  		return sock_no_sendpage(sk->sk_socket, page, offset, size,  					flags); @@ -1134,7 +1133,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)  	}  	/* This should be in poll */ -	clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); +	sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);  	mss_now = tcp_send_mss(sk, &size_goal, flags); @@ -1176,7 +1175,7 @@ new_segment:  			/*  			 * Check whether we can use HW checksum.  			 */ -			if (sk->sk_route_caps & NETIF_F_ALL_CSUM) +			if (sk_check_csum_caps(sk))  				skb->ip_summed = CHECKSUM_PARTIAL;  			skb_entail(sk, skb); @@ -1934,7 +1933,7 @@ void tcp_set_state(struct sock *sk, int state)  	/* Change state AFTER socket is unhashed to avoid closed  	 * socket sitting in hash tables.  	 */ -	sk->sk_state = state; +	sk_state_store(sk, state);  #ifdef STATE_TRACE  	SOCK_DEBUG(sk, "TCP sk=%p, State %s -> %s\n", sk, statename[oldstate], statename[state]); @@ -2644,7 +2643,8 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)  	if (sk->sk_type != SOCK_STREAM)  		return; -	info->tcpi_state = sk->sk_state; +	info->tcpi_state = sk_state_load(sk); +  	info->tcpi_ca_state = icsk->icsk_ca_state;  	info->tcpi_retransmits = icsk->icsk_retransmits;  	info->tcpi_probes = icsk->icsk_probes_out; @@ -2672,7 +2672,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info)  	info->tcpi_snd_mss = tp->mss_cache;  	info->tcpi_rcv_mss = icsk->icsk_ack.rcv_mss; -	if (sk->sk_state == TCP_LISTEN) { +	if (info->tcpi_state == TCP_LISTEN) {  		info->tcpi_unacked = sk->sk_ack_backlog;  		info->tcpi_sacked = sk->sk_max_ack_backlog;  	} else { @@ -3080,6 +3080,52 @@ void tcp_done(struct sock *sk)  }  EXPORT_SYMBOL_GPL(tcp_done); +int tcp_abort(struct sock *sk, int err) +{ +	if (!sk_fullsock(sk)) { +		if (sk->sk_state == TCP_NEW_SYN_RECV) { +			struct request_sock *req = inet_reqsk(sk); + +			local_bh_disable(); +			inet_csk_reqsk_queue_drop_and_put(req->rsk_listener, +							  req); +			local_bh_enable(); +			return 0; +		} +		sock_gen_put(sk); +		return -EOPNOTSUPP; +	} + +	/* Don't race with userspace socket closes such as tcp_close. */ +	lock_sock(sk); + +	if (sk->sk_state == TCP_LISTEN) { +		tcp_set_state(sk, TCP_CLOSE); +		inet_csk_listen_stop(sk); +	} + +	/* Don't race with BH socket closes such as inet_csk_listen_stop. */ +	local_bh_disable(); +	bh_lock_sock(sk); + +	if (!sock_flag(sk, SOCK_DEAD)) { +		sk->sk_err = err; +		/* This barrier is coupled with smp_rmb() in tcp_poll() */ +		smp_wmb(); +		sk->sk_error_report(sk); +		if (tcp_need_reset(sk->sk_state)) +			tcp_send_active_reset(sk, GFP_ATOMIC); +		tcp_done(sk); +	} + +	bh_unlock_sock(sk); +	local_bh_enable(); +	release_sock(sk); +	sock_put(sk); +	return 0; +} +EXPORT_SYMBOL_GPL(tcp_abort); +  extern struct tcp_congestion_ops tcp_reno;  static __initdata unsigned long thash_entries; | 
