Re: [PATCH mptcp-next 3/6] mptcp: remove CB offset field

Geliang Tang <[email protected]> Thu, 30 Jul 2026 09:32:34 +0800
Newsgroups dev.linux.lists.mptcp
Message-ID <[email protected]>
Hi Paolo,

On Mon, 2026-07-27 at 19:29 +0800, Geliang Tang wrote:
> From: Paolo Abeni <[email protected]>
> 
> Instead, use a new msk-level field to track the bytes already
> consumed
> inside each skb, carrying the amount of bytes already copied to
> user-space, alike what TCP is already doing.
> 
> The newly introduce `copied_seq` field is always accessed under the
> msk
> socket lock, delegating the synchronization with IASN to the msk
> release
> CB, when the socket is owned by the user-space at remote key
> reception
> time. Such synchronization preserves any partial progress (copy) made
> on
> the TFO packet.
> 
> Note that the explicit synchronization in __mptcp_move_skb() is
> needed to
> ensure that the TFO skb in the receive queue got its map_seq synched
> before the next skb lands into the receive queue when spooling the
> backlog
> at mptcp_release_cb() time, as the release CB synchronization will
> happen
> later.
> 
> Prior to this patch, the TFO skb dummy mapping was always ignored,
> now it
> affects the `copied_seq` initial update: be sure to extends the sign
> correctly of such mapping initialization time.
> 
> Overall this simplify a bit the __mptcp_recvmsg_mskq(),
> mptcp_inq_hint()
> and the __mptcp_move_skb() code and will also make possible the next
> patch.
> 
> Signed-off-by: Paolo Abeni <[email protected]>
> ---
>  net/mptcp/fastopen.c |  15 ++++--
>  net/mptcp/protocol.c | 126 +++++++++++++++++++----------------------
> --
>  net/mptcp/protocol.h |   8 ++-
>  net/mptcp/subflow.c  |   7 ++-
>  4 files changed, 77 insertions(+), 79 deletions(-)
> 
> diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
> index d6895c2200cc..421a50a85547 100644
> --- a/net/mptcp/fastopen.c
> +++ b/net/mptcp/fastopen.c
> @@ -9,6 +9,7 @@
>  void mptcp_fastopen_subflow_synack_set_params(struct
> mptcp_subflow_context *subflow,
>  					      struct request_sock
> *req)
>  {
> +	struct mptcp_sock *msk;
>  	struct sock *sk, *ssk;
>  	struct sk_buff *skb;
>  	struct tcp_sock *tp;
> @@ -44,20 +45,24 @@ void
> mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context
> *subf
>  	subflow->ssn_offset += skb->len;
>  	has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
>  
> -	/* Only the sequence delta is relevant */
> -	MPTCP_SKB_CB(skb)->map_seq = -skb->len;
> +	/* The TFO segment data sits before the IASN; before
> receiving
> +	 * the remote key, IASN is assumed being 0.
> +	 */
> +	MPTCP_SKB_CB(skb)->map_seq = -(u64)skb->len;
>  	MPTCP_SKB_CB(skb)->end_seq = 0;
> -	MPTCP_SKB_CB(skb)->offset = 0;
>  	MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
>  
>  	mptcp_data_lock(sk);
>  	DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));
>  
> -	mptcp_sk(sk)->rcvd_dummy_seq = true;
> +	msk = mptcp_sk(sk);
> +	msk->rcvd_dummy_seq = true;
> +	msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq;
> +	msk->tfo_skb_len = skb->len;
>  	mptcp_borrow_fwdmem(sk, skb);
>  	skb_set_owner_r(skb, sk);
>  	__skb_queue_tail(&sk->sk_receive_queue, skb);
> -	mptcp_sk(sk)->bytes_received += skb->len;
> +	msk->bytes_received += skb->len;
>  
>  	sk->sk_data_ready(sk);
>  
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index c0b6e312816f..74a426bf6680 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -28,7 +28,7 @@
>  #include "protocol.h"
>  #include "mib.h"
>  
> -static unsigned int mptcp_inq_hint(const struct sock *sk);
> +static unsigned int mptcp_inq_hint(struct sock *sk);
>  
>  #define CREATE_TRACE_POINTS
>  #include <trace/events/mptcp.h>
> @@ -160,7 +160,6 @@ static bool __mptcp_try_coalesce(struct sock *sk,
> struct sk_buff *to,
>  	int limit = READ_ONCE(sk->sk_rcvbuf);
>  
>  	if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq
> ||
> -	    MPTCP_SKB_CB(from)->offset ||
>  	    ((to->len + from->len) > (limit >> 3)) ||
>  	    !skb_try_coalesce(to, from, fragstolen, delta))
>  		return false;
> @@ -342,8 +341,7 @@ static void mptcp_data_queue_ofo(struct
> mptcp_sock *msk, struct sk_buff *skb)
>  	skb_set_owner_r(skb, sk);
>  }
>  
> -static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb,
> int offset,
> -			   int copy_len)
> +static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb,
> int offset)
>  {
>  	struct mptcp_subflow_context *subflow =
> mptcp_subflow_ctx(ssk);
>  	bool has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
> @@ -352,9 +350,9 @@ static void mptcp_init_skb(struct sock *ssk,
> struct sk_buff *skb, int offset,
>  	 * mptcp_subflow_get_mapped_dsn() is based on the current
> tp->copied_seq
>  	 * value
>  	 */
> -	MPTCP_SKB_CB(skb)->map_seq =
> mptcp_subflow_get_mapped_dsn(subflow);
> -	MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq +
> copy_len;
> -	MPTCP_SKB_CB(skb)->offset = offset;
> +	MPTCP_SKB_CB(skb)->map_seq =
> mptcp_subflow_get_mapped_dsn(subflow) -
> +				     offset;
> +	MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq +
> skb->len;
>  	MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
>  
>  	__skb_unlink(skb, &ssk->sk_receive_queue);
> @@ -420,8 +418,8 @@ void __mptcp_sync_rcv_sequence(struct sock *sk)
>  	if (!skb)
>  		return;
>  
> -	MPTCP_SKB_CB(skb)->map_seq = msk->ack_seq - skb->len;
> -	MPTCP_SKB_CB(skb)->end_seq = msk->ack_seq;
> +	MPTCP_SKB_CB(skb)->map_seq = mptcp_iasn(msk) - skb->len;
> +	MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq +
> skb->len;
>  }
>  
>  static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)

Here Sashiko complained that the fastopen code does not update
msk->copied_seq:

	/* Be sure to sync the eventual fastopen dummy mapping before
	 * any other skb lands into the msk.
	 */
	if (unlikely(msk->rcvd_dummy_seq))
		__mptcp_sync_rcv_sequence(sk);

So I changed it to in v2:

        if (unlikely(msk->rcvd_dummy_seq)) {
                msk->copied_seq += mptcp_iasn(msk);
                __mptcp_sync_rcv_sequence(sk);
                /* Release cb() would otherwise re-base copied_seq
                 * again.
                 */
                test_and_clear_bit(MPTCP_SYNC_SEQ, &msk->cb_flags);
        }

I'm not sure if this change is correct.

> @@ -450,6 +448,7 @@ static bool __mptcp_move_skb(struct sock *sk,
> struct sk_buff *skb)
>  	}
>  
>  	if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
> +add_queue:
>  		/* in sequence */
>  		msk->bytes_received += copy_len;
>  		WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
> @@ -463,28 +462,18 @@ static bool __mptcp_move_skb(struct sock *sk,
> struct sk_buff *skb)
>  	} else if (after64(MPTCP_SKB_CB(skb)->map_seq, msk-
> >ack_seq)) {
>  		mptcp_data_queue_ofo(msk, skb);
>  		return false;
> -	}
> +	} else if (after64(MPTCP_SKB_CB(skb)->end_seq, msk-
> >ack_seq)) {
> +		/* Partial packet: map_seq < ack_seq < end_seq. */
> +		int delta = msk->ack_seq - MPTCP_SKB_CB(skb)-
> >map_seq;
>  
> -	/* Completely old data? */
> -	if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
> -		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
> -		mptcp_drop(sk, skb);
> -		return false;
> +		copy_len -= delta;
> +		goto add_queue;
>  	}
>  
> -	/* Partial packet: map_seq < ack_seq < end_seq.
> -	 * Skip the already-acked bytes and enqueue the new data.
> -	 */
> -	copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
> -	MPTCP_SKB_CB(skb)->offset += msk->ack_seq -
> MPTCP_SKB_CB(skb)->map_seq;
> -	MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
> -				      MPTCP_SKB_CB(skb)->map_seq;
> -	msk->bytes_received += copy_len;
> -	WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
> -
> -	skb_set_owner_r(skb, sk);
> -	__skb_queue_tail(&sk->sk_receive_queue, skb);
> -	return true;
> +	/* Completely old data. */
> +	MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
> +	mptcp_drop(sk, skb);
> +	return false;
>  }
>  
>  static void mptcp_stop_rtx_timer(struct sock *sk)
> @@ -829,7 +818,7 @@ static bool __mptcp_move_skbs_from_subflow(struct
> mptcp_sock *msk,
>  		if (offset < skb->len) {
>  			size_t len = skb->len - offset;
>  
> -			mptcp_init_skb(ssk, skb, offset, len);
> +			mptcp_init_skb(ssk, skb, offset);
>  
>  			if (own_msk) {
>  				mptcp_subflow_lend_fwdmem(subflow,
> skb);
> @@ -896,8 +885,6 @@ static bool __mptcp_ofo_queue(struct mptcp_sock
> *msk)
>  			pr_debug("uncoalesced seq=%llx ack seq=%llx
> delta=%d\n",
>  				 MPTCP_SKB_CB(skb)->map_seq, msk-
> >ack_seq,
>  				 delta);
> -			MPTCP_SKB_CB(skb)->offset += delta;
> -			MPTCP_SKB_CB(skb)->map_seq += delta;
>  			__skb_queue_tail(&sk->sk_receive_queue,
> skb);
>  		}
>  		msk->bytes_received += end_seq - msk->ack_seq;
> @@ -2134,34 +2121,23 @@ static void mptcp_eat_recv_skb(struct sock
> *sk, struct sk_buff *skb)
>  }
>  
>  static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
> -				size_t len, int flags, int
> copied_total,
> +				size_t len, int flags, u64 *seq,
>  				struct scm_timestamping_internal
> *tss,
>  				int *cmsg_flags, struct sk_buff
> **last)
>  {
>  	struct mptcp_sock *msk = mptcp_sk(sk);
>  	struct sk_buff *skb, *tmp;
> -	int total_data_len = 0;
>  	int copied = 0;
>  
>  	skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
> -		u32 delta, offset = MPTCP_SKB_CB(skb)->offset;
> +		u64 offset = *seq - MPTCP_SKB_CB(skb)->map_seq;
>  		u32 data_len = skb->len - offset;
>  		u32 count;
>  		int err;
>  
> -		if (flags & MSG_PEEK) {
> -			/* skip already peeked skbs */
> -			if (total_data_len + data_len <=
> copied_total) {
> -				total_data_len += data_len;
> -				*last = skb;
> -				continue;
> -			}
> -
> -			/* skip the already peeked data in the
> current skb */
> -			delta = copied_total - total_data_len;
> -			offset += delta;
> -			data_len -= delta;
> -		}
> +		/* Skip the already peeked data. */
> +		if (offset >= skb->len)
> +			continue;

And here Sashiko complained about a risk of an infinite loop. So in the
next version, v2, I added *last = skb here:

                /* Skip the already peeked data. */
                if (offset >= skb->len) {
                        *last = skb;
                        continue;
                }

Please give me some feedback.

Thanks,
-Geliang

>  
>  		count = min_t(size_t, len - copied, data_len);
>  		if (!(flags & MSG_TRUNC)) {
> @@ -2179,14 +2155,12 @@ static int __mptcp_recvmsg_mskq(struct sock
> *sk, struct msghdr *msg,
>  		}
>  
>  		copied += count;
> +		*seq += count;
>  
>  		if (!(flags & MSG_PEEK)) {
>  			msk->bytes_consumed += count;
> -			if (count < data_len) {
> -				MPTCP_SKB_CB(skb)->offset += count;
> -				MPTCP_SKB_CB(skb)->map_seq += count;
> +			if (count < data_len)
>  				break;
> -			}
>  
>  			mptcp_eat_recv_skb(sk, skb);
>  		} else {
> @@ -2339,25 +2313,23 @@ static bool mptcp_move_skbs(struct sock *sk)
>  	return enqueued;
>  }
>  
> -static unsigned int mptcp_inq_hint(const struct sock *sk)
> +static unsigned int mptcp_inq_hint(struct sock *sk)
>  {
>  	const struct mptcp_sock *msk = mptcp_sk(sk);
> -	const struct sk_buff *skb;
> -
> -	skb = skb_peek(&sk->sk_receive_queue);
> -	if (skb) {
> -		u64 hint_val = READ_ONCE(msk->ack_seq) -
> MPTCP_SKB_CB(skb)->map_seq;
> +	u64 hint_val;
>  
> -		if (hint_val >= INT_MAX)
> -			return INT_MAX;
> -
> -		return (unsigned int)hint_val;
> -	}
> +	/* Avoid races vs ack_seq updates. */
> +	mptcp_data_lock(sk);
> +	hint_val = msk->ack_seq - msk->copied_seq;
> +	mptcp_data_unlock(sk);
> +	if (hint_val >= INT_MAX)
> +		return INT_MAX;
>  
> -	if (sk->sk_state == TCP_CLOSE || (sk->sk_shutdown &
> RCV_SHUTDOWN))
> +	if (!hint_val &&
> +	    (sk->sk_state == TCP_CLOSE || (sk->sk_shutdown &
> RCV_SHUTDOWN)))
>  		return 1;
>  
> -	return 0;
> +	return (unsigned int)hint_val;
>  }
>  
>  static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t
> len,
> @@ -2366,6 +2338,7 @@ static int mptcp_recvmsg(struct sock *sk,
> struct msghdr *msg, size_t len,
>  	struct mptcp_sock *msk = mptcp_sk(sk);
>  	struct scm_timestamping_internal tss;
>  	int copied = 0, cmsg_flags = 0;
> +	u64 peek_seq, *seq;
>  	int target;
>  	long timeo;
>  
> @@ -2385,6 +2358,11 @@ static int mptcp_recvmsg(struct sock *sk,
> struct msghdr *msg, size_t len,
>  
>  	len = min_t(size_t, len, INT_MAX);
>  	target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
> +	seq = &msk->copied_seq;
> +	if (flags & MSG_PEEK) {
> +		peek_seq = msk->copied_seq;
> +		seq = &peek_seq;
> +	}
>  
>  	if (unlikely(msk->recvmsg_inq))
>  		cmsg_flags = MPTCP_CMSG_INQ;
> @@ -2394,7 +2372,7 @@ static int mptcp_recvmsg(struct sock *sk,
> struct msghdr *msg, size_t len,
>  		int err, bytes_read;
>  
>  		bytes_read = __mptcp_recvmsg_mskq(sk, msg, len -
> copied, flags,
> -						  copied, &tss,
> &cmsg_flags,
> +						  seq, &tss,
> &cmsg_flags,
>  						  &last);
>  		if (unlikely(bytes_read < 0)) {
>  			if (!copied)
> @@ -2449,6 +2427,10 @@ static int mptcp_recvmsg(struct sock *sk,
> struct msghdr *msg, size_t len,
>  			err = copied ? : err;
>  			goto out_err;
>  		}
> +
> +		/* Recompute peek offset after eventual seq resync.
> */
> +		if (flags & MSG_PEEK)
> +			peek_seq = msk->copied_seq + copied;
>  	}
>  
>  	mptcp_cleanup_rbuf(msk, copied);
> @@ -3626,11 +3608,13 @@ static int mptcp_disconnect(struct sock *sk,
> int flags)
>  	msk->bytes_retrans = 0;
>  	msk->rcvspace_init = 0;
>  	msk->fastclosing = 0;
> +	msk->tfo_skb_len = 0;
>  	mptcp_init_rtt_est(msk);
>  
>  	/* for fallback's sake */
>  	WRITE_ONCE(msk->ack_seq, 0);
>  	atomic64_set(&msk->rcv_wnd_sent, 0);
> +	msk->copied_seq = 0;
>  
>  	WRITE_ONCE(sk->sk_shutdown, 0);
>  	sk_error_report(sk);
> @@ -3855,8 +3839,10 @@ static void mptcp_release_cb(struct sock *sk)
>  			__mptcp_error_report(sk);
>  		if (__test_and_clear_bit(MPTCP_SYNC_SNDBUF, &msk-
> >cb_flags))
>  			__mptcp_sync_sndbuf(sk);
> -		if (__test_and_clear_bit(MPTCP_SYNC_SEQ, &msk-
> >cb_flags))
> +		if (__test_and_clear_bit(MPTCP_SYNC_SEQ, &msk-
> >cb_flags)) {
> +			msk->copied_seq += mptcp_iasn(msk);
>  			__mptcp_sync_rcv_sequence(sk);
> +		}
>  	}
>  }
>  
> @@ -4517,7 +4503,7 @@ static struct sk_buff *mptcp_recv_skb(struct
> sock *sk, u32 *off)
>  		mptcp_move_skbs(sk);
>  
>  	while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
> -		offset = MPTCP_SKB_CB(skb)->offset;
> +		offset = msk->copied_seq - MPTCP_SKB_CB(skb)-
> >map_seq;
>  		if (offset < skb->len) {
>  			*off = offset;
>  			return skb;
> @@ -4559,11 +4545,9 @@ static int __mptcp_read_sock(struct sock *sk,
> read_descriptor_t *desc,
>  		copied += count;
>  
>  		msk->bytes_consumed += count;
> -		if (count < data_len) {
> -			MPTCP_SKB_CB(skb)->offset += count;
> -			MPTCP_SKB_CB(skb)->map_seq += count;
> +		msk->copied_seq += count;
> +		if (count < data_len)
>  			break;
> -		}
>  
>  		mptcp_eat_recv_skb(sk, skb);
>  		if (!desc->count)
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 19b6eafece71..730af40ec9bc 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
> @@ -129,7 +129,6 @@
>  struct mptcp_skb_cb {
>  	u64 map_seq;
>  	u64 end_seq;
> -	u32 offset;
>  	u8  has_rxtstamp;
>  };
>  
> @@ -289,6 +288,7 @@ struct mptcp_sock {
>  	u64		bytes_sent;
>  	u64		snd_nxt;
>  	u64		bytes_received;
> +	u64		copied_seq;
>  	u64		ack_seq;
>  	atomic64_t	rcv_wnd_sent;
>  	u64		rcv_data_fin_seq;
> @@ -308,6 +308,7 @@ struct mptcp_sock {
>  	u32		last_ack_recv;
>  	unsigned long	timer_ival;
>  	u32		token;
> +	u32		tfo_skb_len;
>  	unsigned long	flags;
>  	unsigned long	cb_flags;
>  	bool		rcvd_dummy_seq;
> @@ -860,6 +861,11 @@ struct sock *mptcp_subflow_get_retrans(struct
> mptcp_sock *msk);
>  int mptcp_sched_get_send(struct mptcp_sock *msk);
>  int mptcp_sched_get_retrans(struct mptcp_sock *msk);
>  
> +static inline u64 mptcp_iasn(const struct mptcp_sock *msk)
> +{
> +	return msk->ack_seq - msk->bytes_received + msk-
> >tfo_skb_len;
> +}
> +
>  static inline u64 mptcp_data_avail(const struct mptcp_sock *msk)
>  {
>  	return READ_ONCE(msk->bytes_received) - READ_ONCE(msk-
> >bytes_consumed);
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index ea9b697c0300..d0af5cb6f1b3 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -499,10 +499,13 @@ static void subflow_set_remote_key(struct
> mptcp_sock *msk,
>  	WRITE_ONCE(msk->can_ack, true);
>  	atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
>  
> -	if (!sock_owned_by_user(sk))
> +	if (!sock_owned_by_user(sk)) {
> +		/* User space could have already read partially the
> TFO skb */
> +		msk->copied_seq += subflow->iasn;
>  		__mptcp_sync_rcv_sequence(sk);
> -	else
> +	} else {
>  		__set_bit(MPTCP_SYNC_SEQ, &msk->cb_flags);
> +	}
>  }
>  
>  static void mptcp_propagate_state(struct sock *sk, struct sock *ssk,