diff options
Diffstat (limited to 'net/tipc/socket.c')
| -rw-r--r-- | net/tipc/socket.c | 330 | 
1 files changed, 197 insertions, 133 deletions
| diff --git a/net/tipc/socket.c b/net/tipc/socket.c index e741416d1d24..aab4948f0aff 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -55,9 +55,6 @@ struct tipc_sock {  #define tipc_sk(sk) ((struct tipc_sock *)(sk))  #define tipc_sk_port(sk) (tipc_sk(sk)->p) -#define tipc_rx_ready(sock) (!skb_queue_empty(&sock->sk->sk_receive_queue) || \ -			(sock->state == SS_DISCONNECTING)) -  static int backlog_rcv(struct sock *sk, struct sk_buff *skb);  static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);  static void wakeupdispatch(struct tipc_port *tport); @@ -239,7 +236,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock, int protocol,  int tipc_sock_create_local(int type, struct socket **res)  {  	int rc; -	struct sock *sk;  	rc = sock_create_lite(AF_TIPC, type, 0, res);  	if (rc < 0) { @@ -248,8 +244,6 @@ int tipc_sock_create_local(int type, struct socket **res)  	}  	tipc_sk_create(&init_net, *res, 0, 1); -	sk = (*res)->sk; -  	return 0;  } @@ -570,6 +564,31 @@ static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m)  	return 0;  } +static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p) +{ +	struct sock *sk = sock->sk; +	struct tipc_port *tport = tipc_sk_port(sk); +	DEFINE_WAIT(wait); +	int done; + +	do { +		int err = sock_error(sk); +		if (err) +			return err; +		if (sock->state == SS_DISCONNECTING) +			return -EPIPE; +		if (!*timeo_p) +			return -EAGAIN; +		if (signal_pending(current)) +			return sock_intr_errno(*timeo_p); + +		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); +		done = sk_wait_event(sk, timeo_p, !tport->congested); +		finish_wait(sk_sleep(sk), &wait); +	} while (!done); +	return 0; +} +  /**   * send_msg - send message in connectionless manner   * @iocb: if NULL, indicates that socket lock is already held @@ -589,9 +608,9 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,  {  	struct sock *sk = sock->sk;  	struct tipc_port *tport = tipc_sk_port(sk); -	struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; +	DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);  	int needs_conn; -	long timeout_val; +	long timeo;  	int res = -EINVAL;  	if (unlikely(!dest)) @@ -628,8 +647,7 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,  		reject_rx_queue(sk);  	} -	timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); - +	timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);  	do {  		if (dest->addrtype == TIPC_ADDR_NAME) {  			res = dest_name_check(dest, m); @@ -663,14 +681,9 @@ static int send_msg(struct kiocb *iocb, struct socket *sock,  				sock->state = SS_CONNECTING;  			break;  		} -		if (timeout_val <= 0L) { -			res = timeout_val ? timeout_val : -EWOULDBLOCK; +		res = tipc_wait_for_sndmsg(sock, &timeo); +		if (res)  			break; -		} -		release_sock(sk); -		timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), -					       !tport->congested, timeout_val); -		lock_sock(sk);  	} while (1);  exit: @@ -679,6 +692,34 @@ exit:  	return res;  } +static int tipc_wait_for_sndpkt(struct socket *sock, long *timeo_p) +{ +	struct sock *sk = sock->sk; +	struct tipc_port *tport = tipc_sk_port(sk); +	DEFINE_WAIT(wait); +	int done; + +	do { +		int err = sock_error(sk); +		if (err) +			return err; +		if (sock->state == SS_DISCONNECTING) +			return -EPIPE; +		else if (sock->state != SS_CONNECTED) +			return -ENOTCONN; +		if (!*timeo_p) +			return -EAGAIN; +		if (signal_pending(current)) +			return sock_intr_errno(*timeo_p); + +		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); +		done = sk_wait_event(sk, timeo_p, +				     (!tport->congested || !tport->connected)); +		finish_wait(sk_sleep(sk), &wait); +	} while (!done); +	return 0; +} +  /**   * send_packet - send a connection-oriented message   * @iocb: if NULL, indicates that socket lock is already held @@ -695,9 +736,9 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,  {  	struct sock *sk = sock->sk;  	struct tipc_port *tport = tipc_sk_port(sk); -	struct sockaddr_tipc *dest = (struct sockaddr_tipc *)m->msg_name; -	long timeout_val; -	int res; +	DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name); +	int res = -EINVAL; +	long timeo;  	/* Handle implied connection establishment */  	if (unlikely(dest)) @@ -709,30 +750,24 @@ static int send_packet(struct kiocb *iocb, struct socket *sock,  	if (iocb)  		lock_sock(sk); -	timeout_val = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); +	if (unlikely(sock->state != SS_CONNECTED)) { +		if (sock->state == SS_DISCONNECTING) +			res = -EPIPE; +		else +			res = -ENOTCONN; +		goto exit; +	} +	timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);  	do { -		if (unlikely(sock->state != SS_CONNECTED)) { -			if (sock->state == SS_DISCONNECTING) -				res = -EPIPE; -			else -				res = -ENOTCONN; -			break; -		} -  		res = tipc_send(tport->ref, m->msg_iov, total_len);  		if (likely(res != -ELINKCONG))  			break; -		if (timeout_val <= 0L) { -			res = timeout_val ? timeout_val : -EWOULDBLOCK; +		res = tipc_wait_for_sndpkt(sock, &timeo); +		if (res)  			break; -		} -		release_sock(sk); -		timeout_val = wait_event_interruptible_timeout(*sk_sleep(sk), -			(!tport->congested || !tport->connected), timeout_val); -		lock_sock(sk);  	} while (1); - +exit:  	if (iocb)  		release_sock(sk);  	return res; @@ -770,16 +805,11 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,  	/* Handle special cases where there is no connection */  	if (unlikely(sock->state != SS_CONNECTED)) { -		if (sock->state == SS_UNCONNECTED) { +		if (sock->state == SS_UNCONNECTED)  			res = send_packet(NULL, sock, m, total_len); -			goto exit; -		} else if (sock->state == SS_DISCONNECTING) { -			res = -EPIPE; -			goto exit; -		} else { -			res = -ENOTCONN; -			goto exit; -		} +		else +			res = sock->state == SS_DISCONNECTING ? -EPIPE : -ENOTCONN; +		goto exit;  	}  	if (unlikely(m->msg_name)) { @@ -876,7 +906,7 @@ static int auto_connect(struct socket *sock, struct tipc_msg *msg)   */  static void set_orig_addr(struct msghdr *m, struct tipc_msg *msg)  { -	struct sockaddr_tipc *addr = (struct sockaddr_tipc *)m->msg_name; +	DECLARE_SOCKADDR(struct sockaddr_tipc *, addr, m->msg_name);  	if (addr) {  		addr->family = AF_TIPC; @@ -961,6 +991,37 @@ static int anc_data_recv(struct msghdr *m, struct tipc_msg *msg,  	return 0;  } +static int tipc_wait_for_rcvmsg(struct socket *sock, long timeo) +{ +	struct sock *sk = sock->sk; +	DEFINE_WAIT(wait); +	int err; + +	for (;;) { +		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); +		if (skb_queue_empty(&sk->sk_receive_queue)) { +			if (sock->state == SS_DISCONNECTING) { +				err = -ENOTCONN; +				break; +			} +			release_sock(sk); +			timeo = schedule_timeout(timeo); +			lock_sock(sk); +		} +		err = 0; +		if (!skb_queue_empty(&sk->sk_receive_queue)) +			break; +		err = sock_intr_errno(timeo); +		if (signal_pending(current)) +			break; +		err = -EAGAIN; +		if (!timeo) +			break; +	} +	finish_wait(sk_sleep(sk), &wait); +	return err; +} +  /**   * recv_msg - receive packet-oriented message   * @iocb: (unused) @@ -980,7 +1041,7 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,  	struct tipc_port *tport = tipc_sk_port(sk);  	struct sk_buff *buf;  	struct tipc_msg *msg; -	long timeout; +	long timeo;  	unsigned int sz;  	u32 err;  	int res; @@ -996,25 +1057,13 @@ static int recv_msg(struct kiocb *iocb, struct socket *sock,  		goto exit;  	} -	timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); +	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);  restart:  	/* Look for a message in receive queue; wait if necessary */ -	while (skb_queue_empty(&sk->sk_receive_queue)) { -		if (sock->state == SS_DISCONNECTING) { -			res = -ENOTCONN; -			goto exit; -		} -		if (timeout <= 0L) { -			res = timeout ? timeout : -EWOULDBLOCK; -			goto exit; -		} -		release_sock(sk); -		timeout = wait_event_interruptible_timeout(*sk_sleep(sk), -							   tipc_rx_ready(sock), -							   timeout); -		lock_sock(sk); -	} +	res = tipc_wait_for_rcvmsg(sock, timeo); +	if (res) +		goto exit;  	/* Look at first message in receive queue */  	buf = skb_peek(&sk->sk_receive_queue); @@ -1086,7 +1135,7 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,  	struct tipc_port *tport = tipc_sk_port(sk);  	struct sk_buff *buf;  	struct tipc_msg *msg; -	long timeout; +	long timeo;  	unsigned int sz;  	int sz_to_copy, target, needed;  	int sz_copied = 0; @@ -1099,31 +1148,19 @@ static int recv_stream(struct kiocb *iocb, struct socket *sock,  	lock_sock(sk); -	if (unlikely((sock->state == SS_UNCONNECTED))) { +	if (unlikely(sock->state == SS_UNCONNECTED)) {  		res = -ENOTCONN;  		goto exit;  	}  	target = sock_rcvlowat(sk, flags & MSG_WAITALL, buf_len); -	timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); +	timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);  restart:  	/* Look for a message in receive queue; wait if necessary */ -	while (skb_queue_empty(&sk->sk_receive_queue)) { -		if (sock->state == SS_DISCONNECTING) { -			res = -ENOTCONN; -			goto exit; -		} -		if (timeout <= 0L) { -			res = timeout ? timeout : -EWOULDBLOCK; -			goto exit; -		} -		release_sock(sk); -		timeout = wait_event_interruptible_timeout(*sk_sleep(sk), -							   tipc_rx_ready(sock), -							   timeout); -		lock_sock(sk); -	} +	res = tipc_wait_for_rcvmsg(sock, timeo); +	if (res) +		goto exit;  	/* Look at first message in receive queue */  	buf = skb_peek(&sk->sk_receive_queue); @@ -1327,14 +1364,12 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)  static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf)  {  	struct tipc_msg *msg = buf_msg(buf); -	unsigned int limit;  	if (msg_connected(msg)) -		limit = sysctl_tipc_rmem[2]; -	else -		limit = sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE << -			msg_importance(msg); -	return limit; +		return sysctl_tipc_rmem[2]; + +	return sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE << +		msg_importance(msg);  }  /** @@ -1448,6 +1483,28 @@ static void wakeupdispatch(struct tipc_port *tport)  	sk->sk_write_space(sk);  } +static int tipc_wait_for_connect(struct socket *sock, long *timeo_p) +{ +	struct sock *sk = sock->sk; +	DEFINE_WAIT(wait); +	int done; + +	do { +		int err = sock_error(sk); +		if (err) +			return err; +		if (!*timeo_p) +			return -ETIMEDOUT; +		if (signal_pending(current)) +			return sock_intr_errno(*timeo_p); + +		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); +		done = sk_wait_event(sk, timeo_p, sock->state != SS_CONNECTING); +		finish_wait(sk_sleep(sk), &wait); +	} while (!done); +	return 0; +} +  /**   * connect - establish a connection to another TIPC port   * @sock: socket structure @@ -1463,7 +1520,8 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,  	struct sock *sk = sock->sk;  	struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest;  	struct msghdr m = {NULL,}; -	unsigned int timeout; +	long timeout = (flags & O_NONBLOCK) ? 0 : tipc_sk(sk)->conn_timeout; +	socket_state previous;  	int res;  	lock_sock(sk); @@ -1485,8 +1543,7 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,  		goto exit;  	} -	timeout = (flags & O_NONBLOCK) ? 0 : tipc_sk(sk)->conn_timeout; - +	previous = sock->state;  	switch (sock->state) {  	case SS_UNCONNECTED:  		/* Send a 'SYN-' to destination */ @@ -1508,43 +1565,22 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,  		 * case is EINPROGRESS, rather than EALREADY.  		 */  		res = -EINPROGRESS; -		break;  	case SS_CONNECTING: -		res = -EALREADY; +		if (previous == SS_CONNECTING) +			res = -EALREADY; +		if (!timeout) +			goto exit; +		timeout = msecs_to_jiffies(timeout); +		/* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ +		res = tipc_wait_for_connect(sock, &timeout);  		break;  	case SS_CONNECTED:  		res = -EISCONN;  		break;  	default:  		res = -EINVAL; -		goto exit; -	} - -	if (sock->state == SS_CONNECTING) { -		if (!timeout) -			goto exit; - -		/* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */ -		release_sock(sk); -		res = wait_event_interruptible_timeout(*sk_sleep(sk), -				sock->state != SS_CONNECTING, -				timeout ? (long)msecs_to_jiffies(timeout) -					: MAX_SCHEDULE_TIMEOUT); -		lock_sock(sk); -		if (res <= 0) { -			if (res == 0) -				res = -ETIMEDOUT; -			else -				; /* leave "res" unchanged */ -			goto exit; -		} +		break;  	} - -	if (unlikely(sock->state == SS_DISCONNECTING)) -		res = sock_error(sk); -	else -		res = 0; -  exit:  	release_sock(sk);  	return res; @@ -1575,6 +1611,42 @@ static int listen(struct socket *sock, int len)  	return res;  } +static int tipc_wait_for_accept(struct socket *sock, long timeo) +{ +	struct sock *sk = sock->sk; +	DEFINE_WAIT(wait); +	int err; + +	/* True wake-one mechanism for incoming connections: only +	 * one process gets woken up, not the 'whole herd'. +	 * Since we do not 'race & poll' for established sockets +	 * anymore, the common case will execute the loop only once. +	*/ +	for (;;) { +		prepare_to_wait_exclusive(sk_sleep(sk), &wait, +					  TASK_INTERRUPTIBLE); +		if (skb_queue_empty(&sk->sk_receive_queue)) { +			release_sock(sk); +			timeo = schedule_timeout(timeo); +			lock_sock(sk); +		} +		err = 0; +		if (!skb_queue_empty(&sk->sk_receive_queue)) +			break; +		err = -EINVAL; +		if (sock->state != SS_LISTENING) +			break; +		err = sock_intr_errno(timeo); +		if (signal_pending(current)) +			break; +		err = -EAGAIN; +		if (!timeo) +			break; +	} +	finish_wait(sk_sleep(sk), &wait); +	return err; +} +  /**   * accept - wait for connection request   * @sock: listening socket @@ -1591,7 +1663,7 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)  	struct tipc_port *new_tport;  	struct tipc_msg *msg;  	u32 new_ref; - +	long timeo;  	int res;  	lock_sock(sk); @@ -1601,18 +1673,10 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)  		goto exit;  	} -	while (skb_queue_empty(&sk->sk_receive_queue)) { -		if (flags & O_NONBLOCK) { -			res = -EWOULDBLOCK; -			goto exit; -		} -		release_sock(sk); -		res = wait_event_interruptible(*sk_sleep(sk), -				(!skb_queue_empty(&sk->sk_receive_queue))); -		lock_sock(sk); -		if (res) -			goto exit; -	} +	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); +	res = tipc_wait_for_accept(sock, timeo); +	if (res) +		goto exit;  	buf = skb_peek(&sk->sk_receive_queue); | 
