All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jingxuan Xie" <jingxuanxie@deeproute.ai>
Cc: linux-rt-devel@lists.linux.dev, linux-kernel@vger.kernel.org
Subject: Re: [RFC net v2 2/2] af_packet: retire TPACKET_V3 blocks without waiting for copiers
Date: Sat, 31 Oct 2026 08:17:33 +0000	[thread overview]
Message-ID: <sashiko-outbox-191824@kernel.org> (raw)
In-Reply-To: <20261030081704.251652-3-jingxuanxie@deeproute.ai>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] af_packet: prb_retire_current_block() trusts user-mapped BLOCK_NUM_PKTS
- [Medium] af_packet: tpacket_rcv() bypasses ROOM_NONE drop when in_flight is non-zero
--

commit 9f9d78a929f32b0ce1f7614bea46eb96c69e1ab8
Author: jingxuanxie <jingxuanxie@deeproute.ai>

af_packet: retire TPACKET_V3 blocks without waiting for copiers

This commit replaces the TPACKET_V3 ring-wide read/write lock with per-block
in-flight copy accounting in struct pgv so that full or expired blocks can
be sealed and advanced without waiting for active copiers to complete.
Sealed blocks are subsequently published in order under the receive queue
lock once their in_flight count reaches zero.

> diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
> index 7b3e1e27f7771..5bc486326be93 100644
> --- a/net/packet/af_packet.c
> +++ b/net/packet/af_packet.c
[ ... ]
> @@ -950,31 +949,24 @@ static void prb_retire_current_block(struct tpacket_kbdq_core *pkc,
>  		struct packet_sock *po, unsigned int status)
>  {
>  	struct tpacket_block_desc *pbd = GET_CURR_PBLOCK_DESC_FROM_CORE(pkc);
> +	struct pgv *block = &pkc->pkbdq[pkc->kactive_blk_num];
>  
> -	/* retire/close the current block */
> -	if (likely(TP_STATUS_KERNEL == BLOCK_STATUS(pbd))) {
> -		/*
> -		 * Plug the case where copy_bits() is in progress on
> -		 * cpu-0 and tpacket_rcv() got invoked on cpu-1, didn't
> -		 * have space to copy the pkt in the current block and
> -		 * called prb_retire_current_block()
> -		 *
> -		 * We don't need to worry about the TMO case because
> -		 * the timer-handler already handled this case.
> -		 */
> -		if (!(status & TP_STATUS_BLK_TMO)) {
> -			/* Waiting for skb_copy_bits to finish... */
> -			write_lock(&pkc->blk_fill_in_prog_lock);
> -			write_unlock(&pkc->blk_fill_in_prog_lock);
> -		}
> -		prb_close_block(pkc, pbd, po, status);
> +	if (!BLOCK_NUM_PKTS(pbd))
>  		return;
> -	}
> +	block->retire_status = status;
> +	WRITE_ONCE(block->last_pkt, (struct tpacket3_hdr *)pkc->prev);
> +	pkc->pending_blocks++;
> +	pkc->kactive_blk_num = GET_NEXT_PRB_BLK_NUM(pkc);
> +	prb_publish_blocks(pkc, po);
>  }

[Severity: High]
Can a user process with the RX ring mapped read-write corrupt the kernel's
block tracking state by writing 0 to pbd->hdr.bh1.num_pkts
(BLOCK_NUM_PKTS(pbd)) while packets are in flight on the current block?

Because struct tpacket_block_desc lives in the shared packet_mmap() buffer,
user space can zero BLOCK_NUM_PKTS(pbd) while tpacket_rcv() copiers are
active on the block (or between the check in
prb_retire_rx_blk_timer_expired() and the check in
prb_retire_current_block()). When that happens, prb_retire_current_block()
returns early without setting block->last_pkt, incrementing
pkc->pending_blocks, or advancing pkc->kactive_blk_num.

Both callers (__packet_lookup_frame_in_block() and
prb_retire_rx_blk_timer_expired()) immediately call
prb_dispatch_next_block(pkc, po) on the next line:

	prb_retire_current_block(pkc, po, 0);

	/* Now, try to dispatch the next block */
	curr = (char *)prb_dispatch_next_block(pkc, po);

Since pkc->kactive_blk_num was not advanced and block->last_pkt is still
NULL, prb_dispatch_next_block() sees prb_curr_blk_in_use(pkc) == 0 on the
same active block and calls prb_open_block(pkc, pbd), which resets
pkc->nxt_offset to the start of the block and runs:

	atomic_set(&block->in_flight, 0);

while the earlier tpacket_rcv() copiers are still writing into that block.
When those earlier copiers finish and call
atomic_dec_return_release(&copy_block->in_flight), block->in_flight can hit
0 prematurely while a newly dispatched copier is still running, or underflow
to -1 and permanently wedge prb_publish_blocks().

[ ... ]
> @@ -2306,8 +2295,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
>  	if (!res)
>  		goto drop_n_restore;
>  
> -	/* If we are flooded, just give up */
> -	if (__packet_rcv_has_room(po, skb) == ROOM_NONE) {
> +	/* V3 allocation must get a chance to publish completed pending blocks. */
> +	if (__packet_rcv_has_room(po, skb) == ROOM_NONE &&
> +	    (po->tp_version != TPACKET_V3 ||
> +	     !READ_ONCE(po->rx_ring.prb_bdqc.pending_blocks))) {
>  		atomic_inc(&po->tp_drops);
>  		goto drop_n_restore;
>  	}

[Severity: Medium]
When the ring is full and a copier on the oldest pending block (at
publish_blk_num) is preempted or still in flight, does this check bypass the
lockless ROOM_NONE drop path on every arriving packet even though
prb_publish_blocks() cannot publish anything?

In prb_publish_blocks(), publication stops as soon as the block at
publish_blk_num has a non-zero in_flight count:

	while (pending--) {
		struct pgv *block = &pkc->pkbdq[pkc->publish_blk_num];
		struct tpacket_block_desc *pbd = (void *)block->buffer;

		/* Pairs with the release decrement after every completed copy. */
		if (atomic_read_acquire(&block->in_flight))
			break;

If a copier on publish_blk_num is preempted and subsequent traffic seals the
remaining blocks in the ring, __packet_rcv_has_room() returns ROOM_NONE
while pending_blocks > 0. Because tpacket_rcv() only checks
!READ_ONCE(po->rx_ring.prb_bdqc.pending_blocks) without checking whether the
head pending block's in_flight count has reached 0, every arriving packet
will take sk->sk_receive_queue.lock, call prb_publish_blocks() which
immediately breaks out of the loop, fail packet_current_rx_frame(), and jump
to drop_n_account where it triggers sk->sk_data_ready(sk) on every dropped
packet.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261030081704.251652-1-jingxuanxie@deeproute.ai?part=2

  reply	other threads:[~2026-10-31  8:17 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30  8:17 [RFC net v2 0/2] af_packet: retire TPACKET_V3 blocks without waiting for copiers Jingxuan Xie
2026-10-30  8:17 ` Jingxuan Xie
2026-10-30  8:17 ` [RFC net v2 1/2] af_packet: prepare the virtio header before reserving an RX frame Jingxuan Xie
2026-10-30  8:17   ` Jingxuan Xie
2026-10-30 17:21   ` Willem de Bruijn
2026-10-30  8:17 ` [RFC net v2 2/2] af_packet: retire TPACKET_V3 blocks without waiting for copiers Jingxuan Xie
2026-10-30  8:17   ` Jingxuan Xie
2026-10-31  8:17   ` sashiko-bot [this message]
  -- strict thread matches above, loose matches on Subject: below --
2026-10-30  8:14 [RFC net v2 0/2] " Jingxuan Xie
2026-10-30  8:14 ` [RFC net v2 2/2] " Jingxuan Xie
2026-10-30  8:14   ` Jingxuan Xie

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-191824@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=jingxuanxie@deeproute.ai \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rt-devel@lists.linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.