[RFC PATCH net v3 0/1] af_packet: avoid waiting for TPACKET_V3 copies at retirement

From: Jingxuan Xie

Date: Tue Nov 03 2026 - 04:34:00 EST


From: jingxuanxie <jingxuanxie@xxxxxxxxxxxx>

This is v3 of the TPACKET_V3 block-retirement fix. It replaces both v2
postings; please review only this thread.

v2 tried to remove the wait by allowing capture into the next block while
copies were still in flight. That required per-block copy accounting,
ordered publication, and handling for a backlog of ready blocks. Willem
asked for a smaller fix first: make block retirement non-blocking and let
the existing timer retry.

v3 does exactly that, in one patch:

- prb_retire_current_block() uses write_trylock() instead of write_lock().
If a copier still holds the read lock, the current block and its
metadata are left untouched and false is returned.
- __packet_lookup_frame_in_block() stops instead of dispatching the next
block when retirement fails; tpacket_rcv() drops this capture record
and accounts it in tp_drops. The original packet is not affected.
- The retirement timer re-arms and retries if it cannot take the lock.
- A later packet can also retire the block once the copier has finished.

There is no spillover into the next block, no per-block state, no worker
and no publication budget. The existing lock order and rwlock completion
ordering are preserved.

The cost is a higher capture drop rate while a copier is preempted. This
is a deliberate trade-off to keep the receive path moving on PREEMPT_RT.

The v2 1/2 virtio-header prevalidation patch is not part of v3. It is an
independent bug fix; I will rework it separately with a concrete
description of the state left behind and a shared error path, as Willem
asked.

Testing so far:
- ARM64 PREEMPT_RT W=1 build of af_packet.o: pass
- QEMU ARM64 SMP PREEMPT_RT (4 CPUs, lockdep, debug spinlocks):
psock_tpacket and psock_fanout pass; three consecutive candidate
boots pass the delayed-copier tests. The TCG fanout queue-length
warning is also present on the unpatched baseline and the program
exits 0.
- Full-block path: with A delayed, B's sendto returned in 0.7 ms, only
B's capture record was dropped (tp_drops=1), and no block was
published. After release, A and the next packet were published, B was
absent, and payload and length were verified.
- Timer-only path: 15 failed timer trylocks while A was delayed; the
timer published A after release.
- Close/unmap while A was delayed: no hang or crash.
- Baseline contrast: without the patch, B blocked 505 ms until A was
released and was captured.
- Control-flow harness covering rx-busy, timer-busy, recovery, full ring
and shutdown: 6/6 pass. checkpatch on the diff: clean.

Not yet run: Orin hardware, KASAN/KCSAN, and capture drop-rate or
latency measurements. Timings above are QEMU TCG observations, not
hardware RT guarantees.

v2 thread (second posting):
https://lore.kernel.org/netdev/20261030081704.251652-1-jingxuanxie@xxxxxxxxxxxx/

v2 review:
- 1/2: https://lore.kernel.org/netdev/willemdebruijn.kernel.17d3748debf8b@xxxxxxxxx/
- 2/2: https://lore.kernel.org/netdev/willemdebruijn.kernel.54d115047658@xxxxxxxxx/

jingxuanxie (1):
af_packet: avoid waiting for TPACKET_V3 copies at retirement

net/packet/af_packet.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)

base-commit: 1f1f8e8c5176234fa31858cd25f2e9b88aac0a45