From: Geliang Tang <geliang@kernel.org>
To: mptcp@lists.linux.dev
Cc: Paolo Abeni <pabeni@redhat.com>
Subject: Re: [PATCH mptcp-next 3/6] mptcp: remove CB offset field
Date: Thu, 30 Jul 2026 09:32:34 +0800 [thread overview]
Message-ID: <7a48107cdf8184b2314ea5c8dde1ea99e36c8240.camel@kernel.org> (raw)
In-Reply-To: <ac89aa00bb102ab121e9090a353b6dc452f5fb43.1785150300.git.tanggeliang@kylinos.cn>
Hi Paolo,
On Mon, 2026-07-27 at 19:29 +0800, Geliang Tang wrote:
> From: Paolo Abeni <pabeni@redhat.com>
>
> 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 <pabeni@redhat.com>
> ---
> 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,
next prev parent reply other threads:[~2026-07-30 1:32 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-27 11:29 [PATCH mptcp-next 0/6] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-07-27 11:29 ` [PATCH mptcp-next 1/6] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
2026-07-27 11:29 ` [PATCH mptcp-next 2/6] mptcp: drop the cant_coalesce CB field Geliang Tang
2026-07-27 11:29 ` [PATCH mptcp-next 3/6] mptcp: remove CB offset field Geliang Tang
2026-07-30 1:32 ` Geliang Tang [this message]
2026-07-27 11:29 ` [PATCH mptcp-next 4/6] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
2026-07-27 11:29 ` [PATCH mptcp-next 5/6] mptcp: trim the duplicated skb head at receive enqueue Geliang Tang
2026-07-29 8:08 ` Paolo Abeni
2026-07-30 1:15 ` Geliang Tang
2026-07-30 15:00 ` Paolo Abeni
2026-08-11 11:42 ` gang.yan
2026-07-27 11:29 ` [PATCH mptcp-next 6/6] mptcp: defer sk_data_ready to the worker Geliang Tang
2026-07-29 8:55 ` Paolo Abeni
2026-07-30 0:57 ` Geliang Tang
2026-07-30 15:18 ` Paolo Abeni
2026-07-27 12:37 ` [PATCH mptcp-next 0/6] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7a48107cdf8184b2314ea5c8dde1ea99e36c8240.camel@kernel.org \
--to=geliang@kernel.org \
--cc=mptcp@lists.linux.dev \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox