All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage
@ 2026-10-29  6:47 Geliang Tang
  2026-10-29  6:47 ` [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Geliang Tang @ 2026-10-29  6:47 UTC (permalink / raw)
  To: mptcp; +Cc: Geliang Tang

From: Geliang Tang <tanggeliang@kylinos.cn>

v16:
 - Simplified the series to retain only the core changes required by both
   kTLS and sockmap: drop the CB offset field, add copied_seq tracking,
   and sync the MPTCP skb CB layout with the TCP one.
 - Dropped patch 2, leaving the fastopen code unchanged.
 - Moved patches 6-11 into a separate series "prepare interfaces for kTLS
   and sockmap support".

v15:
 - Patch 2: drop mptcp_data_lock() in mptcp_inq_hint(), use READ_ONCE()
   and __test_and_clear_bit() instead. This issue is unrelated to this
   series and will be addressed in a separate patch later.
 - Patch 5: Guard mptcp_cleanup_rbuf() with read_copied > 0. Initialize/
   reset msk->read_copied. Drop redundant mptcp_recv_skb() in
   __mptcp_read_sock().
 - Patch 6: simplify memory ordering, drop smp_mb()/smp_rmb(), keep
   existing smp_mb__before_atomic(). Check SOCK_DONE inline.
 - Patch 9: drop tcp_sock_rate_check_app_limited() split, call
   tcp_rate_check_app_limited() directly as Paolo suggested. Add
   write_seq != snd_nxt early bail-out.
 - Patch 10: wrap assertion behind expect_all_features().
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1792718041.git.tanggeliang@kylinos.cn/

v14:
 - v13 can no longer be applied to the export branch, so it has been
   rebased here with the following corrections:
 - Patch 2: move locking in mptcp_inq_hint() to avoid races with ack_seq
   change from patch 3 to here. Update commit log.
 - Patch 3: add mptcp_data_lock() in __mptcp_move_skb() around copied_seq
   update. Remove local delta variable in __mptcp_ofo_queue(). Initialize
   msk->copied_seq to 0 in mptcp_propagate_state() fallback path.
 - Patch 4: add map_seq64 comparison in __mptcp_try_coalesce() for 64-bit
   sequence consistency.
 - New patch 11: rename mptcp_check_readable to mptcp_stream_is_readable,
   and align with TCP by calling sk_is_readable() for KTLS support.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1790734083.git.tanggeliang@kylinos.cn/

v13:
 - use smp_mb instead of smp_mb__before_atomic in mptcp_check_data_fin.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1788245079.git.tanggeliang@kylinos.cn/

v12:
 - Move sock_set_flag(SOCK_DONE) to after smp_mb__before_atomic() in
   mptcp_check_data_fin() and reuse the existing barrier rather than adding
   a new smp_wmb().
 - Sample SOCK_DONE before smp_rmb() in mptcp_inq_hint() and mptcp_inq(),
   so the smp_rmb() only orders SOCK_DONE read relative to the subsequent
   ack_seq/copied_seq reads.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1788171508.git.tanggeliang@kylinos.cn/

v11:
 - __mptcp_move_skb(): gate __mptcp_sync_rcv_sequence() on
   test_and_clear_bit(MPTCP_SYNC_SEQ) so the dummy mapping is only re-based
   after ack_seq = iasn.
 - a new patch to set and test SOCK_DONE.
 - use SOCK_DONE in mptcp_inq_hint() and mptcp_inq().
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1788099847.git.tanggeliang@kylinos.cn/

v10:
 - Drop "mptcp: use atomic64_t for locklessly accessed u64 fields"
   (v9 patch 1) and the squash to bpf_burst test (v9 patch 11).
   Series reduced from 11 to 9 patches.
 - Revert all atomic64_read()/atomic64_set() on msk->ack_seq back to
   plain reads and WRITE_ONCE() in patches 1-4. Remove the local u64
   ack_seq variables that only existed to hold atomic64_read() results.
 - mptcp_inq_hint: move test_bit(MPTCP_SYNC_SEQ) inside mptcp_data_lock()
   to close the race between the flag check and the ack_seq read. Revert
   atomic64_read() to plain msk->ack_seq under the lock.
 - mptcp_inq (patch 6): use READ_ONCE() for both ack_seq and copied_seq
   instead of atomic64_read(). Add comment for the FIN subtraction,
   mirroring tcp_inq(). Update commit log.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787888115.git.tanggeliang@kylinos.cn/

v9:
 - Extend atomic64_t conversion from only msk->ack_seq to all locklessly
   accessed u64 fields: write_seq, snd_nxt, snd_una, wnd_end,
   bytes_received, bytes_consumed.
 - Add mptcp_data_lock() in mptcp_inq_hint() to read ack_seq and
   copied_seq atomically.
 - Add read_copied field and drain it under mptcp_data_lock() in
   mptcp_read_complete() to fix the BH vs worker data race.
 - New patches: mptcp_inq/peek_len and sendmsg_locked proto_ops.
 - Squash to "selftests/bpf: Add bpf_burst scheduler & test".
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787827525.git.tanggeliang@kylinos.cn/

v8:
 - check MPTCP_SYNC_SEQ in mptcp_inq_hint:
       if (hint_val >= INT_MAX) {
               if (test_bit(MPTCP_SYNC_SEQ, &msk->cb_flags))
                       return 0;
               return INT_MAX;
       }
 - rename mptcp_sock_rate_check_app_limited to
   mptcp_rate_check_app_limited.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787644449.git.tanggeliang@kylinos.cn/

v7:
 - Patch 1 is a new one to fix the existing issue Sashiko mentioned -
   using atomic64_t for msk->ack_seq.
 - Fix the map_subflow_seq setting in fallback mode in patch 4.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787537436.git.tanggeliang@kylinos.cn/

v6:
 - Memory ordering for MPTCP_SYNC_SEQ: Add smp_wmb() before set_bit() in
   subflow_set_remote_key() and smp_rmb() after test_and_clear_bit() in
   __mptcp_move_skb() and mptcp_release_cb() to prevent data races on
   weakly-ordered architectures, ensuring ack_seq updates are visible
   before the flag is set and readers see the updated sequence after
   observing the flag.
 - Lockdep nested locking: Use lock_sock_fast_nested(ssk) instead of
   lock_sock_fast(ssk) in mptcp_sock_rate_check_app_limited() to suppress
   false positive recursive locking warnings when acquiring subflow socket
   locks while holding the parent MPTCP socket lock.
 - Receive window advertisement order: Swap mptcp_rcv_space_adjust() and
   mptcp_cleanup_rbuf() in mptcp_read_complete() to expand the receive
   buffer before evaluating window updates, ensuring ACKs advertise the
   new (larger) window size instead of delaying the advertisement until
   the next cycle.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787446274.git.tanggeliang@kylinos.cn/

v5:
 - only patch 3 changed.
 - Fallback offset underflow: Initialize MPTCP sequence space to 0 in
   mptcp_propagate_state() when mp_opt == NULL, ensuring SKB's map_seq
   starts from 0 to match msk->copied_seq and prevent offset calculation
   underflow in fallback mode.
 - Stale peek_seq: Move peek_seq recomputation into the mptcp_move_skbs()
   continue path and add another recomputation after sk_wait_data(),
   ensuring peek_seq stays synchronized with msk->copied_seq updates from
   MPTCP_SYNC_SEQ processing in both paths.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787368526.git.tanggeliang@kylinos.cn/

v4:
 - mptcp_recvmsg: move the peek_seq recompute from after sk_wait_data()
   to before the mptcp_move_skbs() continue check.
   mptcp_move_skbs() -> __mptcp_move_skb() may shift the head skb's
   map_seq to the IASN frame via __mptcp_sync_rcv_sequence(); if
   'continue' then exits the loop, the recompute is skipped, and the
   next __mptcp_recvmsg_mskq() computes offset with peek_seq in the old
   frame minus map_seq in the new frame. The underflow makes the offset
   test fail and the skb is skipped (and mptcp_recv_skb() actually frees
   the TFO skb via mptcp_eat_recv_skb()).
 - Make all three MPTCP_SYNC_SEQ bit accesses atomic:
   - subflow_set_remote_key:   __set_bit            -> set_bit
   - __mptcp_move_skb:         __test_and_clear_bit -> test_and_clear_bit
   - mptcp_release_cb:         __test_and_clear_bit -> test_and_clear_bit
   The flag is set in BH under mptcp_data_lock() (msk slock held) and
   cleared in user context under lock_sock() slow path (only owned=1,
   slock not held). The non-atomic RMW on the two sides races, which can
   lose the IASN sync.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1787295147.git.tanggeliang@kylinos.cn/

v3:
 - Patch 2, handle "offset" in __mptcp_sync_rcv_sequence().
 - Patch 3, update __mptcp_move_skb() in response to Sashiko comments:

 	if (__test_and_clear_bit(MPTCP_SYNC_SEQ, &msk->cb_flags))
		msk->copied_seq += mptcp_iasn(msk);

 - Patch 4, replace after64() to after() in mptcp_prune_ofo_queue(). The
   pre-existing issue raised by Sashiko regarding changing the type of
   ack_seq to atomic64_t is not addressed in this series.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1786445142.git.tanggeliang@kylinos.cn/

v2:
 - Patch 3, updated in response to Sashiko comments:

        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);
        }

        /* Skip the already peeked data. */
        if (offset >= skb->len) {
                *last = skb;
                continue;
        }
 - Patch 5, replaced with Paolo's patch.
 - Patch 6, 7, new patches addressing app-limited conditions.
 - The patch "trim the duplicated skb head at receive enqueue" has been
   dropped, as it is no longer needed after Paolo updated the "mptcp:
   out-of-order queue pruning" series.
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1786158416.git.tanggeliang@kylinos.cn/

v1:
 - https://patchwork.kernel.org/project/mptcp/cover/cover.1785150300.git.tanggeliang@kylinos.cn/

The goal of this series is to reduce the differences between TCP and MPTCP
for TLS usage, in preparation for adding TLS over MPTCP support in the
future.

In previous versions [1], a struct tls_prot_ops was defined to represent
the interface differences between TCP and MPTCP, which contained the
following callbacks:

	struct sk_buff *(*recv_skb)(struct sock *sk, u32 *off);
	bool (*lock_is_held)(struct sock *sk);
	void (*read_done)(struct sock *sk, size_t len);
	u32 (*get_skb_seq)(struct sk_buff *skb);
	int (*skb_get_header)(const struct sk_buff *skb, int offset,
	                      void *to, int len);
	bool (*epollin_ready)(const struct sock *sk);
	void (*check_app_limited)(struct sock *sk);

In reality, some of these callbacks are unnecessary. This series aims to
eliminate the get_skb_seq(), skb_get_header(), and lock_is_held()
callbacks.

The first four patches come from Paolo's "mptcp: address stall under memory
pressure" series v5 [2], with only minor cleanup from my side.

They remove the CB offset field and sync the MPTCP skb CB layout with the
TCP one, so that we can obtain the TCP or MPTCP sequence number in a
unified way, e.g.:

	struct tls_skb_cb {
	    u32 seq;
	};

	#define TLS_SKB_CB(__skb) ((struct tls_skb_cb *)&((__skb)->cb[0]))

This eliminates the need for a separate get_skb_seq() callback.

Building on the removal of the CB offset field, I also added patch 5 that
trims the duplicated skb head at receive enqueue. With that in place, KTLS
can retrieve the record header via skb_copy_bits() directly, so there is no
longer any need for a dedicated MPTCP helper like mptcp_skb_get_header().
The skb_get_header() callback can thus be removed.

Patch 6 defers sk_data_ready to the worker, which avoids recursive locking
when TLS calls back into MPTCP under mptcp_data_lock(). With this change,
the lock_is_held() callback is no longer needed and can be removed.

[1]
https://patchwork.kernel.org/project/mptcp/cover/cover.1782123118.git.tanggeliang@kylinos.cn/
[2]
https://patchwork.kernel.org/project/mptcp/cover/cover.1778446731.git.pabeni@redhat.com/

Paolo Abeni (4):
  mptcp: drop the mptcp_ooo_try_coalesce() helper
  mptcp: remove CB offset field
  mptcp: sync mptcp skb cb layout with tcp one
  mptcp: defer read_sock cleanup to mptcp_worker

 net/mptcp/fastopen.c |  14 +++-
 net/mptcp/protocol.c | 184 ++++++++++++++++++++++++-------------------
 net/mptcp/protocol.h |  11 ++-
 net/mptcp/subflow.c  |  16 +++-
 4 files changed, 137 insertions(+), 88 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper
  2026-10-29  6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
@ 2026-10-29  6:47 ` Geliang Tang
  2026-10-29  6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Geliang Tang @ 2026-10-29  6:47 UTC (permalink / raw)
  To: mptcp; +Cc: Paolo Abeni, Geliang Tang

From: Paolo Abeni <pabeni@redhat.com>

It's used to save an additional comparison for in-order skbs, but is
also a barrier to remove CB offset. Remove the helper, let
__mptcp_try_coalesce() always perform the sequence check and remove
duplicate checks from the callers.

Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 net/mptcp/protocol.c | 21 ++++++---------------
 1 file changed, 6 insertions(+), 15 deletions(-)

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 02a4f479d9d8..528a72da646c 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -167,7 +167,8 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
 {
 	int limit = READ_ONCE(sk->sk_rcvbuf);
 
-	if (unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
+	if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
+	    unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
 	    MPTCP_SKB_CB(from)->offset ||
 	    ((to->len + from->len) > (limit >> 3)) ||
 	    !skb_try_coalesce(to, from, fragstolen, delta))
@@ -200,15 +201,6 @@ static bool mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
 	return true;
 }
 
-static bool mptcp_ooo_try_coalesce(struct mptcp_sock *msk, struct sk_buff *to,
-				   struct sk_buff *from)
-{
-	if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq)
-		return false;
-
-	return mptcp_try_coalesce((struct sock *)msk, to, from);
-}
-
 /* "inspired" by tcp_rcvbuf_grow(), main difference:
  * - mptcp does not maintain a msk-level window clamp
  * - returns true when  the receive buffer is actually updated
@@ -348,7 +340,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
 	/* with 2 subflows, adding at end of ooo queue is quite likely
 	 * Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup.
 	 */
-	if (mptcp_ooo_try_coalesce(msk, msk->ooo_last_skb, skb)) {
+	if (mptcp_try_coalesce(sk, msk->ooo_last_skb, skb)) {
 		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOMERGE);
 		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUETAIL);
 		return;
@@ -394,7 +386,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
 				MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
 				goto merge_right;
 			}
-		} else if (mptcp_ooo_try_coalesce(msk, skb1, skb)) {
+		} else if (mptcp_try_coalesce(sk, skb1, skb)) {
 			MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOMERGE);
 			return;
 		}
@@ -777,8 +769,7 @@ static void __mptcp_add_backlog(struct sock *sk,
 	if (!list_empty(&msk->backlog_list))
 		tail = list_last_entry(&msk->backlog_list, struct sk_buff, list);
 
-	if (tail && MPTCP_SKB_CB(skb)->map_seq == MPTCP_SKB_CB(tail)->end_seq &&
-	    ssk == tail->sk &&
+	if (tail && ssk == tail->sk &&
 	    __mptcp_try_coalesce(sk, tail, skb, &fragstolen, &delta)) {
 		skb->truesize -= delta;
 		kfree_skb_partial(skb, fragstolen);
@@ -902,7 +893,7 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
 
 		end_seq = MPTCP_SKB_CB(skb)->end_seq;
 		tail = skb_peek_tail(&sk->sk_receive_queue);
-		if (!tail || !mptcp_ooo_try_coalesce(msk, tail, skb)) {
+		if (!tail || !mptcp_try_coalesce(sk, tail, skb)) {
 			int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
 
 			/* skip overlapping data, if any */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field
  2026-10-29  6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
  2026-10-29  6:47 ` [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
@ 2026-10-29  6:47 ` Geliang Tang
  2026-10-29  7:10   ` sashiko-bot
  2026-10-29  6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Geliang Tang @ 2026-10-29  6:47 UTC (permalink / raw)
  To: mptcp; +Cc: Paolo Abeni, Geliang Tang

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 (map_seq = -(u32)skb->len,
zero-extended to u64) caused mptcp_inq_hint() to compute a wrapped
ack_seq - map_seq, so SIOCINQ and TCP_CM_INQ returned INT_MAX before the
MPC ACK.  This patch fixes that behaviour through both the -(u64)skb->len
sign fix and the new ack_seq - copied_seq hint.

Overall this simplifies a bit the __mptcp_recvmsg_mskq() and the
__mptcp_move_skb() code and will also make possible the next patch.

Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 net/mptcp/fastopen.c | 12 ++++++++----
 net/mptcp/protocol.c | 42 ++++++++++++++++++------------------------
 net/mptcp/protocol.h |  2 +-
 net/mptcp/subflow.c  | 15 ++++++++++++++-
 4 files changed, 41 insertions(+), 30 deletions(-)

diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index f717750906ff..04b1038b5200 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,23 @@ 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_SKB_CB(skb)->cant_coalesce = 1;
 
 	mptcp_data_lock(sk);
 	DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));
 
+	msk = mptcp_sk(sk);
+	msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq;
 	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 528a72da646c..f89ed94b668d 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -169,7 +169,6 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
 
 	if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
 	    unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
-	    MPTCP_SKB_CB(from)->offset ||
 	    ((to->len + from->len) > (limit >> 3)) ||
 	    !skb_try_coalesce(to, from, fragstolen, delta))
 		return false;
@@ -416,8 +415,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;
@@ -426,9 +424,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;
 	MPTCP_SKB_CB(skb)->cant_coalesce = 0;
 
@@ -472,10 +470,6 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
 	/* Partial packet */
 	if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
 		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;
 		goto insert;
 	}
 
@@ -833,7 +827,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,12 +890,9 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
 		if (!tail || !mptcp_try_coalesce(sk, tail, skb)) {
 			int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
 
-			/* skip overlapping data, if any */
 			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;
@@ -2195,11 +2186,17 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
 	int copied = 0;
 
 	skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
-		u32 delta, offset = MPTCP_SKB_CB(skb)->offset;
+		u32 delta, offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
 		u32 data_len = skb->len - offset;
 		u32 count;
 		int err;
 
+		/* Skip skbs that are entirely before copied_seq */
+		if (offset >= skb->len) {
+			*last = skb;
+			continue;
+		}
+
 		if (flags & MSG_PEEK) {
 			/* skip already peeked skbs */
 			if (total_data_len + data_len <= copied_total) {
@@ -2233,11 +2230,9 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
 
 		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;
+			msk->copied_seq += count;
+			if (count < data_len)
 				break;
-			}
 
 			mptcp_eat_recv_skb(sk, skb);
 		} else {
@@ -3674,6 +3669,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
 	mptcp_pm_data_reset(msk);
 	mptcp_ca_reset(sk);
 	msk->bytes_consumed = 0;
+	msk->copied_seq = 0;
 	msk->bytes_acked = 0;
 	msk->bytes_received = 0;
 	msk->bytes_sent = 0;
@@ -4601,7 +4597,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 = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
 		if (offset < skb->len) {
 			*off = offset;
 			return skb;
@@ -4643,11 +4639,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 3be60da12924..5c1fa117e5d5 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -130,7 +130,6 @@
 struct mptcp_skb_cb {
 	u64 map_seq;
 	u64 end_seq;
-	u32 offset;
 	u8  has_rxtstamp;
 	u8  cant_coalesce;
 };
@@ -311,6 +310,7 @@ struct mptcp_sock {
 	u64		rcv_data_fin_seq;
 	u64		bytes_retrans;
 	u64		bytes_consumed;
+	u64		copied_seq;
 	int		snd_burst;
 	int		old_wspace;
 	u64		recovery_snd_nxt;	/* in recovery mode accept up to this seq;
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 78e886b20258..5e553adb9397 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -533,6 +533,19 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
 	WRITE_ONCE(msk->ack_seq, subflow->iasn);
 	WRITE_ONCE(msk->can_ack, true);
 	atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
+	msk->copied_seq = subflow->iasn;
+
+	if (subflow->is_mptfo) {
+		struct sock *sk = (struct sock *)msk;
+		struct sk_buff *skb;
+
+		skb = skb_peek(&sk->sk_receive_queue);
+		if (skb) {
+			MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
+			MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
+			msk->copied_seq -= skb->len;
+		}
+	}
 }
 
 static void mptcp_propagate_state(struct sock *sk, struct sock *ssk,
@@ -1485,7 +1498,7 @@ static bool subflow_check_data_avail(struct sock *ssk)
 	skb = skb_peek(&ssk->sk_receive_queue);
 	subflow->map_valid = 1;
 	subflow->map_data_len = skb->len;
-	subflow->map_subflow_seq = tcp_sk(ssk)->copied_seq - subflow->ssn_offset;
+	subflow->map_subflow_seq = TCP_SKB_CB(skb)->seq - subflow->ssn_offset;
 	subflow->map_seq = __mptcp_expand_seq(subflow->map_seq,
 					      subflow->iasn +
 					      TCP_SKB_CB(skb)->seq -
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one
  2026-10-29  6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
  2026-10-29  6:47 ` [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
  2026-10-29  6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
@ 2026-10-29  6:47 ` Geliang Tang
  2026-10-29  7:19   ` sashiko-bot
  2026-10-29  6:47 ` [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
  2026-10-29  8:42 ` [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
  4 siblings, 1 reply; 8+ messages in thread
From: Geliang Tang @ 2026-10-29  6:47 UTC (permalink / raw)
  To: mptcp; +Cc: Paolo Abeni, Geliang Tang

From: Paolo Abeni <pabeni@redhat.com>

The MPTCP protocol uses a significantly different CB layout WRT TCP, as it
includes different information and use 64 bits for the sequence numbers.

As the msk-level rcvbuf buffer size is limited by the core socket code the
INT_MAX; after validating the incoming skb vs the current receive window,
we can safely use 32 bits for MPTCP-level sequence number. This allow
updating the MPTCP CB layout so that fields with a corresponding TCP-level
data use the same area inside the CB itself.

The 32-bit sequence numbers are safe because all SKBs in the receive queue
and OFO queue have been validated against the receive window (max INT_MAX),
so their sequence numbers are guaranteed to be within 2^31 of the current
ack_seq or copied_seq. This ensures that 32-bit arithmetic (comparisons
and subtractions) produces correct results even when 64-bit values cross
the 2^32 boundary, as the relative distance remains small.

Add build time check to ensure the latter invariant.

Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 net/mptcp/fastopen.c |  6 ++-
 net/mptcp/protocol.c | 88 +++++++++++++++++++++++++++-----------------
 net/mptcp/protocol.h |  7 +++-
 net/mptcp/subflow.c  |  1 +
 4 files changed, 64 insertions(+), 38 deletions(-)

diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index 04b1038b5200..08f0402324c2 100644
--- a/net/mptcp/fastopen.c
+++ b/net/mptcp/fastopen.c
@@ -48,8 +48,10 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
 	/* 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)->map_seq64 = -(u64)skb->len;
+	MPTCP_SKB_CB(skb)->map_seq = MPTCP_SKB_CB(skb)->map_seq64;
 	MPTCP_SKB_CB(skb)->end_seq = 0;
+	MPTCP_SKB_CB(skb)->flags = 0;
 	MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
 	MPTCP_SKB_CB(skb)->cant_coalesce = 1;
 
@@ -57,7 +59,7 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
 	DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));
 
 	msk = mptcp_sk(sk);
-	msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq;
+	msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq64;
 	mptcp_borrow_fwdmem(sk, skb);
 	skb_set_owner_r(skb, sk);
 	__skb_queue_tail(&sk->sk_receive_queue, skb);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f89ed94b668d..bbce1bafa40e 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -168,12 +168,14 @@ 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)->map_seq64 != MPTCP_SKB_CB(to)->map_seq64 +
+					     to->len ||
 	    unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
 	    ((to->len + from->len) > (limit >> 3)) ||
 	    !skb_try_coalesce(to, from, fragstolen, delta))
 		return false;
 
-	pr_debug("colesced seq %llx into %llx new len %d new end seq %llx\n",
+	pr_debug("colesced seq %x into %x new len %d new end seq %x\n",
 		 MPTCP_SKB_CB(from)->map_seq, MPTCP_SKB_CB(to)->map_seq,
 		 to->len, MPTCP_SKB_CB(from)->end_seq);
 	MPTCP_SKB_CB(to)->end_seq = MPTCP_SKB_CB(from)->end_seq;
@@ -255,8 +257,8 @@ static void mptcp_prune_ofo_queue(struct sock *sk,
 		struct sk_buff *skb = rb_to_skb(node);
 
 		/* Stop pruning if the incoming skb would land in OoO tail. */
-		if (after64(MPTCP_SKB_CB(in_skb)->map_seq,
-			    MPTCP_SKB_CB(skb)->map_seq))
+		if (after(MPTCP_SKB_CB(in_skb)->map_seq,
+			  MPTCP_SKB_CB(skb)->map_seq))
 			break;
 
 		pruned = true;
@@ -302,16 +304,21 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
 {
 	struct sock *sk = (struct sock *)msk;
 	struct rb_node **p, *parent;
-	u64 seq, end_seq, max_seq;
+	u64 end_seq, max_seq;
 	struct sk_buff *skb1;
+	u32 seq;
 
 	seq = MPTCP_SKB_CB(skb)->map_seq;
-	end_seq = MPTCP_SKB_CB(skb)->end_seq;
+	end_seq = MPTCP_SKB_CB(skb)->map_seq64 + skb->len;
 	max_seq = atomic64_read(&msk->rcv_wnd_sent);
 
-	pr_debug("msk=%p seq=%llx limit=%llx empty=%d\n", msk, seq, max_seq,
+	pr_debug("msk=%p seq=%x limit=%llx empty=%d\n", msk, seq, max_seq,
 		 RB_EMPTY_ROOT(&msk->out_of_order_queue));
-	if (after64(end_seq, max_seq)) {
+	/* Use the full sequence space to perform the admission checks, to
+	 * protect vs possible wrap-arounds.
+	 */
+	if (after64(MPTCP_SKB_CB(skb)->map_seq64, max_seq) ||
+	    after64(end_seq, max_seq)) {
 		/* out of window */
 		mptcp_drop(sk, skb);
 		pr_debug("oow by %lld, rcv_wnd_sent %llu\n",
@@ -346,7 +353,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
 	}
 
 	/* Can avoid an rbtree lookup if we are adding skb after ooo_last_skb */
-	if (!before64(seq, MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq)) {
+	if (!before(seq, MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq)) {
 		MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUETAIL);
 		parent = &msk->ooo_last_skb->rbnode;
 		p = &parent->rb_right;
@@ -358,18 +365,18 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
 	while (*p) {
 		parent = *p;
 		skb1 = rb_to_skb(parent);
-		if (before64(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
+		if (before(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
 			p = &parent->rb_left;
 			continue;
 		}
-		if (before64(seq, MPTCP_SKB_CB(skb1)->end_seq)) {
-			if (!after64(end_seq, MPTCP_SKB_CB(skb1)->end_seq)) {
+		if (before(seq, MPTCP_SKB_CB(skb1)->end_seq)) {
+			if (!after((u32)end_seq, MPTCP_SKB_CB(skb1)->end_seq)) {
 				/* All the bits are present. Drop. */
 				mptcp_drop(sk, skb);
 				MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
 				return;
 			}
-			if (after64(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
+			if (after(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
 				/* partial overlap:
 				 *     |     skb      |
 				 *  |     skb1    |
@@ -400,7 +407,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
 merge_right:
 	/* Remove other segments covered by skb. */
 	while ((skb1 = skb_rb_next(skb)) != NULL) {
-		if (before64(end_seq, MPTCP_SKB_CB(skb1)->end_seq))
+		if (before((u32)end_seq, MPTCP_SKB_CB(skb1)->end_seq))
 			break;
 		rb_erase(&skb1->rbnode, &msk->out_of_order_queue);
 		mptcp_drop(sk, skb1);
@@ -422,11 +429,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
 
 	/* the skb map_seq accounts for the skb offset:
 	 * mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
-	 * value
+	 * value; note that end seq number is only available in 32bits format.
 	 */
-	MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow) -
-				     offset;
+	MPTCP_SKB_CB(skb)->map_seq64 = mptcp_subflow_get_mapped_dsn(subflow) -
+				       offset;
+	MPTCP_SKB_CB(skb)->map_seq = (u32)MPTCP_SKB_CB(skb)->map_seq64;
 	MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + skb->len;
+	MPTCP_SKB_CB(skb)->flags = 0;
 	MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
 	MPTCP_SKB_CB(skb)->cant_coalesce = 0;
 
@@ -438,13 +447,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
 
 static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
 {
-	u64 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
+	u32 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
 	struct mptcp_sock *msk = mptcp_sk(sk);
 	struct sk_buff *tail;
 
 	mptcp_borrow_fwdmem(sk, skb);
 
-	if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
+	if (MPTCP_SKB_CB(skb)->map_seq64 == msk->ack_seq) {
 		/* in sequence */
 insert:
 		if (!mptcp_try_rmem_schedule(sk, skb)) {
@@ -462,14 +471,14 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
 		skb_set_owner_r(skb, sk);
 		__skb_queue_tail(&sk->sk_receive_queue, skb);
 		return true;
-	} else if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq)) {
+	} else if (after64(MPTCP_SKB_CB(skb)->map_seq64, msk->ack_seq)) {
 		mptcp_data_queue_ofo(msk, skb);
 		return false;
 	}
 
 	/* Partial packet */
-	if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
-		copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
+	if (after64(MPTCP_SKB_CB(skb)->map_seq64 + skb->len, msk->ack_seq)) {
+		copy_len = MPTCP_SKB_CB(skb)->end_seq - (u32)msk->ack_seq;
 		goto insert;
 	}
 
@@ -864,39 +873,37 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
 {
 	struct sock *sk = (struct sock *)msk;
 	struct sk_buff *skb, *tail;
+	u32 seq_delta, ack_seq;
 	bool moved = false;
 	struct rb_node *p;
-	u64 end_seq;
 
 	p = rb_first(&msk->out_of_order_queue);
 	pr_debug("msk=%p empty=%d\n", msk, RB_EMPTY_ROOT(&msk->out_of_order_queue));
 	while (p) {
+		ack_seq = msk->ack_seq;
 		skb = rb_to_skb(p);
-		if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq))
+		if (after(MPTCP_SKB_CB(skb)->map_seq, ack_seq))
 			break;
 
 		p = rb_next(p);
 		rb_erase(&skb->rbnode, &msk->out_of_order_queue);
 
-		if (unlikely(!after64(MPTCP_SKB_CB(skb)->end_seq,
-				      msk->ack_seq))) {
+		if (unlikely(!after(MPTCP_SKB_CB(skb)->end_seq, ack_seq))) {
 			mptcp_drop(sk, skb);
 			MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
 			continue;
 		}
 
-		end_seq = MPTCP_SKB_CB(skb)->end_seq;
+		seq_delta = MPTCP_SKB_CB(skb)->end_seq - ack_seq;
 		tail = skb_peek_tail(&sk->sk_receive_queue);
 		if (!tail || !mptcp_try_coalesce(sk, tail, skb)) {
-			int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
-
-			pr_debug("uncoalesced seq=%llx ack seq=%llx delta=%d\n",
-				 MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq,
-				 delta);
+			pr_debug("uncoalesced seq=%x ack seq=%x delta=%d\n",
+				 MPTCP_SKB_CB(skb)->map_seq, ack_seq,
+				 ack_seq - MPTCP_SKB_CB(skb)->map_seq);
 			__skb_queue_tail(&sk->sk_receive_queue, skb);
 		}
-		msk->bytes_received += end_seq - msk->ack_seq;
-		WRITE_ONCE(msk->ack_seq, end_seq);
+		msk->bytes_received += seq_delta;
+		WRITE_ONCE(msk->ack_seq, msk->ack_seq + seq_delta);
 		moved = true;
 	}
 	return moved;
@@ -2392,7 +2399,8 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
 
 	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 = READ_ONCE(msk->ack_seq) -
+			       MPTCP_SKB_CB(skb)->map_seq64;
 
 		if (hint_val >= INT_MAX)
 			return INT_MAX;
@@ -4847,11 +4855,23 @@ static int mptcp_napi_poll(struct napi_struct *napi, int budget)
 	return work_done;
 }
 
+#define CHK_CB_FIELD(mptcp_field, tcp_field)	\
+	({					\
+		BUILD_BUG_ON(offsetof(struct mptcp_skb_cb, mptcp_field) !=    \
+			     offsetof(struct tcp_skb_cb, tcp_field));	      \
+		BUILD_BUG_ON(offsetofend(struct mptcp_skb_cb, mptcp_field) != \
+			     offsetofend(struct tcp_skb_cb, tcp_field));      \
+	})
+
 void __init mptcp_proto_init(void)
 {
 	struct mptcp_delegated_action *delegated;
 	int cpu;
 
+	CHK_CB_FIELD(map_seq, seq);
+	CHK_CB_FIELD(end_seq, end_seq);
+	CHK_CB_FIELD(flags, tcp_flags);
+
 	mptcp_prot.h.hashinfo = tcp_prot.h.hashinfo;
 
 	if (percpu_counter_init(&mptcp_sockets_allocated, 0, GFP_KERNEL))
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 5c1fa117e5d5..2ab8342e198d 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -128,10 +128,13 @@
 #define MPTCP_SYNC_SNDBUF	7
 
 struct mptcp_skb_cb {
-	u64 map_seq;
-	u64 end_seq;
+	u32 map_seq;
+	u32 end_seq;
+	u32 unused;
+	u16 flags;
 	u8  has_rxtstamp;
 	u8  cant_coalesce;
+	u64 map_seq64;
 };
 
 #define MPTCP_SKB_CB(__skb)	((struct mptcp_skb_cb *)&((__skb)->cb[0]))
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 5e553adb9397..d144eb2373ef 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -542,6 +542,7 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
 		skb = skb_peek(&sk->sk_receive_queue);
 		if (skb) {
 			MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
+			MPTCP_SKB_CB(skb)->map_seq64 = subflow->iasn - skb->len;
 			MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
 			msk->copied_seq -= skb->len;
 		}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker
  2026-10-29  6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
                   ` (2 preceding siblings ...)
  2026-10-29  6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
@ 2026-10-29  6:47 ` Geliang Tang
  2026-10-29  8:42 ` [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
  4 siblings, 0 replies; 8+ messages in thread
From: Geliang Tang @ 2026-10-29  6:47 UTC (permalink / raw)
  To: mptcp; +Cc: Paolo Abeni, Geliang Tang

From: Paolo Abeni <pabeni@redhat.com>

When MPTCP carries TLS, __mptcp_read_sock() is reached from BH context
(via tls_strp_read_copyin() -> sk->sk_read_sock()), where calling
mptcp_rcv_space_adjust() and mptcp_cleanup_rbuf() is not safe as both
expect process context.

Move both calls to mptcp_worker(): __mptcp_read_sock() accumulates the
consumed bytes in msk->read_copied and signals the worker through a new
MPTCP_WORK_READ_COMPLETE bit. The worker drains the accumulator under
mptcp_data_lock() and runs rcvbuf autotuning and RBU update in process
context.

Also remove the backlog flush from mptcp_recv_skb() (covered by
mptcp_release_cb()), drop the msk_owned_by_me() assertion (BH caller
does not own the msk lock), and drop the redundant mptcp_recv_skb()
call after the main loop.

Initialize msk->read_copied in __mptcp_init_sock() and reset it in
mptcp_disconnect().

Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
 net/mptcp/protocol.c | 37 ++++++++++++++++++++++++++++---------
 net/mptcp/protocol.h |  2 ++
 2 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index bbce1bafa40e..322d7a5ba0b8 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3121,6 +3121,21 @@ static void mptcp_backlog_purge(struct sock *sk)
 	sk_mem_reclaim(sk);
 }
 
+static void mptcp_read_complete(struct sock *sk)
+{
+	struct mptcp_sock *msk = mptcp_sk(sk);
+	int read_copied;
+
+	mptcp_data_lock(sk);
+	read_copied = msk->read_copied;
+	msk->read_copied = 0;
+	mptcp_data_unlock(sk);
+
+	mptcp_rcv_space_adjust(msk, read_copied);
+	if (read_copied > 0)
+		mptcp_cleanup_rbuf(msk, read_copied);
+}
+
 static void mptcp_do_fastclose(struct sock *sk)
 {
 	struct mptcp_subflow_context *subflow, *tmp;
@@ -3196,6 +3211,9 @@ static void mptcp_worker(struct work_struct *work)
 	if (test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags))
 		__mptcp_retrans(sk);
 
+	if (test_and_clear_bit(MPTCP_WORK_READ_COMPLETE, &msk->flags))
+		mptcp_read_complete(sk);
+
 	fail_tout = msk->first ? READ_ONCE(mptcp_subflow_ctx(msk->first)->fail_tout) : 0;
 	if (fail_tout && time_after(jiffies, fail_tout))
 		mptcp_mp_fail_no_response(msk);
@@ -3239,6 +3257,7 @@ static void __mptcp_init_sock(struct sock *sk)
 	msk->last_data_sent = tcp_jiffies32;
 	msk->last_data_recv = tcp_jiffies32;
 	msk->last_ack_recv = tcp_jiffies32;
+	msk->read_copied = 0;
 
 	mptcp_pm_data_init(msk);
 	spin_lock_init(&msk->fallback_lock);
@@ -3684,6 +3703,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
 	msk->bytes_retrans = 0;
 	msk->rcvspace_init = 0;
 	msk->fastclosing = 0;
+	msk->read_copied = 0;
 	mptcp_init_rtt_est(msk);
 
 	/* for fallback's sake */
@@ -4601,9 +4621,6 @@ static struct sk_buff *mptcp_recv_skb(struct sock *sk, u32 *off)
 	struct sk_buff *skb;
 	u32 offset;
 
-	if (!list_empty(&msk->backlog_list))
-		mptcp_move_skbs(sk);
-
 	while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
 		offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
 		if (offset < skb->len) {
@@ -4618,6 +4635,7 @@ static struct sk_buff *mptcp_recv_skb(struct sock *sk, u32 *off)
 /*
  * Note:
  *	- It is assumed that the socket was locked by the caller.
+ *	- Can be invoked in BH scope.
  */
 static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
 			     sk_read_actor_t recv_actor, bool noack)
@@ -4627,8 +4645,6 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
 	int copied = 0;
 	u32 offset;
 
-	msk_owned_by_me(msk);
-
 	if (sk->sk_state == TCP_LISTEN)
 		return -ENOTCONN;
 	while ((skb = mptcp_recv_skb(sk, &offset)) != NULL) {
@@ -4659,11 +4675,14 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
 	if (noack)
 		goto out;
 
-	mptcp_rcv_space_adjust(msk, copied);
-
+	/* The backlog flushing is only needed when some data is actually
+	 * moved and will take place in mptcp_release_cb() via
+	 * release_sock() in the worker.
+	 */
 	if (copied > 0) {
-		mptcp_recv_skb(sk, &offset);
-		mptcp_cleanup_rbuf(msk, copied);
+		msk->read_copied += copied;
+		set_bit(MPTCP_WORK_READ_COMPLETE, &msk->flags);
+		mptcp_schedule_work(sk);
 	}
 out:
 	return copied;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 2ab8342e198d..b0082e1c83f0 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -117,6 +117,7 @@
 #define MPTCP_FALLBACK_DONE	2
 #define MPTCP_WORK_CLOSE_SUBFLOW 3
 #define MPTCP_RTX_ENABLED	4
+#define MPTCP_WORK_READ_COMPLETE 5
 
 /* MPTCP socket release cb flags */
 #define MPTCP_PUSH_PENDING	1
@@ -326,6 +327,7 @@ struct mptcp_sock {
 	u32		last_data_sent;
 	u32		last_data_recv;
 	u32		last_ack_recv;
+	int		read_copied;
 	unsigned long	timer_ival;
 	u32		token;
 	unsigned long	flags;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field
  2026-10-29  6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
@ 2026-10-29  7:10   ` sashiko-bot
  0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-10-29  7:10 UTC (permalink / raw)
  To: Geliang Tang, mptcp

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] mptcp: race and TFO progress loss in subflow_set_remote_key()
- [Critical] mptcp: stream corruption from offset subtraction in mptcp_init_skb()
- [High] mptcp: MSG_PEEK offset underflow across skbs in __mptcp_recvmsg_mskq()
- [Medium] mptcp: stale map_seq used instead of copied_seq in mptcp_inq_hint()
--

commit 285d6e793ca5e906015b250b9d8ba67747179bc7
Author: Paolo Abeni <pabeni@redhat.com>

mptcp: remove CB offset field

This commit replaces MPTCP_SKB_CB(skb)->offset with an msk-level copied_seq
field to track the sequence number of bytes already consumed by user-space
from the MPTCP receive queue.

> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index 528a72da646cc..f89ed94b668d9 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -169,7 +169,6 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
>  
>  	if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
>  	    unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
> -	    MPTCP_SKB_CB(from)->offset ||
>  	    ((to->len + from->len) > (limit >> 3)) ||
>  	    !skb_try_coalesce(to, from, fragstolen, delta))
>  		return false;
> @@ -416,8 +415,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;
> @@ -426,9 +424,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;

[Severity: Critical]
Can subtracting offset from MPTCP_SKB_CB(skb)->map_seq here introduce a
regression that corrupts the MPTCP receive byte stream when an skb has a
non-zero subflow offset (offset = tp->copied_seq - TCP_SKB_CB(skb)->seq > 0
in __mptcp_move_skbs_from_subflow())?

For example, when an skb spans two DSS mappings and the first mapping's bytes
were discarded by mptcp_subflow_discard_data(), or when TCP queues a
partially overlapping segment with TCP_SKB_CB(skb)->seq < tp->copied_seq, the
first offset bytes of skb are discarded subflow data and the valid mapped
data starts at D = mptcp_subflow_get_mapped_dsn(subflow).

Subtracting offset assigns the DSN range [D - offset, D) to the unpulled
0..offset - 1 prefix of skb:

1. Because __mptcp_try_coalesce() no longer checks MPTCP_SKB_CB(from)->offset
   and skb_try_coalesce() copies or appends the from skb starting at byte 0,
   any preceding skb in msk->backlog_list, sk->sk_receive_queue, or
   msk->out_of_order_queue whose end_seq equals D - offset will coalesce
   with this skb and append the discarded 0..offset - 1 prefix into the
   received data stream.

2. When the mapping D is out-of-order (after64(D, msk->ack_seq)),
   __mptcp_move_skb() and mptcp_data_queue_ofo() use map_seq = D - offset:

   - If D - offset > msk->ack_seq, mptcp_data_queue_ofo() inserts the skb at
     D - offset, drops genuine incoming segments for [D - offset, D) as
     duplicate data, and later delivers the stale 0..offset - 1 bytes to
     user-space when msk->ack_seq reaches D - offset.

   - If D - offset <= msk->ack_seq < D, __mptcp_move_skb() treats the
     out-of-order skb as a partial in-sequence packet, queues it immediately
     to sk->sk_receive_queue, and advances msk->ack_seq across the unreceived
     gap [msk->ack_seq, D).

[ ... ]

> @@ -2195,11 +2186,17 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
>  	int copied = 0;
>  
>  	skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
> -		u32 delta, offset = MPTCP_SKB_CB(skb)->offset;
> +		u32 delta, offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
>  		u32 data_len = skb->len - offset;
>  		u32 count;
>  		int err;
>  
> +		/* Skip skbs that are entirely before copied_seq */
> +		if (offset >= skb->len) {
> +			*last = skb;
> +			continue;
> +		}

[Severity: High]
Does this offset calculation break recvmsg() with MSG_PEEK when peeking
across multiple skbs in sk->sk_receive_queue?

When flags & MSG_PEEK is set, msk->copied_seq is not advanced after copying
data from the first skb (skb1). When the loop moves to the second skb (skb2)
where MPTCP_SKB_CB(skb2)->map_seq >= MPTCP_SKB_CB(skb1)->end_seq >
msk->copied_seq, the unsigned subtraction:

    offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb2)->map_seq;

underflows to a large u32 value, so offset >= skb->len evaluates to true and
causes __mptcp_recvmsg_mskq() to skip skb2 and all subsequent skbs in
sk->sk_receive_queue. If MSG_WAITALL or SO_RCVLOWAT requests more bytes than
skb1 contains, mptcp_recvmsg() can then hang in sk_wait_data() even though
enough unread data is already queued.

Conversely, if skb2 had a partially overlapping map_seq <= msk->copied_seq
(since __mptcp_move_skb() and __mptcp_ofo_queue() no longer advance map_seq
to msk->ack_seq), offset on skb2 will not underflow and will start copying
at msk->copied_seq instead of skb1->end_seq, duplicating the overlapping
range [msk->copied_seq, skb1->end_seq).

> +
>  		if (flags & MSG_PEEK) {
>  			/* skip already peeked skbs */
>  			if (total_data_len + data_len <= copied_total) {
> @@ -2233,11 +2230,9 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
>  
>  		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;
> +			msk->copied_seq += count;
> +			if (count < data_len)
>  				break;
> -			}

[Severity: Medium]
Now that partial reads here and in __mptcp_read_sock() advance
msk->copied_seq instead of MPTCP_SKB_CB(skb)->map_seq (and overlapping skbs
no longer advance map_seq), doesn't mptcp_inq_hint() also need to be updated?

In net/mptcp/protocol.c:mptcp_inq_hint():

    skb = skb_peek(&sk->sk_receive_queue);
    if (skb) {
        u64 hint_val = READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq;

Because MPTCP_SKB_CB(skb)->map_seq remains at the initial sequence number of
the head skb after a partial read or overlap, mptcp_inq_hint() will include
the already-consumed bytes (msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq)
when reporting unread bytes for SIOCINQ, TCP_CM_INQ, and
mptcp_rcv_space_adjust(). The commit message mentions using an
ack_seq - copied_seq hint, but mptcp_inq_hint() still subtracts
MPTCP_SKB_CB(skb)->map_seq.

[ ... ]

> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 78e886b202585..5e553adb9397e 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -533,6 +533,19 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
>  	WRITE_ONCE(msk->ack_seq, subflow->iasn);
>  	WRITE_ONCE(msk->can_ack, true);
>  	atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
> +	msk->copied_seq = subflow->iasn;
> +
> +	if (subflow->is_mptfo) {
> +		struct sock *sk = (struct sock *)msk;
> +		struct sk_buff *skb;
> +
> +		skb = skb_peek(&sk->sk_receive_queue);
> +		if (skb) {
> +			MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
> +			MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
> +			msk->copied_seq -= skb->len;
> +		}
> +	}
>  }

[Severity: Critical]
Can this TFO synchronization in subflow_set_remote_key() race with a
concurrent reader on the msk socket and also lose partial read progress?

On a passive TFO connection, mptcp_fastopen_subflow_synack_set_params()
queues the TFO skb onto sk->sk_receive_queue with map_seq = -(u64)skb->len
and msk->copied_seq = -(u64)skb->len before the peer's 3rd ACK arrives.
When the 3rd ACK arrives in BH context, both callers of
subflow_set_remote_key():

    check_fully_established()
        mptcp_data_lock((struct sock *)msk);
        __mptcp_subflow_fully_established(msk, subflow, mp_opt);
            subflow_set_remote_key(msk, subflow, mp_opt);
        mptcp_data_unlock((struct sock *)msk);

and:

    mptcp_propagate_state()
        mptcp_data_lock(sk);
        if (mp_opt) {
            ...
            subflow_set_remote_key(msk, subflow, mp_opt);
        }

only hold mptcp_data_lock(sk) without checking sock_owned_by_user(sk).

1. Meanwhile, user-space can accept() the socket and call mptcp_recvmsg() ->
   __mptcp_recvmsg_mskq() or mptcp_read_sock() -> __mptcp_read_sock() ->
   mptcp_recv_skb() under lock_sock(sk) without holding mptcp_data_lock(sk).
   If mptcp_eat_recv_skb() unlinks and frees the TFO skb from
   sk->sk_receive_queue concurrently while subflow_set_remote_key() peeks
   sk->sk_receive_queue and writes to MPTCP_SKB_CB(skb)->map_seq and end_seq,
   can subflow_set_remote_key() trigger a use-after-free write on the freed
   skb, or overwrite msk->copied_seq with subflow->iasn - skb->len right
   before the consumed skb is unlinked?

2. Even without a concurrent race, if user-space partially reads count bytes
   (0 < count < skb->len) from the TFO skb and releases lock_sock(sk) before
   the 3rd ACK arrives, msk->copied_seq is advanced to
   -(u64)skb->len + count, while the TFO skb remains at the head of
   sk->sk_receive_queue with skb->len unchanged. When the 3rd ACK arrives,
   subflow_set_remote_key() sets msk->copied_seq = subflow->iasn - skb->len,
   discarding the count bytes of progress so the next read re-copies the
   first count bytes to user-space and advances msk->bytes_consumed past
   msk->bytes_received.

The commit message notes that IASN synchronization is delegated to
mptcp_release_cb() and __mptcp_move_skb() to preserve partial TFO progress,
but that logic does not appear to be present in the patch.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793255126.git.tanggeliang@kylinos.cn?part=2

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one
  2026-10-29  6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
@ 2026-10-29  7:19   ` sashiko-bot
  0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-10-29  7:19 UTC (permalink / raw)
  To: Geliang Tang, mptcp

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] mptcp: race and copied_seq reset in subflow_set_remote_key() on TFO
- [Critical] mptcp: mptcp_init_skb() subtracts offset from map_seq64 corrupting data
- [High] mptcp: __mptcp_recvmsg_mskq() offset underflow on MSG_PEEK and skb leak
- [Medium] mptcp: mptcp_inq_hint() uses map_seq64 instead of msk->copied_seq
--

commit 67fabb0bf5f4c5ce5c4d525d8c3f0296f5ef7122
Author: Paolo Abeni <pabeni@redhat.com>

mptcp: sync mptcp skb cb layout with tcp one

This commit aligns the MPTCP SKB control buffer layout with the TCP SKB
control buffer by converting map_seq and end_seq to 32-bit fields while
retaining a 64-bit map_seq64 field for sequence validation.

> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index f89ed94b668d9..bbce1bafa40ee 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
[ ... ]
> @@ -422,11 +429,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
>  
>  	/* the skb map_seq accounts for the skb offset:
>  	 * mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
> -	 * value
> +	 * value; note that end seq number is only available in 32bits format.
>  	 */
> -	MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow) -
> -				     offset;
> +	MPTCP_SKB_CB(skb)->map_seq64 = mptcp_subflow_get_mapped_dsn(subflow) -
> +				       offset;
> +	MPTCP_SKB_CB(skb)->map_seq = (u32)MPTCP_SKB_CB(skb)->map_seq64;
>  	MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + skb->len;

[Severity: Critical]
Can subtracting offset from mptcp_subflow_get_mapped_dsn(subflow) in
mptcp_init_skb() corrupt the MPTCP data stream when an out-of-order DSS
mapping starts at a non-zero offset within a subflow SKB?

In __mptcp_move_skbs_from_subflow(), offset = seq - TCP_SKB_CB(skb)->seq
(where seq is tp->copied_seq) is non-zero when the leading [0, offset)
bytes of the subflow SKB were already consumed on the subflow. For example,
when an SKB spans the end of a previous DSS mapping that was already
acknowledged, subflow_check_data_avail() -> mptcp_subflow_discard_data()
advances tp->copied_seq without eating the SKB and clears
subflow->map_valid, after which get_mapping_status() installs the next
mapping on the remaining [offset, skb->len) bytes:

net/mptcp/subflow.c:subflow_check_data_avail()
	if (unlikely(before64(ack_seq, old_ack))) {
		mptcp_subflow_discard_data(ssk, skb, old_ack - ack_seq);
		continue;
	}

net/mptcp/subflow.c:mptcp_subflow_discard_data()
	if (!before(tcp_sk(ssk)->copied_seq, TCP_SKB_CB(skb)->end_seq))
		sk_eat_skb(ssk, skb);
	if (mptcp_subflow_get_map_offset(subflow) >= subflow->map_data_len)
		subflow->map_valid = 0;

The active DSS mapping only covers skb[offset .. skb->len - 1] starting at
mapped_dsn = mptcp_subflow_get_mapped_dsn(subflow). However,
mptcp_init_skb() sets map_seq64 = mapped_dsn - offset, assigning the MPTCP
sequence range [mapped_dsn - offset, mapped_dsn) to the discarded prefix
bytes skb[0 .. offset - 1].

When mapped_dsn is out of order at the MPTCP level (after64(mapped_dsn,
msk->ack_seq), because the intermediate DSNs were sent on another subflow),
__mptcp_move_skb(), mptcp_data_queue_ofo(), __mptcp_ofo_queue(), and
__mptcp_try_coalesce() treat skb[0 .. offset - 1] as valid payload for
[mapped_dsn - offset, mapped_dsn). Doesn't this advance msk->ack_seq across
that range, deliver the stale subflow prefix bytes to user space, and drop
the actual segment for [mapped_dsn - offset, mapped_dsn) as duplicate data?

> +	MPTCP_SKB_CB(skb)->flags = 0;
>  	MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
>  	MPTCP_SKB_CB(skb)->cant_coalesce = 0;
>  
> @@ -438,13 +447,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
>  
>  static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
>  {
> -	u64 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
> +	u32 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
>  	struct mptcp_sock *msk = mptcp_sk(sk);
>  	struct sk_buff *tail;
>  
>  	mptcp_borrow_fwdmem(sk, skb);
>  
> -	if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
> +	if (MPTCP_SKB_CB(skb)->map_seq64 == msk->ack_seq) {
>  		/* in sequence */
>  insert:
>  		if (!mptcp_try_rmem_schedule(sk, skb)) {
> @@ -462,14 +471,14 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
>  		skb_set_owner_r(skb, sk);
>  		__skb_queue_tail(&sk->sk_receive_queue, skb);
>  		return true;
> -	} else if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq)) {
> +	} else if (after64(MPTCP_SKB_CB(skb)->map_seq64, msk->ack_seq)) {
>  		mptcp_data_queue_ofo(msk, skb);
>  		return false;
>  	}
>  
>  	/* Partial packet */
> -	if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
> -		copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
> +	if (after64(MPTCP_SKB_CB(skb)->map_seq64 + skb->len, msk->ack_seq)) {
> +		copy_len = MPTCP_SKB_CB(skb)->end_seq - (u32)msk->ack_seq;
>  		goto insert;
>  	}

[Severity: High]
With MPTCP_SKB_CB(skb)->map_seq now a 32-bit value and msk->copied_seq
tracking read progress, does __mptcp_recvmsg_mskq() underflow offset during
MSG_PEEK and leak fully skipped SKBs on non-peek reads?

In net/mptcp/protocol.c:__mptcp_recvmsg_mskq():

	skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
		u32 delta, offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
		u32 data_len = skb->len - offset;
		u32 count;
		int err;

		/* Skip skbs that are entirely before copied_seq */
		if (offset >= skb->len) {
			*last = skb;
			continue;
		}

When flags & MSG_PEEK is set, msk->copied_seq is not advanced after copying
from the first SKB in sk->sk_receive_queue. On the second SKB,
MPTCP_SKB_CB(skb)->map_seq is after (u32)msk->copied_seq, so the unsigned
32-bit subtraction (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq wraps
around to a value >= 0x80000000U (>= skb->len). This hits the
offset >= skb->len check and skips every SKB after the first, preventing
MSG_PEEK from ever reading past the first SKB (and causing MSG_PEEK |
MSG_WAITALL to block in sk_wait_data() even when enough data is queued).

Additionally, when !(flags & MSG_PEEK) and an SKB in sk->sk_receive_queue
is genuinely before msk->copied_seq (offset >= skb->len),
__mptcp_recvmsg_mskq() executes *last = skb; continue; without calling
mptcp_eat_recv_skb(sk, skb) (unlike mptcp_recv_skb()). Doesn't that leave
the consumed SKB leaked on sk->sk_receive_queue and charged to
sk->sk_rmem_alloc until the socket is closed?

[ ... ]
> @@ -2392,7 +2399,8 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
>  
>  	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 = READ_ONCE(msk->ack_seq) -
> +			       MPTCP_SKB_CB(skb)->map_seq64;
>  
>  		if (hint_val >= INT_MAX)
>  			return INT_MAX;

[Severity: Medium]
Should mptcp_inq_hint() subtract msk->copied_seq instead of
MPTCP_SKB_CB(skb)->map_seq64?

When user space partially reads the head SKB in sk->sk_receive_queue via
__mptcp_recvmsg_mskq() or __mptcp_read_sock() (or when a partially
overlapping SKB is queued in __mptcp_move_skb() or __mptcp_ofo_queue()),
msk->copied_seq advances while MPTCP_SKB_CB(skb)->map_seq64 on the head SKB
remains unchanged at its initial sequence number:

net/mptcp/protocol.c:__mptcp_recvmsg_mskq()
		if (!(flags & MSG_PEEK)) {
			msk->bytes_consumed += count;
			msk->copied_seq += count;
			if (count < data_len)
				break;

Computing READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq64 counts the
already-consumed or skipped prefix bytes on the head SKB (including when
subsequent segments coalesce into that head SKB via mptcp_try_coalesce())
as unread.

Beyond reporting inflated unread byte counts for SIOCINQ in mptcp_ioctl()
and TCP_CM_INQ in mptcp_recvmsg(), mptcp_rcv_space_adjust() subtracts
mptcp_inq_hint(sk) from msk->rcvq_space.copied:

net/mptcp/protocol.c:mptcp_rcv_space_adjust()
	copied = msk->rcvq_space.copied;
	copied -= mptcp_inq_hint(sk);
	if (copied <= msk->rcvq_space.space)
		goto new_measure;

Won't subtracting the inflated hint_val cause copied to be under-counted
and prevent mptcp_rcvbuf_grow() from auto-tuning the receive buffer?

> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 5e553adb9397e..d144eb2373ef6 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -542,6 +542,7 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
>  		skb = skb_peek(&sk->sk_receive_queue);
>  		if (skb) {
>  			MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
> +			MPTCP_SKB_CB(skb)->map_seq64 = subflow->iasn - skb->len;
>  			MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
>  			msk->copied_seq -= skb->len;
>  		}

[Severity: Critical]
What happens on passive MPTCP Fast Open if user space accepts the socket
and reads from sk->sk_receive_queue before or while the client's 3rd ACK
triggers subflow_set_remote_key()?

In mptcp_fastopen_subflow_synack_set_params(), the TFO data SKB is queued
onto sk->sk_receive_queue with map_seq64 = -(u64)skb->len and
msk->copied_seq = -(u64)skb->len before the 3rd ACK arrives. Once the child
socket is accepted, user space can call mptcp_recvmsg() ->
__mptcp_recvmsg_mskq() (or __mptcp_read_sock() -> mptcp_recv_skb()) under
lock_sock(sk) without holding mptcp_data_lock(sk), while the 3rd ACK
arrives in softirq via check_fully_established() ->
__mptcp_subflow_fully_established() -> subflow_set_remote_key() (or
mptcp_propagate_state() -> subflow_set_remote_key()) under
mptcp_data_lock(sk) without checking sock_owned_by_user(sk):

net/mptcp/subflow.c:subflow_set_remote_key()
	msk->copied_seq = subflow->iasn;

	if (subflow->is_mptfo) {
		struct sock *sk = (struct sock *)msk;
		struct sk_buff *skb;

		skb = skb_peek(&sk->sk_receive_queue);
		if (skb) {
			MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
			MPTCP_SKB_CB(skb)->map_seq64 = subflow->iasn - skb->len;
			MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
			msk->copied_seq -= skb->len;
		}
	}

This appears to cause three regressions:

1. If user space partially reads count bytes (0 < count < skb->len) from
the TFO SKB before the 3rd ACK arrives, __mptcp_recvmsg_mskq() advances
msk->copied_seq to -(u64)skb->len + count while leaving the SKB (with
skb->len unchanged) at the head of sk->sk_receive_queue. When
subflow_set_remote_key() runs, it sets msk->copied_seq to
subflow->iasn - skb->len, discarding the partial read progress so the next
recvmsg() re-delivers the first count bytes.

2. In __mptcp_recvmsg_mskq() and mptcp_recv_skb(), mptcp_eat_recv_skb()
unlinks the TFO SKB from sk->sk_receive_queue and frees it via
skb_attempt_defer_free(skb) under lock_sock(sk) without mptcp_data_lock(sk).
If mptcp_eat_recv_skb() unlinks and frees skb right after
subflow_set_remote_key() peeks it with skb_peek(&sk->sk_receive_queue),
can't subflow_set_remote_key() perform a use-after-free write to
MPTCP_SKB_CB(skb)?

3. In __mptcp_recvmsg_mskq(), msk->copied_seq += count runs before
mptcp_eat_recv_skb(sk, skb). If subflow_set_remote_key() executes between
those two lines when the TFO SKB is completely read, skb_peek() still sees
the SKB and sets msk->copied_seq = subflow->iasn - skb->len before
mptcp_eat_recv_skb() unlinks it, leaving msk->copied_seq permanently behind
msk->ack_seq by skb->len and causing subsequent SKBs to underflow offset
and be skipped.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793255126.git.tanggeliang@kylinos.cn?part=3

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage
  2026-10-29  6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
                   ` (3 preceding siblings ...)
  2026-10-29  6:47 ` [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
@ 2026-10-29  8:42 ` MPTCP CI
  4 siblings, 0 replies; 8+ messages in thread
From: MPTCP CI @ 2026-10-29  8:42 UTC (permalink / raw)
  To: Paolo Abeni; +Cc: mptcp

Hi Paolo,

Thank you for your modifications, that's great!

Our CI did some validations and here is its report:

- KVM Validation: normal (except selftest_mptcp_join): Unstable: 2 failed test(s): packetdrill_syscalls selftest_mptcp_sockopt ⚠️ 
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Success! ✅
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Perf: Unstable: 1 failed test(s): validation: - Critical: mptcp-perf/01-Test_1/client.log: Global Timeout - Critical: mptcp-perf/01-Test_1/server.log: Global Timeout - Critical: sched-baseline/01-slow_primary/client.log: Global Timeout - Critical: sched-baseline/01-slow_primary/server.log: Global Timeout - Critical: sched-baseline/02-tiny_primary/client.log: Global Timeout - Critical: sched-baseline/02-tiny_primary/server.log: Global Timeout - Critical: sched-baseline/03-fast_both/client.log: Global Timeout - Critical: sched-baseline/03-fast_both/server.log: Global Timeout - Critical: sched-baseline/04-balanced/client.log: Global Timeout - Critical: sched-baseline/04-balanced/server.log: Global Timeout - Critical: sched-baseline/05-slow_near_primary/client.log: Global Timeout - Critical: sched-baseline/05-slow_near_primary/server.log: Global Timeout - Critical: sched-baseline/06-slow_secondary/client.log: Global Timeout - Critical: sched-baseline/06-slow_secondary/server.log: Global Timeout - Critical: sched-baseline/07-very_fast/client.log: Global Timeout - Critical: sched-baseline/07-very_fast/server.log: Global Timeout - Critical: sched-baseline/08-shallow_primary/client.log: Global Timeout - Critical: sched-baseline/08-shallow_primary/server.log: Global Timeout - Critical: sched-baseline/09-high_lat_primary/client.log: Global Timeout - Critical: sched-baseline/09-high_lat_primary/server.log: Global Timeout - Critical: sched-baseline/10-huge_lat_primary/client.log: Global Timeout - Critical: sched-baseline/10-huge_lat_primary/server.log: Global Timeout - Critical: sched-baseline/11-high_lat_secondary/client.log: Global Timeout - Critical: sched-baseline/11-high_lat_secondary/server.log: Global Timeout - Critical: sched-baseline/12-huge_lat_secondary/client.log: Global Timeout - Critical: sched-baseline/12-huge_lat_secondary/server.log: Global Timeout - Critical: sched-baseline/13-bloated_primary/client.log: Global Timeout - Critical: sched-baseline/13-bloated_primary/server.log: Global Timeout - Critical: sched-baseline/14-bloated_secondary/client.log: Global Timeout - Critical: sched-baseline/14-bloated_secondary/server.log: Global Timeout - Critical: sched-baseline/15-slow_primary_upload/client.log: Global Timeout - Critical: sched-baseline/15-slow_primary_upload/server.log: Global Timeout - Critical: sched-baseline/16-balanced_upload/client.log: Global Timeout - Critical: sched-baseline/16-balanced_upload/server.log: Global Timeout - Critical: sched-baseline/17-slow_primary_parallel_4/client.log: Global Timeout - Critical: sched-baseline/17-slow_primary_parallel_4/server.log: Global Timeout - Critical: sched-baseline/18-balanced_parallel_4/client.log: Global Timeout - Critical: sched-baseline/18-balanced_parallel_4/server.log: Global Timeout - Critical: sched-bbr/01-slow_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/01-slow_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/02-balanced_bbr/client.log: Global Timeout - Critical: sched-bbr/02-balanced_bbr/server.log: Global Timeout - Critical: sched-bbr/03-slow_near_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/03-slow_near_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/04-high_lat_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/04-high_lat_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/05-huge_lat_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/05-huge_lat_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/06-high_lat_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/06-high_lat_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/07-huge_lat_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/07-huge_lat_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/08-slow_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/08-slow_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/09-very_fast_bbr/client.log: Global Timeout - Critical: sched-bbr/09-very_fast_bbr/server.log: Global Timeout - Critical: sched-bbr/10-bloated_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/10-bloated_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/11-bloated_slow_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/11-bloated_slow_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/12-very_bloated_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/12-very_bloated_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/13-bad_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/13-bad_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/14-far_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/14-far_secondary_bbr/server.log: Global Timeout - Critical: sched-clientlim/01-slow_primary_bw_5/client.log: Global Timeout - Critical: sched-clientlim/01-slow_primary_bw_5/server.log: Global Timeout - Critical: sched-clientlim/02-balanced_bw_5/client.log: Global Timeout - Critical: sched-clientlim/02-balanced_bw_5/server.log: Global Timeout - Critical: sched-clientlim/03-huge_lat_primary_bw_5/client.log: Global Timeout - Critical: sched-clientlim/03-huge_lat_primary_bw_5/server.log: Global Timeout - Critical: sched-clientlim/04-slow_secondary_bw_5/client.log: Global Timeout - Critical: sched-clientlim/04-slow_secondary_bw_5/server.log: Global Timeout - Critical: sched-clientlim/05-slow_primary_bw_10/client.log: Global Timeout - Critical: sched-clientlim/05-slow_primary_bw_10/server.log: Global Timeout - Critical: sched-clientlim/06-balanced_bw_10/client.log: Global Timeout - Critical: sched-clientlim/06-balanced_bw_10/server.log: Global Timeout - Critical: sched-clientlim/07-huge_lat_primary_bw_10/client.log: Global Timeout - Critical: sched-clientlim/07-huge_lat_primary_bw_10/server.log: Global Timeout - Critical: sched-clientlim/08-slow_secondary_bw_10/client.log: Global Timeout - Critical: sched-clientlim/08-slow_secondary_bw_10/server.log: Global Timeout - Critical: sched-clientlim/09-slow_primary_bw_50/client.log: Global Timeout - Critical: sched-clientlim/09-slow_primary_bw_50/server.log: Global Timeout - Critical: sched-clientlim/10-balanced_bw_50/client.log: Global Timeout - Critical: sched-clientlim/10-balanced_bw_50/server.log: Global Timeout - Critical: sched-clientlim/11-huge_lat_primary_bw_50/client.log: Global Timeout - Critical: sched-clientlim/11-huge_lat_primary_bw_50/server.log: Global Timeout - Critical: sched-clientlim/12-slow_secondary_bw_50/client.log: Global Timeout - Critical: sched-clientlim/12-slow_secondary_bw_50/server.log: Global Timeout - Critical: sched-cpu-preempt/01-shallow_primary/client.log: Global Timeout - Critical: sched-cpu-preempt/01-shallow_primary/server.log: Global Timeout - Critical: sched-cpu-preempt/02-shallow_primary_cpu_hog/client.log: Global Timeout - Critical: sched-cpu-preempt/02-shallow_primary_cpu_hog/server.log: Global Timeout - Critical: sched-mem-pressure/01-bloated_slow_secondary_receiver_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/01-bloated_slow_secondary_receiver_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/02-bloated_slow_secondary_receiver_tcp_mem_parallel_16/client.log: Global Timeout - Critical: sched-mem-pressure/02-bloated_slow_secondary_receiver_tcp_mem_parallel_16/server.log: Global Timeout - Critical: sched-mem-pressure/03-bloated_slow_secondary_receiver_tcp_mem_parallel_99/client.log: Global Timeout - Critical: sched-mem-pressure/03-bloated_slow_secondary_receiver_tcp_mem_parallel_99/server.log: Global Timeout - Critical: sched-mem-pressure/04-very_bloated_primary_both_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/04-very_bloated_primary_both_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/05-slow_primary_both_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/05-slow_primary_both_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/06-balanced_both_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/06-balanced_both_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/07-very_bloated_primary_both_tcp_mem_parallel_10/client.log: Global Timeout - Critical: sched-mem-pressure/07-very_bloated_primary_both_tcp_mem_parallel_10/server.log: Global Timeout - Critical: sched-mem-pressure/08-slow_primary_both_tcp_mem_parallel_10/client.log: Global Timeout - Critical: sched-mem-pressure/08-slow_primary_both_tcp_mem_parallel_10/server.log: Global Timeout - Critical: sched-mem-pressure/09-balanced_both_tcp_mem_parallel_10/client.log: Global Timeout - Critical: sched-mem-pressure/09-balanced_both_tcp_mem_parallel_10/server.log: Global Timeout - Critical: sched-rmem/01-slow_primary_rmem_200/client.log: Global Timeout - Critical: sched-rmem/01-slow_primary_rmem_200/server.log: Global Timeout - Critical: sched-rmem/02-balanced_rmem_200/client.log: Global Timeout - Critical: sched-rmem/02-balanced_rmem_200/server.log: Global Timeout - Critical: sched-rmem/03-huge_lat_primary_rmem_200/client.log: Global Timeout - Critical: sched-rmem/03-huge_lat_primary_rmem_200/server.log: Global Timeout - Critical: sched-rmem/04-bloated_primary_rmem_200/client.log: Global Timeout - Critical: sched-rmem/04-bloated_primary_rmem_200/server.log: Global Timeout - Critical: sched-rmem/05-slow_primary_rmem_800/client.log: Global Timeout - Critical: sched-rmem/05-slow_primary_rmem_800/server.log: Global Timeout - Critical: sched-rmem/06-balanced_rmem_800/client.log: Global Timeout - Critical: sched-rmem/06-balanced_rmem_800/server.log: Global Timeout - Critical: sched-rmem/07-huge_lat_primary_rmem_800/client.log: Global Timeout - Critical: sched-rmem/07-huge_lat_primary_rmem_800/server.log: Global Timeout - Critical: sched-rmem/08-bloated_primary_rmem_800/client.log: Global Timeout - Critical: sched-rmem/08-bloated_primary_rmem_800/server.log: Global Timeout - Critical: sched-rmem/09-slow_primary_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/09-slow_primary_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/10-balanced_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/10-balanced_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/11-huge_lat_primary_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/11-huge_lat_primary_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/12-bloated_primary_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/12-bloated_primary_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/13-slow_primary_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/13-slow_primary_rmem_6000/server.log: Global Timeout - Critical: sched-rmem/14-balanced_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/14-balanced_rmem_6000/server.log: Global Timeout - Critical: sched-rmem/15-huge_lat_primary_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/15-huge_lat_primary_rmem_6000/server.log: Global Timeout - Critical: sched-rmem/16-bloated_primary_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/16-bloated_primary_rmem_6000/server.log: Global Timeout - Critical: sched-serverlim/01-slow_primary_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/01-slow_primary_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/02-balanced_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/02-balanced_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/03-huge_lat_primary_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/03-huge_lat_primary_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/04-slow_secondary_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/04-slow_secondary_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/05-slow_primary_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/05-slow_primary_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/06-balanced_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/06-balanced_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/07-huge_lat_primary_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/07-huge_lat_primary_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/08-slow_secondary_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/08-slow_secondary_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/09-slow_primary_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/09-slow_primary_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/10-balanced_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/10-balanced_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/11-huge_lat_primary_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/11-huge_lat_primary_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/12-slow_secondary_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/12-slow_secondary_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/13-slow_primary_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/13-slow_primary_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/14-balanced_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/14-balanced_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/15-huge_lat_primary_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/15-huge_lat_primary_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/16-slow_secondary_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/16-slow_secondary_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/17-slow_primary_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/17-slow_primary_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/18-balanced_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/18-balanced_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/19-huge_lat_primary_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/19-huge_lat_primary_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/20-slow_secondary_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/20-slow_secondary_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/21-slow_primary_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/21-slow_primary_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/22-balanced_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/22-balanced_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/23-huge_lat_primary_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/23-huge_lat_primary_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/24-slow_secondary_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/24-slow_secondary_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/25-slow_primary_bw_10_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/25-slow_primary_bw_10_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-serverlim/26-balanced_bw_10_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/26-balanced_bw_10_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-serverlim/27-slow_primary_bw_50_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/27-slow_primary_bw_50_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-serverlim/28-balanced_bw_50_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/28-balanced_bw_50_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-weak-secondary/01-weak_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/01-weak_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/02-bad_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/02-bad_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/03-far_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/03-far_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/04-bloated_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/04-bloated_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/05-bloated_slow_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/05-bloated_slow_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/06-very_bloated_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/06-very_bloated_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/07-weak_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/07-weak_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/08-bad_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/08-bad_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/09-far_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/09-far_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/10-bloated_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/10-bloated_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/11-bloated_slow_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/11-bloated_slow_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/12-very_bloated_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/12-very_bloated_secondary_backup/server.log: Global Timeout ❌
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/40833457021

Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/50ce0a995dda
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1195957


If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:

    $ cd [kernel source code]
    $ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
        --pull always mptcp/mptcp-upstream-virtme-docker:latest \
        auto-normal

For more details:

    https://github.com/multipath-tcp/mptcp-upstream-virtme-docker


Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)

Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-29  8:42 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29  6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-10-29  6:47 ` [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
2026-10-29  6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
2026-10-29  7:10   ` sashiko-bot
2026-10-29  6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
2026-10-29  7:19   ` sashiko-bot
2026-10-29  6:47 ` [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
2026-10-29  8:42 ` [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.