Merge tag 'v4.9.214' of git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux...
[platform/kernel/linux-amlogic.git] / net / ipv4 / tcp.c
index 02918e0..881e6f6 100644 (file)
@@ -297,6 +297,12 @@ EXPORT_SYMBOL(sysctl_tcp_mem);
 EXPORT_SYMBOL(sysctl_tcp_rmem);
 EXPORT_SYMBOL(sysctl_tcp_wmem);
 
+int sysctl_tcp_delack_seg __read_mostly = TCP_DELACK_SEG;
+EXPORT_SYMBOL(sysctl_tcp_delack_seg);
+
+int sysctl_tcp_use_userconfig __read_mostly;
+EXPORT_SYMBOL(sysctl_tcp_use_userconfig);
+
 atomic_long_t tcp_memory_allocated;    /* Current allocated memory. */
 EXPORT_SYMBOL(tcp_memory_allocated);
 
@@ -538,6 +544,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 (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 +1091,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);
        struct sockaddr *uaddr = msg->msg_name;
        int err, flags;
 
@@ -1096,11 +1109,26 @@ 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, 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 +1146,8 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        lock_sock(sk);
 
        flags = msg->msg_flags;
-       if ((flags & MSG_FASTOPEN) && !tp->repair) {
+       if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect) &&
+           !tp->repair) {
                err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
                if (err == -EINPROGRESS && copied_syn > 0)
                        goto out;
@@ -1440,8 +1469,12 @@ static void tcp_cleanup_rbuf(struct sock *sk, int copied)
                   /* Delayed ACKs frequently hit locked sockets during bulk
                    * receive. */
                if (icsk->icsk_ack.blocked ||
-                   /* Once-per-two-segments ACK was not sent by tcp_input.c */
-                   tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||
+                       /* Once-per-sysctl_tcp_delack_seg segments
+                        * ACK was not sent by tcp_input.c
+                        */
+                       tp->rcv_nxt - tp->rcv_wup > (icsk->icsk_ack.rcv_mss) *
+                               sysctl_tcp_delack_seg ||
+
                    /*
                     * If this read emptied read buffer, we send ACK, if
                     * connection is not bidirectional, user drained
@@ -2321,6 +2354,10 @@ int tcp_disconnect(struct sock *sk, int flags)
        tp->data_segs_in = 0;
        tp->data_segs_out = 0;
 
+       /* 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) {
@@ -2695,6 +2732,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;
@@ -3008,6 +3057,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;