LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: mingming cao <mmc@linux.ibm.com>
To: netdev-bot+sashiko@kernel.org
Cc: netdev@vger.kernel.org, davem@davemloft.net, kuba@kernel.org,
	horms@kernel.org, edumazet@google.com, pabeni@redhat.com,
	andrew+netdev@lunn.ch, nnac123@linux.ibm.com,
	maddy@linux.ibm.com, mpe@ellerman.id.au,
	linuxppc-dev@lists.ozlabs.org, haren@linux.ibm.com,
	ricklind@linux.ibm.com, davemarq@linux.ibm.com,
	bjking1@linux.ibm.com, shaik.abdulla1@ibm.com
Subject: Re: [net-next,v6,09/15] ibmveth: Harden RX poll path with helpers
Date: Thu, 24 Sep 2026 23:40:48 -0700	[thread overview]
Message-ID: <f35e4030-24b7-452e-85d3-6e1e4205260a@linux.ibm.com> (raw)
In-Reply-To: <178845903884.3394541.9429373266253586035@kernel.org>


On 9/3/26 11:10 AM, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential
> issue(s) to consider:
>
> Critical: 0 · High: 2 (1) · Medium: 2 · Low: 0
>
> - [High] The new ibmveth_poll_stopping() check placed between
>    napi_complete_done() and ibmveth_enable_irq() cannot prevent a…
> - [High] The new oversize guard in ibmveth_poll_deliver_frame() bounds
>    the PHYP-reported offset+length against skb_tailroom(skb) instead of…
> - [Medium] A real, user-visible sk_buff leak fix is buried inside a
>    199-line helper-extraction patch with no Fixes: tag and no Cc: stable.
> - [Medium] All 'skip the slot' outcomes (invalid descriptor, bad
>    correlator, oversize offset+length) neither increment frames_processed…
>
> Pre-existing issues:
> - [High] ibmveth_remove() calls cancel_work_sync(&adapter->work) while
>    the netdev is still registered and RX NAPI/IRQ paths are live, then…
>
> --
>
> ibmveth: Harden RX poll path with helpers
>
> This splits the RX slot handling in ibmveth_poll() into a set of small
> helpers (poll_stopping, harvest_slot, recycle_invalid, skip_bad_correlator,
> drop_oversize, deliver_frame, bump_invalid) so the NAPI callback becomes a
> thin budget loop.  Along with the motion it adds an offset+length bounds
> check before skb_put(), frees the copybreak skb on a harvest error, and
> adds stopping checks so close/napi_disable does not re-arm PHYP delivery.
>
> A few questions below.
>
>> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
>> index 46f68f5b52e70..f726d1e3f0b11 100644
>> --- a/drivers/net/ethernet/ibm/ibmveth.c
>> +++ b/drivers/net/ethernet/ibm/ibmveth.c
> [ ... ]
>
>> @@ -2382,125 +2380,236 @@ static void ibmveth_rx_csum_helper(struct sk_buff *skb,
>>   	}
>>   }
>>   
> [ ... ]
>
>> +static bool ibmveth_poll_harvest_slot(struct ibmveth_adapter *adapter,
>> +				      int queue_index, bool reuse)
>> +{
>> +	int rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, reuse);
>> +
>> +	return !rc || rc == -EINVAL || rc == -EFAULT;
>> +}
> [Severity: Medium]
> Can this helper ever return false?  ibmveth_rxq_harvest_buffer() documents
> and returns only 0, -EINVAL or -EFAULT:
>
> 	rc = ibmveth_remove_buffer_from_pool(adapter, cor, queue_index, reuse);
> 	if (unlikely(rc)) {
> 		/* Skip a corrupt slot without claiming pool ownership. */
> 		if (rc == -EINVAL || rc == -EFAULT)
> 			ibmveth_rxq_advance(rxq);
> 		return rc;
> 	}
>
> so every "break" in ibmveth_poll_recycle_invalid(),
> ibmveth_poll_skip_bad_correlator() and ibmveth_poll_drop_oversize() looks
> unreachable.
>
> Combined with the budget accounting in the loop below, is there anything
> left that bounds one ibmveth_poll() invocation?  A skipped slot returns 0
> from ibmveth_poll_deliver_frame(), so neither the break nor
> frames_processed++ runs:
>
> 		rc = ibmveth_poll_deliver_frame(napi, adapter, netdev,
> 						queue_index);
> 		if (rc < 0)
> 			break;
> 		if (rc > 0)
> 			frames_processed++;
>
> and each skip recycles the slot with reuse=true, after which
> ibmveth_replenish_task() re-posts it.  The tail of ibmveth_poll() then does:
>
> 	if (ibmveth_rxq_pending_buffer(adapter, queue_index) &&
> 	    napi_schedule(napi)) {
> 		ibmveth_disable_irq(adapter, queue_index);
> 		goto restart_poll;
> 	}
>
> which re-enters the loop in the same invocation with frames_processed
> unchanged.  If PHYP keeps publishing skippable slots (stale ring contents,
> malformed completions), does the frames_processed == budget exit ever
> become reachable, and does this poll ever return?
>
> The reset escalation in ibmveth_poll_skip_bad_correlator() uses
> schedule_work(), which queues on the current CPU via system_percpu_wq, so
> would the worker be able to run while that CPU is stuck in the poll?  The
> ibmveth_poll_drop_oversize() path escalates nothing at all.
>
> The commit message states:
>
>      Skipped and dropped slots do not count against the NAPI budget; only
>      a delivered frame does.
>
> Is that intentional given it removes the only bound on the loop?
Yes. harvest only returns 0/-EINVAL/-EFAULT, so
harvest_slot is always true. That break is not a
bound.

The loop caps delivered frames, not slots, so
there is no strict per-invocation slot bound.
Each skip advances the ring, so any finite
backlog drains, but a stream of invalid
completions would keep this poll running.
restart_poll can re-enter if PHYP posts more.
skip_bad queues a reset; that worker cannot run
if this poll never returns. drop_oversize
harvests only. I am not counting skips toward
budget: that would starve real packets on a
corrupt ring.
>> +
>> +static bool ibmveth_poll_recycle_invalid(struct net_device *netdev,
>> +					 struct ibmveth_adapter *adapter,
>> +					 int queue_index)
>> +{
>> +	netdev_dbg(netdev, "recycling invalid buffer\n");
>> +	ibmveth_poll_bump_invalid(adapter, queue_index);
>> +	return ibmveth_poll_harvest_slot(adapter, queue_index, true);
>> +}
>> +
>> +static bool ibmveth_poll_skip_bad_correlator(struct net_device *netdev,
>> +					     struct ibmveth_adapter *adapter,
>> +					     int queue_index)
>> +{
>> +	if (net_ratelimit())
>> +		netdev_err(netdev,
>> +			   "bad correlator on queue %d, skipping slot\n",
>> +			   queue_index);
>> +	/* Residual stale slot after resize: recover via reset rather
>> +	 * than spinning forever. Always escalate; only the log is
>> +	 * rate-limited.
>> +	 */
>> +	schedule_work(&adapter->work);
> [Severity: High]
> This isn't a bug introduced by this patch, but this adds another NAPI-side
> producer of adapter->work while ibmveth_remove() still cancels that work
> before the device is quiesced:
>
> 	cancel_work_sync(&adapter->work);
>
> 	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
> 		kobject_put(&adapter->rx_buff_pool[0][i].kobj);
>
> 	unregister_netdev(netdev);
>
> 	free_netdev(netdev);
>
> Can a poll running on another CPU call schedule_work(&adapter->work) from
> here (or from the ibmveth_enable_irq() failure path in ibmveth_poll())
> after cancel_work_sync() has returned?  Nothing cancels or flushes it
> again, and adapter is netdev_priv(netdev), so the queued work_struct and
> everything ibmveth_reset() touches would be freed by free_netdev().
>
> For the record, the ordering appears to be repaired later in this series:
> at the series tip ibmveth_remove() moves the cancellation after
> unregister_netdev(), with a comment that cancel_work_sync() before
> unregister left a window where poll could schedule_work() and the worker
> ran after free_netdev().
Yes, at this commit. Patch 10 unregisters
first, then cancel_work_sync, and the reset worker
is gated on NETREG_REGISTERED. The order and the
gate belong together with the reset producer;
that pair is in Patch 10.
>> +	ibmveth_poll_bump_invalid(adapter, queue_index);
>> +	return ibmveth_poll_harvest_slot(adapter, queue_index, true);
>> +}
>> +
> [ ... ]
>
>> +static int ibmveth_poll_deliver_frame(struct napi_struct *napi,
>> +				      struct ibmveth_adapter *adapter,
>> +				      struct net_device *netdev,
>> +				      int queue_index)
>> +{
> [ ... ]
>
>> +	room = skb_tailroom(skb);
>> +	off = offset;
>> +	len = length;
>> +	if (unlikely(off >= room || len > room - off)) {
>> +		if (!ibmveth_poll_drop_oversize(netdev, adapter, queue_index,
>> +						off, len, room))
>> +			return -1;
>> +		return 0;
>> +	}
> [Severity: High]
> Should the bound here be pool->buff_size rather than skb_tailroom(skb)?
>
> ibmveth_replenish_buffer_pool() allocates, maps and advertises exactly
> pool->buff_size to the hypervisor:
>
> 	skb = netdev_alloc_skb(adapter->netdev, pool->buff_size);
> 	...
> 	dma_addr = dma_map_single_attrs(dev, skb->data, pool->buff_size,
> 					DMA_FROM_DEVICE, DMA_ATTR_NO_WARN);
> 	...
> 	descs[filled].fields.flags_len = IBMVETH_BUF_VALID | pool->buff_size;
>
> netdev_alloc_skb() rounds up to an allocator bucket, so skb_tailroom() on
> an untouched pool buffer is larger than buff_size (a 16 KiB pool buffer
> lands in a 32 KiB allocation).  Does a PHYP-reported off+len in the
> (buff_size, tailroom] gap pass this check and then get committed and
> delivered?
>
> 	skb_put(skb, length);
> 	skb->protocol = eth_type_trans(skb, netdev);
> 	...
> 	napi_gro_receive(napi, skb);
> That would hand uninitialised slab bytes outside the DMA mapping to the
> stack, and on the copybreak path skb_copy_to_linear_data() plus
> ibmveth_flush_buffer(skb->data, length + offset) would touch addresses
> past the mapped window.  The commit message says deliver "rejects a PHYP
> offset+length that would skb_put() past the buffer" - is the buffer here
> meant to be the hardware-visible one?  The correlator has already been
> validated by ibmveth_rxq_correlator_valid(), so
> adapter->rx_buff_pool[queue_index][correlator >> 32].buff_size is
> available as the bound.
Yes — a length in (buff_size, tailroom] would pass
on v6. skb_tailroom is the skb_put bound. The DMA
map is pool->buff_size, and alloc can leave slack.
v7 uses min(tailroom, buff_size). The pool index
is the current slot's correlator; get_buffer
already validated it.
>> +
>> +	if (lrg_pkt) {
>> +		__be64 *rxmss = (__be64 *)(skb->data + 8);
>> +
>> +		mss = (u16)be64_to_cpu(*rxmss);
>> +	}
>> +
>> +	new_skb = NULL;
>> +	if (length < rx_copybreak)
>> +		new_skb = netdev_alloc_skb(netdev, length);
>> +
>> +	if (new_skb) {
>> +		skb_copy_to_linear_data(new_skb, skb->data + offset, length);
>> +		if (rx_flush)
>> +			ibmveth_flush_buffer(skb->data, length + offset);
>> +		rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
>> +		if (unlikely(rc)) {
>> +			kfree_skb(new_skb);
>> +			return -1;
>> +		}
> [Severity: Medium]
> This kfree_skb(new_skb) is a real fix for an sk_buff leak that exists at
> the baseline in ibmveth_poll():
>
> 	new_skb = netdev_alloc_skb(netdev, length);
> 	...
> 	skb_copy_to_linear_data(new_skb, skb->data + offset, length);
> 	...
> 	if (unlikely(ibmveth_rxq_harvest_buffer(adapter, true)))
> 		break;   /* new_skb is neither freed nor handed to the stack */
>
> Could this be split into its own patch with a Fixes: tag (and Cc: stable)
> so it can be routed to net and picked up by older trees?  Buried in a
> 199-insertion helper extraction it is not separable for backporting.  The
> same applies to the new offset+length validation, which the commit message
> describes as preventing an skb_put() past the buffer.
The kfree_skb on the copybreak harvest-fail path
stays here. Same for the offset+length check; I am
not peeling a Fixes: patch out of this extract.
The off/len test has no parent SHA.
Neither is the unwind standalone
(Fixes: d43732ce021f).
>> +		skb = new_skb;
>> +	} else {
>> +		rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, false);
>> +		if (unlikely(rc))
>> +			return -1;
>> +		skb_reserve(skb, offset);
>> +	}
> [ ... ]
>
>> +	return 1;
>> +}
>> +
>>   static int ibmveth_poll(struct napi_struct *napi, int budget)
>>   {
> [ ... ]
>
>>   	if (frames_processed == budget)
>>   		goto out;
>>   
>>   	if (!napi_complete_done(napi, frames_processed))
>>   		goto out;
>>   
>> -	/* We think we are done - reenable interrupts,
>> -	 * then check once more to make sure we are done.
>> +	/*
>> +	 * napi_disable() sets DISABLE then waits for this poll. Without a
>> +	 * second stopping check here, enable_irq() can re-arm PHYP after
>> +	 * resize already masked the queue; late IRQs then hit the handler
>> +	 * after num_rx_queues was published lower (lab WARN at interrupt).
>>   	 */
>> +	if (ibmveth_poll_stopping(netdev, napi))
>> +		goto out;
>> +
>>   	rc = ibmveth_enable_irq(adapter, queue_index);
> [Severity: High]
> Does this check actually close the window it describes?  It is a
> check-then-act pair, and both predicates in ibmveth_poll_stopping() go
> false exactly when teardown is finishing:
>
> 	return !netif_running(netdev) || napi_disable_pending(napi);
>
> napi_disable_locked() clears the DISABLE bit before returning:
>
> 	clear_bit(NAPI_STATE_DISABLE, &n->state);
>
> so napi_disable_pending() is false once close is past its wait.  And
> netif_running() stays true for the driver's internal close callers -
> ibmveth_change_mtu(), ibmveth_set_mac_addr(), the features paths and
> veth_pool_store() all do "if (netif_running(dev)) ibmveth_close(dev);".
>
> Sequence:
>
> CPU0 ibmveth_poll()
> 	napi_complete_done()		/* clears SCHED */
> 					/* delayed: hard IRQ, or vCPU
> 					   dispatch preemption on a
> 					   shared-processor LPAR */
>
> CPU1 ibmveth_change_mtu() -> ibmveth_close() -> ibmveth_cleanup_rx_interrupts()
> 	napi_disable()			/* clears DISABLE on return */
> 	ibmveth_disable_irq(adapter, i);
> 	synchronize_irq(adapter->queue_irq[i]);
> 	free_irq(...)
>
> CPU0 resumes:
> 	if (ibmveth_poll_stopping(netdev, napi))	/* false */
> 		goto out;
> 	rc = ibmveth_enable_irq(adapter, queue_index);	/* re-arms PHYP */
>
> Can the queue end up unmasked after the final remask and after free_irq()
> removed the handler?  That is the case the commit message claims to close:
>
>      ibmveth_poll_stopping() ensures close/napi_disable does not re-arm PHYP.
>
> Would moving the unmask before napi_complete_done(), or moving close's
> final remask after synchronize_net(), be a more reliable ordering than
> adding another check here?  This looks unchanged at the series tip.
The check is not a close barrier. After
napi_disable() returns, DISABLE is clear, and an
internal close+open can leave IFF_UP set. Teardown
already masks PHYP before napi_disable. Inverting
that left IFF_UP with PHYP unmasked. I am not
moving unmask before napi_complete_done or
moving the remask after synchronize_net().

Thanks,
Mingming
>>   	if (rc) {
>>   		netdev_err(netdev,
> Thanks for looking at these.
>


  reply	other threads:[~2026-09-25  6:41 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-31 15:07 [PATCH net-next v6 00/15] ibmveth: Add multi-queue RX support Mingming Cao
2026-08-31 15:07 ` [PATCH net-next v6 01/15] ibmveth: Add MQ RX hypercall wrappers and call definitions Mingming Cao
2026-09-03 18:10   ` [net-next,v6,01/15] " netdev-bot+sashiko
2026-09-25  5:52     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 02/15] ibmveth: Prepare MQ RX adapter data structures Mingming Cao
2026-08-31 15:07 ` [PATCH net-next v6 03/15] ibmveth: Refactor RX resource allocation for MQ RX bring-up Mingming Cao
2026-09-03 18:10   ` [net-next,v6,03/15] " netdev-bot+sashiko
2026-09-25  6:08     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 04/15] ibmveth: Refactor buffer pool management for per-queue MQ RX Mingming Cao
2026-09-03 18:10   ` [net-next,v6,04/15] " netdev-bot+sashiko
2026-09-25  6:16     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 05/15] ibmveth: Refactor RX interrupt control for MQ RX queues Mingming Cao
2026-09-03 18:10   ` [net-next,v6,05/15] " netdev-bot+sashiko
2026-09-25  6:21     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 06/15] ibmveth: Refactor TX resource allocation in open/close paths Mingming Cao
2026-09-03 18:10   ` [net-next,v6,06/15] " netdev-bot+sashiko
2026-09-25  6:28     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 07/15] ibmveth: Add RX queue register helpers for MQ Mingming Cao
2026-08-31 15:07 ` [PATCH net-next v6 08/15] ibmveth: Add queue-aware RX buffer submit helper " Mingming Cao
2026-09-03 18:10   ` [net-next,v6,08/15] " netdev-bot+sashiko
2026-09-25  6:32     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 09/15] ibmveth: Harden RX poll path with helpers Mingming Cao
2026-09-03 18:10   ` [net-next,v6,09/15] " netdev-bot+sashiko
2026-09-25  6:40     ` mingming cao [this message]
2026-08-31 15:07 ` [PATCH net-next v6 10/15] ibmveth: Enable multi-queue RX receive path Mingming Cao
2026-09-03 18:10   ` [net-next,v6,10/15] " netdev-bot+sashiko
2026-09-25  6:48     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 11/15] ibmveth: Add per-queue RX and TX statistics collection Mingming Cao
2026-09-03 18:10   ` [net-next,v6,11/15] " netdev-bot+sashiko
2026-09-25  7:08     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 12/15] ibmveth: Report MQ-aware RX counts in ethtool get_channels Mingming Cao
2026-09-03 18:10   ` [net-next,v6,12/15] " netdev-bot+sashiko
2026-09-25  7:43     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 13/15] ibmveth: Expose per-queue buffer pool details via debugfs Mingming Cao
2026-08-31 15:07 ` [PATCH net-next v6 14/15] ibmveth: Implement incremental MQ RX queue resize Mingming Cao
2026-09-03 18:10   ` [net-next,v6,14/15] " netdev-bot+sashiko
2026-09-25  7:43     ` mingming cao
2026-08-31 15:07 ` [PATCH net-next v6 15/15] ibmveth: Complete set_channels down-path and mq_fallback max_rx cap Mingming Cao
2026-09-03 18:10   ` [net-next,v6,15/15] " netdev-bot+sashiko
2026-09-25  7:43     ` mingming cao

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=f35e4030-24b7-452e-85d3-6e1e4205260a@linux.ibm.com \
    --to=mmc@linux.ibm.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=bjking1@linux.ibm.com \
    --cc=davem@davemloft.net \
    --cc=davemarq@linux.ibm.com \
    --cc=edumazet@google.com \
    --cc=haren@linux.ibm.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=maddy@linux.ibm.com \
    --cc=mpe@ellerman.id.au \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=nnac123@linux.ibm.com \
    --cc=pabeni@redhat.com \
    --cc=ricklind@linux.ibm.com \
    --cc=shaik.abdulla1@ibm.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox