Re: [PATCH mptcp-next 6/6] mptcp: defer sk_data_ready to the worker
Paolo Abeni <[email protected]> Wed, 29 Jul 2026 10:55:03 +0200
| Newsgroups | dev.linux.lists.mptcp |
|---|---|
| Message-ID | <[email protected]> |
On 7/27/26 1:29 PM, Geliang Tang wrote: > From: Geliang Tang <[email protected]> > > 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. Is mptcp_move_skbs() really needed in mptcp_recv_skb()? Anyway it looks like that is not the only constraint: AFAICS, before the mptcp_recv_skb() calls, the TLS code would call __mptcp_read_sock() which in turns calls mptcp_rcv_space_adjust() and mptcp_cleanup_rbuf() that requires 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_DATA_READY bit, re-using 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. I think that unconditionally adding the work latency for non-TLS application is a no-go. Instead I *think* that the constraints in __mptcp_read_sock() could be relaxed with something alike the following (completely untested): --- diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index ca644ec53eed..b09e267f5d46 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -2986,6 +2986,15 @@ static void mptcp_do_fastclose(struct sock *sk) } } +static void mptcp_read_complete(struct sock *sk) +{ + struct mptcp_sock *msk = mptcp_sk(sk); + + mptcp_cleanup_rbuf(msk, msk->read_copied); + mptcp_rcv_space_adjust(msk, msk->read_copied); + msk->read_copied = 0; +} + static void mptcp_worker(struct work_struct *work) { struct mptcp_sock *msk = container_of(work, struct mptcp_sock, work); @@ -3026,6 +3035,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); @@ -4381,9 +4393,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 = MPTCP_SKB_CB(skb)->offset; if (offset < skb->len) { @@ -4395,10 +4404,7 @@ static struct sk_buff *mptcp_recv_skb(struct sock *sk, u32 *off) return NULL; } -/* - * 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) { @@ -4441,11 +4447,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 4a2d40cd7b13..89902a98d383 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -115,6 +115,7 @@ #define MPTCP_WORK_RTX 1 #define MPTCP_FALLBACK_DONE 2 #define MPTCP_WORK_CLOSE_SUBFLOW 3 +#define MPTCP_WORK_READ_COMPLETE 4 /* MPTCP socket release cb flags */ #define MPTCP_PUSH_PENDING 1 @@ -305,6 +306,7 @@ struct mptcp_sock { u32 last_data_sent; u32 last_data_recv; u32 last_ack_recv; + int read_copied; unsigned long timer_ival; u32 token; unsigned long flags;