Netdev List
 help / color / mirror / Atom feed
From: mingming cao <mmc@linux.ibm.com>
To: Jakub Kicinski <kuba@kernel.org>
Cc: netdev@vger.kernel.org, davem@davemloft.net, 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: [PATCH net-next v5 10/15] ibmveth: Enable multi-queue RX receive path
Date: Mon, 31 Aug 2026 12:10:59 -0700	[thread overview]
Message-ID: <243fb549-22d2-407c-8774-d3ade7882958@linux.ibm.com> (raw)
In-Reply-To: <20260818014731.3854329-1-kuba@kernel.org>


On 8/17/26 6:47 PM, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> ibmveth: Enable multi-queue RX receive path
>
> This turns on the multi-queue RX path when firmware reports
> IBMVETH_ILLAN_RX_MULTI_QUEUE_SUPPORT, registering subordinate queues via
> H_REG_LOGICAL_LAN_QUEUE and wiring per-queue IRQs and NAPI instances. It
> also reworks open()/close() to walk every live RX queue, adds an
> mq_fallback latch, and refreshes the VIO CMO entitlement at probe.
>
> A few questions below.
>
> Regarding this part of the changelog:
>
>    Up to IBMVETH_MAX_RX_QUEUES (16) remains available via ethtool -L.
>
> [Severity: Low]
> Is that true at this commit?  ibmveth_get_channels() still does:
>
> 	channels->max_rx = netdev->real_num_rx_queues;
> 	channels->rx_count = netdev->real_num_rx_queues;
>
> and ibmveth_set_channels() never reads channels->rx_count, so an
> "ethtool -L <dev> rx N" returns success without changing anything, and 16
> RX queues cannot be requested until the later get_channels/set_channels
> patches land.  Could this sentence move to the patch that actually wires
> rx_count up?
No, not at this commit. get_channels still echoes real_num_rx_queues
and set_channels is still TX-only, so ethtool -L rx N succeeds without
changing RX.

v6 drops that sentence here. Patch 14 is the one that reads
channels->rx_count and calls resize. Patch 12 only reports the counts.
Patch 15 finishes the down-path and caps max_rx once mq_fallback
latches; it does not wire the setter.
>> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
>> index 86299c62d4ecf..36f0926711e60 100644
>> --- a/drivers/net/ethernet/ibm/ibmveth.c
>> +++ b/drivers/net/ethernet/ibm/ibmveth.c
> [ ... ]
>
>> @@ -503,7 +519,7 @@ static int
>>   ibmveth_setup_rx_interrupts(struct ibmveth_adapter *adapter)
>>   {
>>   	struct net_device *netdev = adapter->netdev;
>> -	int i, rc, num = adapter->num_rx_queues;
>> +	int i, rc, num = ibmveth_get_num_rx_queues(adapter);
>>   
>>   	for (i = 0; i < num; i++) {
>>   		if (!adapter->queue_irq[i]) {
> [Severity: Low]
> Now that this function can enable PHYP delivery on several queues, can the
> err_disable_napi rollback leave a queue armed with no handler?
>
> The rollback masks the already-enabled queues and calls synchronize_irq(),
> but synchronize_irq() does not wait for NAPI.  A poll already running for
> one of those queues reaches:
>
> 	if (ibmveth_poll_stopping(netdev, napi))
> 		goto out;
>
> 	rc = ibmveth_enable_irq(adapter, queue_index);
>
> ibmveth_poll_stopping() is "!netif_running(netdev) || napi_disable_pending()",
> and __dev_open() sets __LINK_STATE_START before calling ndo_open, so it is
> false here and the poll re-arms PHYP.  err_disable_napi then does:
>
> 	for (i = 0; i < num; i++)
> 		napi_disable(&adapter->napi[i]);
> 	for (i = 0; i < num; i++) {
> 		if (adapter->queue_irq[i])
> 			free_irq(adapter->queue_irq[i], &adapter->napi[i]);
> 	}
>
> with no second mask pass, whereas ibmveth_cleanup_rx_interrupts()
> deliberately masks again after napi_disable().  Should the rollback follow
> the cleanup pattern and remask after napi_disable()?
Yes. Without a second mask after napi_disable, a poll still running
can re-arm PHYP after the first mask: poll_stopping is false during
open because __LINK_STATE_START is already set.

Already in patch 5: err_disable_napi remasks after napi_disable, same
as cleanup_rx_interrupts. This patch does not add a second rollback.

>> @@ -649,6 +665,24 @@ static bool ibmveth_schedule_rx_queue(struct ibmveth_adapter *adapter,
>>   	return false;
>>   }
>>   
>> +/**
>> + * ibmveth_kick_rx_queue_if_pending - Schedule NAPI if PHYP posted while masked
>> + * @adapter: ibmveth adapter
>> + * @queue_index: RX queue index just unmasked
>> + *
>> + * After enable_irq() / MQ open unmask, descriptors may already be pending
>> + * (buffers were posted while PHYP was masked). Use schedule_rx_queue() so
>> + * PHYP is masked before NAPI runs — napi_schedule() then disable_irq() from
>> + * process context can race a completing poll and leave the queue masked
>> + * with NAPI idle.
>> + */
>> +static void
>> +ibmveth_kick_rx_queue_if_pending(struct ibmveth_adapter *adapter,
>> +				 int queue_index)
>> +{
>> +	if (ibmveth_rxq_pending_buffer(adapter, queue_index))
>> +		ibmveth_schedule_rx_queue(adapter, queue_index);
>> +}
> [Severity: Low]
> Does this commit build on its own?  ibmveth_kick_rx_queue_if_pending() is a
> non-inline static function with no caller anywhere in the tree at this
> commit (open() uses ibmveth_restart_rx_queue() instead), so gcc/clang emit
> "defined but not used", which is fatal with the default CONFIG_WERROR=y.
>
> The first callers appear only in the later incremental resize patch.  Could
> the helper be introduced there instead, to keep this commit bisectable?
Yes. Unused static, fatal under CONFIG_WERROR. First-use: v6 deletes
it here. Patch 14 calls restart_rx_queue() at both sites that would
have used the helper, so there is nothing to move.

> Also, the blank line before the following comment block was dropped:
>
> 	+		ibmveth_schedule_rx_queue(adapter, queue_index);
> 	+}
> 	 /* setup the initial settings for a buffer pool */

Ah, its back now.


>> @@ -963,11 +997,21 @@ static int ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter,
>>    * because there was not a buffer in the buffer list capable of holding
>>    * the frame.
>>    */
>> -static void ibmveth_update_rx_no_buffer(struct ibmveth_adapter *adapter)
>> +static void ibmveth_update_rx_no_buffer(struct ibmveth_adapter *adapter,
>> +					int queue_index)
>>   {
>> -	__be64 *p = adapter->buffer_list_addr[0] + 4096 - 8;
>> +	__be64 *p;
>> +	u64 drops;
>> +
>> +	if (queue_index < 0 ||
>> +	    queue_index >= ibmveth_get_num_rx_queues(adapter) ||
>> +	    !adapter->buffer_list_addr[queue_index])
>> +		return;
>> +
>> +	p = adapter->buffer_list_addr[queue_index] + 4096 - 8;
>> +	drops = be64_to_cpup(p);
>>   
>> -	adapter->rx_no_buffer = be64_to_cpup(p);
>> +	adapter->rx_no_buffer = drops;
>>   }
> [Severity: Low]
> The function became queue-aware, but the store target is still the single
> adapter-wide field exported by ethtool -S.  With several RX queues, does
> rx_no_buffer end up being "whichever queue ran last", and can it decrease?
>
> Each queue reads its own absolute PHYP page counter and overwrites the same
> scalar, from per-queue replenish paths that hold different replenish_locks,
> plus the lock-free loop added in ibmveth_close():
>
> 	for (i = 0; i < ibmveth_get_num_rx_queues(adapter); i++)
> 		ibmveth_update_rx_no_buffer(adapter, i);
>
> The next patch in the series ("ibmveth: Add per-queue RX and TX statistics
> collection") moves this to adapter->rx_qstats[queue_index] with a summing
> helper, so only this intermediate commit reports the wrong value.  Could
> the per-queue slot land together with the queue_index argument?
Yes, last-writer-wins, and it can go backwards when a later queue
stores a smaller PHYP page counter.

The per-queue slot stays in patch 11 with the rest of qstats, next to
the retired-queue carry so one queue owns both. Not here: this patch
is the MQ switch.
>> @@ -1033,6 +1077,7 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
>>   		dev_err_ratelimited(&adapter->netdev->dev,
>>   				    "MQ buffer add H_FUNCTION (q=%d, batch=%u), reset\n",
>>   				    queue_index, fail.batch);
>> +		adapter->mq_fallback = true;
>>   		schedule_work(&adapter->work);
>>   	}
> [Severity: High]
> This isn't a bug introduced by this patch, but it adds another producer of
> adapter->work while ibmveth_remove() still cancels the work before the
> interface is unregistered:
>
> 	cancel_work_sync(&adapter->work);
> 	...
> 	unregister_netdev(netdev);
> 	free_netdev(netdev);
>
> Can this sequence use freed memory?
>
> CPU0 (rmmod / DLPAR remove)
>    ibmveth_remove()
>      cancel_work_sync(&adapter->work);   /* returns */
>
> CPU1 (NAPI poll)
>    ibmveth_replenish_task()
>      adapter->mq_fallback = true;
>      schedule_work(&adapter->work);      /* re-queued */
>
> CPU0
>      unregister_netdev(netdev);          /* holds RTNL */
>      free_netdev(netdev);                /* frees adapter + work_struct */
>
> The queued ibmveth_reset() blocks in rtnl_lock() while unregister_netdev()
> runs, then derives adapter via container_of() on the freed work_struct and
> touches adapter->netdev.  Would unregistering/quiescing first and calling
> cancel_work_sync() after be safer?
Yes, UAF at this commit: cancel returns, poll can schedule_work
again, then unregister/free runs while the worker still takes RTNL
and container_of on the freed work_struct.

Unregister first, then cancel_work_sync, is the right order. Patch 11
does that, and gates the worker on NETREG_REGISTERED so a reset that
is already past cancel cannot close/open after unregister. This patch
only adds the extra schedule_work producers.
>> @@ -1052,6 +1097,30 @@ static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
>>   				     fail.filled, fail.lpar_rc, fail.batch);
>>   }
>>   
>> +/**
>> + * ibmveth_restart_rx_queue - Post buffers and ensure Q can take RX
>> + * @adapter: ibmveth adapter
>> + * @qindex: RX queue index
>> + *
>> + * SQ open leaves PHYP masked until the first poll. If schedule_prep fails,
>> + * NAPI never runs and the queue stays masked (TX OK, RX/ARP dead) until
>> + * reload. Replenish first so an enable_irq fallback can actually deliver.
>> + * Also used after every open (SQ and MQ) and after scale-down so a
>> + * queue is not left idle+masked.
>> + */
>> +static void ibmveth_restart_rx_queue(struct ibmveth_adapter *adapter,
>> +				     int qindex)
>> +{
>> +	int rc;
>> +
>> +	ibmveth_replenish_task(adapter, qindex);
>> +	if (ibmveth_schedule_rx_queue(adapter, qindex))
>> +		return;
>> +
>> +	rc = ibmveth_enable_irq(adapter, qindex);
>> +	WARN_ON(rc);
>> +}
> [Severity: Medium]
> The enable_irq() fallback here is reached exactly when
> napi_schedule_prep() failed, i.e. when a poll for that queue is already
> scheduled or running.  In MQ mode ibmveth_setup_rx_interrupts() has already
> unmasked PHYP before open() calls this helper, so a frame can arrive first:
>
>    ibmveth_interrupt(q)
>      ibmveth_schedule_rx_queue(q)   /* masks PHYP, schedules NAPI */
>
>    ibmveth_open()
>      ibmveth_restart_rx_queue(adapter, q)
>        napi_schedule_prep() == false
>        ibmveth_enable_irq(adapter, q);   /* re-arms PHYP mid-poll */
>
> Since ibmveth_schedule_rx_queue() masks PHYP only inside the successful
> prep branch:
>
> 	if (napi_schedule_prep(napi)) {
> 		rc = ibmveth_disable_irq(adapter, qindex);
> 		WARN_ON(rc);
> 		__napi_schedule(napi);
> 		return true;
> 	}
> 	return false;
>
> can the handler still quiesce the source afterwards?  Each subsequent
> interrupt returns IRQ_HANDLED without masking and sets NAPI_STATE_MISSED,
> so napi_complete_done() keeps returning false and the queue stays unmasked
> while traffic flows, giving an interrupt per frame on that queue.
>
> Related: this WARN_ON(rc) treats a failed unmask hcall as advisory, while
> ibmveth_poll() treats the same failure as a hardware-state problem:
>
> 	rc = ibmveth_enable_irq(adapter, queue_index);
> 	if (rc) {
> 		netdev_err(...);
> 		schedule_work(&adapter->work);
> 		goto out;
> 	}
>
> Should the two paths handle the failure the same way?
>
> Also, the comment says the helper is used "after scale-down", but no
> scale-down caller exists at this commit; that path arrives with the later
> resize patch.
No, later IRQs cannot quiet the source once the enable_irq fallback
has run: prep fails again, there is no mask, and you get IRQ_HANDLED
plus NAPI_STATE_MISSED until poll finishes.

That is the path we want. Mask-on-prep-fail raced poll and left RX
dead in the lab (TX still OK). enable-on-prep-fail stays. Storm
control is STOP plus poll no-rearm, not mask-on-fail.

The two enable_irq failures should not do the same thing. Poll is the
live path after complete_done; a failed unmask there is hardware-state
and schedules reset. restart_rx_queue is open/scale kickstart only;
scheduling reset from there nests close/open mid-open. v6 drops the
WARN_ON; the helper already logs the firmware rc.

The scale-down phrase is still in the kdoc. First caller is patch 14,
and it uses this same helper, so the comment is the helper's job at

the tip rather than a second function.


>
> [ ... ]
>
>> @@ -1556,10 +1757,12 @@ static void ibmveth_free_all_queues(struct ibmveth_adapter *adapter)
>>    * @adapter: ibmveth adapter structure
>>    * @mac_address: MAC address for device registration
>>    *
>> - * Registers queue 0 via ibmveth_register_logical_lan(). Subordinate queue
>> - * registration is added when multi-queue RX is enabled.
>> + * Registers queue 0 via ibmveth_register_logical_lan(), then subordinate
>> + * queues 1..N when multi-queue mode is enabled.
>>    *
>> - * Return: 0 on success, -ENONET if queue 0 registration fails
>> + * Return: 0 on success, -ENONET if queue 0 registration fails,
>> + *         -EOPNOTSUPP if firmware rejects a subordinate queue (H_FUNCTION;
>> + *         caller latches mq_fallback), -EIO on other subordinate failures
> [Severity: Low]
> The kernel-doc says the caller latches mq_fallback, but the function body
> does it itself and ibmveth_open() only propagates rc.  Could the wording be
> changed to say this function latches adapter->mq_fallback, so a future
> change does not add a second latch or remove this one?
Yes. v6 says this function latches mq_fallback on -EOPNOTSUPP. open()
only propagates rc.
>> @@ -1592,9 +1796,67 @@ ibmveth_register_rx_queues(struct ibmveth_adapter *adapter, u64 mac_address)
>>   		return -ENONET;
>>   	}
>>   
>> +	num = ibmveth_get_num_rx_queues(adapter);
>> +	if (num == 1 || !adapter->multi_queue) {
>> +		netdev_dbg(netdev,
>> +			   "registered 1 RX queue with hypervisor (single-queue mode)\n");
>> +		return 0;
>> +	}
>> +
>> +	netdev_dbg(netdev, "Registering %d subordinate queues (1-%d)\n",
>> +		   num - 1, num - 1);
>> +
>> +	for (i = 1; i < num; i++) {
>> +		rc = ibmveth_register_single_rx_queue(adapter, i);
>> +		if (rc) {
>> +			/* Firmware MQ gone: fall back to SQ on next open. */
>> +			if (rc == -EOPNOTSUPP)
>> +				adapter->mq_fallback = true;
>> +			goto err_unregister;
>> +		}
>> +	}
> [Severity: High]
> Who performs the "next open" that consumes the latched mq_fallback?
>
> The only automatic recovery path is the reset work:
>
>    ibmveth_reset()
>      rtnl_lock();
>      dev_close(adapter->netdev);
>      dev_open(adapter->netdev, NULL);   /* return value discarded */
>      rtnl_unlock();
>
> If firmware stops honouring H_REG_LOGICAL_LAN_QUEUE (the LPM case the code
> comment describes), that dev_open() fails with -EOPNOTSUPP, mq_fallback is
> set, nothing looks at the error, and no further open is attempted.  Does
> the interface then stay administratively up but dead until an administrator
> runs ifdown/ifup, even though the driver already knows the single-queue
> retry would succeed?
>
> Would retrying single-queue inside open(), or re-arming the reset work when
> dev_open() fails, match the intent stated in the changelog?
apply_mq_fallback() runs at the start of open. The next successful
ndo_open consumes the latch, from admin ifdown/ifup or from reset's
dev_open when that open succeeds.

Yes: if reset's dev_open fails with -EOPNOTSUPP, the latch stays set,
the return is discarded, and the interface can stay administratively
up but dead until ifdown/ifup. Single-queue was not applied on that
failed open.

No retry inside open(), and reset does not re-arm when that
dev_open() fails. That is the "next open drops to SQ" rule, not a
retry loop.

> [ ... ]
>
>> @@ -2732,23 +3019,35 @@ static unsigned long ibmveth_get_desired_dma(struct vio_dev *vdev)
>>   
>>   	adapter = netdev_priv(netdev);
>>   
>> -	ret = IBMVETH_BUFF_LIST_SIZE + IBMVETH_FILT_LIST_SIZE;
>> +	/* One buffer list page per RX queue; filter list is shared. */
>> +	ret = IBMVETH_BUFF_LIST_SIZE * ibmveth_get_num_rx_queues(adapter) +
>> +	      IBMVETH_FILT_LIST_SIZE;
>>   	ret += IOMMU_PAGE_ALIGN(netdev->mtu, tbl);
>>   	/* add size of mapped tx buffers */
>>   	ret += IOMMU_PAGE_ALIGN(IBMVETH_MAX_TX_BUF_SIZE, tbl);
> [Severity: Medium]
> This is a pre-existing shortfall rather than something this patch created,
> but since the function is being rewritten and its result now becomes the
> probe-time CMO request, should the TX term scale with the TX queue count?
>
> ibmveth_alloc_tx_resources() maps one long-term buffer per TX queue:
>
> 	for (i = 0; i < netdev->real_num_tx_queues; i++) {
> 		if (ibmveth_allocate_tx_ltb(adapter, i))
> 			goto err_free_ltbs;
> 	}
>
> with real_num_tx_queues defaulting to min(nr_cpus, 8), while the accounting
> above adds exactly one IBMVETH_MAX_TX_BUF_SIZE.  Can the requested
> entitlement be short by up to 7 x 64KB, so that dma_map_single() in
> ibmveth_allocate_tx_ltb() fails at open on an entitlement-constrained
> partition?  vio_cmo_set_dev_desired() returns void, so the shortfall is
> silent.
Yes. One IBMVETH_MAX_TX_BUF_SIZE against up to eight mapped LTBs can
be short by 7×64KB; vio_cmo_set_dev_desired() is void, so the
shortfall is silent and dma_map_single() can fail at open.

