|
@@ -533,6 +533,12 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
|
|
|
|
|
|
if (tp->urg_data & TCP_URG_VALID)
|
|
|
mask |= POLLPRI;
|
|
|
+ } else if (sk->sk_state == TCP_SYN_SENT && inet_sk(sk)->defer_connect) {
|
|
|
+ /* Active TCP fastopen socket with defer_connect
|
|
|
+ * Return POLLOUT so application can call write()
|
|
|
+ * in order for kernel to generate SYN+data
|
|
|
+ */
|
|
|
+ mask |= POLLOUT | POLLWRNORM;
|
|
|
}
|
|
|
/* This barrier is coupled with smp_wmb() in tcp_reset() */
|
|
|
smp_rmb();
|
|
@@ -1071,6 +1077,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,
|
|
|
int *copied, size_t size)
|
|
|
{
|
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
+ struct inet_sock *inet = inet_sk(sk);
|
|
|
int err, flags;
|
|
|
|
|
|
if (!(sysctl_tcp_fastopen & TFO_CLIENT_ENABLE))
|
|
@@ -1085,9 +1092,19 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,
|
|
|
tp->fastopen_req->data = msg;
|
|
|
tp->fastopen_req->size = size;
|
|
|
|
|
|
+ if (inet->defer_connect) {
|
|
|
+ err = tcp_connect(sk);
|
|
|
+ /* Same failure procedure as in tcp_v4/6_connect */
|
|
|
+ if (err) {
|
|
|
+ tcp_set_state(sk, TCP_CLOSE);
|
|
|
+ inet->inet_dport = 0;
|
|
|
+ sk->sk_route_caps = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
flags = (msg->msg_flags & MSG_DONTWAIT) ? O_NONBLOCK : 0;
|
|
|
err = __inet_stream_connect(sk->sk_socket, msg->msg_name,
|
|
|
msg->msg_namelen, flags);
|
|
|
+ inet->defer_connect = 0;
|
|
|
*copied = tp->fastopen_req->copied;
|
|
|
tcp_free_fastopen_req(tp);
|
|
|
return err;
|
|
@@ -1107,7 +1124,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
|
|
|
lock_sock(sk);
|
|
|
|
|
|
flags = msg->msg_flags;
|
|
|
- if (flags & MSG_FASTOPEN) {
|
|
|
+ if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) {
|
|
|
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
|
|
|
if (err == -EINPROGRESS && copied_syn > 0)
|
|
|
goto out;
|
|
@@ -2656,6 +2673,18 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
|
|
|
err = -EINVAL;
|
|
|
}
|
|
|
break;
|
|
|
+ case TCP_FASTOPEN_CONNECT:
|
|
|
+ if (val > 1 || val < 0) {
|
|
|
+ err = -EINVAL;
|
|
|
+ } else if (sysctl_tcp_fastopen & TFO_CLIENT_ENABLE) {
|
|
|
+ if (sk->sk_state == TCP_CLOSE)
|
|
|
+ tp->fastopen_connect = val;
|
|
|
+ else
|
|
|
+ err = -EINVAL;
|
|
|
+ } else {
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+ break;
|
|
|
case TCP_TIMESTAMP:
|
|
|
if (!tp->repair)
|
|
|
err = -EPERM;
|
|
@@ -3016,6 +3045,10 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
|
|
|
val = icsk->icsk_accept_queue.fastopenq.max_qlen;
|
|
|
break;
|
|
|
|
|
|
+ case TCP_FASTOPEN_CONNECT:
|
|
|
+ val = tp->fastopen_connect;
|
|
|
+ break;
|
|
|
+
|
|
|
case TCP_TIMESTAMP:
|
|
|
val = tcp_time_stamp + tp->tsoffset;
|
|
|
break;
|