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,10/15] ibmveth: Enable multi-queue RX receive path
Date: Thu, 24 Sep 2026 23:48:56 -0700	[thread overview]
Message-ID: <e7242d1f-6cad-4a81-8a29-839e11fafa72@linux.ibm.com> (raw)
In-Reply-To: <178845904035.3394541.12032685320275695105@kernel.org>

Thanks for the review feedback.

On 9/3/26 11:10 AM, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 11 potential
> issue(s) to consider:
>
> Critical: 0 · High: 1 (2) · Medium: 4 · Low: 6
>
> - [High] In multi-queue mode ibmveth_setup_rx_interrupts() already
>    unmasks the PHYP interrupt for every live RX queue, and ibmveth_open()…
> - [Medium] Adapter-wide RX counters are not converted to per-queue
>    storage while this commit enables up to min(nr_cpus,8) live RX queues:…
> - [Medium] ibmveth_apply_mq_fallback() publishes adapter->num_rx_queues =
>    1 at the top of ibmveth_open() but deliberately leaves…
> - [Medium] ibmveth_open() now runs ibmveth_replenish_task() for every
>    queue, and that function can queue reset work…
> - [Medium] The new probe error paths call kobject_put() on the pool
>    kobjects, which are embedded in struct ibmveth_adapter inside…
> - [Low] ibmveth_get_channels() begins advertising max_rx/rx_count > 1 (up
>    to min(num_online_cpus(), IBMVETH_DEFAULT_QUEUES)) while its peer…
> - [Low] Probe enables min(num_online_cpus(), IBMVETH_DEFAULT_QUEUES) RX
>    queues whenever the firmware MQ bit is present; each queue clones…
> - [Low] The commit message itself states that this commit ships known-
>    wrong statistics and new schedule_work() producers whose remove-path…
> - [Low] The pool sysfs kobject creation loop in ibmveth_probe() changes
>    from 'ignore kobject_init_and_add() failure and continue probing' to…
> - [Low] The ibmveth_setup_rx_interrupts() kernel-doc still describes the
>    pre-MQ open sequence: "Single-queue open leaves PHYP masked here and…
> - [Low] ibmveth_register_single_rx_queue() logs a signed `long lpar_rc`
>    with the unsigned conversion "rc=0x%lx".
>
> Pre-existing issues:
> - [High] This commit makes the MQ reset producer live
>    (adapter->multi_queue was hardcoded 0 before) and adds a new…
> - [High] ibmveth_poll_controller() now replenishes and schedules every
>    published RX queue instead of only queue 0, multiplying the exposure…
>
> --
>
> ibmveth: Enable multi-queue RX receive path
>
> This turns on the multi-queue RX path when firmware advertises
> IBMVETH_ILLAN_RX_MULTI_QUEUE_SUPPORT, raises IBMVETH_MAX_RX_QUEUES from 1
> to 16, and wires subordinate queue registration, IRQ setup and per-queue
> replenish into open()/close().  Probe now selects min(num_online_cpus(),
> IBMVETH_DEFAULT_QUEUES) RX queues and refreshes the VIO CMO entitlement.
>
> A few questions on the commit message first.
>
> [Severity: Low]
> The changelog says:
>
>      Two things are knowingly left for patch 11. RX counters are racy:
>      adapter->rx_no_buffer is assigned rather than summed from one queue's
>      buffer-list page, so it reports whichever queue replenished last and
>      can go backwards, while rx_packets, rx_bytes, rx_invalid_buffer and
>      rx_large_packets are plain read-modify-writes now reached from several
>      NAPI instances at once, so they can lose counts
>
> and:
>
>      This commit also adds schedule_work() producers, and the remove-path
>      unregister / cancel_work_sync reorder and reset reg_state gate that
>      make them safe land there too.
>
> Would it be preferable to reorder so the counter conversion and the
> remove-path fix precede the commit that makes the MQ path live, rather
> than shipping an intermediate commit that is documented as incorrect in
> those two respects?
>
> The same paragraph documents a third, unrelated change:
>
>      On probe failure after pool kobjects were created, put them before
>      free_netdev(). The leak is pre-existing and unrelated to multi-queue,
>      so a standalone version also goes to net; the probe_cleanup helper
>      lands in patch 11.
>
> Since this is described as unrelated and is being posted separately, could
> it be dropped from this patch instead of open-coded on four error paths
> here and then replaced by a helper later in the series?
The remove-path fix (unregister_netdev then cancel_work_sync)
and the NETREG_REGISTERED gate are folded into this patch,
alongside the buffer-add H_FUNCTION reset producer.

The counter conversion stays in Patch 11: we keep activation in
this patch so all MQ bring-up logic is exercised together at
this commit, and explicitly document the transient counter race
resolved in Patch 11. If you prefer a Prepare/Enable split to
keep intermediate counters strictly race-free, we can separate
activation if requested.

The pool kobject puts stay here: dropping them
leaks on probe fail until Patch 11 extracts
probe_cleanup. That is not the unwind
standalone (Fixes: d43732ce021f). v7 waits
for release before free_netdev().
>> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
>> index f726d1e3f0b11..2e8896ea5af2e 100644
>> --- a/drivers/net/ethernet/ibm/ibmveth.c
>> +++ b/drivers/net/ethernet/ibm/ibmveth.c
> [ ... ]
>
>> @@ -992,11 +1014,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;
>>   
>> -	adapter->rx_no_buffer = be64_to_cpup(p);
>> +	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 = drops;
>>   }
> [Severity: Medium]
> ibmveth_update_rx_no_buffer() now takes a queue index but still ends in a
> plain assignment to the single adapter-wide field:
>
> 	adapter->rx_no_buffer = drops;
>
> With more than one live queue, does each call simply overwrite the value
> harvested for the previous queue?  In ibmveth_close() the new loop calls it
> for every live queue back to back:
>
> 	for (i = 0; i < ibmveth_get_num_rx_queues(adapter); i++)
> 		ibmveth_update_rx_no_buffer(adapter, i);
>
> so the reported counter ends up being whichever queue was visited last, and
> can move backwards between reads.
>
> Similarly, ibmveth_poll_deliver_frame() does unlocked read-modify-writes:
>
> 	netdev->stats.rx_packets++;
> 	netdev->stats.rx_bytes += length;
>
> and the replenish counters (replenish_task_cycles, replenish_add_buff_success
> / _failure, replenish_no_mem) are serialized only by the per-queue
> rx_queue[i].replenish_lock, which does not serialize different queues against
> each other.  Can these lose counts once several NAPI instances run
> concurrently?
Yes, at this commit. Patch 11 already moves replenish_* and
the packet/byte counters to per-queue u64, summed on read.
rx_no_buffer gets a per-queue carry there too.

> The changelog notes that the following patch moves these counters to
> per-queue storage summed on read, which does resolve it; the question is
> whether this intermediate state is worth carrying.
>
>> @@ -1062,6 +1094,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 this commit makes the reset
> producer reachable for the first time (adapter->multi_queue was hardcoded to
> 0 before) and adds a second schedule_work() site here.
>
> At this commit ibmveth_reset() has no registration-state check:
>
> 	rtnl_lock();
> 	dev_close(adapter->netdev);
> 	dev_open(adapter->netdev, NULL);
> 	rtnl_unlock();
>
> and ibmveth_remove() still runs cancel_work_sync(&adapter->work) before
> unregister_netdev().  If a live queue hits MQ buffer-add H_FUNCTION after
> the cancel, can the requeued work then run dev_close()/dev_open() against a
> netdev that unregister_netdev()/free_netdev() is tearing down?
Folded into this patch: unregister_netdev() moves
ahead of cancel_work_sync(), and ibmveth_reset()
is gated on NETREG_REGISTERED here alongside the
buffer-add H_FUNCTION reset producer. Patch 11
then frees the per-queue statistics arrays between
cancel_work_sync() and free_netdev().
> Both halves are fixed later in this series (ibmveth_remove() is reordered to
> unregister_netdev() before cancel_work_sync(), and ibmveth_reset() gains the
> netdev->reg_state != NETREG_REGISTERED gate), so the exposure is limited to
> this intermediate commit.
>
>> @@ -1081,6 +1114,27 @@ 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)
>> +{
>> +	ibmveth_replenish_task(adapter, qindex);
>> +	if (ibmveth_schedule_rx_queue(adapter, qindex))
>> +		return;
>> +
>> +	ibmveth_enable_irq(adapter, qindex);
>> +}
> [Severity: High]
> Is the unconditional ibmveth_enable_irq() here safe in multi-queue mode?
>
> ibmveth_schedule_rx_queue() returns false in two different situations:
>
> 	if (napi_schedule_prep(napi)) {
> 		ibmveth_disable_irq(adapter, qindex);
> 		__napi_schedule(napi);
> 		return true;
> 	}
> 	return false;
>
> The second case is "NAPI already claimed", which is exactly what the IRQ
> handler does after it has masked PHYP.  In MQ mode
> ibmveth_setup_rx_interrupts() has already unmasked every queue:
>
> 	if (adapter->multi_queue && num > 1) {
> 		for (i = 0; i < num; i++) {
> 			rc = ibmveth_enable_irq(adapter, i);
>
> so by the time open() runs its restart loop an interrupt may already have
> claimed NAPI and masked the queue.  restart then re-unmasks it under the
> in-flight poll, and when that poll finishes ibmveth_poll() calls
> ibmveth_enable_irq() again on an already-enabled subordinate interrupt.
>
> ibmveth_toggle_irq() folds H_PARAMETER only on the disable side:
>
> 		if (h_rc == H_PARAMETER && !enable) {
> 			dev_warn_ratelimited(...);
> 			return 0;
> 		}
>
> so the redundant enable returns -EIO, and ibmveth_poll() escalates that to
> schedule_work(&adapter->work), i.e. a full dev_close()/dev_open() of an
> otherwise healthy adapter.
>
> Since the PHYP mask/unmask hcalls are not reference counted, should restart
> skip the enable when schedule_rx_queue() failed because NAPI was already
> scheduled, rather than when prep failed for lack of a pending descriptor?
> The same enable-then-restart sequence appears later in the ethtool -L
> scale-up path of ibmveth_resize_rx_queues_incremental():
>
> 	rc = ibmveth_enable_irq(adapter, i);
> 	...
> 	ibmveth_restart_rx_queue(adapter, i);
>
> [ ... ]
It should not skip the enable. When prep fails,
NAPI is already scheduled, so enabling there is
deliberate; masking on that path left the queue
idle and masked.
>> +static int
>> +ibmveth_register_single_rx_queue(struct ibmveth_adapter *adapter,
>> +				 int queue_idx)
>> +{
>> +	struct net_device *netdev = adapter->netdev;
>> +	union ibmveth_buf_desc rxq_desc;
>> +	long lpar_rc;
>> +
>> +	if (WARN_ON(queue_idx < 1 || queue_idx >= IBMVETH_MAX_RX_QUEUES))
>> +		return -EINVAL;
>> +
>> +	rxq_desc.fields.flags_len = IBMVETH_BUF_VALID |
>> +				    adapter->rx_queue[queue_idx].queue_len;
>> +	rxq_desc.fields.address = adapter->rx_queue[queue_idx].queue_dma;
>> +
>> +	lpar_rc = ibmveth_register_logical_lan_queue(adapter, rxq_desc,
>> +						     queue_idx);
>> +	if (lpar_rc != H_SUCCESS) {
>> +		netdev_err(netdev, "Failed to register queue %d: rc=0x%lx\n",
>> +			   queue_idx, lpar_rc);
> [Severity: Low]
> lpar_rc is a signed long here, and ibmveth_register_logical_lan_queue()
> returns -EINVAL when irq_create_mapping() fails, so that case prints as
> rc=0xffffffffffffffea.  Would %ld (or splitting the errno and hypervisor
> cases) read better?  Note this will not produce a build warning, since the
> signed/unsigned format mismatch is only diagnosed under
> -Wformat-signedness, which the kernel does not enable.
>
> [ ... ]
v7 uses %ld. The hypervisor fail one function
up already prints that way.
>> +static void ibmveth_apply_mq_fallback(struct ibmveth_adapter *adapter)
>> +{
>> +	struct net_device *netdev = adapter->netdev;
>> +
>> +	if (!adapter->mq_fallback)
>> +		return;
>> +
>> +	adapter->mq_fallback = false;
>> +
>> +	if (!adapter->multi_queue && ibmveth_get_num_rx_queues(adapter) == 1)
>> +		return;
>> +
>> +	netdev_warn(netdev,
>> +		    "Falling back to single RX queue (firmware MQ unavailable)\n");
>> +	adapter->multi_queue = false;
>> +	ibmveth_publish_num_rx_queues(adapter, 1);
>> +	/* real_num_rx_queues is set later in open after resources exist. */
> [Severity: Medium]
> Can the two counts be left permanently out of sync here?
>
> ibmveth_apply_mq_fallback() runs at the very top of ibmveth_open() and
> publishes adapter->num_rx_queues = 1, but netdev->real_num_rx_queues is only
> updated further down.  Four failure exits sit in between:
>
> 	ibmveth_alloc_filter_list()
> 	ibmveth_alloc_rx_queues()
> 	ibmveth_alloc_buffer_pools()
> 	ibmveth_register_rx_queues()
> 	...
> 	netif_set_real_num_rx_queues(netdev, ibmveth_get_num_rx_queues(adapter))
>
> If the fallback open fails at any of those (for example -ENOMEM from pool
> allocation, or -ENONET from queue-0 registration), the device stays down with
> adapter->num_rx_queues == 1 while netdev->real_num_rx_queues is still the old
> multi-queue value, so /sys/class/net/ethX/queues/rx-1..7 remain present for
> queues the driver will never drive.
>
> That contradicts the invariant the probe path documents for exactly this
> case:
>
> 	 * alloc_etherdev_mqs() sized real_num_rx_queues to MAX. Match the
> 	 * advertised default (or SQ 1) before register_netdev so down-state
> 	 * readers agree with adapter->num_rx_queues / ethtool -l.
>
> Would it be better to lower real_num_rx_queues in the fallback path itself,
> or to restore the published count on the open failure paths?
real_num is set after the resources exist. If this
open fails first, the old real_num can sit while
num_rx_queues is already 1. The next successful
open calls set_real. The fallback helper only
publishes the count.
>> @@ -1651,6 +1900,8 @@ static int ibmveth_open(struct net_device *netdev)
>>   
>>   	netdev_dbg(netdev, "open starting\n");
>>   
>> +	ibmveth_apply_mq_fallback(adapter);
>> +
>>   	for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++)
>>   		rxq_entries += adapter->rx_buff_pool[0][i].size;
>>   
>> @@ -1670,18 +1921,34 @@ static int ibmveth_open(struct net_device *netdev)
>>   	if (rc)
>>   		goto out_free_buffer_pools;
>>   
>> -	rc = netif_set_real_num_rx_queues(netdev, adapter->num_rx_queues);
>> +	rc = netif_set_real_num_rx_queues(netdev,
>> +					  ibmveth_get_num_rx_queues(adapter));
>> +
>>   	if (rc) {
>>   		netdev_err(netdev, "failed to set number of rx queues\n");
>>   		goto out_unregister_queues;
>>   	}
>>   
>> +	/*
>> +	 * Post buffers before setup_rx_interrupts(). MQ setup then unmasks
>> +	 * PHYP; SQ setup leaves PHYP masked. Scheduling NAPI only when a
>> +	 * descriptor is already pending is not enough: after ifdown/up
>> +	 * (RX=8, no -L) NAPI can be idle with nothing pending and the
>> +	 * queue stays dead (TX OK, ARP/RX fail).
>> +	 * restart_rx_queue() replenishes, schedules NAPI, and unmasks if
>> +	 * prep fails.
>> +	 */
>> +	for (i = 0; i < ibmveth_get_num_rx_queues(adapter); i++) {
>> +		netdev_dbg(netdev, "initial replenish cycle for queue %d\n", i);
>> +		ibmveth_replenish_task(adapter, i);
>> +	}
>> +
>>   	rc = ibmveth_setup_rx_interrupts(adapter);
>>   	if (rc)
>>   		goto out_free_all_queues; /* setup already disposed IRQs */
>>   
>> -	netdev_dbg(netdev, "initial replenish cycle\n");
>> -	ibmveth_schedule_rx_queue(adapter, 0);
>> +	for (i = 0; i < ibmveth_get_num_rx_queues(adapter); i++)
>> +		ibmveth_restart_rx_queue(adapter, i);
> [Severity: Medium]
> This replenish loop can queue reset work, and ibmveth_replenish_task()
> returns void, so open() cannot see it:
>
> 	} else if (need_reset == IBMVETH_REPLENISH_RESET_MQ) {
> 		...
> 		adapter->mq_fallback = true;
> 		schedule_work(&adapter->work);
> 	}
>
> If a buffer-add hcall returns H_FUNCTION here and the open then fails at
> ibmveth_setup_rx_interrupts() or ibmveth_alloc_tx_resources(), userspace gets
> an error back from ndo_open and the device is left down, but the pending
> ibmveth_reset() only checks registration state:
>
> 	rtnl_lock();
> 	if (netdev->reg_state != NETREG_REGISTERED) {
> 		rtnl_unlock();
> 		return;
> 	}
>
> 	dev_close(adapter->netdev);
> 	dev_open(adapter->netdev, NULL);
>
> so it brings the interface up anyway a moment later.  Should the reset worker
> also check that the device was successfully opened before reopening it?
No. After a failed open the worker can still
dev_open(); the netdev is registered. mq_fallback
is already latched, so that open comes up
single-queue. That is the H_FUNCTION recovery.
> [Severity: Low]
> This isn't a bug, but the kernel-doc on ibmveth_setup_rx_interrupts() still
> describes the pre-MQ ordering:
>
>   * Single-queue open leaves PHYP masked
>   * here and kicks NAPI afterward (classic path: first poll posts then
>   * enables).
>
> After this change open() posts buffers for every live queue (queue 0
> included, in single-queue mode) before calling setup_rx_interrupts(), and
> ibmveth_restart_rx_queue() posts again, so the "first poll posts" behaviour no
> longer applies to either mode.  Could that comment be refreshed?
v7 refreshes it. Open posts first in both
modes; restart_rx_queue still kicks NAPI
after setup.
>> @@ -1714,6 +1981,7 @@ static int ibmveth_open(struct net_device *netdev)
>>   static int ibmveth_close(struct net_device *netdev)
>>   {
>>   	struct ibmveth_adapter *adapter = netdev_priv(netdev);
>> +	int i;
>>   
>>   	/* Gate on opened, not IFF_UP: pool_store/change_mtu close+open can
>>   	 * leave IFF_UP set after a failed reopen.
>> @@ -1734,7 +2002,8 @@ static int ibmveth_close(struct net_device *netdev)
>>   	/* Wait for softirq/poll that already passed shutdown checks. */
>>   	synchronize_net();
>>   
>> -	ibmveth_update_rx_no_buffer(adapter);
>> +	for (i = 0; i < ibmveth_get_num_rx_queues(adapter); i++)
>> +		ibmveth_update_rx_no_buffer(adapter, i);
>>   	ibmveth_free_all_queues(adapter);
> [ ... ]
>
>> @@ -2527,6 +2796,7 @@ static int ibmveth_poll_deliver_frame(struct napi_struct *napi,
>>   		ibmveth_rx_csum_helper(skb, adapter);
>>   	}
>>   
>> +	skb_record_rx_queue(skb, queue_index);
>>   	napi_gro_receive(napi, skb);
>>   
>>   	netdev->stats.rx_packets++;
> [ ... ]
>
>> @@ -2750,9 +3021,14 @@ static int ibmveth_change_mtu(struct net_device *dev, int new_mtu)
>>   static void ibmveth_poll_controller(struct net_device *dev)
>>   {
>>   	struct ibmveth_adapter *adapter = netdev_priv(dev);
>> +	unsigned int num = ibmveth_get_num_rx_queues(adapter);
>> +	int i;
>>   
>> -	ibmveth_replenish_task(adapter, 0);
>> -	ibmveth_schedule_rx_queue(adapter, 0);
>> +	for (i = 0; i < num; i++)
>> +		ibmveth_replenish_task(adapter, i);
>> +
>> +	for (i = 0; i < num; i++)
>> +		ibmveth_schedule_rx_queue(adapter, i);
>>   }
>>   #endif
> [Severity: High]
> This is a pre-existing race, not one introduced here, but walking every
> published queue widens it considerably.
>
> The in-driver callers of ibmveth_close() (veth_pool_store(),
> ibmveth_set_csum_offload(), ibmveth_set_tso(), ibmveth_change_mtu()) call
> ndo_stop directly rather than through dev_close(), so
> netpoll_poll_disable() is never called and netif_running() stays true.
> netpoll_poll_dev() can therefore call ndo_poll_controller() while close is
> freeing the pools.
>
> The replenish side samples the pool and then dereferences it:
>
> 		if (pool->active && pool->free_map &&
> 		    (atomic_read(&pool->available) < pool->threshold)) {
> 			rc = ibmveth_replenish_buffer_pool(adapter, pool,
> 							   queue_index, &fail);
>
> while the freeing side takes no replenish_lock at all:
>
> 	kfree(pool->free_map);
> 	pool->free_map = NULL;
>
> Can this access freed free_map[]/skbuff[]/dma_addr[] arrays, and the freed
> buffer-list page in ibmveth_update_rx_no_buffer()?
>
> A later patch in the series adds an "if (!adapter->opened) return;" check to
> ibmveth_poll_controller(), but ibmveth_close() clears opened before it frees
> anything and does not wait for an in-flight poll_controller, so the window
> appears to remain.
Yes, at this commit. Patch 15 already returns if !opened.
That does not wait for an in-flight poll_controller.
close still skips netpoll_poll_disable, and
free_buffer_pool still skips the lock. Those two
stay with shortening irqsave after this series.
>> @@ -2781,23 +3056,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);
>>   
>> +	for (q = 0; q < ibmveth_get_num_rx_queues(adapter); q++) {
>> +		int rxqentries = 1;
>> +
>> +		for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
>> +			struct ibmveth_buff_pool *bpool =
>> +				&adapter->rx_buff_pool[0][i];
>> +
>> +			if (bpool->active)
>> +				ret += bpool->size *
>> +					IOMMU_PAGE_ALIGN(bpool->buff_size, tbl);
>> +			rxqentries += bpool->size;
>> +		}
> [ ... ]
>
>> @@ -2954,16 +3253,30 @@ 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 = true;
>> +		ibmveth_publish_num_rx_queues(adapter,
>> +					      min(num_online_cpus(),
>> +						  IBMVETH_DEFAULT_QUEUES));
> [ ... ]
>
>> @@ -2981,25 +3294,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;
>> +		}
> [Severity: Medium]
> Can this free the memory the kobjects live in before their cleanup runs?
>
> The pool kobjects are embedded in struct ibmveth_adapter, which is
> netdev_priv(netdev), and ktype_veth_pool has .release = NULL.  With
> CONFIG_DEBUG_KOBJECT_RELEASE=y the last kobject_put() goes to
> kobject_release(), which defers the cleanup:
>
> lib/kobject.c:kobject_release() {
> 	...
> #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
> 	INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
> 	schedule_delayed_work(&kobj->release, delay);
> 	...
> }
>
> The delayed_work lives inside the kobject, i.e. inside the storage that
> free_netdev() releases immediately on this path (reg_state is
> NETREG_UNINITIALIZED), and the pool%d directories stay published against it
> until the work fires a few seconds later.
>
> The same put-then-free sequence is repeated on the
> netif_set_real_num_tx_queues(), netif_set_real_num_rx_queues() and
> register_netdev() failure paths below, and survives into the later
> ibmveth_probe_cleanup() helper.  The changelog lists "pool kobj vs
> DEBUG_KOBJECT_RELEASE" as a leftover - would adding a real release() with a
> container that outlives the kobjects, or creating them only after the last
> failure point, be the fix?
v7 waits. A real release completes, and last
put waits before free_netdev(). Patch 11
probe_cleanup and remove use that helper.
> [Severity: Low]
> This isn't a bug, but the policy here changes from "ignore
> kobject_init_and_add() failure and keep probing" to "fail the probe", so a
> kernfs name collision or -ENOMEM now leaves the interface unavailable
> instead of merely missing the pool%d tuning knobs.  Checking the return
> value is the right thing to do; could the changelog mention the behaviour
> change, since it currently only describes adding cleanup?
Failing the probe on kobject_init_and_add() is intended.
The commit message already names the puts.
>> +	/*
>> +	 * VIO CMO entitlement was set before probe (netdev NULL, so 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));
>> +
> [Severity: Low]
> This isn't a bug, but probe unconditionally selects min(num_online_cpus(),
> IBMVETH_DEFAULT_QUEUES) RX queues whenever the firmware bit is set, and each
> queue clones queue 0's full pool geometry plus its own buffer-list page and
> RX ring.  ibmveth_get_desired_dma() therefore multiplies the desired DMA
> entitlement by num_rx_queues, and vio_cmo_set_dev_desired() returns void, so
> an entitlement request that cannot be granted is not visible here.
>
> On a CMO partition with many CPUs, is defaulting to eight RX queues the
> behaviour you want out of the box, or would defaulting to one and letting
> ethtool -L opt in be safer?  The series does add the runtime opt-out in a
> later patch.
Yes. The default is min(num_online_cpus(), 8), same cap as
TX. ethtool -L can raise or lower it later in the series.

CMO is Power9 and earlier; the MQ firmware bit is
Power11+. Those partitions do not coexist, so this is not a CMO
tradeoff. get_desired_dma() still adds one TX LTB,
but that is only the CMO desired hint. The real
TX LTBs already scale with real_num_tx_queues
(kzalloc + dma_map_single). Those two CMO leftovers
come off the v7 cover. For the same reason v7
drops the probe-time CMO desired refresh; a CMO
partition keeps its single-queue default.
>>   	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;
>> +	}
>> +
>> +	/*
>> +	 * alloc_etherdev_mqs() sized real_num_rx_queues to MAX. Match the
>> +	 * advertised default (or SQ 1) before register_netdev so down-state
>> +	 * readers agree with adapter->num_rx_queues / ethtool -l.
>> +	 */
>> +	rc = netif_set_real_num_rx_queues(netdev,
>> +					  ibmveth_get_num_rx_queues(adapter));
> [Severity: Low]
> With real_num_rx_queues now published as up to eight before
> register_netdev(), ibmveth_get_channels() starts reporting max_rx and
> rx_count greater than one:
>
> 	channels->max_rx = netdev->real_num_rx_queues;
> 	channels->rx_count = netdev->real_num_rx_queues;
>
> but ibmveth_set_channels() at this commit only reads channels->tx_count.
> Does "ethtool -L ethX rx 4" then pass the core's max_rx check, get silently
> discarded by the driver and report success?
Yes, at this commit. set_channels only reads
tx_count, so rx 4 is discarded and the call
succeeds. Patch 12 rejects an RX count change
with -EOPNOTSUPP; Patch 14 implements live
resize.

Thanks,
Mingming
> The changelog acknowledges this ("This commit does not implement
> set_channels / rx_count") and patch 14 wires rx_count up, so the question is
> whether the advertisement should wait for the implementation.
>
> [ ... ]
>


  reply	other threads:[~2026-09-25  6:49 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
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 [this message]
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=e7242d1f-6cad-4a81-8a29-839e11fafa72@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