From 8c4606c4dbb1cf47f357cc3620ba5b70b433b670 Mon Sep 17 00:00:00 2001
From: Ying Xue <ying.xue@windriver.com>
Date: Mon, 3 Sep 2012 10:29:28 +0800
Subject: [PATCH v2] tipc: correct return value of connect() when it is interrupted by a signal
When connect() is interrupted by a signal, it returns -ERESTARTSYS
error code, meanwhile socket state is also changed to SS_DISCONNECTING
state. If connect() syscall exits from kernel space to userspace,
kernel will deal with the pending signal. Once seeing the -ERESTARTSYS,
it will retry this syscall again. As a result, finally userspace gets
-EISCONN error code from connect() at the moment.
Correct behavior of connect() is that we should not set socket state
SS_DISCONNECTING when signal happens, but it may return a successful
code to userspace after signal is handled.
Signed-off-by: Ying Xue <ying.xue@windriver.com>
---
net/tipc/tipc_socket.c | 109 +++++++++++++++++++++++++-----------------------
1 files changed, 57 insertions(+), 52 deletions(-)
@@ -1456,21 +1456,6 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
goto exit;
}
- /* Issue Posix-compliant error code if socket is in the wrong state */
-
- if (sock->state == SS_LISTENING) {
- res = -EOPNOTSUPP;
- goto exit;
- }
- if (sock->state == SS_CONNECTING) {
- res = -EALREADY;
- goto exit;
- }
- if (sock->state != SS_UNCONNECTED) {
- res = -EISCONN;
- goto exit;
- }
-
/*
* Reject connection attempt using multicast address
*
@@ -1483,54 +1468,74 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
goto exit;
}
- /* Reject any messages already in receive queue (very unlikely) */
-
- reject_rx_queue(sk);
+ switch (sock->state) {
+ case SS_UNCONNECTED:
+ /* Reject any messages already in receive queue
+ * (very unlikely) */
+ reject_rx_queue(sk);
- /* Send a 'SYN-' to destination */
+ /* Send a 'SYN-' to destination */
+ m.msg_name = dest;
+ m.msg_namelen = destlen;
+ res = send_msg(NULL, sock, &m, 0);
+ if (res < 0) {
+ goto exit;
+ }
- m.msg_name = dest;
- m.msg_namelen = destlen;
- res = send_msg(NULL, sock, &m, 0);
- if (res < 0) {
- goto exit;
+ /*
+ * Just entered SS_CONNECTING state; the only
+ * difference is that return value in non-blocking
+ * case is EINPROGRESS, rather than EALREADY.
+ */
+ res = -EINPROGRESS;
+ break;
+ case SS_LISTENING:
+ res = -EOPNOTSUPP;
+ break;
+ case SS_CONNECTED:
+ res = -EISCONN;
+ break;
+ default:
+ res = -EINVAL;
+ break;
}
- /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
-
- timeout = tipc_sk(sk)->conn_timeout;
- release_sock(sk);
- res = wait_event_interruptible_timeout(*sk->sk_sleep,
- (!skb_queue_empty(&sk->sk_receive_queue) ||
- (sock->state != SS_CONNECTING)),
- timeout ? (long)msecs_to_jiffies(timeout)
- : MAX_SCHEDULE_TIMEOUT);
- lock_sock(sk);
+ if (sock->state == SS_CONNECTING) {
+ /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
+ timeout = tipc_sk(sk)->conn_timeout;
+ release_sock(sk);
+ res = wait_event_interruptible_timeout(*sk->sk_sleep,
+ (!skb_queue_empty(&sk->sk_receive_queue) ||
+ (sock->state != SS_CONNECTING)),
+ timeout ? (long)msecs_to_jiffies(timeout)
+ : MAX_SCHEDULE_TIMEOUT);
+ lock_sock(sk);
- if (res > 0) {
- buf = skb_peek(&sk->sk_receive_queue);
- if (buf != NULL) {
- msg = buf_msg(buf);
- res = auto_connect(sock, msg);
- if (!res) {
- if (!msg_data_sz(msg))
- advance_rx_queue(sk);
+ if (res > 0) {
+ buf = skb_peek(&sk->sk_receive_queue);
+ if (buf != NULL) {
+ msg = buf_msg(buf);
+ res = auto_connect(sock, msg);
+ if (!res) {
+ if (!msg_data_sz(msg))
+ advance_rx_queue(sk);
+ }
+ } else {
+ if (sock->state == SS_CONNECTED) {
+ res = -EISCONN;
+ } else {
+ res = -ECONNREFUSED;
+ }
}
} else {
- if (sock->state == SS_CONNECTED) {
- res = -EISCONN;
+ if (res == 0) {
+ sock->state = SS_DISCONNECTING;
+ res = -ETIMEDOUT;
} else {
- res = -ECONNREFUSED;
+ ; /* leave "res" unchanged */
}
}
- } else {
- if (res == 0)
- res = -ETIMEDOUT;
- else
- ; /* leave "res" unchanged */
- sock->state = SS_DISCONNECTING;
}
-
exit:
release_sock(sk);
return res;
--
1.7.1