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: Add queue-aware RX buffer submit helper for MQ

This adds ibmveth_add_logical_lan_buffers() to pick between the legacy
single/multi buffer hcalls and the new per-queue H_ADD_LOGICAL_LAN_BUFFERS_QUEUE
hcall, threads queue_index through the RX replenish/poll helpers, and
introduces a per-queue replenish_lock plus deferred (post-unlock) failure
logging and reset scheduling.

A few questions below about the new locking scope and about some of the
error paths that changed behaviour.

> diff --git a/drivers/net/ethernet/ibm/ibmveth.c 
> b/drivers/net/ethernet/ibm/ibmveth.c
> index 8519cad503225..58a639a962a67 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.c
> +++ b/drivers/net/ethernet/ibm/ibmveth.c

[ ... ]

> @@ -262,6 +272,7 @@ ibmveth_alloc_rx_queues(struct ibmveth_adapter *adapter, 
> int rxq_entries)
>               adapter->rx_queue[i].index = 0;
>               adapter->rx_queue[i].num_slots = rxq_entries;
>               adapter->rx_queue[i].toggle = 1;
> +             spin_lock_init(&adapter->rx_queue[i].replenish_lock);

[Severity: Medium]
Is it safe to re-initialize replenish_lock here, given that
ibmveth_alloc_rx_queues() runs on every ndo_open?

The driver restarts itself directly in several places, guarded only by
netif_running(), for example ibmveth_set_csum_offload():

        if (netif_running(dev)) {
                restart = 1;
                ibmveth_close(dev);
        }
        ...
        if (restart)
                rc2 = ibmveth_open(dev);

veth_pool_store() and the MTU change path do the same. Because
__LINK_STATE_START is never cleared on these paths, netpoll_poll_disable()
is never called, and netpoll_poll_dev() only bails out on !npinfo or a held
dev_lock (dev_lock is taken solely by netpoll_poll_disable() from
__dev_open/__dev_close_many).

So can this sequence happen while netconsole is configured on this device?

CPU0 (printk -> netconsole)
  ibmveth_poll_controller()
    ibmveth_replenish_task(adapter, 0)
      spin_lock_irqsave(&rxq->replenish_lock, flags);

CPU1 (ethtool -K restart)
  ibmveth_open()
    ibmveth_alloc_rx_queues()
      spin_lock_init(&adapter->rx_queue[0].replenish_lock);

That resets a held spinlock to unlocked, and the later unlock on CPU0 runs
against re-initialized state.

[ ... ]

