* [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage
@ 2026-08-27 10:57 Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields Geliang Tang
` (11 more replies)
0 siblings, 12 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <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".
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/
Geliang Tang (6):
mptcp: use atomic64_t for locklessly accessed u64 fields
mptcp: implement peek_len for proto_ops
mptcp: add sendmsg_locked to proto_ops
mptcp: track app-limited state in mptcp_sendmsg
selftests: mptcp: sockopt: check app_limited
Squash to "selftests/bpf: Add bpf_burst scheduler & test"
Paolo Abeni (5):
mptcp: drop the mptcp_ooo_try_coalesce() helper
mptcp: drop the cant_coalesce CB field
mptcp: remove CB offset field
mptcp: sync mptcp skb cb layout with tcp one
mptcp: defer read_sock cleanup to mptcp_worker
include/net/tcp.h | 1 +
include/trace/events/mptcp.h | 7 +-
net/ipv4/tcp.c | 9 +-
net/mptcp/fastopen.c | 17 +-
net/mptcp/options.c | 26 +-
net/mptcp/protocol.c | 454 ++++++++++++------
net/mptcp/protocol.h | 46 +-
net/mptcp/sockopt.c | 8 +-
net/mptcp/subflow.c | 37 +-
.../selftests/bpf/progs/mptcp_bpf_burst.c | 3 +-
.../selftests/net/mptcp/mptcp_sockopt.c | 1 +
11 files changed, 401 insertions(+), 208 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 11:15 ` sashiko-bot
2026-08-27 10:57 ` [PATCH mptcp-next v9 02/11] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
` (10 subsequent siblings)
11 siblings, 1 reply; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Several u64 fields in struct mptcp_sock are accessed locklessly and
could suffer from torn reads on 32-bit architectures:
- ack_seq: updated in process context without mptcp_data_lock() and
read concurrently in softirq via mptcp_write_options()
- write_seq, snd_nxt: sequence numbers accessed from multiple contexts
- bytes_received, bytes_consumed: byte counters for flow control
- snd_una, wnd_end: send window tracking fields
On 32-bit architectures, 64-bit READ_ONCE/WRITE_ONCE compiles to two
32-bit accesses, so a torn read could produce corrupted values.
Convert these fields from u64 to atomic64_t to guarantee tear-free
accesses on all architectures. Also cache atomic64_read() in local
variables where the same field is read multiple times within a function
to avoid inconsistent repeated reads (e.g., in __mptcp_move_skb(),
__mptcp_ofo_queue(), __mptcp_retrans(), __mptcp_clean_una(),
mptcp_check_send_data_fin(), mptcp_update_post_push(), and
mptcp_sk_clone_init()).
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/trace/events/mptcp.h | 7 +-
net/mptcp/fastopen.c | 4 +-
net/mptcp/options.c | 26 +++---
net/mptcp/protocol.c | 151 ++++++++++++++++++++---------------
net/mptcp/protocol.h | 24 +++---
net/mptcp/sockopt.c | 8 +-
net/mptcp/subflow.c | 18 +++--
7 files changed, 134 insertions(+), 104 deletions(-)
diff --git a/include/trace/events/mptcp.h b/include/trace/events/mptcp.h
index 22882bd03459..b3660cc94601 100644
--- a/include/trace/events/mptcp.h
+++ b/include/trace/events/mptcp.h
@@ -215,7 +215,9 @@ TRACE_EVENT(mptcp_rcvbuf_grow,
struct inet_sock *inet = inet_sk(sk);
bool ofo_empty;
__be32 *p32;
+ u64 ack_seq;
+ ack_seq = atomic64_read(&msk->ack_seq);
__entry->time = time;
__entry->rtt_us = mptcp_rtt_us_est(msk) >> 3;
__entry->copied = msk->rcvq_space.copied;
@@ -224,11 +226,10 @@ TRACE_EVENT(mptcp_rcvbuf_grow,
ofo_empty = RB_EMPTY_ROOT(&msk->out_of_order_queue);
__entry->ooo_space = ofo_empty ? 0 :
MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq -
- msk->ack_seq;
+ ack_seq;
__entry->rcvbuf = sk->sk_rcvbuf;
- __entry->rcv_wnd = atomic64_read(&msk->rcv_wnd_sent) -
- msk->ack_seq;
+ __entry->rcv_wnd = atomic64_read(&msk->rcv_wnd_sent) - ack_seq;
__entry->scaling_ratio = msk->scaling_ratio;
__entry->sport = ntohs(inet->inet_sport);
__entry->dport = ntohs(inet->inet_dport);
diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index f717750906ff..0012690a2202 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;
@@ -54,10 +55,11 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
mptcp_data_lock(sk);
DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));
+ msk = mptcp_sk(sk);
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;
+ atomic64_add(skb->len, &msk->bytes_received);
sk->sk_data_ready(sk);
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index f87707110c75..b33bf445c0ba 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -600,7 +600,8 @@ static void mptcp_write_data_fin(struct mptcp_subflow_context *subflow,
/* The write_seq value has already been incremented, so the actual
* sequence number for the DATA_FIN is one less.
*/
- u64 data_fin_tx_seq = READ_ONCE(mptcp_sk(subflow->conn)->write_seq) - 1;
+ u64 data_fin_tx_seq =
+ atomic64_read(&mptcp_sk(subflow->conn)->write_seq) - 1;
if (!ext->use_map || !skb->len) {
/* RFC6824 requires a DSS mapping with specific values
@@ -1086,8 +1087,8 @@ u64 __mptcp_expand_seq(u64 old_seq, u64 cur_seq)
static void __mptcp_snd_una_update(struct mptcp_sock *msk, u64 new_snd_una)
{
- msk->bytes_acked += new_snd_una - msk->snd_una;
- WRITE_ONCE(msk->snd_una, new_snd_una);
+ msk->bytes_acked += new_snd_una - atomic64_read(&msk->snd_una);
+ atomic64_set(&msk->snd_una, new_snd_una);
}
static void rwin_update(struct mptcp_sock *msk, struct sock *ssk,
@@ -1120,7 +1121,7 @@ static void ack_update_msk(struct mptcp_sock *msk,
struct sock *ssk,
struct mptcp_options_received *mp_opt)
{
- u64 new_wnd_end, new_snd_una, snd_nxt = READ_ONCE(msk->snd_nxt);
+ u64 new_wnd_end, new_snd_una, snd_nxt = atomic64_read(&msk->snd_nxt);
struct sock *sk = (struct sock *)msk;
u64 old_snd_una;
@@ -1130,7 +1131,7 @@ static void ack_update_msk(struct mptcp_sock *msk,
* wrongly expanding to a future ack sequence number, which is way
* more dangerous than missing an ack
*/
- old_snd_una = msk->snd_una;
+ old_snd_una = atomic64_read(&msk->snd_una);
new_snd_una = mptcp_expand_seq(old_snd_una, mp_opt->data_ack, mp_opt->ack64);
/* ACK for data not even sent yet? Ignore.*/
@@ -1139,11 +1140,11 @@ static void ack_update_msk(struct mptcp_sock *msk,
new_wnd_end = new_snd_una + tcp_sk(ssk)->snd_wnd;
- if (after64(new_wnd_end, msk->wnd_end))
- WRITE_ONCE(msk->wnd_end, new_wnd_end);
+ if (after64(new_wnd_end, atomic64_read(&msk->wnd_end)))
+ atomic64_set(&msk->wnd_end, new_wnd_end);
/* this assumes mptcp_incoming_options() is invoked after tcp_ack() */
- if (after64(msk->wnd_end, snd_nxt))
+ if (after64(atomic64_read(&msk->wnd_end), snd_nxt))
__mptcp_check_push(sk, ssk);
if (after64(new_snd_una, old_snd_una)) {
@@ -1155,7 +1156,7 @@ static void ack_update_msk(struct mptcp_sock *msk,
trace_ack_update_msk(mp_opt->data_ack,
old_snd_una, new_snd_una,
- new_wnd_end, READ_ONCE(msk->wnd_end));
+ new_wnd_end, atomic64_read(&msk->wnd_end));
}
bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq, bool use_64bit)
@@ -1169,7 +1170,8 @@ bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq, bool us
return false;
WRITE_ONCE(msk->rcv_data_fin_seq,
- mptcp_expand_seq(READ_ONCE(msk->ack_seq), data_fin_seq, use_64bit));
+ mptcp_expand_seq(atomic64_read(&msk->ack_seq),
+ data_fin_seq, use_64bit));
WRITE_ONCE(msk->rcv_data_fin, 1);
return true;
@@ -1242,7 +1244,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
/* on fallback we just need to ignore the msk-level snd_una, as
* this is really plain TCP
*/
- __mptcp_snd_una_update(msk, READ_ONCE(msk->snd_nxt));
+ __mptcp_snd_una_update(msk, atomic64_read(&msk->snd_nxt));
__mptcp_data_acked(subflow->conn);
mptcp_data_unlock(subflow->conn);
@@ -1539,7 +1541,7 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp,
*/
subflow = mptcp_subflow_ctx(ssk);
msk = mptcp_sk(subflow->conn);
- ack_seq = READ_ONCE(msk->ack_seq);
+ ack_seq = atomic64_read(&msk->ack_seq);
if (mpext->ack64) {
put_unaligned_be64(ack_seq, ptr);
ptr += 2;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f22d64ab1c53..26b49480941c 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -59,7 +59,7 @@ static struct net_device *mptcp_napi_dev;
/* Returns end sequence number of the receiver's advertised window */
u64 mptcp_wnd_end(const struct mptcp_sock *msk)
{
- return READ_ONCE(msk->wnd_end);
+ return atomic64_read(&msk->wnd_end);
}
static const struct proto_ops *mptcp_fallback_tcp_ops(const struct sock *sk)
@@ -451,10 +451,12 @@ 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;
struct mptcp_sock *msk = mptcp_sk(sk);
struct sk_buff *tail;
+ u64 ack_seq;
mptcp_borrow_fwdmem(sk, skb);
- if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
+ ack_seq = atomic64_read(&msk->ack_seq);
+ if (MPTCP_SKB_CB(skb)->map_seq == ack_seq) {
/* in sequence */
insert:
if (!mptcp_try_rmem_schedule(sk, skb)) {
@@ -463,8 +465,8 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
return false;
}
- msk->bytes_received += copy_len;
- WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len);
+ atomic64_add(copy_len, &msk->bytes_received);
+ atomic64_add(copy_len, &msk->ack_seq);
tail = skb_peek_tail(&sk->sk_receive_queue);
if (tail && mptcp_try_coalesce(sk, tail, skb))
return true;
@@ -472,17 +474,17 @@ 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_seq, 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;
- MPTCP_SKB_CB(skb)->offset += msk->ack_seq -
+ if (after64(MPTCP_SKB_CB(skb)->end_seq, ack_seq)) {
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - ack_seq;
+ MPTCP_SKB_CB(skb)->offset += ack_seq -
MPTCP_SKB_CB(skb)->map_seq;
- MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
+ MPTCP_SKB_CB(skb)->map_seq += ack_seq -
MPTCP_SKB_CB(skb)->map_seq;
goto insert;
}
@@ -533,7 +535,7 @@ static bool mptcp_pending_data_fin_ack(struct sock *sk)
return ((1 << sk->sk_state) &
(TCPF_FIN_WAIT1 | TCPF_CLOSING | TCPF_LAST_ACK)) &&
- msk->write_seq == READ_ONCE(msk->snd_una);
+ atomic64_read(&msk->write_seq) == atomic64_read(&msk->snd_una);
}
static void mptcp_check_data_fin_ack(struct sock *sk)
@@ -569,7 +571,7 @@ static bool mptcp_pending_data_fin(struct sock *sk, u64 *seq)
(TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2))) {
u64 rcv_data_fin_seq = READ_ONCE(msk->rcv_data_fin_seq);
- if (READ_ONCE(msk->ack_seq) == rcv_data_fin_seq) {
+ if (atomic64_read(&msk->ack_seq) == rcv_data_fin_seq) {
if (seq)
*seq = rcv_data_fin_seq;
@@ -710,7 +712,7 @@ static void mptcp_check_data_fin(struct sock *sk)
*/
if (mptcp_pending_data_fin(sk, &rcv_data_fin_seq)) {
- WRITE_ONCE(msk->ack_seq, msk->ack_seq + 1);
+ atomic64_inc(&msk->ack_seq);
WRITE_ONCE(msk->rcv_data_fin, 0);
WRITE_ONCE(sk->sk_shutdown, sk->sk_shutdown | RCV_SHUTDOWN);
@@ -879,22 +881,22 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
{
struct sock *sk = (struct sock *)msk;
struct sk_buff *skb, *tail;
+ u64 end_seq, 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 = atomic64_read(&msk->ack_seq);
skb = rb_to_skb(p);
- if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq))
+ if (after64(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(!after64(MPTCP_SKB_CB(skb)->end_seq, ack_seq))) {
mptcp_drop(sk, skb);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
continue;
@@ -903,18 +905,18 @@ 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)) {
- int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
+ int delta = 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,
+ MPTCP_SKB_CB(skb)->map_seq, 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;
- WRITE_ONCE(msk->ack_seq, end_seq);
+ atomic64_add(end_seq - ack_seq, &msk->bytes_received);
+ atomic64_set(&msk->ack_seq, end_seq);
moved = true;
}
return moved;
@@ -1072,7 +1074,7 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk)
static void mptcp_subflow_joined(struct mptcp_sock *msk, struct sock *ssk)
{
- mptcp_subflow_ctx(ssk)->map_seq = READ_ONCE(msk->ack_seq);
+ mptcp_subflow_ctx(ssk)->map_seq = atomic64_read(&msk->ack_seq);
msk->allow_infinite_fallback = false;
mptcp_event(MPTCP_EVENT_SUB_ESTABLISHED, msk, ssk, GFP_ATOMIC);
}
@@ -1178,7 +1180,7 @@ static bool mptcp_frag_can_collapse_to(const struct mptcp_sock *msk,
pfrag->page == df->page &&
pfrag->size - pfrag->offset > 0 &&
pfrag->offset == (df->offset + df->data_len) &&
- df->data_seq + df->data_len == msk->write_seq;
+ df->data_seq + df->data_len == atomic64_read(&msk->write_seq);
}
static void dfrag_uncharge(struct sock *sk, int len)
@@ -1201,9 +1203,11 @@ static void __mptcp_clean_una(struct sock *sk)
{
struct mptcp_sock *msk = mptcp_sk(sk);
struct mptcp_data_frag *dtmp, *dfrag;
- u64 snd_una;
+ u64 snd_una, snd_nxt, write_seq;
- snd_una = msk->snd_una;
+ snd_una = atomic64_read(&msk->snd_una);
+ snd_nxt = atomic64_read(&msk->snd_nxt);
+ write_seq = atomic64_read(&msk->write_seq);
list_for_each_entry_safe(dfrag, dtmp, &msk->rtx_queue, list) {
if (after64(dfrag->data_seq + dfrag->data_len, snd_una))
break;
@@ -1241,11 +1245,11 @@ static void __mptcp_clean_una(struct sock *sk)
}
/* all retransmitted data acked, recovery completed */
- if (unlikely(msk->recovery) && after64(msk->snd_una, msk->recovery_snd_nxt))
+ if (unlikely(msk->recovery) && after64(snd_una, msk->recovery_snd_nxt))
msk->recovery = false;
out:
- if (snd_una == msk->snd_nxt && snd_una == msk->write_seq) {
+ if (snd_una == snd_nxt && snd_una == write_seq) {
if (mptcp_rtx_timer_pending(sk) && !mptcp_data_fin_enabled(msk))
mptcp_stop_rtx_timer(sk);
} else {
@@ -1305,7 +1309,7 @@ mptcp_carve_data_frag(const struct mptcp_sock *msk, struct page_frag *pfrag,
dfrag = (struct mptcp_data_frag *)(page_to_virt(pfrag->page) + offset);
dfrag->data_len = 0;
- dfrag->data_seq = msk->write_seq;
+ dfrag->data_seq = atomic64_read(&msk->write_seq);
dfrag->overhead = offset - orig_offset + sizeof(struct mptcp_data_frag);
dfrag->offset = offset + sizeof(struct mptcp_data_frag);
dfrag->already_sent = 0;
@@ -1499,13 +1503,13 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
/* Zero window and all data acked? Probe. */
copy = mptcp_check_allowed_size(msk, ssk, data_seq, copy);
if (copy == 0) {
- u64 snd_una = READ_ONCE(msk->snd_una);
+ u64 snd_una = atomic64_read(&msk->snd_una);
/* No need for zero probe if there are any data pending
* either at the msk or ssk level; skb is the current write
* queue tail and can be empty at this point.
*/
- if (snd_una != msk->snd_nxt || skb->len ||
+ if (snd_una != atomic64_read(&msk->snd_nxt) || skb->len ||
skb != tcp_send_head(ssk)) {
tcp_remove_empty_skb(ssk);
return 0;
@@ -1686,7 +1690,8 @@ struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
if (!ssk || !sk_stream_memory_free(ssk))
return NULL;
- burst = min(MPTCP_SEND_BURST_SIZE, mptcp_wnd_end(msk) - msk->snd_nxt);
+ burst = min(MPTCP_SEND_BURST_SIZE,
+ mptcp_wnd_end(msk) - atomic64_read(&msk->snd_nxt));
wmem = READ_ONCE(ssk->sk_wmem_queued);
if (!burst)
return ssk;
@@ -1709,6 +1714,7 @@ static void mptcp_update_post_push(struct mptcp_sock *msk,
struct mptcp_data_frag *dfrag,
u32 sent)
{
+ u64 snd_nxt = atomic64_read(&msk->snd_nxt);
u64 snd_nxt_new = dfrag->data_seq;
dfrag->already_sent += sent;
@@ -1726,9 +1732,9 @@ static void mptcp_update_post_push(struct mptcp_sock *msk,
* that has been handed to the subflow for transmission
* and skip update in case it was old dfrag.
*/
- if (likely(after64(snd_nxt_new, msk->snd_nxt))) {
- msk->bytes_sent += snd_nxt_new - msk->snd_nxt;
- WRITE_ONCE(msk->snd_nxt, snd_nxt_new);
+ if (likely(after64(snd_nxt_new, snd_nxt))) {
+ msk->bytes_sent += snd_nxt_new - snd_nxt;
+ atomic64_set(&msk->snd_nxt, snd_nxt_new);
}
}
@@ -1915,7 +1921,8 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk, bool
mptcp_reset_rtx_timer(sk);
if (msk->snd_data_fin_enable &&
- msk->snd_nxt + 1 == msk->write_seq)
+ atomic64_read(&msk->snd_nxt) + 1 ==
+ atomic64_read(&msk->write_seq))
mptcp_schedule_work(sk);
}
}
@@ -2009,7 +2016,8 @@ static u32 mptcp_send_limit(const struct sock *sk)
if (limit == UINT_MAX)
return UINT_MAX;
- not_sent = msk->write_seq - msk->snd_nxt;
+ not_sent = atomic64_read(&msk->write_seq) -
+ atomic64_read(&msk->snd_nxt);
if (not_sent >= limit)
return 0;
@@ -2121,7 +2129,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
dfrag->data_len += psize;
frag_truesize += psize;
pfrag->offset += frag_truesize;
- WRITE_ONCE(msk->write_seq, msk->write_seq + psize);
+ atomic64_add(psize, &msk->write_seq);
/* charge data on mptcp pending queue to the msk socket
* Note: we charge such data both to sk and ssk
@@ -2231,7 +2239,7 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
copied += count;
if (!(flags & MSG_PEEK)) {
- msk->bytes_consumed += count;
+ atomic64_add(count, &msk->bytes_consumed);
if (count < data_len) {
MPTCP_SKB_CB(skb)->offset += count;
MPTCP_SKB_CB(skb)->map_seq += count;
@@ -2393,10 +2401,12 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
{
const struct mptcp_sock *msk = mptcp_sk(sk);
const struct sk_buff *skb;
+ u64 hint_val, ack_seq;
skb = skb_peek(&sk->sk_receive_queue);
if (skb) {
- u64 hint_val = READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq;
+ ack_seq = atomic64_read(&msk->ack_seq);
+ hint_val = ack_seq - MPTCP_SKB_CB(skb)->map_seq;
if (hint_val >= INT_MAX)
return INT_MAX;
@@ -2609,7 +2619,7 @@ bool __mptcp_retransmit_pending_data(struct sock *sk)
return false;
}
- msk->recovery_snd_nxt = msk->snd_nxt;
+ msk->recovery_snd_nxt = atomic64_read(&msk->snd_nxt);
msk->recovery = true;
mptcp_data_unlock(sk);
@@ -2969,7 +2979,7 @@ static void __mptcp_retrans(struct sock *sk)
/* Get an updated and consistent rtx queue status. */
mptcp_data_lock(sk);
__mptcp_clean_una_wakeup(sk);
- retrans_seq = msk->snd_una;
+ retrans_seq = atomic64_read(&msk->snd_una);
dfrag = mptcp_rtx_head(sk);
need_retrans = !!dfrag;
mptcp_data_unlock(sk);
@@ -2999,8 +3009,8 @@ static void __mptcp_retrans(struct sock *sk)
/* With csum enabled, retransmission can send new data. */
sent_seq = dfrag->already_sent + dfrag->data_seq;
- if (after64(sent_seq, msk->snd_nxt))
- WRITE_ONCE(msk->snd_nxt, sent_seq);
+ if (after64(sent_seq, atomic64_read(&msk->snd_nxt)))
+ atomic64_set(&msk->snd_nxt, sent_seq);
/* Attempt the next fragment only if the current one is
* completely retransmitted.
@@ -3017,15 +3027,16 @@ static void __mptcp_retrans(struct sock *sk)
* across loop iterations, if so start again from RTX head.
*/
mptcp_data_lock(sk);
- already_acked = !before64(msk->snd_una, dfrag->data_seq +
+ already_acked = !before64(atomic64_read(&msk->snd_una),
+ dfrag->data_seq +
dfrag->already_sent);
if (already_acked) {
__mptcp_clean_una_wakeup(sk);
- retrans_seq = msk->snd_una;
+ retrans_seq = atomic64_read(&msk->snd_una);
dfrag = mptcp_rtx_head(sk);
need_retrans = !!dfrag;
- } else if (after64(msk->snd_una, retrans_seq)) {
- retrans_seq = msk->snd_una;
+ } else if (after64(atomic64_read(&msk->snd_una), retrans_seq)) {
+ retrans_seq = atomic64_read(&msk->snd_una);
}
mptcp_data_unlock(sk);
}
@@ -3314,6 +3325,8 @@ void mptcp_cancel_work(struct sock *sk)
void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how)
{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+
lock_sock(ssk);
switch (ssk->sk_state) {
@@ -3325,7 +3338,7 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how)
WARN_ON_ONCE(tcp_disconnect(ssk, O_NONBLOCK));
break;
default:
- if (__mptcp_check_fallback(mptcp_sk(sk))) {
+ if (__mptcp_check_fallback(msk)) {
pr_debug("Fallback\n");
ssk->sk_shutdown |= how;
tcp_shutdown(ssk, how);
@@ -3333,7 +3346,8 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how)
/* simulate the data_fin ack reception to let the state
* machine move forward
*/
- WRITE_ONCE(mptcp_sk(sk)->snd_una, mptcp_sk(sk)->snd_nxt);
+ atomic64_set(&msk->snd_una,
+ atomic64_read(&msk->snd_nxt));
mptcp_schedule_work(sk);
} else {
pr_debug("Sending DATA_FIN on subflow %p\n", ssk);
@@ -3404,19 +3418,23 @@ static void mptcp_check_send_data_fin(struct sock *sk)
{
struct mptcp_subflow_context *subflow;
struct mptcp_sock *msk = mptcp_sk(sk);
+ u64 snd_nxt, write_seq;
+
+ snd_nxt = atomic64_read(&msk->snd_nxt);
+ write_seq = atomic64_read(&msk->write_seq);
pr_debug("msk=%p snd_data_fin_enable=%d pending=%d snd_nxt=%llu write_seq=%llu\n",
msk, msk->snd_data_fin_enable, !!mptcp_send_head(sk),
- msk->snd_nxt, msk->write_seq);
+ snd_nxt, write_seq);
/* we still need to enqueue subflows or not really shutting down,
* skip this
*/
- if (!msk->snd_data_fin_enable || msk->snd_nxt + 1 != msk->write_seq ||
+ if (!msk->snd_data_fin_enable || snd_nxt + 1 != write_seq ||
mptcp_send_head(sk))
return;
- WRITE_ONCE(msk->snd_nxt, msk->write_seq);
+ atomic64_set(&msk->snd_nxt, write_seq);
mptcp_for_each_subflow(msk, subflow) {
struct sock *tcp_sk = mptcp_subflow_tcp_sock(subflow);
@@ -3434,7 +3452,7 @@ static void __mptcp_wr_shutdown(struct sock *sk)
!!mptcp_send_head(sk));
/* will be ignored by fallback sockets */
- WRITE_ONCE(msk->write_seq, msk->write_seq + 1);
+ atomic64_add(1, &msk->write_seq);
WRITE_ONCE(msk->snd_data_fin_enable, 1);
mptcp_check_send_data_fin(sk);
@@ -3668,9 +3686,9 @@ static int mptcp_disconnect(struct sock *sk, int flags)
WRITE_ONCE(msk->csum_enabled, mptcp_is_checksum_enabled(sock_net(sk)));
mptcp_pm_data_reset(msk);
mptcp_ca_reset(sk);
- msk->bytes_consumed = 0;
+ atomic64_set(&msk->bytes_consumed, 0);
msk->bytes_acked = 0;
- msk->bytes_received = 0;
+ atomic64_set(&msk->bytes_received, 0);
msk->bytes_sent = 0;
msk->bytes_retrans = 0;
msk->rcvspace_init = 0;
@@ -3678,7 +3696,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
mptcp_init_rtt_est(msk);
/* for fallback's sake */
- WRITE_ONCE(msk->ack_seq, 0);
+ atomic64_set(&msk->ack_seq, 0);
atomic64_set(&msk->rcv_wnd_sent, 0);
WRITE_ONCE(sk->sk_shutdown, 0);
@@ -3742,6 +3760,7 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk,
struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
struct sock *nsk = sk_clone_lock(sk, GFP_ATOMIC);
struct mptcp_subflow_context *subflow;
+ u64 seq = subflow_req->idsn + 1;
struct mptcp_sock *msk;
if (!nsk)
@@ -3769,10 +3788,10 @@ struct sock *mptcp_sk_clone_init(const struct sock *sk,
if (mp_opt->suboptions & OPTION_MPTCP_CSUMREQD)
WRITE_ONCE(msk->csum_enabled, true);
- WRITE_ONCE(msk->write_seq, subflow_req->idsn + 1);
- WRITE_ONCE(msk->snd_nxt, msk->write_seq);
- WRITE_ONCE(msk->snd_una, msk->write_seq);
- WRITE_ONCE(msk->wnd_end, msk->snd_nxt + tcp_sk(ssk)->snd_wnd);
+ atomic64_set(&msk->write_seq, seq);
+ atomic64_set(&msk->snd_nxt, seq);
+ atomic64_set(&msk->snd_una, seq);
+ atomic64_set(&msk->wnd_end, seq + tcp_sk(ssk)->snd_wnd);
msk->setsockopt_seq = mptcp_sk(sk)->setsockopt_seq;
mptcp_init_sched(msk, mptcp_sk(sk)->sched);
@@ -4120,7 +4139,7 @@ static int mptcp_ioctl_outq(const struct mptcp_sock *msk, u64 v)
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))
return 0;
- delta = msk->write_seq - v;
+ delta = atomic64_read(&msk->write_seq) - v;
if (__mptcp_check_fallback(msk) && msk->first) {
struct tcp_sock *tp = tcp_sk(msk->first);
@@ -4156,12 +4175,12 @@ static int mptcp_ioctl(struct sock *sk, int cmd, int *karg)
break;
case SIOCOUTQ:
slow = lock_sock_fast(sk);
- *karg = mptcp_ioctl_outq(msk, READ_ONCE(msk->snd_una));
+ *karg = mptcp_ioctl_outq(msk, atomic64_read(&msk->snd_una));
unlock_sock_fast(sk, slow);
break;
case SIOCOUTQNSD:
slow = lock_sock_fast(sk);
- *karg = mptcp_ioctl_outq(msk, msk->snd_nxt);
+ *karg = mptcp_ioctl_outq(msk, atomic64_read(&msk->snd_nxt));
unlock_sock_fast(sk, slow);
break;
default:
@@ -4201,9 +4220,9 @@ static int mptcp_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
MPTCP_MIB_TOKENFALLBACKINIT);
}
- WRITE_ONCE(msk->write_seq, subflow->idsn);
- WRITE_ONCE(msk->snd_nxt, subflow->idsn);
- WRITE_ONCE(msk->snd_una, subflow->idsn);
+ atomic64_set(&msk->write_seq, subflow->idsn);
+ atomic64_set(&msk->snd_nxt, subflow->idsn);
+ atomic64_set(&msk->snd_una, subflow->idsn);
if (likely(!__mptcp_check_fallback(msk)))
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPCAPABLEACTIVE);
@@ -4607,7 +4626,7 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
copied += count;
- msk->bytes_consumed += count;
+ atomic64_add(count, &msk->bytes_consumed);
if (count < data_len) {
MPTCP_SKB_CB(skb)->offset += count;
MPTCP_SKB_CB(skb)->map_seq += count;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 3d250e8204d5..29405d045291 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -288,15 +288,15 @@ struct mptcp_sock {
* lockless access read
*/
u64 remote_key; /* same as above */
- u64 write_seq;
+ atomic64_t write_seq;
u64 bytes_sent;
- u64 snd_nxt;
- u64 bytes_received;
- u64 ack_seq;
+ atomic64_t snd_nxt;
+ atomic64_t bytes_received;
+ atomic64_t ack_seq;
atomic64_t rcv_wnd_sent;
u64 rcv_data_fin_seq;
u64 bytes_retrans;
- u64 bytes_consumed;
+ atomic64_t bytes_consumed;
int snd_burst;
int old_wspace;
u64 recovery_snd_nxt; /* in recovery mode accept up to this seq;
@@ -304,8 +304,8 @@ struct mptcp_sock {
* protection
*/
u64 bytes_acked;
- u64 snd_una;
- u64 wnd_end;
+ atomic64_t snd_una;
+ atomic64_t wnd_end;
u32 last_data_sent;
u32 last_data_recv;
u32 last_ack_recv;
@@ -488,7 +488,7 @@ static inline struct mptcp_data_frag *mptcp_rtx_head(struct sock *sk)
{
struct mptcp_sock *msk = mptcp_sk(sk);
- if (msk->snd_una == msk->snd_nxt)
+ if (atomic64_read(&msk->snd_una) == atomic64_read(&msk->snd_nxt))
return NULL;
return list_first_entry_or_null(&msk->rtx_queue, struct mptcp_data_frag, list);
@@ -866,7 +866,8 @@ int mptcp_sched_get_retrans(struct mptcp_sock *msk);
static inline u64 mptcp_data_avail(const struct mptcp_sock *msk)
{
- return READ_ONCE(msk->bytes_received) - READ_ONCE(msk->bytes_consumed);
+ return atomic64_read(&msk->bytes_received) -
+ atomic64_read(&msk->bytes_consumed);
}
static inline bool mptcp_epollin_ready(const struct sock *sk)
@@ -989,7 +990,7 @@ bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq, bool us
static inline bool mptcp_data_fin_enabled(const struct mptcp_sock *msk)
{
return READ_ONCE(msk->snd_data_fin_enable) &&
- READ_ONCE(msk->write_seq) == READ_ONCE(msk->snd_nxt);
+ atomic64_read(&msk->write_seq) == atomic64_read(&msk->snd_nxt);
}
static inline u32 mptcp_notsent_lowat(const struct sock *sk)
@@ -1006,7 +1007,8 @@ static inline bool mptcp_stream_memory_free(const struct sock *sk, int wake)
const struct mptcp_sock *msk = mptcp_sk(sk);
u32 notsent_bytes;
- notsent_bytes = READ_ONCE(msk->write_seq) - READ_ONCE(msk->snd_nxt);
+ notsent_bytes = atomic64_read(&msk->write_seq) -
+ atomic64_read(&msk->snd_nxt);
return (notsent_bytes << wake) < mptcp_notsent_lowat(sk);
}
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 922f6ae5c80c..ed221b36fd25 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -1086,10 +1086,10 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
slow = lock_sock_fast(sk);
info->mptcpi_csum_enabled = READ_ONCE(msk->csum_enabled);
info->mptcpi_token = msk->token;
- info->mptcpi_write_seq = msk->write_seq;
+ info->mptcpi_write_seq = atomic64_read(&msk->write_seq);
info->mptcpi_retransmits = inet_csk(sk)->icsk_retransmits;
info->mptcpi_bytes_sent = msk->bytes_sent;
- info->mptcpi_bytes_received = msk->bytes_received;
+ info->mptcpi_bytes_received = atomic64_read(&msk->bytes_received);
info->mptcpi_bytes_retrans = msk->bytes_retrans;
info->mptcpi_subflows_total = info->mptcpi_extra_subflows +
__mptcp_has_initial_subflow(msk);
@@ -1100,8 +1100,8 @@ void mptcp_diag_fill_info(struct mptcp_sock *msk, struct mptcp_info *info)
mptcp_data_lock(sk);
info->mptcpi_last_ack_recv = jiffies_to_msecs(now - msk->last_ack_recv);
- info->mptcpi_snd_una = msk->snd_una;
- info->mptcpi_rcv_nxt = msk->ack_seq;
+ info->mptcpi_snd_una = atomic64_read(&msk->snd_una);
+ info->mptcpi_rcv_nxt = atomic64_read(&msk->ack_seq);
info->mptcpi_bytes_acked = msk->bytes_acked;
mptcp_data_unlock(sk);
}
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 2d7ccb01d234..8dc200d8d072 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -457,16 +457,18 @@ void __mptcp_sync_state(struct sock *sk, int state)
struct mptcp_subflow_context *subflow;
struct mptcp_sock *msk = mptcp_sk(sk);
struct sock *ssk = msk->first;
+ u64 seq;
subflow = mptcp_subflow_ctx(ssk);
+ seq = subflow->idsn + 1;
__mptcp_propagate_sndbuf(sk, ssk);
if (sk->sk_state == TCP_SYN_SENT) {
/* subflow->idsn is always available is TCP_SYN_SENT state,
* even for the FASTOPEN scenarios
*/
- WRITE_ONCE(msk->write_seq, subflow->idsn + 1);
- WRITE_ONCE(msk->snd_nxt, msk->write_seq);
+ atomic64_set(&msk->write_seq, seq);
+ atomic64_set(&msk->snd_nxt, seq);
mptcp_set_state(sk, state);
sk->sk_state_change(sk);
}
@@ -491,7 +493,7 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
subflow->map_seq = subflow->iasn;
WRITE_ONCE(msk->remote_key, subflow->remote_key);
- WRITE_ONCE(msk->ack_seq, subflow->iasn);
+ atomic64_set(&msk->ack_seq, subflow->iasn);
WRITE_ONCE(msk->can_ack, true);
atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
}
@@ -501,14 +503,15 @@ static void mptcp_propagate_state(struct sock *sk, struct sock *ssk,
const struct mptcp_options_received *mp_opt)
{
struct mptcp_sock *msk = mptcp_sk(sk);
+ u64 seq = subflow->idsn + 1;
mptcp_data_lock(sk);
if (mp_opt) {
/* Options are available only in the non fallback cases
* avoid updating rx path fields otherwise
*/
- WRITE_ONCE(msk->snd_una, subflow->idsn + 1);
- WRITE_ONCE(msk->wnd_end, subflow->idsn + 1 + tcp_sk(ssk)->snd_wnd);
+ atomic64_set(&msk->snd_una, seq);
+ atomic64_set(&msk->wnd_end, seq + tcp_sk(ssk)->snd_wnd);
subflow_set_remote_key(msk, subflow, mp_opt);
}
@@ -1198,7 +1201,8 @@ static enum mapping_status get_mapping_status(struct sock *ssk,
data_len--;
}
- map_seq = mptcp_expand_seq(READ_ONCE(msk->ack_seq), mpext->data_seq, mpext->dsn64);
+ map_seq = mptcp_expand_seq(atomic64_read(&msk->ack_seq),
+ mpext->data_seq, mpext->dsn64);
WRITE_ONCE(mptcp_sk(subflow->conn)->use_64bit_ack, !!mpext->dsn64);
if (subflow->map_valid) {
@@ -1386,7 +1390,7 @@ static bool subflow_check_data_avail(struct sock *ssk)
if (unlikely(!READ_ONCE(msk->can_ack)))
goto fallback;
- old_ack = READ_ONCE(msk->ack_seq);
+ old_ack = atomic64_read(&msk->ack_seq);
ack_seq = mptcp_subflow_get_mapped_dsn(subflow);
pr_debug("msk ack_seq=%llx subflow ack_seq=%llx\n", old_ack,
ack_seq);
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 02/11] mptcp: drop the mptcp_ooo_try_coalesce() helper
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 03/11] mptcp: drop the cant_coalesce CB field Geliang Tang
` (9 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 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 26b49480941c..39371a16b8bf 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;
}
@@ -779,8 +771,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);
@@ -904,7 +895,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 = ack_seq - MPTCP_SKB_CB(skb)->map_seq;
/* skip overlapping data, if any */
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 03/11] mptcp: drop the cant_coalesce CB field
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 02/11] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 04/11] mptcp: remove CB offset field Geliang Tang
` (8 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Paolo Abeni, Geliang Tang
From: Paolo Abeni <pabeni@redhat.com>
Such field is used to ensure in-sequence processing in case of fastopen.
Instead let's perform synchronization of the fastopen skb sequence
when the IASN becomes available with the 3rd ack.
When the `cant_coalesce` field has been introduced, commit f03afb3aeb9d
("mptcp: drop __mptcp_fastopen_gen_msk_ackseq()") noted that updating the
already queued skb for passive fastopen socket at 3rd ack time would be
difficult and race prone. The main point is that such update don't need
to be synchronously performed at 3rd ack time, but is sufficient to
perform it before the next segment is introduced into the msk.
To such extent, add an explicit test in __mptcp_move_skb(). Performance
wise this trades a conditional in the fast path - in __mptcp_try_coalesce()
- with a similar one in __mptcp_move_skb() and a couple more in slow paths.
After this change the user-space will always observe consistent sequence
numbers in the receive queue, even in the TFO dummy mapping case.
There is still a potential race in mptcp_inq_hint() that will be addressed
by a later patch in the series.
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 | 2 +-
net/mptcp/protocol.c | 37 +++++++++++++++++++++++++++++++++++--
net/mptcp/protocol.h | 4 +++-
net/mptcp/subflow.c | 10 ++++++++++
4 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index 0012690a2202..e72fb982614a 100644
--- a/net/mptcp/fastopen.c
+++ b/net/mptcp/fastopen.c
@@ -50,12 +50,12 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
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->rcvd_dummy_seq = true;
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 39371a16b8bf..7d1422518aad 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -168,7 +168,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 ||
- 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))
@@ -430,7 +429,6 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + copy_len;
MPTCP_SKB_CB(skb)->offset = offset;
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
- MPTCP_SKB_CB(skb)->cant_coalesce = 0;
__skb_unlink(skb, &ssk->sk_receive_queue);
@@ -438,6 +436,28 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
skb_dst_drop(skb);
}
+void __mptcp_sync_rcv_sequence(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct sk_buff *skb;
+ u64 ack_seq;
+ u32 offset;
+
+ if (likely(!msk->rcvd_dummy_seq))
+ return;
+
+ /* User space can have already received the TFO skb. */
+ msk->rcvd_dummy_seq = false;
+ skb = skb_peek_tail(&sk->sk_receive_queue);
+ if (!skb)
+ return;
+
+ ack_seq = atomic64_read(&msk->ack_seq);
+ offset = MPTCP_SKB_CB(skb)->offset;
+ MPTCP_SKB_CB(skb)->map_seq = ack_seq - skb->len + offset;
+ MPTCP_SKB_CB(skb)->end_seq = ack_seq;
+}
+
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;
@@ -447,6 +467,12 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
mptcp_borrow_fwdmem(sk, skb);
+ /* 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);
+
ack_seq = atomic64_read(&msk->ack_seq);
if (MPTCP_SKB_CB(skb)->map_seq == ack_seq) {
/* in sequence */
@@ -3914,6 +3940,13 @@ 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)) {
+ /* Ensure we see the updated ack_seq after seeing
+ * the flag
+ */
+ smp_rmb();
+ __mptcp_sync_rcv_sequence(sk);
+ }
}
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 29405d045291..839b1a36fc60 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -126,13 +126,13 @@
#define MPTCP_FLUSH_JOIN_LIST 5
#define MPTCP_SYNC_STATE 6
#define MPTCP_SYNC_SNDBUF 7
+#define MPTCP_SYNC_SEQ 8
struct mptcp_skb_cb {
u64 map_seq;
u64 end_seq;
u32 offset;
u8 has_rxtstamp;
- u8 cant_coalesce;
};
#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0]))
@@ -313,6 +313,7 @@ struct mptcp_sock {
u32 token;
unsigned long flags;
unsigned long cb_flags;
+ bool rcvd_dummy_seq;
bool recovery; /* closing subflow write queue reinjected */
bool can_ack;
bool fully_established;
@@ -1174,6 +1175,7 @@ void mptcp_event_pm_listener(const struct sock *ssk,
enum mptcp_event_type event);
bool mptcp_userspace_pm_active(const struct mptcp_sock *msk);
+void __mptcp_sync_rcv_sequence(struct sock *sk);
void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subflow,
struct request_sock *req);
int mptcp_pm_genl_fill_addr(struct sk_buff *msg,
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 8dc200d8d072..a6f231c6b4bf 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -478,6 +478,8 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
struct mptcp_subflow_context *subflow,
const struct mptcp_options_received *mp_opt)
{
+ struct sock *sk = (struct sock *)msk;
+
/* active MPC subflow will reach here multiple times:
* at subflow_finish_connect() time and at 4th ack time
*/
@@ -496,6 +498,14 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
atomic64_set(&msk->ack_seq, subflow->iasn);
WRITE_ONCE(msk->can_ack, true);
atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
+
+ if (!sock_owned_by_user(sk)) {
+ __mptcp_sync_rcv_sequence(sk);
+ } else {
+ /* Ensure ack_seq is visible before setting the flag */
+ smp_wmb();
+ set_bit(MPTCP_SYNC_SEQ, &msk->cb_flags);
+ }
}
static void mptcp_propagate_state(struct sock *sk, struct sock *ssk,
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 04/11] mptcp: remove CB offset field
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (2 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 03/11] mptcp: drop the cant_coalesce CB field Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 11:13 ` sashiko-bot
2026-08-27 10:57 ` [PATCH mptcp-next v9 05/11] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
` (7 subsequent siblings)
11 siblings, 1 reply; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 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 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.
Initialize MPTCP sequence space to 0 in mptcp_propagate_state() when mp_opt
is NULL, ensuring SKB's map_seq starts from 0 to match msk->copied_seq and
prevent offset calculation underflow in fallback mode.
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 | 9 ++--
net/mptcp/protocol.c | 115 +++++++++++++++++++++----------------------
net/mptcp/protocol.h | 9 +++-
net/mptcp/subflow.c | 9 ++++
4 files changed, 79 insertions(+), 63 deletions(-)
diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index e72fb982614a..15febd359f66 100644
--- a/net/mptcp/fastopen.c
+++ b/net/mptcp/fastopen.c
@@ -45,10 +45,11 @@ 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);
@@ -56,6 +57,8 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
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);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 7d1422518aad..4cf7ad97ae0a 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -29,7 +29,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>
@@ -168,7 +168,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;
@@ -415,8 +414,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;
@@ -425,9 +423,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);
@@ -440,8 +438,6 @@ void __mptcp_sync_rcv_sequence(struct sock *sk)
{
struct mptcp_sock *msk = mptcp_sk(sk);
struct sk_buff *skb;
- u64 ack_seq;
- u32 offset;
if (likely(!msk->rcvd_dummy_seq))
return;
@@ -452,10 +448,8 @@ void __mptcp_sync_rcv_sequence(struct sock *sk)
if (!skb)
return;
- ack_seq = atomic64_read(&msk->ack_seq);
- offset = MPTCP_SKB_CB(skb)->offset;
- MPTCP_SKB_CB(skb)->map_seq = ack_seq - skb->len + offset;
- MPTCP_SKB_CB(skb)->end_seq = 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)
@@ -467,6 +461,12 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
mptcp_borrow_fwdmem(sk, skb);
+ if (test_and_clear_bit(MPTCP_SYNC_SEQ, &msk->cb_flags)) {
+ /* Ensure we see the updated ack_seq after seeing the flag */
+ smp_rmb();
+ msk->copied_seq += mptcp_iasn(msk);
+ }
+
/* Be sure to sync the eventual fastopen dummy mapping before any other
* skb lands into the msk.
*/
@@ -500,10 +500,6 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
/* Partial packet */
if (after64(MPTCP_SKB_CB(skb)->end_seq, ack_seq)) {
copy_len = MPTCP_SKB_CB(skb)->end_seq - ack_seq;
- MPTCP_SKB_CB(skb)->offset += ack_seq -
- MPTCP_SKB_CB(skb)->map_seq;
- MPTCP_SKB_CB(skb)->map_seq += ack_seq -
- MPTCP_SKB_CB(skb)->map_seq;
goto insert;
}
@@ -861,7 +857,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);
@@ -928,8 +924,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, ack_seq,
delta);
- MPTCP_SKB_CB(skb)->offset += delta;
- MPTCP_SKB_CB(skb)->map_seq += delta;
__skb_queue_tail(&sk->sk_receive_queue, skb);
}
atomic64_add(end_seq - ack_seq, &msk->bytes_received);
@@ -2209,33 +2203,24 @@ 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) {
+ *last = skb;
+ continue;
}
count = min_t(size_t, len - copied, data_len);
@@ -2254,14 +2239,12 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
}
copied += count;
+ *seq += count;
if (!(flags & MSG_PEEK)) {
atomic64_add(count, &msk->bytes_consumed);
- 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 {
@@ -2414,27 +2397,27 @@ 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;
u64 hint_val, ack_seq;
- skb = skb_peek(&sk->sk_receive_queue);
- if (skb) {
- ack_seq = atomic64_read(&msk->ack_seq);
- hint_val = ack_seq - MPTCP_SKB_CB(skb)->map_seq;
-
- if (hint_val >= INT_MAX)
- return INT_MAX;
+ if (test_bit(MPTCP_SYNC_SEQ, &msk->cb_flags))
+ return 0;
- return (unsigned int)hint_val;
- }
+ /* Avoid races vs ack_seq updates. */
+ mptcp_data_lock(sk);
+ ack_seq = atomic64_read(&msk->ack_seq);
+ hint_val = 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,
@@ -2443,6 +2426,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;
@@ -2461,6 +2445,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;
@@ -2470,7 +2459,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)
@@ -2480,8 +2469,11 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
copied += bytes_read;
- if (!list_empty(&msk->backlog_list) && mptcp_move_skbs(sk))
+ if (!list_empty(&msk->backlog_list) && mptcp_move_skbs(sk)) {
+ if (flags & MSG_PEEK)
+ peek_seq = msk->copied_seq + copied;
continue;
+ }
/* only the MPTCP socket status is relevant here. The exit
* conditions mirror closely tcp_recvmsg()
@@ -2525,6 +2517,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);
@@ -3710,11 +3706,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 */
atomic64_set(&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);
@@ -3945,6 +3943,7 @@ static void mptcp_release_cb(struct sock *sk)
* the flag
*/
smp_rmb();
+ msk->copied_seq += mptcp_iasn(msk);
__mptcp_sync_rcv_sequence(sk);
}
}
@@ -4609,7 +4608,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;
@@ -4651,11 +4650,9 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
copied += count;
atomic64_add(count, &msk->bytes_consumed);
- 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 839b1a36fc60..1a0ba1ba3e87 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -131,7 +131,6 @@
struct mptcp_skb_cb {
u64 map_seq;
u64 end_seq;
- u32 offset;
u8 has_rxtstamp;
};
@@ -292,6 +291,7 @@ struct mptcp_sock {
u64 bytes_sent;
atomic64_t snd_nxt;
atomic64_t bytes_received;
+ u64 copied_seq;
atomic64_t ack_seq;
atomic64_t rcv_wnd_sent;
u64 rcv_data_fin_seq;
@@ -311,6 +311,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;
@@ -865,6 +866,12 @@ 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 atomic64_read(&msk->ack_seq) -
+ atomic64_read(&msk->bytes_received) + msk->tfo_skb_len;
+}
+
static inline u64 mptcp_data_avail(const struct mptcp_sock *msk)
{
return atomic64_read(&msk->bytes_received) -
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index a6f231c6b4bf..37594143fdc0 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -500,6 +500,8 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
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 {
/* Ensure ack_seq is visible before setting the flag */
@@ -523,6 +525,13 @@ static void mptcp_propagate_state(struct sock *sk, struct sock *ssk,
atomic64_set(&msk->snd_una, seq);
atomic64_set(&msk->wnd_end, seq + tcp_sk(ssk)->snd_wnd);
subflow_set_remote_key(msk, subflow, mp_opt);
+ } else {
+ /* Fallback: initialize sequence space to 0 (no remote key) */
+ subflow->map_seq = 0;
+ /* ensure mptcp_subflow_get_map_offset() returns 0 */
+ subflow->map_subflow_seq = tcp_sk(ssk)->copied_seq -
+ subflow->ssn_offset;
+ atomic64_set(&msk->ack_seq, 0);
}
if (!sock_owned_by_user(sk)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 05/11] mptcp: sync mptcp skb cb layout with tcp one
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (3 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 04/11] mptcp: remove CB offset field Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 06/11] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
` (6 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 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.
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 | 81 +++++++++++++++++++++++++++-----------------
net/mptcp/protocol.h | 7 ++--
3 files changed, 59 insertions(+), 35 deletions(-)
diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index 15febd359f66..62dab62e00f9 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_data_lock(sk);
@@ -57,7 +59,7 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
msk = mptcp_sk(sk);
msk->rcvd_dummy_seq = true;
- msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq;
+ msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq64;
msk->tfo_skb_len = skb->len;
mptcp_borrow_fwdmem(sk, skb);
skb_set_owner_r(skb, sk);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 4cf7ad97ae0a..e53d5c184065 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -172,7 +172,7 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
!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;
@@ -254,8 +254,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;
@@ -301,15 +301,19 @@ 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));
+ /* Use the full sequence space to perform the admission checks, to
+ * protect vs possible wrap-arounds.
+ */
if (after64(end_seq, max_seq)) {
/* out of window */
mptcp_drop(sk, skb);
@@ -345,7 +349,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;
@@ -357,18 +361,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(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 |
@@ -399,7 +403,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);
@@ -421,11 +425,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;
__skb_unlink(skb, &ssk->sk_receive_queue);
@@ -448,13 +454,14 @@ void __mptcp_sync_rcv_sequence(struct sock *sk)
if (!skb)
return;
- MPTCP_SKB_CB(skb)->map_seq = mptcp_iasn(msk) - skb->len;
+ MPTCP_SKB_CB(skb)->map_seq64 = mptcp_iasn(msk) - skb->len;
+ 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;
}
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;
u64 ack_seq;
@@ -474,7 +481,7 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
__mptcp_sync_rcv_sequence(sk);
ack_seq = atomic64_read(&msk->ack_seq);
- if (MPTCP_SKB_CB(skb)->map_seq == ack_seq) {
+ if (MPTCP_SKB_CB(skb)->map_seq64 == ack_seq) {
/* in sequence */
insert:
if (!mptcp_try_rmem_schedule(sk, skb)) {
@@ -492,14 +499,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, ack_seq)) {
+ } else if (after64(MPTCP_SKB_CB(skb)->map_seq64, ack_seq)) {
mptcp_data_queue_ofo(msk, skb);
return false;
}
/* Partial packet */
- if (after64(MPTCP_SKB_CB(skb)->end_seq, ack_seq)) {
- copy_len = MPTCP_SKB_CB(skb)->end_seq - ack_seq;
+ if (after64(MPTCP_SKB_CB(skb)->map_seq64 + skb->len, ack_seq)) {
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - (u32)ack_seq;
goto insert;
}
@@ -894,40 +901,40 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
{
struct sock *sk = (struct sock *)msk;
struct sk_buff *skb, *tail;
- u64 end_seq, ack_seq;
+ u32 seq_delta, ack_seq;
bool moved = false;
struct rb_node *p;
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 = atomic64_read(&msk->ack_seq);
+ ack_seq = (u32)atomic64_read(&msk->ack_seq);
skb = rb_to_skb(p);
- if (after64(MPTCP_SKB_CB(skb)->map_seq, 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, 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 = ack_seq - MPTCP_SKB_CB(skb)->map_seq;
/* skip overlapping data, if any */
- pr_debug("uncoalesced seq=%llx ack seq=%llx delta=%d\n",
+ pr_debug("uncoalesced seq=%x ack seq=%x delta=%d\n",
MPTCP_SKB_CB(skb)->map_seq, ack_seq,
delta);
__skb_queue_tail(&sk->sk_receive_queue, skb);
}
- atomic64_add(end_seq - ack_seq, &msk->bytes_received);
- atomic64_set(&msk->ack_seq, end_seq);
+ atomic64_add(seq_delta, &msk->bytes_received);
+ atomic64_add(seq_delta, &msk->ack_seq);
moved = true;
}
return moved;
@@ -2212,7 +2219,7 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
int copied = 0;
skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
- u64 offset = *seq - MPTCP_SKB_CB(skb)->map_seq;
+ u32 offset = (u32)(*seq) - MPTCP_SKB_CB(skb)->map_seq;
u32 data_len = skb->len - offset;
u32 count;
int err;
@@ -4608,7 +4615,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 = msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
+ offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
if (offset < skb->len) {
*off = offset;
return skb;
@@ -4859,11 +4866,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 1a0ba1ba3e87..a00cf6898463 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -129,9 +129,12 @@
#define MPTCP_SYNC_SEQ 8
struct mptcp_skb_cb {
- u64 map_seq;
- u64 end_seq;
+ u32 map_seq;
+ u32 end_seq;
+ u32 unused;
+ u16 flags;
u8 has_rxtstamp;
+ u64 map_seq64;
};
#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0]))
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 06/11] mptcp: defer read_sock cleanup to mptcp_worker
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (4 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 05/11] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 07/11] mptcp: implement peek_len for proto_ops Geliang Tang
` (5 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Paolo Abeni, Geliang Tang
From: Paolo Abeni <pabeni@redhat.com>
When MPTCP carries TLS, the data path runs under mptcp_data_lock().
Reaching sk->sk_data_ready(sk) synchronously ends up at
tls_strp_check_rcv() -> mptcp_recv_skb() -> mptcp_move_skbs(), which
calls mptcp_data_lock() on the same sk and recurses on sk_lock.slock.
The TLS path is not the only constraint: before the mptcp_recv_skb()
calls, the TLS code would also reach __mptcp_read_sock(), which calls
mptcp_rcv_space_adjust() and mptcp_cleanup_rbuf(). Both require holding
the msk socket lock in process context, while the mptcp/TLS caller is
in BH scope.
Fix this by deferring sk->sk_data_ready(sk) to mptcp_worker() via a new
MPTCP_WORK_READ_COMPLETE bit, reusing the existing mptcp_schedule_work()/
mptcp_cancel_work() infrastructure. The wakeup bit is consumed after the
SOCK_DEAD && TCP_CLOSE destroy branch, so a socket that reaches the destroy
path drops the pending wakeup rather than running it post-free.
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 | 32 ++++++++++++++++++++++++--------
net/mptcp/protocol.h | 2 ++
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index e53d5c184065..cc8747162cca 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3149,6 +3149,20 @@ 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);
+ mptcp_cleanup_rbuf(msk, read_copied);
+}
+
static void mptcp_do_fastclose(struct sock *sk)
{
struct mptcp_subflow_context *subflow, *tmp;
@@ -3225,6 +3239,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);
@@ -4611,9 +4628,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) {
@@ -4628,6 +4642,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)
@@ -4637,8 +4652,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) {
@@ -4669,11 +4682,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 the workers's release callback.
+ */
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 a00cf6898463..06fddbe742c8 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_DISABLED 4
+#define MPTCP_WORK_READ_COMPLETE 5
/* MPTCP socket release cb flags */
#define MPTCP_PUSH_PENDING 1
@@ -312,6 +313,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;
u32 tfo_skb_len;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 07/11] mptcp: implement peek_len for proto_ops
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (5 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 06/11] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 11:10 ` sashiko-bot
2026-08-27 10:57 ` [PATCH mptcp-next v9 08/11] mptcp: add sendmsg_locked to proto_ops Geliang Tang
` (4 subsequent siblings)
11 siblings, 1 reply; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Add mptcp_inq() to compute the number of readable bytes at the MPTCP
level. It derives the count from mptcp_inq_hint(), returns 0 while
the connection is still handshaking (TCP_SYN_SENT/TCP_SYN_RECV), and
subtracts 1 once a FIN has been received, since the FIN consumes a
sequence number but carries no data. This keeps the reported count in
sync with the bytes actually queued, preventing upper layers from
trying to read data that has not yet arrived.
Wire mptcp_inq() into mptcp_peek_len() and assign .peek_len in both
mptcp_stream_ops and mptcp_v6_stream_ops, so upper layers get the
correct in-queue byte count for MPTCP connections.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/protocol.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index cc8747162cca..9b9ace7cb977 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -4814,6 +4814,37 @@ static ssize_t mptcp_splice_read(struct socket *sock, loff_t *ppos,
return ret;
}
+static int mptcp_inq(struct sock *sk)
+{
+ const struct mptcp_sock *msk = mptcp_sk(sk);
+ int answ;
+
+ if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
+ answ = 0;
+ } else if (test_bit(MPTCP_SYNC_SEQ, &msk->cb_flags)) {
+ answ = 0;
+ } else {
+ u64 hint_val;
+
+ hint_val = atomic64_read(&msk->ack_seq) - msk->copied_seq;
+ if (hint_val >= INT_MAX)
+ hint_val = INT_MAX;
+
+ answ = (unsigned int)hint_val;
+ if (answ &&
+ (sk->sk_state == TCP_CLOSE ||
+ (sk->sk_shutdown & RCV_SHUTDOWN)))
+ answ--;
+ }
+
+ return answ;
+}
+
+static int mptcp_peek_len(struct socket *sock)
+{
+ return mptcp_inq(sock->sk);
+}
+
static const struct proto_ops mptcp_stream_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
@@ -4836,6 +4867,7 @@ static const struct proto_ops mptcp_stream_ops = {
.set_rcvlowat = mptcp_set_rcvlowat,
.read_sock = mptcp_read_sock,
.splice_read = mptcp_splice_read,
+ .peek_len = mptcp_peek_len,
};
static struct inet_protosw mptcp_protosw = {
@@ -4960,6 +4992,7 @@ static const struct proto_ops mptcp_v6_stream_ops = {
.set_rcvlowat = mptcp_set_rcvlowat,
.read_sock = mptcp_read_sock,
.splice_read = mptcp_splice_read,
+ .peek_len = mptcp_peek_len,
};
static struct proto mptcp_v6_prot;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 08/11] mptcp: add sendmsg_locked to proto_ops
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (6 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 07/11] mptcp: implement peek_len for proto_ops Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 09/11] mptcp: track app-limited state in mptcp_sendmsg Geliang Tang
` (3 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
MPTCP currently provides a standard sendmsg() implementation which
acquires and releases the socket lock internally. However, certain
upper layers need to call the sendmsg method while the socket lock
is already held.
Split the existing mptcp_sendmsg() into mptcp_sendmsg_locked() which
assumes the caller holds the socket lock, and a tiny wrapper
mptcp_sendmsg() that acquires the lock and calls the locked version.
Expose .sendmsg_locked in both mptcp_stream_ops and mptcp_v6_stream_ops.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/protocol.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 9b9ace7cb977..98a1bfdfbdc4 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2056,7 +2056,7 @@ static void mptcp_rps_record_subflows(const struct mptcp_sock *msk)
}
}
-static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+static int mptcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t len)
{
struct mptcp_sock *msk = mptcp_sk(sk);
struct page_frag *pfrag;
@@ -2068,8 +2068,6 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
msg->msg_flags &= MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL |
MSG_FASTOPEN | MSG_EOR;
- lock_sock(sk);
-
mptcp_rps_record_subflows(msk);
if (unlikely(inet_test_bit(DEFER_CONNECT, sk) ||
@@ -2185,7 +2183,6 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
}
out:
- release_sock(sk);
return copied;
do_error:
@@ -2196,6 +2193,17 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
goto out;
}
+static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ int ret;
+
+ lock_sock(sk);
+ ret = mptcp_sendmsg_locked(sk, msg, len);
+ release_sock(sk);
+
+ return ret;
+}
+
static void mptcp_rcv_space_adjust(struct mptcp_sock *msk, int copied);
static void mptcp_eat_recv_skb(struct sock *sk, struct sk_buff *skb)
@@ -4868,6 +4876,7 @@ static const struct proto_ops mptcp_stream_ops = {
.read_sock = mptcp_read_sock,
.splice_read = mptcp_splice_read,
.peek_len = mptcp_peek_len,
+ .sendmsg_locked = mptcp_sendmsg_locked,
};
static struct inet_protosw mptcp_protosw = {
@@ -4993,6 +5002,7 @@ static const struct proto_ops mptcp_v6_stream_ops = {
.read_sock = mptcp_read_sock,
.splice_read = mptcp_splice_read,
.peek_len = mptcp_peek_len,
+ .sendmsg_locked = mptcp_sendmsg_locked,
};
static struct proto mptcp_v6_prot;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 09/11] mptcp: track app-limited state in mptcp_sendmsg
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (7 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 08/11] mptcp: add sendmsg_locked to proto_ops Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 10/11] selftests: mptcp: sockopt: check app_limited Geliang Tang
` (2 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
The application-limited accounting in TCP is updated by
tcp_rate_check_app_limited(), which currently takes a struct sock * and
internally calls tcp_sk(). MPTCP needs to apply the same accounting to
each subflow individually - every subflow is an independent TCP socket
with its own tp->app_limited / delivered state - so wrapping the call as
a struct sock * -> tcp_sk() helper is awkward at the call site.
Split the existing function: keep the logic as
tcp_sock_rate_check_app_limited(struct tcp_sock *tp), and turn
tcp_rate_check_app_limited(struct sock *) into a thin wrapper so the
exported API is unchanged for other TCP users.
Then add mptcp_sock_rate_check_app_limited() that walks every subflow of
the mptcp_sock and runs tcp_sock_rate_check_app_limited() under each
subflow's socket lock. Invoke it from mptcp_sendmsg() right after the
send-side setup, so the delivery-rate app_limited state stays in sync
with what the application actually has to send across each subflow.
With this in place, TCP_INFO.tcpi_delivery_rate_app_limited is reported
correctly for MPTCP connections instead of being left at 0.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
include/net/tcp.h | 1 +
net/ipv4/tcp.c | 9 +++++++--
net/mptcp/protocol.c | 18 ++++++++++++++++++
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 670c20876f26..f5cbb5a8a288 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -849,6 +849,7 @@ static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
/* tcp.c */
void tcp_get_info(struct sock *, struct tcp_info *);
+void tcp_sock_rate_check_app_limited(struct tcp_sock *tp);
void tcp_rate_check_app_limited(struct sock *sk);
/* Read 'sendfile()'-style from a TCP socket */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 1452c40e22d4..f0f843fd1897 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1096,9 +1096,9 @@ int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int *copied,
}
/* If a gap is detected between sends, mark the socket application-limited. */
-void tcp_rate_check_app_limited(struct sock *sk)
+void tcp_sock_rate_check_app_limited(struct tcp_sock *tp)
{
- struct tcp_sock *tp = tcp_sk(sk);
+ struct sock *sk = (struct sock *)tp;
if (/* We have less than one packet to send. */
tp->write_seq - tp->snd_nxt < tp->mss_cache &&
@@ -1111,6 +1111,11 @@ void tcp_rate_check_app_limited(struct sock *sk)
tp->app_limited =
(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
}
+
+void tcp_rate_check_app_limited(struct sock *sk)
+{
+ tcp_sock_rate_check_app_limited(tcp_sk(sk));
+}
EXPORT_SYMBOL_GPL(tcp_rate_check_app_limited);
int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 98a1bfdfbdc4..07813765eeaa 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -2056,6 +2056,21 @@ static void mptcp_rps_record_subflows(const struct mptcp_sock *msk)
}
}
+static void mptcp_rate_check_app_limited(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct mptcp_subflow_context *subflow;
+
+ mptcp_for_each_subflow(msk, subflow) {
+ struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+ bool slow;
+
+ slow = lock_sock_fast_nested(ssk);
+ tcp_sock_rate_check_app_limited(tcp_sk(ssk));
+ unlock_sock_fast(ssk, slow);
+ }
+}
+
static int mptcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t len)
{
struct mptcp_sock *msk = mptcp_sk(sk);
@@ -2084,6 +2099,9 @@ static int mptcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t len)
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+ /* is sending application-limited? */
+ mptcp_rate_check_app_limited(sk);
+
if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
ret = sk_stream_wait_connect(sk, &timeo);
if (ret)
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 10/11] selftests: mptcp: sockopt: check app_limited
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (8 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 09/11] mptcp: track app-limited state in mptcp_sendmsg Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 11/11] Squash to "selftests/bpf: Add bpf_burst scheduler & test" Geliang Tang
2026-08-27 12:13 ` [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
11 siblings, 0 replies; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
connect_one_server() in mptcp_sockopt exchanges only a few packets between
the client and server, then closes the socket. After such a small transfer
the application has nothing further to send, so the connection is, by
definition, application-limited.
Extend the TCP_INFO readback at the end of the function to assert
s.tcp_info.tcpi_delivery_rate_app_limited == 1.
Without the preceding commit, mptcp_sendmsg() never updates the per-subflow
app-limited state, and this field stays at 0 - the assertion would fail.
With it in place, the value is forced to 1, turning this into a regression
guard for the subflow-side application-limited accounting.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index d68515b7903b..8d712bdb4325 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -640,6 +640,7 @@ static void connect_one_server(int fd, int pipefd)
total += 1; /* sequence advances due to FIN */
assert(s.mptcpi_rcv_delta == (uint64_t)total);
+ assert(s.tcp_info.tcpi_delivery_rate_app_limited == 1);
close(fd);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH mptcp-next v9 11/11] Squash to "selftests/bpf: Add bpf_burst scheduler & test"
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (9 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 10/11] selftests: mptcp: sockopt: check app_limited Geliang Tang
@ 2026-08-27 10:57 ` Geliang Tang
2026-08-27 11:17 ` sashiko-bot
2026-08-27 12:13 ` [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
11 siblings, 1 reply; 17+ messages in thread
From: Geliang Tang @ 2026-08-27 10:57 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
msk->snd_nxt was converted to atomic64_t. Use READ_ONCE on the .counter
field to read it safely in BPF context.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c b/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c
index 3d512b0efe6f..a391f6563a61 100644
--- a/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c
+++ b/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c
@@ -108,7 +108,8 @@ int BPF_PROG(bpf_burst_get_send, struct mptcp_sock *msk)
if (!subflow || !bpf_sk_stream_memory_free(subflow))
return -1;
- burst = min(MPTCP_SEND_BURST_SIZE, mptcp_wnd_end(msk) - msk->snd_nxt);
+ burst = min(MPTCP_SEND_BURST_SIZE,
+ mptcp_wnd_end(msk) - READ_ONCE(msk->snd_nxt.counter));
ssk = mptcp_subflow_tcp_sock(subflow);
wmem = ssk->sk_wmem_queued;
if (!burst)
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH mptcp-next v9 07/11] mptcp: implement peek_len for proto_ops
2026-08-27 10:57 ` [PATCH mptcp-next v9 07/11] mptcp: implement peek_len for proto_ops Geliang Tang
@ 2026-08-27 11:10 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 11:10 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Lockless read of 64-bit `copied_seq` causes torn reads on 32-bit systems and concurrent update races, leading to spurious INT_MAX return values.
- [High] Subtracting 1 for a FIN flag breaks EOF detection for stream consumers by reporting 0 instead of 1.
--
commit 7df02914f0a616d203067af77b84f00d4f6bc119
Author: Geliang Tang <geliang@kernel.org>
mptcp: implement peek_len for proto_ops
This commit adds mptcp_inq() to compute the readable bytes at the MPTCP
level and derives the count from mptcp_inq_hint(). It wires mptcp_inq()
into mptcp_peek_len() and assigns .peek_len in the stream operations
so upper layers get the in-queue byte count.
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index cc8747162cca9..9b9ace7cb977d 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -4814,6 +4814,37 @@ static ssize_t mptcp_splice_read(struct socket *sock, loff_t *ppos,
> return ret;
> }
>
> +static int mptcp_inq(struct sock *sk)
> +{
> + const struct mptcp_sock *msk = mptcp_sk(sk);
> + int answ;
> +
> + if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
> + answ = 0;
> + } else if (test_bit(MPTCP_SYNC_SEQ, &msk->cb_flags)) {
> + answ = 0;
> + } else {
> + u64 hint_val;
> +
> + hint_val = atomic64_read(&msk->ack_seq) - msk->copied_seq;
> + if (hint_val >= INT_MAX)
> + hint_val = INT_MAX;
[Severity: High]
Could this unprotected 64-bit read of copied_seq cause torn reads on 32-bit
systems?
If a concurrent caller invokes poll or a vhost loop calls .peek_len while
another thread is calling recvmsg(), the unsynchronized read of ack_seq and
copied_seq can result in an inconsistent state and cause an underflow.
This would then trigger the INT_MAX clamp, leading .peek_len to incorrectly
report a massive amount of queued data (up to 2GB). Is there a risk that this
causes stream consumers to allocate excessive memory or loop endlessly
expecting data?
> +
> + answ = (unsigned int)hint_val;
> + if (answ &&
> + (sk->sk_state == TCP_CLOSE ||
> + (sk->sk_shutdown & RCV_SHUTDOWN)))
> + answ--;
[Severity: High]
Does subtracting 1 here break EOF detection for stream consumers?
When the peer sends a FIN and the local MPTCP stack sets RCV_SHUTDOWN, this
logic actively suppresses the 1-byte EOF signal by reporting 0 instead of 1.
Callers relying on .peek_len for stream parsing (like strparser or KCM) might
enter an infinite wait or hang because they will see 0 data available and
wait for more data that will never arrive, instead of issuing the final
recvmsg() to observe EOF.
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787827525.git.tanggeliang@kylinos.cn?part=7
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH mptcp-next v9 04/11] mptcp: remove CB offset field
2026-08-27 10:57 ` [PATCH mptcp-next v9 04/11] mptcp: remove CB offset field Geliang Tang
@ 2026-08-27 11:13 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 11:13 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Lockless check of MPTCP_SYNC_SEQ in mptcp_inq_hint() leads to a TOCTOU race condition that can incorrectly return a bogus TCP_CM_INQ value (often INT_MAX).
--
commit 6c708b896ef73852cc85c2a1debeca40b8a72f1c
Author: Paolo Abeni <pabeni@redhat.com>
mptcp: remove CB offset field
This commit replaces the offset field in MPTCP's skb control block with a
socket-level copied_seq field. This mirrors TCP's approach for tracking
bytes consumed and delegating IASN synchronization to the msk release CB
during Fastopen.
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index 7d1422518aada..4cf7ad97ae0af 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
[ ... ]
> @@ -2414,27 +2397,27 @@ 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;
> u64 hint_val, ack_seq;
>
> - skb = skb_peek(&sk->sk_receive_queue);
> - if (skb) {
> - ack_seq = atomic64_read(&msk->ack_seq);
> - hint_val = ack_seq - MPTCP_SKB_CB(skb)->map_seq;
> -
> - if (hint_val >= INT_MAX)
> - return INT_MAX;
> + if (test_bit(MPTCP_SYNC_SEQ, &msk->cb_flags))
> + return 0;
>
> - return (unsigned int)hint_val;
> - }
> + /* Avoid races vs ack_seq updates. */
> + mptcp_data_lock(sk);
> + ack_seq = atomic64_read(&msk->ack_seq);
> + hint_val = ack_seq - msk->copied_seq;
> + mptcp_data_unlock(sk);
[Severity: Medium]
Could this lockless check of MPTCP_SYNC_SEQ lead to a Time-of-Check to
Time-of-Use (TOCTOU) race condition?
If an application calls recvmsg() with msg_control to read TCP_CM_INQ, and
concurrent to mptcp_inq_hint() executing, a softirq updates the socket's
ack_seq to the remote key (iasn) and sets the MPTCP_SYNC_SEQ flag:
mptcp_inq_hint() could test MPTCP_SYNC_SEQ outside of mptcp_data_lock(sk),
read 0 (false), wait for the lock, and then calculate hint_val using the
newly updated ack_seq but a stale copied_seq.
Since the new ack_seq is an absolute cryptographic sequence number and the
stale copied_seq is relative (negative), the difference could be larger
than INT_MAX, causing the function to incorrectly return INT_MAX.
> + 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;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787827525.git.tanggeliang@kylinos.cn?part=4
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields
2026-08-27 10:57 ` [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields Geliang Tang
@ 2026-08-27 11:15 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 11:15 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The patch misses converting the `rcv_data_fin_seq` field to `atomic64_t`.
--
commit 964553db62216cd000671e7e1848df8e7c8c6a5d
Author: Geliang Tang <geliang@kernel.org>
Date: Thu Aug 27 10:57:25 2026 +0000
mptcp: use atomic64_t for locklessly accessed u64 fields
This commit converts several u64 fields in struct mptcp_sock to atomic64_t
to prevent torn reads and writes on 32-bit architectures. It also caches
the read values in local variables to avoid inconsistent repeated reads.
> diff --git a/net/mptcp/options.c b/net/mptcp/options.c
> index f87707110c752..b33bf445c0ba5 100644
> --- a/net/mptcp/options.c
> +++ b/net/mptcp/options.c
[ ... ]
> @@ -1169,7 +1170,8 @@ bool mptcp_update_rcv_data_fin(struct mptcp_sock *msk, u64 data_fin_seq, bool us
> return false;
>
> WRITE_ONCE(msk->rcv_data_fin_seq,
> - mptcp_expand_seq(READ_ONCE(msk->ack_seq), data_fin_seq, use_64bit));
> + mptcp_expand_seq(atomic64_read(&msk->ack_seq),
> + data_fin_seq, use_64bit));
[Severity: High]
This isn't a bug introduced by this patch, but is it possible for
msk->rcv_data_fin_seq to suffer from a torn write here?
In mptcp_update_rcv_data_fin(), msk->rcv_data_fin_seq is written locklessly.
On 32-bit architectures, this WRITE_ONCE() compiles to two 32-bit stores.
If a remote peer sends DATA_FIN packets with different sequence numbers on two
subflows simultaneously, the two softirqs could concurrently write to
msk->rcv_data_fin_seq, interleaving their 32-bit stores and corrupting the
64-bit sequence number.
> WRITE_ONCE(msk->rcv_data_fin, 1);
>
> return true;
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index f22d64ab1c53b..26b49480941c6 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
[ ... ]
> @@ -569,7 +571,7 @@ static bool mptcp_pending_data_fin(struct sock *sk, u64 *seq)
> (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2))) {
> u64 rcv_data_fin_seq = READ_ONCE(msk->rcv_data_fin_seq);
>
> - if (READ_ONCE(msk->ack_seq) == rcv_data_fin_seq) {
> + if (atomic64_read(&msk->ack_seq) == rcv_data_fin_seq) {
> if (seq)
> *seq = rcv_data_fin_seq;
[Severity: High]
This is a pre-existing issue, but does this lockless read in
mptcp_pending_data_fin() risk reading a torn value?
If msk->rcv_data_fin_seq is corrupted due to the torn write described above,
the MPTCP connection would never match the corrupted sequence number here and
might fail to close, resulting in a connection hang and resource leak.
> diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
> index 3d250e8204d52..29405d045291d 100644
> --- a/net/mptcp/protocol.h
> +++ b/net/mptcp/protocol.h
[ ... ]
> @@ -288,15 +288,15 @@ struct mptcp_sock {
> * lockless access read
> */
> u64 remote_key; /* same as above */
> - u64 write_seq;
> + atomic64_t write_seq;
> u64 bytes_sent;
> - u64 snd_nxt;
> - u64 bytes_received;
> - u64 ack_seq;
> + atomic64_t snd_nxt;
> + atomic64_t bytes_received;
> + atomic64_t ack_seq;
> atomic64_t rcv_wnd_sent;
> u64 rcv_data_fin_seq;
> u64 bytes_retrans;
> - u64 bytes_consumed;
> + atomic64_t bytes_consumed;
[Severity: High]
This isn't a bug introduced by this patch, but shouldn't rcv_data_fin_seq
also be converted to atomic64_t here in struct mptcp_sock?
Since the explicit goal of this patch is to convert all locklessly accessed
u64 fields to atomic64_t to fix torn accesses on 32-bit architectures,
missing this field leaves the vulnerability unpatched.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787827525.git.tanggeliang@kylinos.cn?part=1
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH mptcp-next v9 11/11] Squash to "selftests/bpf: Add bpf_burst scheduler & test"
2026-08-27 10:57 ` [PATCH mptcp-next v9 11/11] Squash to "selftests/bpf: Add bpf_burst scheduler & test" Geliang Tang
@ 2026-08-27 11:17 ` sashiko-bot
0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-27 11:17 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Reading a 64-bit atomic variable directly via READ_ONCE() from a BPF program causes load tearing on 32-bit architectures.
--
commit a7fdecf17f713344ff1f523b442eda6174e11cd2
Author: Geliang Tang <geliang@kernel.org>
Squash to "selftests/bpf: Add bpf_burst scheduler & test"
This commit modifies the BPF burst scheduler test to safely read the newly
converted atomic64_t field msk->snd_nxt using READ_ONCE() on its .counter
member within the BPF context.
> diff --git a/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c b/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c
> index 3d512b0efe6f7..a391f6563a618 100644
> --- a/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c
> +++ b/tools/testing/selftests/bpf/progs/mptcp_bpf_burst.c
> @@ -108,7 +108,8 @@ int BPF_PROG(bpf_burst_get_send, struct mptcp_sock *msk)
> if (!subflow || !bpf_sk_stream_memory_free(subflow))
> return -1;
>
> - burst = min(MPTCP_SEND_BURST_SIZE, mptcp_wnd_end(msk) - msk->snd_nxt);
> + burst = min(MPTCP_SEND_BURST_SIZE,
> + mptcp_wnd_end(msk) - READ_ONCE(msk->snd_nxt.counter));
[Severity: Medium]
Will this cause load tearing on 32-bit architectures?
When the kernel concurrently updates msk->snd_nxt using atomic64_set(), on
32-bit architectures (e.g. ARM32, i386) configured with CONFIG_GENERIC_ATOMIC64,
atomic64_t operations are protected by hashed spinlocks. The BPF JIT will
translate the 64-bit load of READ_ONCE(msk->snd_nxt.counter) into two separate,
unsynchronized 32-bit memory loads.
If the BPF program executes these memory loads while the kernel is writing to
the counter, it could observe a torn read, leading to an incorrect burst size
calculation that breaks the MPTCP bpf_burst scheduler logic.
> ssk = mptcp_subflow_tcp_sock(subflow);
> wmem = ssk->sk_wmem_queued;
> if (!burst)
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1787827525.git.tanggeliang@kylinos.cn?part=11
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (10 preceding siblings ...)
2026-08-27 10:57 ` [PATCH mptcp-next v9 11/11] Squash to "selftests/bpf: Add bpf_burst scheduler & test" Geliang Tang
@ 2026-08-27 12:13 ` MPTCP CI
11 siblings, 0 replies; 17+ messages in thread
From: MPTCP CI @ 2026-08-27 12:13 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Hi Geliang,
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): Success! ✅
- 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! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/33067237251
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/762a5ba00967
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1152595
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] 17+ messages in thread
end of thread, other threads:[~2026-08-27 12:13 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-27 10:57 [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 01/11] mptcp: use atomic64_t for locklessly accessed u64 fields Geliang Tang
2026-08-27 11:15 ` sashiko-bot
2026-08-27 10:57 ` [PATCH mptcp-next v9 02/11] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 03/11] mptcp: drop the cant_coalesce CB field Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 04/11] mptcp: remove CB offset field Geliang Tang
2026-08-27 11:13 ` sashiko-bot
2026-08-27 10:57 ` [PATCH mptcp-next v9 05/11] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 06/11] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 07/11] mptcp: implement peek_len for proto_ops Geliang Tang
2026-08-27 11:10 ` sashiko-bot
2026-08-27 10:57 ` [PATCH mptcp-next v9 08/11] mptcp: add sendmsg_locked to proto_ops Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 09/11] mptcp: track app-limited state in mptcp_sendmsg Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 10/11] selftests: mptcp: sockopt: check app_limited Geliang Tang
2026-08-27 10:57 ` [PATCH mptcp-next v9 11/11] Squash to "selftests/bpf: Add bpf_burst scheduler & test" Geliang Tang
2026-08-27 11:17 ` sashiko-bot
2026-08-27 12:13 ` [PATCH mptcp-next v9 00/11] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox