diff -drupN a/net/ipv4/tcp.c b/net/ipv4/tcp.c --- a/net/ipv4/tcp.c 2018-08-06 17:23:04.000000000 +0300 +++ b/net/ipv4/tcp.c 2022-06-12 05:28:14.000000000 +0300 @@ -538,6 +538,12 @@ unsigned int tcp_poll(struct file *file, if (tp->urg_data & TCP_URG_VALID) mask |= POLLPRI; + } else if (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(); @@ -1079,6 +1085,7 @@ static int tcp_sendmsg_fastopen(struct s int *copied, size_t size) { struct tcp_sock *tp = tcp_sk(sk); + struct inet_sock *inet = inet_sk(sk); struct sockaddr *uaddr = msg->msg_name; int err, flags; @@ -1096,11 +1103,26 @@ static int tcp_sendmsg_fastopen(struct s 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, uaddr, msg->msg_namelen, flags); - *copied = tp->fastopen_req->copied; - tcp_free_fastopen_req(tp); + /* fastopen_req could already be freed in __inet_stream_connect + * if the connection times out or gets rst + */ + if (tp->fastopen_req) { + *copied = tp->fastopen_req->copied; + tcp_free_fastopen_req(tp); + inet->defer_connect = 0; + } return err; } @@ -1118,7 +1140,7 @@ int tcp_sendmsg(struct sock *sk, struct lock_sock(sk); flags = msg->msg_flags; - if ((flags & MSG_FASTOPEN) && !tp->repair) { + 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; @@ -2300,7 +2322,6 @@ int tcp_disconnect(struct sock *sk, int tp->snd_cwnd_cnt = 0; tp->window_clamp = 0; tcp_set_ca_state(sk, TCP_CA_Open); - tp->is_sack_reneg = 0; tcp_clear_retrans(tp); inet_csk_delack_init(sk); /* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0 @@ -2314,6 +2335,10 @@ int tcp_disconnect(struct sock *sk, int sk->sk_rx_dst = NULL; tcp_saved_syn_free(tp); + /* Clean up fastopen related fields */ + tcp_free_fastopen_req(tp); + inet->defer_connect = 0; + WARN_ON(inet->inet_num && !icsk->icsk_bind_hash); if (sk->sk_frag.page) { @@ -2688,6 +2713,18 @@ static int do_tcp_setsockopt(struct sock 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; @@ -3001,6 +3038,10 @@ static int do_tcp_getsockopt(struct sock 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; @@ -3276,7 +3317,11 @@ EXPORT_SYMBOL_GPL(tcp_abort); extern struct tcp_congestion_ops tcp_reno; +#ifdef CONFIG_BASE_SMALL +static __initdata unsigned long thash_entries = 16; +#else static __initdata unsigned long thash_entries; +#endif static int __init set_thash_entries(char *str) { ssize_t ret;