> -/* replenish the buffers for a pool.  note that we don't need to
> - * skb_reserve these since they are used for incoming...
> +/**
> + * ibmveth_add_logical_lan_buffers - Add receive buffers to hypervisor
> + * @adapter: ibmveth adapter structure
> + * @descs: array of buffer descriptors to add
> + * @filled: number of valid descriptors in the array
> + * @buff_size: size of each buffer (multi-queue mode only)
> + * @queue_index: RX queue index
> + *
> + * Return: hypervisor return code
>   */
> -static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter,
> -                                       struct ibmveth_buff_pool *pool)
> +static long ibmveth_add_logical_lan_buffers(struct ibmveth_adapter *adapter,
> +                                         union ibmveth_buf_desc *descs,
> +                                         int filled,
> +                                         unsigned long buff_size,
> +                                         int queue_index)
> +{
> +     struct vio_dev *vdev = adapter->vdev;
> +     unsigned long rc;
> +
> +     if (adapter->multi_queue) {
> +             unsigned long buffersznum = (buff_size << 32) | filled;
> +             unsigned long ioba[IBMVETH_MAX_RX_PER_HCALL / 2] = {0};
> +             unsigned long handle = adapter->queue_handle[queue_index];
> +             int i;

[Severity: Low]
This call site passes adapter->queue_handle[queue_index], which includes
queue 0, but the kernel-doc of h_add_logical_lan_buffers_queue() in
ibmveth.h describes it as "Add buffers to subordinate queue" with
"@queue_handle: Queue handle from h_register_logical_lan_queue()", and its
Return section lists only H_SUCCESS/H_PARAMETER/H_HARDWARE.

The queue 0 handle comes from h_register_logical_lan_with_handle(), whose
own doc says the handle is "needed in multi-queue mode to use
h_add_logical_lan_buffers_queue() for all queues including queue 0", and
ibmveth_replenish_buffer_pool() has dedicated recovery for H_FUNCTION from
this hcall.

Could the h_add_logical_lan_buffers_queue() kernel-doc be updated to cover
the primary queue handle and H_FUNCTION?

[ ... ]

> +             rc = h_add_logical_lan_buffers_queue(vdev->unit_address,
> +                                                  handle,
> +                                                  buffersznum,
> +                                                  ioba[0], ioba[1], ioba[2],
> +                                                  ioba[3], ioba[4], ioba[5]);
> +             adapter->hcall_stats.add_bufs_queue++;
> +     } else if (filled == 1) {
> +             rc = h_add_logical_lan_buffer(vdev->unit_address,
> +                                           descs[0].desc);
> +             adapter->hcall_stats.add_buf++;
> +     } else {
> +             rc = h_add_logical_lan_buffers(vdev->unit_address,
> +                                            descs[0].desc, descs[1].desc,
> +                                            descs[2].desc, descs[3].desc,
> +                                            descs[4].desc, descs[5].desc,
> +                                            descs[6].desc, descs[7].desc);
> +             adapter->hcall_stats.add_bufs++;
> +     }

[Severity: Low]
IBMVETH_MAX_RX_PER_HCALL changes from 8 to 12 in this patch, which resizes
descs[] and correlators[] in ibmveth_replenish_buffer_pool() to 12, but this
legacy branch still hands only descs[0..7] to the 8-descriptor
h_add_logical_lan_buffers(), and the caller then accounts all "filled"
descriptors as posted (consumer_index advanced, pool->available
incremented).

The combination needed to hit that (multi_queue == 0 with
rx_buffers_per_hcall > 8) does not appear reachable, since probe now sets
IBMVETH_MAX_RX_REGULAR (8) or 1, and later in the series
ibmveth_apply_mq_fallback() clamps to IBMVETH_MAX_RX_REGULAR.

Would a clamp on filled here, or a BUILD_BUG_ON tying the legacy branch to
IBMVETH_MAX_RX_REGULAR, be worth adding so the arity contract is enforced
rather than implied?

Also, the 12-address MQ packing (ioba[4], ioba[5]) cannot be exercised at
this commit, since probe caps the batch at 8:

> -             adapter->rx_buffers_per_hcall = IBMVETH_MAX_RX_PER_HCALL;
> +             adapter->rx_buffers_per_hcall = IBMVETH_MAX_RX_REGULAR;

[ ... ]

> +/* Replenish the buffers for a pool.
> + * Caller must hold the per-queue replenish_lock. Do not printk here —
> + * netconsole on the same device can re-enter replenish_task.
> + */
> +static int ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter,
> +                                      struct ibmveth_buff_pool *pool,
> +                                      int queue_index,
> +                                      struct ibmveth_replenish_fail *fail)
>  {

[Severity: High]
The "do not printk here" invariant is documented for this function, but can
code called from inside the critical section still printk indirectly?

dma_map_single() in the fill loop goes through iommu_map_page() on pseries,
and the driver passes attrs = 0:

arch/powerpc/kernel/iommu.c:iommu_map_page() {
        ...
                if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit())  {
                        dev_info(dev, "iommu_alloc failed, tbl %p vaddr %p 
npages %d\n", ...);
        ...
}

That is exactly the condition replenish hits (TCE table or CMO entitlement
exhausted under pressure). With netconsole on this device, the printk
recurses:

        printk -> write_msg -> netpoll_send_udp -> __netpoll_send_skb
          -> netpoll_poll_dev -> ibmveth_poll_controller
            -> ibmveth_replenish_task -> spin_lock_irqsave(&rxq->replenish_lock)

Since the lock is already held on this CPU with interrupts disabled, does
this self-deadlock? The same re-entry also reaches
ibmveth_rxq_harvest_buffer() -> ibmveth_remove_buffer_from_pool(), which now
takes the same lock.

[ ... ]

> @@ -883,21 +971,85 @@ static void ibmveth_update_rx_no_buffer(struct 
> ibmveth_adapter *adapter)
>  }
>  
>  /* replenish routine */
> -static void ibmveth_replenish_task(struct ibmveth_adapter *adapter)
> +static void ibmveth_replenish_task(struct ibmveth_adapter *adapter,
> +                                int queue_index)
>  {
> -     int i;
> +     struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
> +     struct ibmveth_replenish_fail fail = {};
> +     unsigned long flags;
> +     int i, rc;
> +     int need_reset = 0;
> +     int batch_fallback = 0;
> +     int hcall_fail = 0;
> +
> +     if (queue_index >= adapter->num_rx_queues) {
> +             netdev_dbg(adapter->netdev,
> +                        "Skipping replenish for freed queue %d 
> (num_queues=%d)\n",
> +                        queue_index, adapter->num_rx_queues);
> +             return;
> +     }
>  
>       adapter->replenish_task_cycles++;

[Severity: Medium]
Several adapter-global fields are updated while holding only a per-queue
lock, or none at all. replenish_task_cycles++ here is outside the lock, and
inside ibmveth_replenish_buffer_pool() / ibmveth_add_logical_lan_buffers()
the following are adapter-wide:

        batch = adapter->rx_buffers_per_hcall;
        ...
        adapter->rx_buffers_per_hcall = 1;
        adapter->hcall_stats.add_buf/add_bufs/add_bufs_queue++;
        adapter->replenish_add_buff_success/failure, replenish_no_mem

Two queues replenishing on different CPUs hold different replenish_lock
instances, so the intersection of locks protecting these is empty. Can this
lose or tear the ethtool-visible hcall_stats and replenish_* counters? Today
it is already racy between ibmveth_poll_controller() and NAPI for
replenish_task_cycles++, which is incremented before the lock is taken.

>  
> -     for (i = (IBMVETH_NUM_BUFF_POOLS - 1); i >= 0; i--) {
> -             struct ibmveth_buff_pool *pool = &adapter->rx_buff_pool[0][i];
> +     spin_lock_irqsave(&rxq->replenish_lock, flags);
>  
> -             if (pool->active &&
> -                 (atomic_read(&pool->available) < pool->threshold))
> -                     ibmveth_replenish_buffer_pool(adapter, pool);
> +     for (i = (IBMVETH_NUM_BUFF_POOLS - 1); i >= 0; i--) {
> +             struct ibmveth_buff_pool *pool =
> +                     &adapter->rx_buff_pool[queue_index][i];
> +
> +             if (pool->active && pool->free_map &&
> +                 (atomic_read(&pool->available) < pool->threshold)) {
> +                     rc = ibmveth_replenish_buffer_pool(adapter, pool,
> +                                                        queue_index, &fail);

[Severity: High]
How long can interrupts stay disabled here?

The lock is taken with spin_lock_irqsave() around the whole five-pool walk,
and ibmveth_replenish_buffer_pool() loops "while (remaining > 0)" doing
netdev_alloc_skb() plus dma_map_single() per buffer, then a hypercall per
batch. With pool_active { 1, 1, 0, 0, 1 } and pool_count { 256, 512, 256,
256, 256 }, a cold start posts roughly 1024 buffers, including 256 of
buff_size 64 KB.

ibmveth_open() hits this deterministically:

        for (i = 0; i < ibmveth_get_num_rx_queues(adapter); i++)
                ibmveth_replenish_task(adapter, i);

So every ifup does on the order of a thousand GFP_ATOMIC allocations
(some high order), a thousand IOMMU mappings and ~128 hypercalls with local
interrupts off. Previously this ran with interrupts enabled and no lock.

Can that trip the hard lockup watchdog or produce RCU stalls, and does
disabling BH/IRQ progress here also make the GFP_ATOMIC allocations more
likely to fail exactly when memory is tight? Since
ibmveth_remove_buffer_from_pool() now takes the same lock per received
packet, the NAPI consumer is also blocked for the whole region.

The changelog notes "Defer adapter-global counter atomics and irqsave
critical-section shorten to cover follow-up" - would it be better to shorten
the region in this patch, since as merged the region exists?

[ ... ]

> +out_unlock:
>       ibmveth_update_rx_no_buffer(adapter);

[Severity: Low]
This helper is now called per queue, but ibmveth_update_rx_no_buffer() still
reads queue 0's buffer-list page:

        __be64 *p = adapter->buffer_list_addr[0] + 4096 - 8;

Each RX queue has its own buffer-list page whose last 8 bytes hold that
queue's no-buffer drop count, so rx_no_buffer would under-report queues
1..N. This is addressed later in the series, where the helper takes
queue_index and uses buffer_list_addr[queue_index], and MQ is only turned on
by a later patch, so no wrong-page read looks reachable at this commit.
Would it be cleaner to make the helper queue-aware in the same patch that
makes its caller queue-aware?

> +
> +     spin_unlock_irqrestore(&rxq->replenish_lock, flags);
> +
> +     /* Log and schedule reset only after dropping replenish_lock. */
> +     if (need_reset == IBMVETH_REPLENISH_RESET_MAP) {
> +             netdev_info(adapter->netdev,
> +                         "Invalid RX free_map entry on queue %d, reset\n",
> +                         queue_index);
> +             schedule_work(&adapter->work);

[Severity: Low]
This isn't a bug introduced by this patch, but the new schedule_work() sites
add more ways to arm adapter->work from NAPI context, and ibmveth_remove()
still cancels before it stops the device:

        cancel_work_sync(&adapter->work);
        ...
        unregister_netdev(netdev);
        free_netdev(netdev);

RX activity between cancel_work_sync() and the ndo_close() done by
unregister_netdev() can re-queue the work, which may then run after
free_netdev(). The ordering is swapped later in this series
(unregister_netdev() first, then cancel_work_sync()), with a comment
describing exactly this window.

> +     } else if (need_reset == IBMVETH_REPLENISH_RESET_MQ) {
> +             dev_err_ratelimited(&adapter->netdev->dev,
> +                                 "MQ buffer add H_FUNCTION (q=%d, batch=%u), 
> reset\n",
> +                                 queue_index, fail.batch);
> +             schedule_work(&adapter->work);
> +     }
> +
> +     if (batch_fallback)
> +             dev_warn_ratelimited(&adapter->netdev->dev,
> +                                  "Legacy batch add H_FUNCTION (batch=%u), 
> fallback\n",
> +                                  fail.batch);
> +
> +     if (hcall_fail)
> +             dev_warn_ratelimited(&adapter->netdev->dev,
> +                                  "RX %s failed: filled=%u, rc=%lu, 
> batch=%u\n",
> +                                  adapter->multi_queue ?
> +                                  "h_add_logical_lan_buffers_queue" :
> +                                  (fail.batch == 1 ?
> +                                   "h_add_logical_lan_buffer" :
> +                                   "h_add_logical_lan_buffers"),
> +                                  fail.filled, fail.lpar_rc, fail.batch);
>  }

[Severity: Low]
Does this pick the wrong hcall name? The dispatch in
ibmveth_add_logical_lan_buffers() selects on filled, not batch:

        } else if (filled == 1) {
                rc = h_add_logical_lan_buffer(vdev->unit_address, 
descs[0].desc);

If batch is 8 and the fill loop breaks early after one buffer
(netdev_alloc_skb() or dma_mapping_error() failure), the failing call was
h_add_logical_lan_buffer(), but this prints h_add_logical_lan_buffers.
Should the selector be fail.filled == 1?

Separately, one "struct ibmveth_replenish_fail fail" and the
batch_fallback/hcall_fail flags are shared by all five pool iterations, so a
later pool's failure overwrites an earlier pool's rc/filled/batch, and one
log line can mix values from two pools.

>  
>  /* empty and free ana buffer pool - also used to do cleanup in error paths */
> @@ -932,6 +1084,14 @@ static void ibmveth_free_buffer_pool(struct 
> ibmveth_adapter *adapter,
>               kfree(pool->skbuff);
>               pool->skbuff = NULL;
>       }
> +
> +     /*
> +      * Keep probe/sysfs geometry (active, size, buff_size, threshold).
> +      * Clearing active here was a v3 ifdown/up regression: open skips
> +      * !active pools, so reopen posted no RX buffers (TX OK, ARP/RX
> +      * dead) at any queue count, including RX=8 with no -L.
> +      */
> +     atomic_set(&pool->available, 0);
>  }

[Severity: Low]
This comment describes a regression that does not exist in the tree: no
version of ibmveth_free_buffer_pool() ever cleared pool->active, and this
hunk is additive only. The same applies to the new comment in
ibmveth_rxq_correlator_valid() ("free_buffer_pool() clears skbuff but used
to leave size/active set").

References to "v3" and to lab options like "RX=8 with no -L" aren't
meaningful to a reader of mainline. Could the comments be rewritten in terms
of the code as it stands, and could the functional change here
(atomic_set(&pool->available, 0)) be mentioned in the commit message? Since
ibmveth_alloc_buffer_pool() already zeroes available on reallocation, it is
hard to tell from the patch whether this is a fix or a no-op.

[Severity: High]
This is a pre-existing race, but the new locking makes it look handled while
leaving it open. ibmveth_free_buffer_pool() kfrees pool->free_map,
pool->skbuff and pool->dma_addr without taking the per-queue
replenish_lock, while ibmveth_replenish_task() checks and uses those arrays
under that lock:

                if (pool->active && pool->free_map &&
                    (atomic_read(&pool->available) < pool->threshold)) {

So the check is a TOCTOU: replenish touches the pool arrays with
{rxq->replenish_lock} held, the free path touches and frees them with no
lock, giving an empty lock intersection.

Reachable through a direct close, which does not run
netpoll_poll_disable():

CPU0 (printk -> netconsole)
  ibmveth_poll_controller() -> ibmveth_replenish_task(adapter, 0)
    passes "pool->free_map != NULL", then dereferences
    pool->free_map / pool->skbuff / pool->dma_addr

CPU1 (ethtool -K / veth_pool_store / MTU change)
  ibmveth_close() -> ibmveth_free_buffer_pools()
    -> kfree(pool->free_map)

Can that produce a use-after-free of those arrays, and post freed buffers to
the hypervisor? Should ibmveth_free_buffer_pool() take replenish_lock (or
should the pool teardown otherwise be serialized against replenish)?

[ ... ]

> +static bool ibmveth_rxq_correlator_valid(struct ibmveth_adapter *adapter,
> +                                      int queue_index, u64 correlator)
> +{
> +     unsigned int pool = correlator >> 32;
> +     unsigned int index = correlator & 0xffffffffUL;
> +     struct ibmveth_buff_pool *bpool;
> +
> +     if (pool >= IBMVETH_NUM_BUFF_POOLS)
> +             return false;
> +
> +     bpool = &adapter->rx_buff_pool[queue_index][pool];
> +
> +     /* init_buffer_pool() sets size for inactive pools; free_buffer_pool()
> +      * clears skbuff but used to leave size/active set. Require a live
> +      * pool with allocated arrays before indexing.
> +      */
> +     if (!bpool->active || !bpool->skbuff || !bpool->free_map)
> +             return false;
> +
> +     return index < bpool->size;
> +}

[Severity: Medium]
Do the in-tree KUnit cases still pass with the added
"!bpool->free_map" precondition? Neither
ibmveth_remove_buffer_from_pool_test() nor ibmveth_rxq_get_buffer_test()
allocates free_map - both only kunit_kcalloc() pool->skbuff.

That makes validation fail before the skbuff[index] NULL check, so the two
-EFAULT expectations should now receive -EINVAL:

        correlator = (u64)0 | 0;
        pool->skbuff[0] = NULL;
        KUNIT_EXPECT_EQ(test, -EFAULT,
                        ibmveth_remove_buffer_from_pool(adapter,
                                                        correlator, 0, false));

and the positive expectation in ibmveth_rxq_get_buffer_test() should receive
NULL:

        pool->skbuff[0] = skb;
        adapter->rx_queue[0].queue_addr[0].correlator = (u64)0 << 32 | 0;
        KUNIT_EXPECT_PTR_EQ(test, skb, ibmveth_rxq_get_buffer(adapter, 0));

The index-out-of-range expectations also pass vacuously now, so the bound
check they were written for is no longer covered. The patch updates the
kernel-doc of both tests but not the fixtures - should the fixtures also
allocate free_map?

[ ... ]

>   * Return:
>   * * %0       - success
>   * * %-EINVAL - correlator maps to pool or index out of range
>   * * %-EFAULT - pool and index map to null skb
>   */
>  static int ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter,
> -                                        u64 correlator, bool reuse)
> +                                        u64 correlator, int queue_index,
> +                                        bool reuse)
>  {

[Severity: Low]
The documented meaning of -EINVAL is now incomplete: via
ibmveth_rxq_correlator_valid() this function also returns -EINVAL for an
inactive pool and for a pool whose skbuff/free_map arrays are not
allocated, which is a different condition from "out of range". The new
ibmveth_rxq_harvest_buffer() kernel-doc reinforces the narrow reading by
telling callers that -EINVAL/-EFAULT mean a "corrupt slot" safe to skip.
Could the return-code documentation be extended to cover the not-live-pool
case?

> +     struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
>       unsigned int pool  = correlator >> 32;
>       unsigned int index = correlator & 0xffffffffUL;
>       unsigned int free_index;
>       struct sk_buff *skb;
> +     unsigned long flags;
> +     int rc = 0;
>  
> -     if (WARN_ON(pool >= IBMVETH_NUM_BUFF_POOLS) ||
> -         WARN_ON(index >= adapter->rx_buff_pool[0][pool].size)) {
> -             schedule_work(&adapter->work);
> -             return -EINVAL;
> +     spin_lock_irqsave(&rxq->replenish_lock, flags);
> +
> +     if (!ibmveth_rxq_correlator_valid(adapter, queue_index, correlator)) {
> +             rc = -EINVAL;
> +             goto out_unlock;
>       }
>  
> -     skb = adapter->rx_buff_pool[0][pool].skbuff[index];
> -     if (WARN_ON(!skb)) {
> -             schedule_work(&adapter->work);
> -             return -EFAULT;
> +     skb = adapter->rx_buff_pool[queue_index][pool].skbuff[index];
> +     if (!skb) {
> +             rc = -EFAULT;
> +             goto out_unlock;
>       }

[ ... ]

>  static int ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter,
> -                                   bool reuse)
> +                                   int queue_index, bool reuse)
>  {
> +     struct ibmveth_rx_q *rxq = &adapter->rx_queue[queue_index];
>       u64 cor;
>       int rc;
>  
> -     struct ibmveth_rx_q *rxq = &adapter->rx_queue[0];
> -
>       cor = rxq->queue_addr[rxq->index].correlator;
> -     rc = ibmveth_remove_buffer_from_pool(adapter, cor, reuse);
> -     if (unlikely(rc))
> +     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;
> -
> -     if (++adapter->rx_queue[0].index == adapter->rx_queue[0].num_slots) {
> -             adapter->rx_queue[0].index = 0;
> -             adapter->rx_queue[0].toggle = !adapter->rx_queue[0].toggle;
>       }

[Severity: Medium]
The reset that previously repaired this state is gone -
ibmveth_remove_buffer_from_pool() no longer calls
schedule_work(&adapter->work) for -EINVAL/-EFAULT, and here the ring is just
advanced. Does that permanently retire the buffer the hypervisor already
consumed?

On this path free_map does not regain the index, the
atomic_dec(&pool->available) is skipped, and pool->skbuff[index] plus its
DMA mapping stay owned by nobody.

Since replenish is gated on available < threshold (size * 7 / 8):

                if (pool->active && pool->free_map &&
                    (atomic_read(&pool->available) < pool->threshold)) {

after roughly size/8 such events, does replenish stop for that pool
permanently, with the link still up and no recovery short of ifdown/up?

The poll "recycling invalid buffer" path and ibmveth_drain_rx_queue() both
swallow these errors without escalating:

        /* -EINVAL/-EFAULT already advanced past the slot. */
        if (rc == -EINVAL || rc == -EFAULT) {
                drained++;
                continue;
        }

At the end of the series only the get_buffer path escalates via
ibmveth_poll_skip_bad_correlator(), and that code's own comment ("Residual
stale slot after resize") suggests stale correlators do occur. Should the
recycle and drain paths also reconcile available or schedule a reset?

[ ... ]

> @@ -2127,34 +2340,41 @@ static void ibmveth_rx_csum_helper(struct sk_buff 
> *skb,
> -                     skb = ibmveth_rxq_get_buffer(adapter);
> +                     skb = ibmveth_rxq_get_buffer(adapter, queue_index);
>                       if (unlikely(!skb))
>                               break;

[Severity: Low]
This isn't a bug introduced by this patch, but breaking out here leaves
rxq->index and rxq->toggle untouched, so the descriptor stays pending. The
tail of ibmveth_poll() then completes NAPI, re-enables the IRQ, and:

        if (ibmveth_rxq_pending_buffer(adapter, queue_index) &&
            napi_schedule(napi)) {
                rc = ibmveth_disable_irq(adapter, queue_index);
                WARN_ON(rc);
                goto restart_poll;
        }

Can that spin on the same slot in softirq context when a VALID descriptor
has a correlator that fails validation, or whose pool->skbuff[index] is
NULL? The changelog says this lands with the poll harden patch ("Bad
queue_index poll path: napi_complete before return lands with poll
harden"), and later in the series
ibmveth_poll_skip_bad_correlator() schedules a reset and advances the ring.

[ ... ]

> diff --git a/drivers/net/ethernet/ibm/ibmveth.h 
> b/drivers/net/ethernet/ibm/ibmveth.h
> index fae3473cc4985..d02444d5b3b89 100644
> --- a/drivers/net/ethernet/ibm/ibmveth.h
> +++ b/drivers/net/ethernet/ibm/ibmveth.h
> @@ -259,7 +261,8 @@ static inline long h_illan_attributes(unsigned long 
> unit_address,
>  #define IBMVETH_DEFAULT_QUEUES 8U
>  #define IBMVETH_MAX_RX_QUEUES 1U
>  #define IBMVETH_DEFAULT_RX_QUEUES 1U
> -#define IBMVETH_MAX_RX_PER_HCALL 8U
> +#define IBMVETH_MAX_RX_REGULAR 8U
> +#define IBMVETH_MAX_RX_PER_HCALL 12U

The change of IBMVETH_MAX_RX_PER_HCALL from 8 to 12 is described in the
commit message only as "up to 12 buffers" for the MQ hcall - could the
commit message also state that the existing constant is being redefined, so
that the descs[]/correlators[] resize in ibmveth_replenish_buffer_pool() and
the new IBMVETH_MAX_RX_REGULAR use in probe are easy to follow?

> @@ -301,6 +304,7 @@ struct ibmveth_rx_q {
>      dma_addr_t queue_dma;
>      u32        queue_len;
>      struct ibmveth_rx_q_entry *queue_addr;
> +     spinlock_t      replenish_lock; /* per-queue buffer replenish */
>  };

This isn't a bug, but the new member uses tabs while the surrounding members
in this struct use the older space-based alignment - was the mixed style
intentional?

Reply via email to