Commit 4f6e14bd authored by Maxim Galaganov's avatar Maxim Galaganov Committed by Jakub Kicinski
Browse files

mptcp: support TCP_CORK and TCP_NODELAY



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: default avatarPaolo Abeni <pabeni@redhat.com>
Reviewed-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: default avatarMaxim Galaganov <max@internet.ru>
Signed-off-by: default avatarMat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 8b38217a
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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;
+70 −0
Original line number Diff line number Diff line
@@ -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;