From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C940F1A680E; Fri, 7 Aug 2026 13:50:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786110646; cv=none; b=Tf+Kk5SalIQrpo1hrstzNk+q/Uynh3AaOnI1WsHCzYiN/wcDjJVymEwo45mnxWKSQabvrLcy2ZJntSiOHtJ6zRQfo2cZy1XRfFlfZ7EAucZ57AqMkh9hQH20RopuIXY+aeyMYKRE4z4P2TFfmvbf9NEpc5/9NpYW9CudKot4NX0= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786110646; c=relaxed/simple; bh=xXbe9KM0R1FPpoF7uqBwLCbyzCw//JO/HJwcZUALB5o=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fHObBiq3QODP9jchh/jCa/b1vgNWDgKTi9mR9JKTgY548Z3BIy9+pk9a67EkdyoP4tycAFBMgRP3yOj5jvOSwdGXloHJXfPN3cGlBbr1jHtaCsvsEXgA9mgCP7bX+1iWNNqjfVQ3DN+lXa7NyDK10pJ0svqJ4o3N3owXVvVHIsc= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HJSnpX+J; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HJSnpX+J" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6790D1F00ADF; Fri, 7 Aug 2026 13:50:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786110635; bh=3Vg4ZBTK8EICstEJx6hBbT9srnzcGKNDXlH6HZAELBA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=HJSnpX+J3ay99hkW2+v0FN/5B0OCgO0TxK0AbOimfKsIBNSCJ1ufhM/fKG3zQXsjc prIb1i8OZ5mu8Za0Av3hZ7BrxHdUxkM9w5aQO84m8ovkEvD0PKdKL1M+9ER+gbIoZT f4r1n8vIXBRNJfmxiihBEXUi/UTuWBFTHb2HCc4h8QMe+0KKeGvUfRizVQ8UuU8A+D x9a5dTqCQXls2LDJiRX5r4mDfTuXIVloNeiY6DUQtRWt/Z13hORbUHvEzsQcCQNYUT sSjC8jpLhLzPtS0B+ru9C0P3Tqqv94GVhj5Sh/sHPJOt6pb2hrdjm+s39OlH9eqVLQ uKIfQIOwTDMBA== From: "Matthieu Baerts (NGI0)" Date: Fri, 07 Aug 2026 15:49:04 +0200 Subject: [PATCH net-next v3 4/7] mptcp: explicitly drop over memory limits Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260807-net-next-mptcp-oooq-pruning-v3-4-dbc1eb853cc3@kernel.org> References: <20260807-net-next-mptcp-oooq-pruning-v3-0-dbc1eb853cc3@kernel.org> In-Reply-To: <20260807-net-next-mptcp-oooq-pruning-v3-0-dbc1eb853cc3@kernel.org> To: Mat Martineau , Geliang Tang , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman Cc: netdev@vger.kernel.org, mptcp@lists.linux.dev, linux-kernel@vger.kernel.org, "Matthieu Baerts (NGI0)" X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=7160; i=matttbe@kernel.org; h=from:subject:message-id; bh=9Ivb2sQePeKn04xop488JA4iBD50hNuL9sI1rE/g7tk=; b=owGbwMvMwCVWo/Th0Gd3rumMp9WSGLJKH82dmHY39NTcU77++/RsD1TPbbhjVlVw/9et4gnaJ /23H13V3FHKwiDGxSArpsgi3RaZP/N5FW+Jl58FzBxWJpAhDFycAjCRu3sYGdqYwqq0lnaf3ho2 i6XnC/vJqvsSGRfYGGRWvnzycPmi0IuMDB3ZLWkHHmedzjcU0FGq9T0Xef/Fo94lArvZYvxP/HV k4gIA X-Developer-Key: i=matttbe@kernel.org; a=openpgp; fpr=E8CB85F76877057A6E27F77AF6B7824F4269A073 From: Paolo Abeni Currently the enforcement of the rcvbuf constraint is implemented when moving the skbs into the msk receive or OoO queue, keeping the incoming skbs in the subflow queue when over limits. Under significant memory pressure the above can cause permanent data transfer stalls, as the skb needed to make forward progress can be stuck in a subflow queue. Over memory limits, drop the incoming skb, relying on MPTCP-level retransmissions. Note that fallback socket must perform the limit before the skb reaches the subflow-level queue, as dropping an in-sequence already acked skb would break the stream. This is not a complete fix for the stall issue, as the drop strategy needs refinements that will come in the next patches. Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) --- v2: - mib: typo: "constrains" -> "constraints". - mptcp_over_limit: more than 0-win: retrans, dup or old acks. - mptcp_over_limit: bump LINUX_MIB_TCPRCVQDROP. - Note: Sashiko might point to a possible forward-allocated memory leak: this is a temp leak, and releasing additionally allocated fwd memory in the error path will be fix in a patch for -net. v3: - refine mptcp_over_limit() to check separately backlog and rcvbuf - do not drop rst (with data) --- net/mptcp/mib.c | 2 ++ net/mptcp/mib.h | 2 ++ net/mptcp/options.c | 32 +++++++++++++++++++++++++++++--- net/mptcp/protocol.c | 31 +++++++++++++++++++++++-------- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c index f23fda0c55a7..ef65e2df709f 100644 --- a/net/mptcp/mib.c +++ b/net/mptcp/mib.c @@ -85,6 +85,8 @@ static const struct snmp_mib mptcp_snmp_list[] = { SNMP_MIB_ITEM("SimultConnectFallback", MPTCP_MIB_SIMULTCONNFALLBACK), SNMP_MIB_ITEM("FallbackFailed", MPTCP_MIB_FALLBACKFAILED), SNMP_MIB_ITEM("WinProbe", MPTCP_MIB_WINPROBE), + SNMP_MIB_ITEM("BacklogDrop", MPTCP_MIB_BACKLOGDROP), + SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED), }; /* mptcp_mib_alloc - allocate percpu mib counters diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h index 812218b5ed2b..9271205f682e 100644 --- a/net/mptcp/mib.h +++ b/net/mptcp/mib.h @@ -88,6 +88,8 @@ enum linux_mptcp_mib_field { MPTCP_MIB_SIMULTCONNFALLBACK, /* Simultaneous connect */ MPTCP_MIB_FALLBACKFAILED, /* Can't fallback due to msk status */ MPTCP_MIB_WINPROBE, /* MPTCP-level zero window probe */ + MPTCP_MIB_BACKLOGDROP, /* Backlog over memory limit */ + MPTCP_MIB_RCVPRUNED, /* Dropped due to memory constraints */ __MPTCP_MIB_MAX }; diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 1057d500577b..b8318e030138 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -1190,8 +1190,34 @@ static bool add_addr_hmac_valid(struct mptcp_sock *msk, return hmac == mp_opt->ahmac; } -/* Return false in case of error (or subflow has been reset), - * else return true. +static bool mptcp_over_limit(struct sock *sk, struct sock *ssk, + const struct sk_buff *skb) +{ + struct mptcp_sock *msk = mptcp_sk(sk); + u32 rcvbuf = READ_ONCE(sk->sk_rcvbuf); + + if (likely((u32)sk_rmem_alloc_get(sk) <= rcvbuf && + READ_ONCE(msk->backlog_len) <= rcvbuf)) + return false; + + /* Avoid silently dropping pure acks, fin, rst or already-acked segm. */ + if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq || + TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_FIN | TCPHDR_RST) || + !after(TCP_SKB_CB(skb)->end_seq, tcp_sk(ssk)->rcv_nxt)) + return false; + + /* Dropped due to memory constraints, schedule an ack. */ + inet_csk(ssk)->icsk_ack.pending |= ICSK_ACK_NOMEM | ICSK_ACK_NOW; + inet_csk_schedule_ack(ssk); + + /* Plain TCP (fallback) and skb is dropped before the TCP recv queue. */ + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRCVQDROP); + + return true; +} + +/* Return false when the caller must drop the packet, i.e. in case of error, + * subflow has been reset, or over memory limits. */ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) { @@ -1217,7 +1243,7 @@ bool mptcp_incoming_options(struct sock *sk, struct sk_buff *skb) __mptcp_data_acked(subflow->conn); mptcp_data_unlock(subflow->conn); - return true; + return !mptcp_over_limit(subflow->conn, sk, skb); } mptcp_get_options(skb, &mp_opt); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 09cd3c1f3cdb..cfb77a32518a 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -387,6 +387,16 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb) mptcp_borrow_fwdmem(sk, skb); + /* Can't drop packets for fallback socket this late, or the stream + * will break. + */ + if (unlikely(sk_rmem_alloc_get(sk) > READ_ONCE(sk->sk_rcvbuf)) && + !__mptcp_check_fallback(msk)) { + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED); + mptcp_drop(sk, skb); + return false; + } + if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) { /* in sequence */ msk->bytes_received += copy_len; @@ -681,6 +691,7 @@ static void __mptcp_add_backlog(struct sock *sk, struct sk_buff *tail = NULL; struct sock *ssk = skb->sk; bool fragstolen; + u64 limit; int delta; if (unlikely(sk->sk_state == TCP_CLOSE)) { @@ -688,6 +699,16 @@ static void __mptcp_add_backlog(struct sock *sk, return; } + /* Similar additional allowance as plain TCP. */ + limit = READ_ONCE(sk->sk_rcvbuf); + limit += (limit >> 1) + 64 * 1024; + limit = min_t(u64, limit, UINT_MAX); + if (msk->backlog_len > limit && !__mptcp_check_fallback(msk)) { + __MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_BACKLOGDROP); + kfree_skb_reason(skb, SKB_DROP_REASON_SOCKET_BACKLOG); + return; + } + /* Try to coalesce with the last skb in our backlog */ if (!list_empty(&msk->backlog_list)) tail = list_last_entry(&msk->backlog_list, struct sk_buff, list); @@ -759,7 +780,7 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk, mptcp_init_skb(ssk, skb, offset, len); - if (own_msk && sk_rmem_alloc_get(sk) < sk->sk_rcvbuf) { + if (own_msk) { mptcp_subflow_lend_fwdmem(subflow, skb); ret |= __mptcp_move_skb(sk, skb); } else { @@ -2210,10 +2231,6 @@ static bool __mptcp_move_skbs(struct sock *sk, struct list_head *skbs, u32 *delt *delta = 0; while (1) { - /* If the msk recvbuf is full stop, don't drop */ - if (sk_rmem_alloc_get(sk) > sk->sk_rcvbuf) - break; - prefetch(skb->next); list_del(&skb->list); *delta += skb->truesize; @@ -2241,9 +2258,7 @@ static bool mptcp_can_spool_backlog(struct sock *sk, struct list_head *skbs) DEBUG_NET_WARN_ON_ONCE(msk->backlog_unaccounted && sk->sk_socket && mem_cgroup_from_sk(sk)); - /* Don't spool the backlog if the rcvbuf is full. */ - if (list_empty(&msk->backlog_list) || - sk_rmem_alloc_get(sk) > sk->sk_rcvbuf) + if (list_empty(&msk->backlog_list)) return false; INIT_LIST_HEAD(skbs); -- 2.53.0