mptcp: support TCP_CORK and TCP_NODELAY
authorMaxim Galaganov <max@internet.ru>
Fri, 3 Dec 2021 22:35:41 +0000 (14:35 -0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 7 Dec 2021 19:36:31 +0000 (11:36 -0800)
First, add cork and nodelay fields to the mptcp_sock structure
so they can be used in sync_socket_options(), and fill them on setsockopt
while holding the msk socket lock.

Then, on setsockopt set proper tcp_sk(ssk)->nonagle values for subflows
by calling __tcp_sock_set_cork() or __tcp_sock_set_nodelay() on the ssk
while holding the ssk socket lock.

tcp_push_pending_frames() will be invoked on the ssk if a cork was cleared
or nodelay was set. Also set MPTCP_PUSH_PENDING bit by calling
mptcp_check_and_set_pending(). This will lead to __mptcp_push_pending()
being called inside mptcp_release_cb() with new tcp_sk(ssk)->nonagle.

Also add getsockopt support for TCP_CORK and TCP_NODELAY.

Acked-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Maxim Galaganov <max@internet.ru>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/mptcp/protocol.h
net/mptcp/sockopt.c

index 147b22d..e146915 100644 (file)
@@ -249,7 +249,9 @@ struct mptcp_sock {
        bool            rcv_fastclose;
        bool            use_64bit_ack; /* Set when we received a 64-bit DSN */
        bool            csum_enabled;
-       u8              recvmsg_inq:1;
+       u8              recvmsg_inq:1,
+                       cork:1,
+                       nodelay:1;
        spinlock_t      join_list_lock;
        struct work_struct work;
        struct sk_buff  *ooo_last_skb;
index 44e0a37..3c3db22 100644 (file)
@@ -616,6 +616,66 @@ static int mptcp_setsockopt_sol_tcp_congestion(struct mptcp_sock *msk, sockptr_t
        return ret;
 }
 
+static int mptcp_setsockopt_sol_tcp_cork(struct mptcp_sock *msk, sockptr_t optval,
+                                        unsigned int optlen)
+{
+       struct mptcp_subflow_context *subflow;
+       struct sock *sk = (struct sock *)msk;
+       int val;
+
+       if (optlen < sizeof(int))
+               return -EINVAL;
+
+       if (copy_from_sockptr(&val, optval, sizeof(val)))
+               return -EFAULT;
+
+       lock_sock(sk);
+       sockopt_seq_inc(msk);
+       msk->cork = !!val;
+       mptcp_for_each_subflow(msk, subflow) {
+               struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+               lock_sock(ssk);
+               __tcp_sock_set_cork(ssk, !!val);
+               release_sock(ssk);
+       }
+       if (!val)
+               mptcp_check_and_set_pending(sk);
+       release_sock(sk);
+
+       return 0;
+}
+
+static int mptcp_setsockopt_sol_tcp_nodelay(struct mptcp_sock *msk, sockptr_t optval,
+                                           unsigned int optlen)
+{
+       struct mptcp_subflow_context *subflow;
+       struct sock *sk = (struct sock *)msk;
+       int val;
+
+       if (optlen < sizeof(int))
+               return -EINVAL;
+
+       if (copy_from_sockptr(&val, optval, sizeof(val)))
+               return -EFAULT;
+
+       lock_sock(sk);
+       sockopt_seq_inc(msk);
+       msk->nodelay = !!val;
+       mptcp_for_each_subflow(msk, subflow) {
+               struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+               lock_sock(ssk);
+               __tcp_sock_set_nodelay(ssk, !!val);
+               release_sock(ssk);
+       }
+       if (val)
+               mptcp_check_and_set_pending(sk);
+       release_sock(sk);
+
+       return 0;
+}
+
 static int mptcp_setsockopt_sol_ip_set_transparent(struct mptcp_sock *msk, int optname,
                                                   sockptr_t optval, unsigned int optlen)
 {
@@ -717,6 +777,10 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
                return -EOPNOTSUPP;
        case TCP_CONGESTION:
                return mptcp_setsockopt_sol_tcp_congestion(msk, optval, optlen);
+       case TCP_CORK:
+               return mptcp_setsockopt_sol_tcp_cork(msk, optval, optlen);
+       case TCP_NODELAY:
+               return mptcp_setsockopt_sol_tcp_nodelay(msk, optval, optlen);
        }
 
        return -EOPNOTSUPP;
@@ -1087,6 +1151,10 @@ static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
                                                      optval, optlen);
        case TCP_INQ:
                return mptcp_put_int_option(msk, optval, optlen, msk->recvmsg_inq);
+       case TCP_CORK:
+               return mptcp_put_int_option(msk, optval, optlen, msk->cork);
+       case TCP_NODELAY:
+               return mptcp_put_int_option(msk, optval, optlen, msk->nodelay);
        }
        return -EOPNOTSUPP;
 }
@@ -1189,6 +1257,8 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
 
        if (inet_csk(sk)->icsk_ca_ops != inet_csk(ssk)->icsk_ca_ops)
                tcp_set_congestion_control(ssk, msk->ca_name, false, true);
+       __tcp_sock_set_cork(ssk, !!msk->cork);
+       __tcp_sock_set_nodelay(ssk, !!msk->nodelay);
 
        inet_sk(ssk)->transparent = inet_sk(sk)->transparent;
        inet_sk(ssk)->freebind = inet_sk(sk)->freebind;