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 06/15] ibmveth: Refactor TX resource allocation in open/close paths
Date: Mon, 31 Aug 2026 11:46:41 -0700 [thread overview]
Message-ID: <9d3412bf-a3ef-49ed-b9aa-7645dad7791d@linux.ibm.com> (raw)
In-Reply-To: <20260818014724.3854085-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: Refactor TX resource allocation in open/close paths
>
> This moves the per-queue TX long term buffer alloc/free loops out of
> ibmveth_open()/ibmveth_close() into ibmveth_alloc_tx_resources() and
> ibmveth_free_tx_resources(), moves the TX allocation to the end of open()
> after RX is live, and adds a tx_ltb_ptr[] presence check to
> ibmveth_start_xmit(). No Link: tags are present in the commit header.
>
> A few questions on the LTB lifetime rules the patch establishes.
>
>> diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
>> index 99eeb6ef51bf3..b39e8c53cbfd1 100644
>> --- a/drivers/net/ethernet/ibm/ibmveth.c
>> +++ b/drivers/net/ethernet/ibm/ibmveth.c
>> @@ -1183,8 +1183,12 @@ static int ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter,
>>
>> static void ibmveth_free_tx_ltb(struct ibmveth_adapter *adapter, int idx)
>> {
>> + if (!adapter->tx_ltb_ptr[idx])
>> + return;
>> +
>> dma_unmap_single(&adapter->vdev->dev, adapter->tx_ltb_dma[idx],
>> adapter->tx_ltb_size, DMA_TO_DEVICE);
>> + adapter->tx_ltb_dma[idx] = 0;
>> kfree(adapter->tx_ltb_ptr[idx]);
>> adapter->tx_ltb_ptr[idx] = NULL;
>> }
> [Severity: Medium]
> tx_ltb_ptr[] now doubles as the guard for tx_ltb_dma[], but the retire
> order here clears the guarded value first:
>
> dma_unmap_single(...);
> adapter->tx_ltb_dma[idx] = 0;
> kfree(adapter->tx_ltb_ptr[idx]);
> adapter->tx_ltb_ptr[idx] = NULL;
>
> Can an ibmveth_start_xmit() that already passed the new check
>
> if (unlikely(!adapter->tx_ltb_ptr[queue_num])) {
>
> then reach
>
> desc.fields.address = adapter->tx_ltb_dma[queue_num];
>
> and hand a zeroed DMA address to H_SEND_LOGICAL_LAN?
>
> The publish side has the mirror-image order: ibmveth_allocate_tx_ltb()
> stores tx_ltb_ptr[idx] from kzalloc() before tx_ltb_dma[idx] is filled in
> by dma_map_single(), with no release/acquire pairing.
>
> Both the check in ibmveth_start_xmit() and its later uses (the memcpy into
> tx_ltb_ptr[queue_num] and the descriptor address read) are plain loads with
> no READ_ONCE and no common lock, so nothing prevents a reload of
> tx_ltb_ptr[queue_num] after it was set to NULL.
>
> Against ibmveth_close() this appears harmless, since ibmveth does not set
> lltx and netif_tx_disable() therefore takes every __netif_tx_lock. Against
> ibmveth_set_channels() (more below) there is no such serialization. Would a
> real lock, or gating on adapter->opened, express this better than a pointer
> used as a flag?
close() is safe: no lltx, so netif_tx_disable() waits out xmit. The
-L shrink window is real; answered below. v6 clears both slots before
unmap/free so a later start_xmit() fails the pointer check.
A lock or opened gate in start_xmit() would not replace that. opened
is false after a failed reopen, when the LTBs are already gone; the
-L race runs while opened is true. The right fix there is
netif_tx_disable() on the shrink path, not another flag on xmit.
>> @@ -1207,12 +1211,54 @@ static int ibmveth_allocate_tx_ltb(struct ibmveth_adapter *adapter, int idx)
>> "unable to DMA map tx long term buffer\n");
>> kfree(adapter->tx_ltb_ptr[idx]);
>> adapter->tx_ltb_ptr[idx] = NULL;
>> + adapter->tx_ltb_dma[idx] = 0;
>> return -ENOMEM;
>> }
>>
>> return 0;
>> }
>>
>> +/**
>> + * ibmveth_alloc_tx_resources - Allocate TX resources for all queues
>> + * @adapter: ibmveth adapter structure
>> + *
>> + * Allocates TX Long Term Buffers (LTBs) for all TX queues.
>> + *
>> + * Return: 0 on success, -ENOMEM on failure
>> + */
>> +static int ibmveth_alloc_tx_resources(struct ibmveth_adapter *adapter)
>> +{
>> + struct net_device *netdev = adapter->netdev;
>> + int i;
>> +
>> + for (i = 0; i < netdev->real_num_tx_queues; i++) {
>> + if (ibmveth_allocate_tx_ltb(adapter, i))
>> + goto err_free_ltbs;
>> + }
> [Severity: Low]
> This isn't a bug introduced by this patch, and it looks unreachable by the
> end of the series, but noting it for traceability: ibmveth_allocate_tx_ltb()
> assigns the kzalloc() and dma_map_single() results straight into
> tx_ltb_ptr[i] / tx_ltb_dma[i] with no check for an already-populated slot,
> unlike ibmveth_set_channels() which does
>
> if (adapter->tx_ltb_ptr[i])
> continue;
>
> so a caller reaching this helper with a live mapping in the slot would
> strand the old TCE mapping. With the later set_channels rewrite in this
> series the !adapter->opened branch allocates nothing and the opened branch
> frees everything it allocated on failure, so no path leaves tx_ltb_ptr[]
> populated while adapter->opened is false and open() cannot overwrite a live
> mapping. Would a defensive check (or a comment stating the invariant) be
> worth it here?
A comment. A defensive skip would hide a caller that left a live
mapping.
>> +
>> + return 0;
>> +
>> +err_free_ltbs:
>> + while (--i >= 0)
>> + ibmveth_free_tx_ltb(adapter, i);
>> + return -ENOMEM;
>> +}
>> +
>> +/**
>> + * ibmveth_free_tx_resources - Free TX resources for all queues
>> + * @adapter: ibmveth adapter structure
>> + *
>> + * Frees TX Long Term Buffers (LTBs) for all TX queues.
>> + */
>> +static void ibmveth_free_tx_resources(struct ibmveth_adapter *adapter)
>> +{
>> + struct net_device *netdev = adapter->netdev;
>> + int i;
>> +
>> + for (i = 0; i < netdev->real_num_tx_queues; i++)
>> + ibmveth_free_tx_ltb(adapter, i);
>> +}
> [Severity: Low]
> Small wording mismatch: the kernel-doc says "Frees TX Long Term Buffers
> (LTBs) for all TX queues", but the loop is bounded by real_num_tx_queues
> while tx_ltb_ptr[] is IBMVETH_MAX_QUEUES wide. Since ibmveth_free_tx_ltb()
> is now NULL-safe, either iterating IBMVETH_MAX_QUEUES or saying
> "real_num_tx_queues" in the comment would make the two agree.
The kdoc now says real_num_tx_queues.
>> @@ -1263,12 +1309,6 @@ static int ibmveth_open(struct net_device *netdev)
> [ ... ]
>
>> rc = ibmveth_alloc_buffer_pools(adapter);
>> if (rc)
>> - goto out_free_tx_ltb;
>> + goto out_unregister_lan;
>>
>> rc = ibmveth_setup_rx_interrupts(adapter);
>> - if (rc) {
>> - do {
>> - lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
>> - } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
>> - goto out_free_buffer_pools;
>> - }
>> + if (rc)
>> + goto out_unregister_lan;
> [ ... ]
>
>> -out_free_buffer_pools:
>> +out_cleanup_rx_interrupts:
>> + ibmveth_cleanup_rx_interrupts(adapter);
>> +out_unregister_lan:
>> + do {
>> + lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
>> + } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
>> ibmveth_free_buffer_pools(adapter);
>> -out_free_tx_ltb:
>> - while (--i >= 0)
>> - ibmveth_free_tx_ltb(adapter, i);
>> +out_free_queue_mem:
>> ibmveth_cleanup_rx_resources(adapter);
> [Severity: Medium]
> This isn't a bug introduced by this patch, but is the changelog wording
> accurate about what changed here? The commit message says:
>
> "After LAN registration, open-fail teardown frees the logical LAN before
> tearing down RX pool DMA (intentional safer order than leaving the LAN
> registered while unmapping RX memory)."
>
> At the parent commit, an ibmveth_alloc_buffer_pools() failure after a
> successful ibmveth_register_logical_lan() went
>
> rc = ibmveth_alloc_buffer_pools(adapter);
> if (rc)
> goto out_free_tx_ltb;
>
> and out_free_tx_ltb fell through to ibmveth_cleanup_rx_resources() and
> ibmveth_free_filter_list() with no h_free_logical_lan() anywhere on that
> path, so the RX queue and buffer/filter lists were unmapped and freed while
> PHYP still had the LAN registered. Post-patch that failure goes to
> out_unregister_lan, which issues the deregistration hcall first.
>
> So this looks less like a reordering and more like adding a hcall that was
> never issued on that path. Should this be split out as its own patch with a
> Fixes: tag so it can be picked up for stable, rather than described as an
> ordering preference inside a TX-only refactor?
Yes. That path never issued the hcall; v6 says so.
The tag would be:
Fixes: d43732ce021f ("ibmveth: properly unwind on init errors")
Same d43732ce021f origin as patch 3 — one standalone after this
series covers both. The posting stays 15.
>> @@ -1343,7 +1385,10 @@ static int ibmveth_close(struct net_device *netdev)
> [ ... ]
>
>> - netif_tx_stop_all_queues(netdev);
>> + /* Disable and wait for in-flight ndo_start_xmit (stop_all_queues
>> + * alone does not). Direct close() callers bypass synchronize_net().
>> + */
>> + netif_tx_disable(netdev);
> [Severity: High]
> This isn't a bug introduced by this patch, but the reasoning in this new
> comment seems to apply equally to ibmveth_set_channels(), which still frees
> the same LTBs after only netif_tx_stop_all_queues():
>
> ibmveth_set_channels() {
> ...
> netif_tx_stop_all_queues(netdev);
> ...
> /* Free any that are no longer needed */
> for (i = old; i > goal; i--) {
> if (adapter->tx_ltb_ptr[i - 1])
> ibmveth_free_tx_ltb(adapter, i - 1);
> }
>
> netif_tx_wake_all_queues(netdev);
> }
>
> netif_tx_stop_all_queues() only sets __QUEUE_STATE_DRV_XOFF; it takes no
> __netif_tx_lock, and netif_set_real_num_tx_queues() only touches the qdisc
> root lock. Since ibmveth does not declare lltx, an ndo_start_xmit already
> running on another CPU holds the txq lock and keeps going:
>
> CPU0 ibmveth_start_xmit()
> /* already passed !adapter->tx_ltb_ptr[queue_num] */
> memcpy(adapter->tx_ltb_ptr[queue_num], skb->data, skb_headlen(skb));
> ...
> desc.fields.address = adapter->tx_ltb_dma[queue_num];
>
> CPU1 ibmveth_set_channels()
> ibmveth_free_tx_ltb(adapter, i - 1);
> dma_unmap_single(...);
> adapter->tx_ltb_dma[idx] = 0;
> kfree(adapter->tx_ltb_ptr[idx]);
>
> Can an ethtool -L shrink therefore memcpy into kfree()d memory and pass a
> stale or zeroed DMA address to H_SEND_LOGICAL_LAN? The new one-shot
> tx_ltb_ptr[] check in ibmveth_start_xmit() does not close this window, and
> this stop-then-free sequence is unchanged at the end of the series. Should
> ibmveth_set_channels() use netif_tx_disable() too?
Yes. close() already uses netif_tx_disable(). The shrink path stays
stop-then-free here and at series tip. Patches 14 and 15 switch the
IFF_UP vs opened gate; they do not add disable.
>> @@ -1359,13 +1404,14 @@ static int ibmveth_close(struct net_device *netdev)
> [ ... ]
>
>> @@ -1789,6 +1835,12 @@ static netdev_tx_t ibmveth_start_xmit(struct sk_buff *skb,
>> int i, queue_num = skb_get_queue_mapping(skb);
>> unsigned long mss = 0;
>>
>> + /* Close / failed reopen can free LTBs while IFF_UP is still set. */
>> + if (unlikely(!adapter->tx_ltb_ptr[queue_num])) {
>> + dev_kfree_skb_any(skb);
>> + return NETDEV_TX_OK;
>> + }
>> +
> [Severity: Medium]
> Should this drop bump netdev->stats.tx_dropped like every other drop in this
> function does?
>
> if (unlikely(skb->len > adapter->tx_ltb_size)) {
> ...
> netdev->stats.tx_dropped++;
> goto out;
> }
>
> The other drop sites (ibmveth_is_packet_unsupported(), the checksum-help
> failure, the oversize check, the total_bytes mismatch and the
> ibmveth_send() failure) all increment tx_dropped, and returning
> NETDEV_TX_OK tells the core the skb was consumed so the core cannot account
> for it either. In exactly the abnormal states this guard targets, traffic
> is then discarded with nothing visible in ip -s link or in
> /sys/class/net/<if>/statistics/tx_dropped. Could this reuse the existing
> out: label after the tx_dropped++ instead?
Yes. v6 increments tx_dropped and falls into out:.
> [Severity: Low]
> This isn't a bug introduced by this patch, and it looks resolved later in
> the series, but noting it for traceability against the claim in the commit
> message that "close/failed-reopen with IFF_UP set cannot UAF".
>
> The guard keys off tx_ltb_ptr[] rather than adapter->opened, while
> ibmveth_set_channels() at this point in the series still selects its live
> path from IFF_UP alone:
>
> if (!(netdev->flags & IFF_UP))
> return netif_set_real_num_tx_queues(netdev, goal);
>
> In the state the driver documents in ibmveth_close() (IFF_UP set,
> adapter->opened false after a failed reopen), ethtool -L would allocate LTBs
> and finish with netif_tx_wake_all_queues() on an adapter whose logical LAN
> was already released, so packets pass this pointer-only check and reach
> ibmveth_send() with no registered LAN, and those LTBs are not freed by a
> later close() because it early-returns on !adapter->opened. The later patch
> "ibmveth: Wire ethtool set_channels to MQ RX queue resize" replaces the
> IFF_UP gating with an adapter->opened test whose !opened branch allocates
> nothing and wakes no queues, which removes this window.
The v5 claim was too broad. NULL-first only closes the check-then-use
window; close-path safety is netif_tx_disable(). The IFF_UP vs opened
window closes in patches 14 and 15.
Thanks,
Mingming
next prev parent reply other threads:[~2026-08-31 18:47 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 [this message]
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
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=9d3412bf-a3ef-49ed-b9aa-7645dad7791d@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