* [PATCH net v4 0/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative
@ 2026-10-26 3:22 Octavian Purdila
2026-10-26 3:22 ` [PATCH net v4 1/2] " Octavian Purdila
2026-10-26 3:22 ` [PATCH net v4 2/2] selftests: net: packetdrill: add test for negative sk_forward_alloc in tcp_wmem_schedule() Octavian Purdila
0 siblings, 2 replies; 5+ messages in thread
From: Octavian Purdila @ 2026-10-26 3:22 UTC (permalink / raw)
To: Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima, David S . Miller,
David Ahern, Jakub Kicinski, Paolo Abeni, Shuah Khan
Cc: Simon Horman, Willem de Bruijn, netdev, linux-kselftest,
linux-kernel, Octavian Purdila
This series fixes a bug discovered in production in tcp_wmem_schedule()
where a negative sk->sk_forward_alloc under TCP memory pressure causes
tcp_wmem_schedule() to return a negative copy length when
sk->sk_wmem_queued already reaches or exceeds sysctl_tcp_wmem[0].
sk->sk_forward_alloc can transiently become negative when tcp_collapse()
re-packs partially read or high payload-to-truesize SKBs in the receive
queue and charges newly allocated SKBs via sk_mem_charge() before
freeing the original SKBs. When a subsequent send hits memory pressure
in sk_wmem_schedule() with sk->sk_wmem_queued >= sysctl_tcp_wmem[0],
tcp_wmem_schedule() returns min(copy, sk->sk_forward_alloc) < 0.
Callers such as tcp_sendmsg_locked() then pass that negative copy length
to skb_copy_to_page_nocache() -> copy_from_iter_full() ->
check_copy_size(), triggering WARN_ON_ONCE(bytes > INT_MAX) in
production and returning -EFAULT instead of waiting for memory or
returning -EAGAIN.
Patch 1 clamps the return value of tcp_wmem_schedule() to [0, copy] so
a negative sk->sk_forward_alloc is treated as 0 available bytes.
Patch 2 adds a packetdrill selftest that reproduces the negative
sk->sk_forward_alloc condition via tcp_collapse() under memory pressure
and verifies that a non-blocking write fails with EAGAIN rather than
EFAULT.
Changes in v4:
- Add --define=REQUIRES_MEMCG="1" support in ksft_runner.sh to skip the
test when the cgroup2 memory controller is unavailable and clean up
pkt_* cgroups and cgroup.subtree_control via an EXIT trap.
- Pin packetdrill to CPU 0, write 0 to memory.high twice to guarantee
local per-CPU memcg stock is drained synchronously, and set
memory.swap.max=0 in the test cgroup.
- Fix checkpatch warnings (duplicate Signed-off-by trailer and comment
line length) and preserve the "net" target prefix on all patches.
Changes in v3:
- Fix packetdrill test failure in netdev CI (CONFIG_MAX_SKB_FRAGS=17):
adjust segment sizes (35 KB) and buffer limits so the receive SKB
truesize is identical across CONFIG_MAX_SKB_FRAGS=17 and 45, and
tcp_collapse() deterministically drives sk_forward_alloc below -16 KB
without invoking the memcg OOM killer.
Changes in v2:
- Include production warning trace in Patch 1 commit message.
- Skip the packetdrill test when PAGE_SIZE != 4096 via
--define=REQUIRES_PAGESIZE="4096" in ksft_runner.sh.
- Collect Reviewed-by tags from Kuniyuki Iwashima and Eric Dumazet.
Octavian Purdila (2):
tcp: clamp return value of tcp_wmem_schedule() to non-negative
selftests: net: packetdrill: add test for negative sk_forward_alloc in
tcp_wmem_schedule()
net/ipv4/tcp.c | 2 +-
.../testing/selftests/net/packetdrill/config | 2 +
.../selftests/net/packetdrill/ksft_runner.sh | 28 +++++
.../tcp_wmem_schedule_negative_fwd_alloc.pkt | 116 ++++++++++++++++++
4 files changed, 147 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/net/packetdrill/tcp_wmem_schedule_negative_fwd_alloc.pkt
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH net v4 1/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative 2026-10-26 3:22 [PATCH net v4 0/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative Octavian Purdila @ 2026-10-26 3:22 ` Octavian Purdila 2026-10-26 12:06 ` Eric Dumazet 2026-10-26 3:22 ` [PATCH net v4 2/2] selftests: net: packetdrill: add test for negative sk_forward_alloc in tcp_wmem_schedule() Octavian Purdila 1 sibling, 1 reply; 5+ messages in thread From: Octavian Purdila @ 2026-10-26 3:22 UTC (permalink / raw) To: Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski, Paolo Abeni, Shuah Khan Cc: Simon Horman, Willem de Bruijn, netdev, linux-kselftest, linux-kernel, Octavian Purdila, Eric Dumazet When sk_wmem_schedule(sk, copy) fails under TCP memory pressure and sk->sk_wmem_queued already reaches or exceeds sysctl_tcp_wmem[0], tcp_wmem_schedule() returns min(copy, sk->sk_forward_alloc). However, sk->sk_forward_alloc is a signed int that can transiently become negative when tcp_collapse() re-packs partially read or high payload-to-truesize SKBs in the receive queue and charges newly allocated SKBs via sk_mem_charge() before freeing the original SKB. If tcp_wmem_schedule() returns a negative value, tcp_sendmsg_locked() passes that negative copy length to skb_copy_to_page_nocache() -> copy_from_iter_full() -> check_copy_size(), where it is converted to an unsigned size_t > INT_MAX, triggering a WARN_ON_ONCE(bytes > INT_MAX) in production and returning -EFAULT instead of waiting for memory or returning -EAGAIN: WARNING: CPU: 1 PID: 939331 at include/linux/thread_info.h:249 tcp_sendmsg+0x10c1/0x1530 Call Trace: <TASK> __sys_sendmsg+0x292/0x490 do_syscall_64+0x88/0x340 entry_SYSCALL_64_after_hwframe+0x76/0x7e Clamp the return value of tcp_wmem_schedule() to [0, copy] so callers treat negative sk_forward_alloc as 0 available bytes. Fixes: 849b425cd091 ("tcp: fix possible freeze in tx path under memory pressure") Assisted-by: LLM Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Reviewed-by: Eric Dumazet <edumazet@kernel.org> Signed-off-by: Octavian Purdila <tavip@google.com> --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 87ef6d5cbfeba..0e062fcd94a6c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1037,7 +1037,7 @@ int tcp_wmem_schedule(struct sock *sk, int copy) left = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_wmem[0]) - sk->sk_wmem_queued; if (left > 0) sk_forced_mem_schedule(sk, min(left, copy)); - return min(copy, sk->sk_forward_alloc); + return clamp(sk->sk_forward_alloc, 0, copy); } void tcp_free_fastopen_req(struct tcp_sock *tp) -- 2.56.0.257.gf7497c7d27-goog ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net v4 1/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative 2026-10-26 3:22 ` [PATCH net v4 1/2] " Octavian Purdila @ 2026-10-26 12:06 ` Eric Dumazet 2026-10-27 2:28 ` Kuniyuki Iwashima 0 siblings, 1 reply; 5+ messages in thread From: Eric Dumazet @ 2026-10-26 12:06 UTC (permalink / raw) To: Octavian Purdila Cc: Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski, Paolo Abeni, Shuah Khan, Simon Horman, Willem de Bruijn, netdev, linux-kselftest, linux-kernel Le lun. 26 oct. 2026 à 04:22, Octavian Purdila <tavip@google.com> a écrit : > > When sk_wmem_schedule(sk, copy) fails under TCP memory pressure and > sk->sk_wmem_queued already reaches or exceeds sysctl_tcp_wmem[0], > tcp_wmem_schedule() returns min(copy, sk->sk_forward_alloc). > > However, sk->sk_forward_alloc is a signed int that can transiently > become negative when tcp_collapse() re-packs partially read or high > payload-to-truesize SKBs in the receive queue and charges newly > allocated SKBs via sk_mem_charge() before freeing the original SKB. > > If tcp_wmem_schedule() returns a negative value, tcp_sendmsg_locked() > passes that negative copy length to skb_copy_to_page_nocache() -> > copy_from_iter_full() -> check_copy_size(), where it is converted to an > unsigned size_t > INT_MAX, triggering a WARN_ON_ONCE(bytes > INT_MAX) > in production and returning -EFAULT instead of waiting for memory or > returning -EAGAIN: > > WARNING: CPU: 1 PID: 939331 at include/linux/thread_info.h:249 tcp_sendmsg+0x10c1/0x1530 > Call Trace: > <TASK> > __sys_sendmsg+0x292/0x490 > do_syscall_64+0x88/0x340 > entry_SYSCALL_64_after_hwframe+0x76/0x7e > > Clamp the return value of tcp_wmem_schedule() to [0, copy] so callers > treat negative sk_forward_alloc as 0 available bytes. > > Fixes: 849b425cd091 ("tcp: fix possible freeze in tx path under memory pressure") > Assisted-by: LLM > Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> > Reviewed-by: Eric Dumazet <edumazet@kernel.org> > Signed-off-by: Octavian Purdila <tavip@google.com> > --- > net/ipv4/tcp.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c > index 87ef6d5cbfeba..0e062fcd94a6c 100644 > --- a/net/ipv4/tcp.c > +++ b/net/ipv4/tcp.c > @@ -1037,7 +1037,7 @@ int tcp_wmem_schedule(struct sock *sk, int copy) > left = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_wmem[0]) - sk->sk_wmem_queued; > if (left > 0) > sk_forced_mem_schedule(sk, min(left, copy)); > - return min(copy, sk->sk_forward_alloc); > + return clamp(sk->sk_forward_alloc, 0, copy); > } > > void tcp_free_fastopen_req(struct tcp_sock *tp) I do wonder if the following fix could be better (no change in the fast path) Before a complete fix in tcp_collapse() (in net-next) tcp: do not let tcp_collapse() drive sk_forward_alloc negative tcp_collapse() charges each newly allocated skb with skb_set_owner_r() before the skbs it replaces are freed, without scheduling memory first. Since sk_mem_uncharge() reclaims eagerly, sk->sk_forward_alloc has no cushion and can become negative. This can be durable when the collapsed skbs had a high payload/truesize ratio or were partially read, because the new skbs can have a bigger total truesize than the old ones. A negative sk_forward_alloc then confuses tcp_wmem_schedule(), which can return a negative length to tcp_sendmsg_locked(), triggering a WARN_ON_ONCE(bytes > INT_MAX) in check_copy_size(). Call sk_forced_mem_schedule() before charging nskb. Fixes: 4890b686f408 ("net: keep sk->sk_forward_alloc as small as possible") diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 1ddce9c4b7e8cf91bfd4f5361abec3925fe25046..e879bdfddf1abe2ced80b12ea8a40c1f7c958708 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5847,6 +5847,10 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root, __skb_queue_before(list, skb, nskb); else __skb_queue_tail(&tmp, nskb); /* defer rbtree insertion */ + /* nskb is charged before the skbs it replaces are freed. + * Make sure sk->sk_forward_alloc can not become negative. + */ + sk_forced_mem_schedule(sk, nskb->truesize); skb_set_owner_r(nskb, sk); mptcp_skb_ext_move(nskb, skb); ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net v4 1/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative 2026-10-26 12:06 ` Eric Dumazet @ 2026-10-27 2:28 ` Kuniyuki Iwashima 0 siblings, 0 replies; 5+ messages in thread From: Kuniyuki Iwashima @ 2026-10-27 2:28 UTC (permalink / raw) To: Eric Dumazet Cc: Octavian Purdila, Eric Dumazet, Neal Cardwell, David S . Miller, David Ahern, Jakub Kicinski, Paolo Abeni, Shuah Khan, Simon Horman, Willem de Bruijn, netdev, linux-kselftest, linux-kernel On Mon, 26 Oct 2026 13:06:56 +0100, Eric Dumazet wrote: > Le lun. 26 oct. 2026 à 04:22, Octavian Purdila <tavip@google.com> a écrit : > > > > When sk_wmem_schedule(sk, copy) fails under TCP memory pressure and > > sk->sk_wmem_queued already reaches or exceeds sysctl_tcp_wmem[0], > > tcp_wmem_schedule() returns min(copy, sk->sk_forward_alloc). > > > > However, sk->sk_forward_alloc is a signed int that can transiently > > become negative when tcp_collapse() re-packs partially read or high > > payload-to-truesize SKBs in the receive queue and charges newly > > allocated SKBs via sk_mem_charge() before freeing the original SKB. > > > > If tcp_wmem_schedule() returns a negative value, tcp_sendmsg_locked() > > passes that negative copy length to skb_copy_to_page_nocache() -> > > copy_from_iter_full() -> check_copy_size(), where it is converted to an > > unsigned size_t > INT_MAX, triggering a WARN_ON_ONCE(bytes > INT_MAX) > > in production and returning -EFAULT instead of waiting for memory or > > returning -EAGAIN: > > > > WARNING: CPU: 1 PID: 939331 at include/linux/thread_info.h:249 tcp_sendmsg+0x10c1/0x1530 > > Call Trace: > > <TASK> > > __sys_sendmsg+0x292/0x490 > > do_syscall_64+0x88/0x340 > > entry_SYSCALL_64_after_hwframe+0x76/0x7e > > > > Clamp the return value of tcp_wmem_schedule() to [0, copy] so callers > > treat negative sk_forward_alloc as 0 available bytes. > > > > Fixes: 849b425cd091 ("tcp: fix possible freeze in tx path under memory pressure") > > Assisted-by: LLM > > Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> > > Reviewed-by: Eric Dumazet <edumazet@kernel.org> > > Signed-off-by: Octavian Purdila <tavip@google.com> > > --- > > net/ipv4/tcp.c | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c > > index 87ef6d5cbfeba..0e062fcd94a6c 100644 > > --- a/net/ipv4/tcp.c > > +++ b/net/ipv4/tcp.c > > @@ -1037,7 +1037,7 @@ int tcp_wmem_schedule(struct sock *sk, int copy) > > left = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_wmem[0]) - sk->sk_wmem_queued; > > if (left > 0) > > sk_forced_mem_schedule(sk, min(left, copy)); > > - return min(copy, sk->sk_forward_alloc); > > + return clamp(sk->sk_forward_alloc, 0, copy); > > } > > > > void tcp_free_fastopen_req(struct tcp_sock *tp) > > I do wonder if the following fix could be better (no change in the fast path) Exactly, this looks better too, we can avoid negative sk_forward_alloc and clamp(). The packetdrill will need to be adjusted as it triggers cgroup OOM with this approach, or we could defer the test to net-next too. > > Before a complete fix in tcp_collapse() (in net-next) > > tcp: do not let tcp_collapse() drive sk_forward_alloc negative > > tcp_collapse() charges each newly allocated skb with skb_set_owner_r() > before the skbs it replaces are freed, without scheduling memory first. > > Since sk_mem_uncharge() reclaims eagerly, sk->sk_forward_alloc has no > cushion and can become negative. This can be durable when the collapsed > skbs had a high payload/truesize ratio or were partially read, because > the new skbs can have a bigger total truesize than the old ones. > > A negative sk_forward_alloc then confuses tcp_wmem_schedule(), which > can return a negative length to tcp_sendmsg_locked(), triggering > a WARN_ON_ONCE(bytes > INT_MAX) in check_copy_size(). > > Call sk_forced_mem_schedule() before charging nskb. > > Fixes: 4890b686f408 ("net: keep sk->sk_forward_alloc as small as possible") > > diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c > index 1ddce9c4b7e8cf91bfd4f5361abec3925fe25046..e879bdfddf1abe2ced80b12ea8a40c1f7c958708 > 100644 > --- a/net/ipv4/tcp_input.c > +++ b/net/ipv4/tcp_input.c > @@ -5847,6 +5847,10 @@ tcp_collapse(struct sock *sk, struct > sk_buff_head *list, struct rb_root *root, > __skb_queue_before(list, skb, nskb); > else > __skb_queue_tail(&tmp, nskb); /* defer rbtree > insertion */ > + /* nskb is charged before the skbs it replaces are freed. > + * Make sure sk->sk_forward_alloc can not become negative. > + */ > + sk_forced_mem_schedule(sk, nskb->truesize); > skb_set_owner_r(nskb, sk); > mptcp_skb_ext_move(nskb, skb); ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v4 2/2] selftests: net: packetdrill: add test for negative sk_forward_alloc in tcp_wmem_schedule() 2026-10-26 3:22 [PATCH net v4 0/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative Octavian Purdila 2026-10-26 3:22 ` [PATCH net v4 1/2] " Octavian Purdila @ 2026-10-26 3:22 ` Octavian Purdila 1 sibling, 0 replies; 5+ messages in thread From: Octavian Purdila @ 2026-10-26 3:22 UTC (permalink / raw) To: Eric Dumazet, Neal Cardwell, Kuniyuki Iwashima, David S . Miller, David Ahern, Jakub Kicinski, Paolo Abeni, Shuah Khan Cc: Simon Horman, Willem de Bruijn, netdev, linux-kselftest, linux-kernel, Octavian Purdila Add a packetdrill test that drives sk->sk_forward_alloc negative via tcp_collapse() on the receive queue while sk->sk_wmem_queued exceeds sysctl_tcp_wmem[0], and verifies that a subsequent non-blocking write() under TCP memory pressure fails with EAGAIN rather than returning a negative copy length from tcp_wmem_schedule() and failing with EFAULT. Assisted-by: LLM Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Signed-off-by: Octavian Purdila <tavip@google.com> --- .../testing/selftests/net/packetdrill/config | 2 + .../selftests/net/packetdrill/ksft_runner.sh | 28 +++++ .../tcp_wmem_schedule_negative_fwd_alloc.pkt | 116 ++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 tools/testing/selftests/net/packetdrill/tcp_wmem_schedule_negative_fwd_alloc.pkt diff --git a/tools/testing/selftests/net/packetdrill/config b/tools/testing/selftests/net/packetdrill/config index b7df8bf309200..a14a704dcbd49 100644 --- a/tools/testing/selftests/net/packetdrill/config +++ b/tools/testing/selftests/net/packetdrill/config @@ -1,6 +1,8 @@ +CONFIG_CGROUPS=y CONFIG_HZ=1000 CONFIG_HZ_1000=y CONFIG_IPV6=y +CONFIG_MEMCG=y CONFIG_NET_NS=y CONFIG_NET_SCH_FIFO=y CONFIG_NET_SCH_FQ=y diff --git a/tools/testing/selftests/net/packetdrill/ksft_runner.sh b/tools/testing/selftests/net/packetdrill/ksft_runner.sh index 0a97d5ae34699..ba62f3840eb57 100755 --- a/tools/testing/selftests/net/packetdrill/ksft_runner.sh +++ b/tools/testing/selftests/net/packetdrill/ksft_runner.sh @@ -52,6 +52,34 @@ if [[ -n "${KSFT_MACHINE_SLOW}" ]]; then failfunc=ktap_test_xfail fi +pagesize=$(grep -E '^--define=REQUIRES_PAGESIZE=' $script | cut -d '=' -f 3 | tr -d '"') +if [[ -n "$pagesize" && "$(getconf PAGESIZE)" != "$pagesize" ]]; then + ktap_skip_all "requires ${pagesize}B PAGE_SIZE" + exit "$KSFT_SKIP" +fi + +if grep -q -E '^--define=REQUIRES_MEMCG=' $script; then + cgroot=$(mount -t cgroup2 | head -1 | awk '{print $3}') + memcg_was_enabled=0 + if [[ -n "$cgroot" ]] && + grep -qw memory "${cgroot}/cgroup.subtree_control" 2>/dev/null; then + memcg_was_enabled=1 + fi + if [[ -z "$cgroot" ]] || + ! echo +memory > "${cgroot}/cgroup.subtree_control" 2>/dev/null; then + ktap_skip_all "requires cgroup2 memory controller" + exit "$KSFT_SKIP" + fi + cleanup_memcg() { + rmdir "${cgroot}"/pkt_* 2>/dev/null || true + if [[ "$memcg_was_enabled" -eq 0 ]]; then + echo -memory > "${cgroot}/cgroup.subtree_control" \ + 2>/dev/null || true + fi + } + trap cleanup_memcg EXIT +fi + ip_versions=$(grep -E '^--ip_version=' $script | cut -d '=' -f 2) if [[ -z $ip_versions ]]; then ip_versions="ipv4 ipv6 ipv4-mapped-ipv6" diff --git a/tools/testing/selftests/net/packetdrill/tcp_wmem_schedule_negative_fwd_alloc.pkt b/tools/testing/selftests/net/packetdrill/tcp_wmem_schedule_negative_fwd_alloc.pkt new file mode 100644 index 0000000000000..e4d8d13144f0a --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_wmem_schedule_negative_fwd_alloc.pkt @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +// Test that tcp_wmem_schedule() clamps negative sk->sk_forward_alloc to 0 +// when sk_wmem_schedule() fails under memory pressure and +// sk->sk_wmem_queued >= sysctl_tcp_wmem[0], returning EAGAIN rather than +// EFAULT. +// +// Buffer and segment sizes are tuned for 4 KB pages and standard +// CONFIG_MAX_SKB_FRAGS values (17 to 45). + +--define=REQUIRES_PAGESIZE="4096" +--define=REQUIRES_MEMCG="1" +--mtu=65535 +--tolerance_usecs=100000 + +`set -e +./defaults.sh +ip link set dev tun0 mtu 65535 +sysctl -q net.ipv4.tcp_rmem="4096 131072 6291456" +sysctl -q net.ipv4.tcp_wmem="4096 16384 4194304" +taskset -cp 0 $PPID >/dev/null 2>&1 || true +cgroot=$(mount -t cgroup2 | head -1 | awk '{print $3}') +[ -n "$cgroot" ] +echo +memory > "${cgroot}/cgroup.subtree_control" +rmdir "${cgroot}/pkt_${PPID}" 2>/dev/null || true +mkdir "${cgroot}/pkt_${PPID}" +echo 0 > "${cgroot}/pkt_${PPID}/memory.swap.max" 2>/dev/null || true +echo $PPID > "${cgroot}/pkt_${PPID}/cgroup.procs" +` + +// Create listening socket in pkt_${PPID} memcg and establish connection +0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 ++0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 ++0.000 bind(3, ..., ...) = 0 ++0.000 listen(3, 1) = 0 + ++0.000 < S 0:0(0) win 65535 <mss 35000,nop,wscale 7> ++0.000 > S. 0:0(0) ack 1 <...> ++0.000 < . 1:1(0) ack 1 win 512 ++0.000 accept(3, ..., ...) = 4 + +// Move packetdrill back to its original cgroup so subsequent packetdrill +// allocations are not charged to pkt_${PPID}. ++0.000 `cgroot=$(mount -t cgroup2 | head -1 | awk '{print $3}') +runner_pid=$(awk '/^PPid:/ {print $2}' /proc/$PPID/status) +orig_cg=$(sed -n 's/^0:://p' /proc/$runner_pid/cgroup) +echo $PPID > "${cgroot}${orig_cg}/cgroup.procs"` + ++0.000 setsockopt(4, SOL_SOCKET, SO_SNDBUF, [200000], 4) = 0 ++0.000 setsockopt(4, SOL_SOCKET, SO_RCVBUF, [112500], 4) = 0 + +// 1. Warm up rcv_ssthresh to full window ++0.000 < P. 1:35001(35000) ack 1 win 512 ++0.000 > . 1:1(0) ack 35001 ++0.000 read(4, ..., 35000) = 35000 + ++0.000 < P. 35001:70001(35000) ack 1 win 512 ++0.000 > . 1:1(0) ack 70001 ++0.000 read(4, ..., 35000) = 35000 + ++0.000 < P. 70001:105001(35000) ack 1 win 512 ++0.000 > . 1:1(0) ack 105001 ++0.000 read(4, ..., 35000) = 35000 + ++0.000 < P. 105001:140001(35000) ack 1 win 512 ++0.000 > . 1:1(0) ack 140001 ++0.000 read(4, ..., 35000) = 35000 + +// 2. Queue 79 KB on TX so sk_wmem_queued >= sysctl_tcp_wmem[0], while leaving +// both tail SKB space (26 KB) and sk_page_frag space (19.3 KB) >= 16 KB. ++0.000 write(4, ..., 79000) = 79000 + +// 3a. Inject 6 high-density 35 KB segments (210 KB payload, 222.7 KB truesize +// across 8-page order-3 frags on both CONFIG_MAX_SKB_FRAGS=17 and 45) and +// read 100 bytes from the head SKB so before(skb->seq, tp->copied_seq) +// holds. ++0.000 < P. 140001:175001(35000) ack 1 win 512 ++0.000 < P. 175001:210001(35000) ack 1 win 512 ++0.000 read(4, ..., 100) = 100 + ++0.000 < P. 210001:245001(35000) ack 1 win 512 ++0.000 < P. 245001:280001(35000) ack 1 win 512 ++0.000 < P. 280001:315001(35000) ack 1 win 512 ++0.000 < P. 315001:350001(35000) ack 1 win 512 + +// 3b. Inject three smaller segments into the remaining advertised rcv_wnd. +// Their higher truesize pushes sk_rmem_alloc > sk_rcvbuf (225000) and +// exhausts sk_forward_alloc, triggering tcp_prune_queue() -> tcp_collapse() +// on the 3rd segment and driving sk->sk_forward_alloc <= -16.4 KB. ++0.000 < P. 350001:350801(800) ack 1 win 512 ++0.000 < P. 350801:351601(800) ack 1 win 512 ++0.000 < P. 351601:351901(300) ack 1 win 512 + +// 4. Drain per-CPU memcg stock via memory.high=0, cap memory.max=0 (O_NONBLOCK +// avoids synchronous reclaim/OOM), and expect -EAGAIN on a 16 KB non-blocking +// write. Because copy (16 KB) minus sk_forward_alloc (<= -16.4 KB) exceeds +// PAGE_ALLOC_COSTLY_ORDER (32 KB), try_charge_memcg() fails without invoking +// the memcg OOM killer. ++0.010 `cgroot=$(mount -t cgroup2 | head -1 | awk '{print $3}') +echo 0 > "${cgroot}/pkt_${PPID}/memory.high" +echo 0 > "${cgroot}/pkt_${PPID}/memory.high" +printf 0 | dd of="${cgroot}/pkt_${PPID}/memory.max" oflag=nonblock status=none` ++0.000 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0 ++0.000 write(4, ..., 16384) = -1 EAGAIN (Resource temporarily unavailable) + ++0.000 close(4) = 0 ++0.000 close(3) = 0 + +// Untimed cleanup command executed by packetdrill on both pass and early +// failure. +`cgroot=$(mount -t cgroup2 | head -1 | awk '{print $3}') +[ -n "$cgroot" ] || exit 0 +runner_pid=$(awk '/^PPid:/ {print $2}' /proc/$PPID/status) +orig_cg=$(sed -n 's/^0:://p' /proc/$runner_pid/cgroup) +echo $PPID > "${cgroot}${orig_cg}/cgroup.procs" 2>/dev/null || true +echo $$ > "${cgroot}${orig_cg}/cgroup.procs" 2>/dev/null || true +rmdir "${cgroot}/pkt_${PPID}"` -- 2.56.0.257.gf7497c7d27-goog ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-27 2:28 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-26 3:22 [PATCH net v4 0/2] tcp: clamp return value of tcp_wmem_schedule() to non-negative Octavian Purdila 2026-10-26 3:22 ` [PATCH net v4 1/2] " Octavian Purdila 2026-10-26 12:06 ` Eric Dumazet 2026-10-27 2:28 ` Kuniyuki Iwashima 2026-10-26 3:22 ` [PATCH net v4 2/2] selftests: net: packetdrill: add test for negative sk_forward_alloc in tcp_wmem_schedule() Octavian Purdila
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®