Not in this patch. TX-term scaling is on the cover leftovers list
with the rest of the CMO refresh.

> [ ... ]
>
>> @@ -2895,16 +3206,29 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
>>   		netdev->features |= NETIF_F_FRAGLIST;
>>   	}
>>   
>> -	/* Initialize queue count - always 1 for now */
>> -	adapter->multi_queue = 0;
>> -	adapter->num_rx_queues = IBMVETH_DEFAULT_RX_QUEUES;
>> +	if (ret == H_SUCCESS &&
>> +	    (ret_attr & IBMVETH_ILLAN_RX_MULTI_QUEUE_SUPPORT)) {
>> +		adapter->multi_queue = 1;
>> +		ibmveth_publish_num_rx_queues(adapter,
>> +					      min(num_online_cpus(),
>> +						  IBMVETH_DEFAULT_QUEUES));
>> +		netdev_dbg(netdev, "RX multi queue mode enabled: %d queues\n",
>> +			   ibmveth_get_num_rx_queues(adapter));
>> +	} else {
>> +		adapter->multi_queue = 0;
>> +		ibmveth_publish_num_rx_queues(adapter,
>> +					      IBMVETH_DEFAULT_RX_QUEUES);
>> +	}
> [Severity: Medium]
> Activating several RX NAPI instances makes a number of adapter-wide
> counters multi-writer.  Can these lose increments?
>
> ibmveth_replenish_task() bumps a shared counter outside any lock:
>
> 	adapter->replenish_task_cycles++;
>
> 	spin_lock_irqsave(&rxq->replenish_lock, flags);
>
> and ibmveth_replenish_buffer_pool() updates shared counters under only the
> queue-local lock:
>
> 	adapter->replenish_add_buff_success += filled;
>
> Per-queue replenish_locks give no mutual exclusion between different
> queues, so plain read-modify-write on replenish_task_cycles,
> replenish_add_buff_success/failure, replenish_no_mem and
> hcall_stats.add_bufs_queue races.  At this commit rx_invalid_buffer,
> rx_large_packets and netdev->stats.rx_packets/rx_bytes are in the same
> situation; the following qstats patch moves those to per-queue storage, but
> the replenish counters remain shared through the end of the series.  Should
> they get per-queue slots as well?
Yes. Plain u64 RMW can lose increments: replenish_task_cycles is
outside any lock, and per-queue replenish_locks do not serialise
different queues.

Patch 11 moves replenish_* into the per-queue structs as plain u64,
summed on the existing adapter-wide keys. Pools are already per
queue, so that matches the lock. The eight hypercall counters are
gone (patch 7); they are not ethtool keys. No atomics.

>> @@ -2922,25 +3246,62 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id)
>>   
>>   	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
>>   		struct kobject *kobj = &adapter->rx_buff_pool[0][i].kobj;
>> -		int error;
>>   
>>   		ibmveth_init_buffer_pool(&adapter->rx_buff_pool[0][i], i,
>>   					 pool_count[i], pool_size[i],
>>   					 pool_active[i]);
>> -		error = kobject_init_and_add(kobj, &ktype_veth_pool,
>> -					     &dev->dev.kobj, "pool%d", i);
>> -		if (!error)
>> -			kobject_uevent(kobj, KOBJ_ADD);
>> +		rc = kobject_init_and_add(kobj, &ktype_veth_pool,
>> +					  &dev->dev.kobj, "pool%d", i);
>> +		if (rc) {
>> +			dev_err(&dev->dev,
>> +				"failed to create pool%d kobject: %d\n", i, rc);
>> +			/* init_and_add takes a ref even on failure */
>> +			kobject_put(kobj);
>> +			ibmveth_put_pool_kobjs(adapter, pools_ready);
>> +			dev_set_drvdata(&dev->dev, NULL);
>> +			free_netdev(netdev);
>> +			return rc;
>> +		}
>> +
>> +		pools_ready++;
>> +		kobject_uevent(kobj, KOBJ_ADD);
>>   	}
> [Severity: Low]
> This is a pre-existing problem being repaired rather than a new one, but
> propagating kobject_init_and_add() failure and putting already-created pool
> kobjects before free_netdev() is a standalone fix for probe error handling
> that predates the MQ work.  Could it be split into its own patch with a
> Fixes: tag so it can be backported independently of this feature?
Yes, pre-existing, and it predates MQ. The put-before-free stays in
this series so a bisect does not leak. probe_cleanup lands in patch
11. Happy to send a standalone to net the same way as the reopen
unwind, if you would rather pick it up that way.

> [Severity: Medium]
> This isn't a bug introduced by this patch either (ibmveth_remove() already
> does kobject_put() followed by free_netdev()), but the pattern is now
> replicated in four new probe error paths.  Can these free memory that a
> deferred kobject release still uses?
>
> ktype_veth_pool has no release callback:
>
> 	static const struct kobj_type ktype_veth_pool = {
> 		.release        = NULL,
> 		.sysfs_ops      = &veth_pool_ops,
> 		.default_groups = veth_pool_groups,
> 	};
>
> and the kobjects live inside netdev_priv().  With
> CONFIG_DEBUG_KOBJECT_RELEASE=y, kobject_release() in lib/kobject.c does not
> clean up synchronously:
>
> 	unsigned long delay = HZ + HZ * get_random_u32_below(4);
> 	...
> 	INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
>
> 	schedule_delayed_work(&kobj->release, delay);
>
> The delayed_work lives inside the kobject, so free_netdev() right after
> kobject_put() leaves a queued work item in freed memory and
> kobject_delayed_cleanup() dereferences a freed kobj 1-5 seconds later.
> Would a no-op .release for ktype_veth_pool, or deferring free_netdev()
> until the kobjects are really gone, avoid that?
Yes. With CONFIG_DEBUG_KOBJECT_RELEASE=y, kobject_put() then
free_netdev() can touch freed memory: the delayed_work lives inside
the kobject in netdev_priv(). This patch copies the same pattern onto
the probe error paths.

A no-op .release or deferring free_netdev() would close it. Not in
this series; it is on the cover leftovers list.

>> +	/*
>> +	 * VIO CMO entitlement was set before probe (netdev NULL → default).
>> +	 * Recompute now that num_rx_queues and pool 0 metadata are known.
>> +	 */
>> +	if (firmware_has_feature(FW_FEATURE_CMO))
>> +		vio_cmo_set_dev_desired(dev, ibmveth_get_desired_dma(dev));
>> +
>>   	rc = netif_set_real_num_tx_queues(netdev, min(num_online_cpus(),
>>   						      IBMVETH_DEFAULT_QUEUES));
>>   	if (rc) {
>>   		netdev_dbg(netdev, "failed to set number of tx queues rc=%d\n",
>>   			   rc);
>> +		ibmveth_put_pool_kobjs(adapter, pools_ready);
>> +		dev_set_drvdata(&dev->dev, NULL);
>>   		free_netdev(netdev);
>>   		return rc;
>>   	}
> [ ... ]
>
> Cross-instance finding from sashiko-gemini (91dce8e1ae8c5dfd96a2db94571e030c1951674452013e31a94e2df21bfc801b):
> [Severity: Medium]
> Missing `smp_rmb()` in `ibmveth_get_num_rx_queues` allows out-of-order reads of uninitialized queue data.
v6 publishes with smp_store_release() and the getter uses
smp_load_acquire(), instead of smp_wmb() plus WRITE_ONCE / READ_ONCE.

That is the pair. No extra smp_rmb().

Thanks,

Mingming


  reply	other threads:[~2026-08-31 19:11 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-14  7:36 [PATCH net-next v5 00/15] ibmveth: Add multi-queue RX support Mingming Cao
2026-08-14  7:36 ` [PATCH net-next v5 01/15] ibmveth: Add MQ RX hypercall wrappers and call definitions Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 17:58     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 02/15] ibmveth: Prepare MQ RX adapter data structures Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 18:03     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 03/15] ibmveth: Refactor RX resource allocation for MQ RX bring-up Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 18:12     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 04/15] ibmveth: Refactor buffer pool management for per-queue MQ RX Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 18:20     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 05/15] ibmveth: Refactor RX interrupt control for MQ RX queues Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 18:24     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 06/15] ibmveth: Refactor TX resource allocation in open/close paths Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 18:46     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 07/15] ibmveth: Add RX queue register helpers for MQ Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 18:54     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 08/15] ibmveth: Add queue-aware RX buffer submit helper " Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:00     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 09/15] ibmveth: Harden RX poll path with helpers Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:06     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 10/15] ibmveth: Enable multi-queue RX receive path Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:10     ` mingming cao [this message]
2026-08-14  7:36 ` [PATCH net-next v5 11/15] ibmveth: Add per-queue RX and TX statistics collection Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:12     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 12/15] ibmveth: Report MQ-aware RX counts in ethtool get_channels Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:19     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 13/15] ibmveth: Expose per-queue buffer pool details via debugfs Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:20     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 14/15] ibmveth: Implement incremental MQ RX queue resize Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:22     ` mingming cao
2026-08-14  7:36 ` [PATCH net-next v5 15/15] ibmveth: Wire ethtool set_channels to " Mingming Cao
2026-08-18  1:47   ` Jakub Kicinski
2026-08-31 19:24     ` 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=243fb549-22d2-407c-8774-d3ade7882958@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=kuba@kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=maddy@linux.ibm.com \
    --cc=mpe@ellerman.id.au \
    --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