Commit 3979ad7e authored by Willy Tarreau's avatar Willy Tarreau Committed by David S. Miller

net/tcp-fastopen: make connect()'s return case more consistent with non-TFO

Without TFO, any subsequent connect() call after a successful one returns
-1 EISCONN. The last API update ensured that __inet_stream_connect() can
return -1 EINPROGRESS in response to sendmsg() when TFO is in use to
indicate that the connection is now in progress. Unfortunately since this
function is used both for connect() and sendmsg(), it has the undesired
side effect of making connect() now return -1 EINPROGRESS as well after
a successful call, while at the same time poll() returns POLLOUT. This
can confuse some applications which happen to call connect() and to
check for -1 EISCONN to ensure the connection is usable, and for which
EINPROGRESS indicates a need to poll, causing a loop.

This problem was encountered in haproxy where a call to connect() is
precisely used in certain cases to confirm a connection's readiness.
While arguably haproxy's behaviour should be improved here, it seems
important to aim at a more robust behaviour when the goal of the new
API is to make it easier to implement TFO in existing applications.

This patch simply ensures that we preserve the same semantics as in
the non-TFO case on the connect() syscall when using TFO, while still
returning -1 EINPROGRESS on sendmsg(). For this we simply tell
__inet_stream_connect() whether we're doing a regular connect() or in
fact connecting for a sendmsg() call.

Cc: Wei Wang <weiwan@google.com>
Cc: Yuchung Cheng <ycheng@google.com>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: default avatarWilly Tarreau <w@1wt.eu>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent eb92f76e
...@@ -17,7 +17,7 @@ int inet_release(struct socket *sock); ...@@ -17,7 +17,7 @@ int inet_release(struct socket *sock);
int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags); int addr_len, int flags);
int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags); int addr_len, int flags, int is_sendmsg);
int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags); int addr_len, int flags);
int inet_accept(struct socket *sock, struct socket *newsock, int flags); int inet_accept(struct socket *sock, struct socket *newsock, int flags);
......
...@@ -570,7 +570,7 @@ static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias) ...@@ -570,7 +570,7 @@ static long inet_wait_for_connect(struct sock *sk, long timeo, int writebias)
* TCP 'magic' in here. * TCP 'magic' in here.
*/ */
int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags) int addr_len, int flags, int is_sendmsg)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int err; int err;
...@@ -605,7 +605,7 @@ int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, ...@@ -605,7 +605,7 @@ int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
goto out; goto out;
case SS_CONNECTING: case SS_CONNECTING:
if (inet_sk(sk)->defer_connect) if (inet_sk(sk)->defer_connect)
err = -EINPROGRESS; err = is_sendmsg ? -EINPROGRESS : -EISCONN;
else else
err = -EALREADY; err = -EALREADY;
/* Fall out of switch with err, set for this state */ /* Fall out of switch with err, set for this state */
...@@ -679,7 +679,7 @@ int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, ...@@ -679,7 +679,7 @@ int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int err; int err;
lock_sock(sock->sk); lock_sock(sock->sk);
err = __inet_stream_connect(sock, uaddr, addr_len, flags); err = __inet_stream_connect(sock, uaddr, addr_len, flags, 0);
release_sock(sock->sk); release_sock(sock->sk);
return err; return err;
} }
......
...@@ -1103,7 +1103,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, ...@@ -1103,7 +1103,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,
} }
flags = (msg->msg_flags & MSG_DONTWAIT) ? O_NONBLOCK : 0; flags = (msg->msg_flags & MSG_DONTWAIT) ? O_NONBLOCK : 0;
err = __inet_stream_connect(sk->sk_socket, msg->msg_name, err = __inet_stream_connect(sk->sk_socket, msg->msg_name,
msg->msg_namelen, flags); msg->msg_namelen, flags, 1);
inet->defer_connect = 0; inet->defer_connect = 0;
*copied = tp->fastopen_req->copied; *copied = tp->fastopen_req->copied;
tcp_free_fastopen_req(tp); tcp_free_fastopen_req(tp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment