Commit cc9d2566 authored by Paolo Abeni's avatar Paolo Abeni Committed by David S. Miller
Browse files

mptcp: update per unacked sequence on pkt reception



So that we keep per unacked sequence number consistent; since
we update per msk data, use an atomic64 cmpxchg() to protect
against concurrent updates from multiple subflows.

Initialize the snd_una at connect()/accept() time.

Co-developed-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Signed-off-by: default avatarMat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 926bdeab
Loading
Loading
Loading
Loading
+46 −6
Original line number Diff line number Diff line
@@ -744,6 +744,46 @@ fully_established:
	return true;
}

static u64 expand_ack(u64 old_ack, u64 cur_ack, bool use_64bit)
{
	u32 old_ack32, cur_ack32;

	if (use_64bit)
		return cur_ack;

	old_ack32 = (u32)old_ack;
	cur_ack32 = (u32)cur_ack;
	cur_ack = (old_ack & GENMASK_ULL(63, 32)) + cur_ack32;
	if (unlikely(before(cur_ack32, old_ack32)))
		return cur_ack + (1LL << 32);
	return cur_ack;
}

static void update_una(struct mptcp_sock *msk,
		       struct mptcp_options_received *mp_opt)
{
	u64 new_snd_una, snd_una, old_snd_una = atomic64_read(&msk->snd_una);
	u64 write_seq = READ_ONCE(msk->write_seq);

	/* avoid ack expansion on update conflict, to reduce the risk of
	 * wrongly expanding to a future ack sequence number, which is way
	 * more dangerous than missing an ack
	 */
	new_snd_una = expand_ack(old_snd_una, mp_opt->data_ack, mp_opt->ack64);

	/* ACK for data not even sent yet? Ignore. */
	if (after64(new_snd_una, write_seq))
		new_snd_una = old_snd_una;

	while (after64(new_snd_una, old_snd_una)) {
		snd_una = old_snd_una;
		old_snd_una = atomic64_cmpxchg(&msk->snd_una, snd_una,
					       new_snd_una);
		if (old_snd_una == snd_una)
			break;
	}
}

static bool add_addr_hmac_valid(struct mptcp_sock *msk,
				struct mptcp_options_received *mp_opt)
{
@@ -805,6 +845,12 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
	if (!mp_opt->dss)
		return;

	/* we can't wait for recvmsg() to update the ack_seq, otherwise
	 * monodirectional flows will stuck
	 */
	if (mp_opt->use_ack)
		update_una(msk, mp_opt);

	mpext = skb_ext_add(skb, SKB_EXT_MPTCP);
	if (!mpext)
		return;
@@ -831,12 +877,6 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb,
		mpext->use_map = 1;
	}

	if (mp_opt->use_ack) {
		mpext->data_ack = mp_opt->data_ack;
		mpext->use_ack = 1;
		mpext->ack64 = mp_opt->ack64;
	}

	mpext->data_fin = mp_opt->data_fin;
}

+2 −0
Original line number Diff line number Diff line
@@ -906,6 +906,7 @@ struct sock *mptcp_sk_clone(const struct sock *sk, struct request_sock *req)
	}

	msk->write_seq = subflow_req->idsn + 1;
	atomic64_set(&msk->snd_una, msk->write_seq);
	if (subflow_req->remote_key_valid) {
		msk->can_ack = true;
		msk->remote_key = subflow_req->remote_key;
@@ -1107,6 +1108,7 @@ void mptcp_finish_connect(struct sock *ssk)
	WRITE_ONCE(msk->write_seq, subflow->idsn + 1);
	WRITE_ONCE(msk->ack_seq, ack_seq);
	WRITE_ONCE(msk->can_ack, 1);
	atomic64_set(&msk->snd_una, msk->write_seq);

	mptcp_pm_new_connection(msk, 0);
}
+1 −0
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@ struct mptcp_sock {
	u64		remote_key;
	u64		write_seq;
	u64		ack_seq;
	atomic64_t	snd_una;
	u32		token;
	unsigned long	flags;
	bool		can_ack;