Queue 0 and subordinate RX queues use different interrupt control
interfaces in PHYP:

  - queue 0: h_vio_signal() after h_register_logical_lan()
  - queue N: H_VIOCTL against the queue's mapped hwirq

The current code is single-queue oriented and cannot safely scale to
multiple RX queues in poll completion and open/close IRQ setup.

Introduce queue-indexed interrupt helpers and wire them into
open()/close()/poll()/interrupt in the same patch:

  ibmveth_toggle_irq() / enable_irq() / disable_irq()
  ibmveth_setup_rx_interrupts() / ibmveth_cleanup_rx_interrupts()
  ibmveth_schedule_rx_queue()

These helpers centralize queue0-vs-subordinate dispatch.
request_irq() uses &adapter->napi[i] as the per-queue cookie so the
handler can resolve the queue index.

Add ibmveth_dispose_subordinate_irq_mapping() and a bulk 1..N
variant; queue 0 keeps netdev->irq and its mapping is left
alone. The interrupt handler returns IRQ_NONE for an
out-of-range queue index instead of warning.

Move napi_enable() into setup_rx_interrupts(). It still runs only
after LAN registration and buffer-pool allocation, so everything
ibmveth_poll() touches exists, but now after request_irq() rather than
just before it: PHYP delivery is still masked at that point. In this
single-queue tree, setup does not yet unmask PHYP; schedule_rx_queue()
masks queue 0 and schedules NAPI, and ibmveth_poll() is what unmasks
it on completion. That order matches the later scale-up rule (NAPI
live before PHYP unmask), not an inverted window relative to it.

Factor process-context RX kicks (open, resume, pool sysfs)
into ibmveth_schedule_rx_queue(); keep ibmveth_interrupt() as a thin
IRQ-only wrapper.

cleanup_rx_interrupts() masks PHYP and synchronizes IRQs before
napi_disable, waits for the poll tail, then remasks and
synchronizes again before free_irq. napi_disable waits for SCHED. The
synchronize_net() after it covers enable_irq after complete, so
remask/free_irq cannot race that tail, and close() needs no wait of
its own. Close then proceeds to h_free_logical_lan(): free_irq
before free_lan is intentional once PHYP delivery is masked.
close() now runs netif_tx_disable() first; net-next ran it after
napi_disable() and synchronize_net(). Its h_free_logical_lan
failure message is shortened.

rx_irq_setup gates whether cleanup walks IRQ/NAPI state. close()
remains gated on adapter->opened from commit 95811c873d1b ("ibmveth:
do not close twice after a failed reopen"); this patch does not
introduce adapter->opened or change that gate.

schedule_rx_queue() masks PHYP only when napi_schedule_prep() succeeds.
Masking on prep failure can race a completing poll that already
re-enabled PHYP and leave NAPI idle with the queue masked (TX OK, RX
stalled until reload). Teardown storm control stays on STOP
(ibmveth_disable_irq() + synchronize_irq() before napi_disable).
Poll still enables after complete; cleanup's wait covers that tail
versus free_irq. The RX poll helper patch adds the stopping check.

IRQ helpers return 0 or negative errno only (never raw H_* to
ethtool/resize). On subordinate queues (H_VIOCTL), H_PARAMETER on
disable is folded to success (idempotent mask). Queue 0 uses
h_vio_signal() and any failure is -EIO. On enable, H_PARAMETER does
not mean already enabled (enabling an enabled interrupt succeeds); it
means the interrupt number is out of range for the unit address. Keep
it an error so a missed LISN stays visible to poll/resize recovery.
A failed h_vio_signal() in poll or the interrupt handler is now
reported by a rate-limited dev_err() in ibmveth_toggle_irq() instead
of WARN_ON(); poll still schedules a reset on an enable failure.

Runtime remains single-queue (num_rx_queues is still 1).

Signed-off-by: Mingming Cao <[email protected]>
Reviewed-by: Dave Marquardt <[email protected]>
Tested-by: Shaik Abdulla <[email protected]>
---
Changes in v8:
- teardown: synchronize_net() after napi_disable(), then remask and
  free_irq()
- rebased onto net-next's poll tail (mask then schedule, no
  restart_poll)
- close() keeps net-next's order and samples the no_buffer counter
  after h_free_logical_lan()
- ibmveth_schedule_rx_queue() returns void
- commit message: the H_PARAMETER fold applies to the subordinate
  H_VIOCTL disable only

Dropped from this series (merged fixes own them):
- commit 8d9ecac6b96e: poll_controller() netpoll kick (ndo removed)
- commit 95811c873d1b: adapter->opened close gate (in base; not re-added)
- commit 3f872b34368c: close/request_irq synchronize_net() hunks; helpers
  carry the close wait; request_irq fails before napi_enable
- commit af0524bf4ce1: setup-fail inline h_free; out_free_buffer_pools
  already calls h_free_logical_lan()

Changes in v7:
- reword schedule_rx_queue() kdoc: out-of-range is
  WARN_ON, then false
- commit message: remask only catches a re-arm that lands
  before it; name harvest before h_free and synchronize_net()
  after teardown
- noted: no Fixes: peel of the opened-gate hang; stays in
  this patch, not the unwind standalone

Changes in v6:
- setup_rx enable-fail remasks queues 0..i, including the one that
  failed (the rollback used while (--i) and left queue i unmasked
  across napi_disable); err_disable_napi remasks after napi_disable
- drop WARN_ON() on enable/disable_irq() returns (schedule_rx and
  poll); the helper already logs the hcall rc, and rate-limit that
  print
- reword schedule_rx_queue() kdoc: true means NAPI was scheduled and
  the mask attempted; a failed disable_irq() does not change the
  return, and an out-of-range qindex returns false
- noted: pool-fail without h_free predates this; the opened gate
  also drops the accidental h_free a later ndo_stop used to give.
  P06/P07 close it
- noted: set_channels() IFF_UP vs opened TX LTB window closes in P14/P15
- noted: poll() takes queue_index in P08; range check / skip helpers
  in P09

Changes in v5:
- Remask+sync after napi_disable in cleanup (in-flight poll can re-arm)
- Interrupt: quiet IRQ_NONE on out-of-range qindex (no WARN storm)
- Opened / rx_irq_setup gate cleanup so close after a failed open
  cannot napi_disable / free_irq without a prior enable/request; set
  opened on successful open; rx_irq_setup only on full setup success
- H_PARAMETER fold disable-only; enable stays error (stuck-masked visible)
- IRQ helpers return 0 / negative errno only (never raw H_* to ethtool)
- Decision: keep open IRQ order request_irq -> napi_enable while PHYP
  stays masked until schedule/enable (coherent with later scale-up)
- schedule_rx_queue returns bool (napi_schedule_prep success)
- Keep mask-only-on-prep-success (no else-mask; avoids idle+masked race)
- Call out free_irq-before-free_lan as intentional once PHYP is masked
- Poll re-arm during teardown lands with the RX poll helper patch
  (not claimed here)
- synchronize_net() after RX IRQ/NAPI teardown in close
- Add subordinate IRQ dispose helpers (per-queue + bulk 1..N; bound to MAX)

Changes in v4:
- Include irq.h / irqdomain.h with first irq_dispose_mapping() use.
- Introduce IRQ helpers in the same patch that wires open/close/poll
  callers, instead of leaving unused statics.
- Factor process-context RX kicks into ibmveth_schedule_rx_queue();
  keep ibmveth_interrupt() as the IRQ-only wrapper.
- On cleanup, mask PHYP and synchronize_irq before napi_disable
  (storm-safety; not fully behavior-preserving vs classic close).
- Leave queue_irq[0] set after cleanup (queue 0 uses netdev->irq;
  next open reuses it). Only subordinate virqs are disposed.

 drivers/net/ethernet/ibm/ibmveth.c | 404 +++++++++++++++++++++++++----
 drivers/net/ethernet/ibm/ibmveth.h |   2 +
 2 files changed, 357 insertions(+), 49 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c 
b/drivers/net/ethernet/ibm/ibmveth.c
index 31fad7713dc7..62e6c9a9f5e9 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -21,6 +21,8 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/mm.h>
 #include <linux/pm.h>
 #include <linux/ethtool.h>
@@ -347,6 +349,325 @@ ibmveth_cleanup_rx_resources(struct ibmveth_adapter 
*adapter)
        }
 }
 
+/**
+ * ibmveth_toggle_irq - Common helper to enable/disable queue interrupts
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Index of the queue (0 for primary, 1+ for subordinate)
+ * @enable: true to enable, false to disable
+ *
+ * For queue 0 (primary), uses h_vio_signal() as it's registered via
+ * h_register_logical_lan(). For subordinate queues (1+), uses H_VIOCTL
+ * with H_ENABLE/DISABLE_VIO_INTERRUPT for per-queue interrupt control.
+ *
+ * Return: 0 on success, negative errno on failure (never raw H_*).
+ */
+static int
+ibmveth_toggle_irq(struct ibmveth_adapter *adapter, int queue_index,
+                  bool enable)
+{
+       unsigned long h_rc;
+       unsigned long irq = adapter->queue_irq[queue_index];
+       const char *action = enable ? "enable" : "disable";
+
+       if (queue_index == 0) {
+               /* Primary queue: use h_vio_signal() */
+               h_rc = h_vio_signal(adapter->vdev->unit_address,
+                                   enable ? VIO_IRQ_ENABLE : VIO_IRQ_DISABLE);
+       } else {
+               /* Subordinate queues: use H_VIOCTL with hardware IRQ */
+               struct irq_data *irq_data = irq_get_irq_data(irq);
+               irq_hw_number_t hwirq;
+               u64 vioctl_cmd = enable ? H_ENABLE_VIO_INTERRUPT :
+                       H_DISABLE_VIO_INTERRUPT;
+
+               if (!irq_data) {
+                       netdev_err(adapter->netdev,
+                                  "Failed to get IRQ data for queue %d 
(virq=%lu)\n",
+                                  queue_index, irq);
+                       return -EINVAL;
+               }
+
+               hwirq = irqd_to_hwirq(irq_data);
+               h_rc = plpar_hcall_norets(H_VIOCTL,
+                                         adapter->vdev->unit_address,
+                                         vioctl_cmd,
+                                         hwirq, 0, 0);
+
+               /*
+                * Fold H_PARAMETER only on disable (idempotent mask). On
+                * enable it is not already-enabled: a trivial enable
+                * returns success. H_PARAMETER means the interrupt
+                * number was not in the valid range for that unit
+                * address. Keep that an error so a missed LISN stays
+                * visible to poll/resize recovery.
+                */
+               if (h_rc == H_PARAMETER && !enable) {
+                       dev_warn_ratelimited(&adapter->netdev->dev,
+                                            "H_VIOCTL %s IRQ returned 
H_PARAMETER for queue %d (hwirq=%lu)\n",
+                                            action, queue_index, hwirq);
+                       return 0;
+               }
+       }
+
+       if (h_rc) {
+               dev_err_ratelimited(&adapter->netdev->dev,
+                                   "Failed to %s IRQ for queue %d, rc=0x%lx\n",
+                                   action, queue_index, h_rc);
+               return -EIO;
+       }
+       return 0;
+}
+
+/**
+ * ibmveth_disable_irq - Disable interrupt for a specific queue
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Index of the queue (0 for primary, 1+ for subordinate)
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int
+ibmveth_disable_irq(struct ibmveth_adapter *adapter, int queue_index)
+{
+       return ibmveth_toggle_irq(adapter, queue_index, false);
+}
+
+/**
+ * ibmveth_enable_irq - Enable interrupt for a specific queue
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Index of the queue (0 for primary, 1+ for subordinate)
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int
+ibmveth_enable_irq(struct ibmveth_adapter *adapter, int queue_index)
+{
+       return ibmveth_toggle_irq(adapter, queue_index, true);
+}
+
+/**
+ * ibmveth_dispose_subordinate_irq_mapping - Drop one subordinate virq mapping
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: RX queue index (1..N)
+ *
+ * Subordinate queues get mappings from irq_create_mapping() during PHYP
+ * registration. Queue 0 uses netdev->irq from device tree and is left alone.
+ *
+ * Bound against IBMVETH_MAX_RX_QUEUES, not num_rx_queues: a caller may
+ * dispose a queue that is no longer in the published live set but still
+ * owns a virq in queue_irq[]. Contrast with the bulk helper, which only
+ * walks 1..num_rx_queues-1 (close / open-fail cleanup of the live set).
+ *
+ * Linux virq lifetime is owned by interrupt cleanup helpers. Call this only
+ * after free_irq() when a handler was installed, or from registration failure
+ * cleanup before request_irq().
+ */
+static void
+ibmveth_dispose_subordinate_irq_mapping(struct ibmveth_adapter *adapter,
+                                       int queue_idx)
+{
+       if (queue_idx <= 0 || queue_idx >= IBMVETH_MAX_RX_QUEUES)
+               return;
+
+       if (adapter->queue_irq[queue_idx]) {
+               irq_dispose_mapping(adapter->queue_irq[queue_idx]);
+               adapter->queue_irq[queue_idx] = 0;
+       }
+}
+
+/**
+ * ibmveth_dispose_subordinate_irq_mappings - Drop virq mappings for queues 
1..N
+ * @adapter: ibmveth adapter structure
+ *
+ * Bulk helper for close / open-fail cleanup of the published live set
+ * (queues 1..num_rx_queues-1). Paths that need a retired or not-yet-published
+ * queue must call ibmveth_dispose_subordinate_irq_mapping() directly.
+ */
+static void
+ibmveth_dispose_subordinate_irq_mappings(struct ibmveth_adapter *adapter)
+{
+       int i;
+
+       for (i = 1; i < adapter->num_rx_queues; i++)
+               ibmveth_dispose_subordinate_irq_mapping(adapter, i);
+}
+
+/**
+ * ibmveth_setup_rx_interrupts - Register IRQs and enable NAPI
+ * @adapter: ibmveth adapter structure
+ *
+ * Registers interrupt handlers for all RX queues, enables NAPI, then
+ * enables hypervisor interrupt delivery for multi-queue mode after
+ * every queue has a Linux handler installed. For multi-queue open the
+ * caller should replenish RX buffers before this helper so traffic
+ * during open is not dropped (PHYP only interrupts after a successful
+ * enqueue, which needs buffers). Single-queue open leaves PHYP masked
+ * here and kicks NAPI afterward (classic path: first poll posts then
+ * enables).
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_setup_rx_interrupts(struct ibmveth_adapter *adapter)
+{
+       struct net_device *netdev = adapter->netdev;
+       int i, rc, num = adapter->num_rx_queues;
+
+       for (i = 0; i < num; i++) {
+               if (!adapter->queue_irq[i]) {
+                       netdev_err(netdev, "queue %d has invalid IRQ (0)\n", i);
+                       rc = -EINVAL;
+                       goto err_free_irqs;
+               }
+
+               rc = request_irq(adapter->queue_irq[i], ibmveth_interrupt,
+                                0, netdev->name, &adapter->napi[i]);
+               if (rc) {
+                       netdev_err(netdev,
+                                  "request_irq() failed for irq 0x%x queue %d: 
%d\n",
+                                  adapter->queue_irq[i], i, rc);
+                       goto err_free_irqs;
+               }
+       }
+
+       for (i = 0; i < num; i++)
+               napi_enable(&adapter->napi[i]);
+
+       if (adapter->multi_queue && num > 1) {
+               for (i = 0; i < num; i++) {
+                       rc = ibmveth_enable_irq(adapter, i);
+                       if (rc) {
+                               netdev_err(netdev,
+                                          "Failed to enable IRQ for queue %d, 
rc=%d\n",
+                                          i, rc);
+                               for (; i >= 0; i--) {
+                                       ibmveth_disable_irq(adapter, i);
+                                       synchronize_irq(adapter->queue_irq[i]);
+                               }
+                               rc = -EIO;
+                               goto err_disable_napi;
+                       }
+               }
+       }
+
+       /* Set only on full success; fail paths leave this false so a later
+        * close() / cleanup is a no-op.
+        */
+       adapter->rx_irq_setup = true;
+       return 0;
+
+err_disable_napi:
+       /* STOP: wait for the poll tail, then remask; an in-flight poll
+        * can re-arm after napi_complete_done().
+        */
+       for (i = 0; i < num; i++)
+               napi_disable(&adapter->napi[i]);
+       synchronize_net();
+       for (i = 0; i < num; i++) {
+               if (!adapter->queue_irq[i])
+                       continue;
+               ibmveth_disable_irq(adapter, i);
+               synchronize_irq(adapter->queue_irq[i]);
+       }
+       for (i = 0; i < num; i++) {
+               if (adapter->queue_irq[i])
+                       free_irq(adapter->queue_irq[i], &adapter->napi[i]);
+       }
+       goto err_dispose_mappings;
+
+err_free_irqs:
+       while (--i >= 0)
+               free_irq(adapter->queue_irq[i], &adapter->napi[i]);
+err_dispose_mappings:
+       /* Both setup failure paths own subordinate virq disposal. */
+       ibmveth_dispose_subordinate_irq_mappings(adapter);
+       return rc;
+}
+
+/**
+ * ibmveth_cleanup_rx_interrupts - Mask PHYP IRQs, stop NAPI, and free IRQs
+ * @adapter: ibmveth adapter structure
+ *
+ * Mask and synchronize each queue IRQ before napi_disable() so the handler
+ * cannot miss a PHYP mask while NAPI is already dead. napi_disable() only
+ * waits for SCHED to clear at napi_complete_done(), so wait for the poll
+ * tail after it, then remask and synchronize again before free_irq(). The
+ * remask alone does not wait for poll to return. Safe for close and for
+ * open failure after setup_rx_interrupts() already unmasked PHYP. No-op if
+ * setup never succeeded (avoids double napi_disable / free_irq after a
+ * failed close+open while IFF_UP remains set).
+ */
+static void
+ibmveth_cleanup_rx_interrupts(struct ibmveth_adapter *adapter)
+{
+       int i;
+
+       if (!adapter->rx_irq_setup)
+               return;
+
+       for (i = 0; i < adapter->num_rx_queues; i++) {
+               if (!adapter->queue_irq[i])
+                       continue;
+               ibmveth_disable_irq(adapter, i);
+               synchronize_irq(adapter->queue_irq[i]);
+       }
+
+       for (i = 0; i < adapter->num_rx_queues; i++)
+               napi_disable(&adapter->napi[i]);
+
+       synchronize_net();
+
+       for (i = 0; i < adapter->num_rx_queues; i++) {
+               if (!adapter->queue_irq[i])
+                       continue;
+               ibmveth_disable_irq(adapter, i);
+               synchronize_irq(adapter->queue_irq[i]);
+       }
+
+       for (i = 0; i < adapter->num_rx_queues; i++) {
+               if (adapter->queue_irq[i])
+                       free_irq(adapter->queue_irq[i], &adapter->napi[i]);
+       }
+
+       ibmveth_dispose_subordinate_irq_mappings(adapter);
+
+       /* Queue 0 uses netdev->irq; leave queue_irq[0] for next open. */
+       adapter->rx_irq_setup = false;
+}
+
+/**
+ * ibmveth_schedule_rx_queue - Mask PHYP IRQ and schedule NAPI for one RX queue
+ * @adapter: ibmveth adapter structure
+ * @qindex: RX queue index
+ *
+ * Shared by the IRQ handler and process-context kick sites (open, resume,
+ * pool sysfs).
+ *
+ * Does nothing if the index is out of range (WARN_ON) or
+ * napi_schedule_prep() fails (NAPI already scheduled, or disabled
+ * because the device is closed). A failed
+ * disable_irq() is logged by the helper; the queue may stay unmasked.
+ */
+static void ibmveth_schedule_rx_queue(struct ibmveth_adapter *adapter,
+                                     int qindex)
+{
+       struct napi_struct *napi = &adapter->napi[qindex];
+
+       if (WARN_ON(qindex < 0 || qindex >= adapter->num_rx_queues))
+               return;
+
+       /*
+        * Only mask PHYP when NAPI will run. Masking on prep failure can
+        * race a completing poll that already re-enabled the queue, leaving
+        * NAPI idle with the IRQ masked (TX works, RX stalls) until reload.
+        * Storm prevention on teardown remains in cleanup/disable paths.
+        */
+       if (napi_schedule_prep(napi)) {
+               /* Failure is already logged with the hcall rc by the helper. */
+               ibmveth_disable_irq(adapter, qindex);
+               __napi_schedule(napi);
+       }
+}
+
 /* setup the initial settings for a buffer pool */
 static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool,
                                     u32 pool_index, u32 pool_size,
@@ -1024,7 +1345,8 @@ static int ibmveth_open(struct net_device *netdev)
                                        adapter->rx_queue[0].queue_len;
        rxq_desc.fields.address = adapter->rx_queue[0].queue_dma;
 
-       h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
+       adapter->queue_irq[0] = netdev->irq;
+       ibmveth_disable_irq(adapter, 0);
 
        lpar_rc = ibmveth_register_logical_lan(adapter, rxq_desc, mac_address);
 
@@ -1045,26 +1367,16 @@ static int ibmveth_open(struct net_device *netdev)
        if (rc)
                goto out_free_buffer_pools;
 
-       /* NAPI can run as soon as it is enabled, from netpoll during the
-        * direct close()/open() pairs or from a direct ibmveth_interrupt()
-        * call, so enable it only once everything ibmveth_poll() touches
-        * exists.
+       /* Enables NAPI, so everything ibmveth_poll() touches must exist:
+        * netpoll during the direct close()/open() pairs, or a direct
+        * kick, can run it as soon as it is enabled.
         */
-       napi_enable(&adapter->napi[0]);
-
-       netdev_dbg(netdev, "registering irq 0x%x\n", netdev->irq);
-       rc = request_irq(netdev->irq, ibmveth_interrupt, 0, netdev->name,
-                        netdev);
-       if (rc != 0) {
-               napi_disable(&adapter->napi[0]);
-               synchronize_net();
-               netdev_err(netdev, "unable to request irq 0x%x, rc %d\n",
-                          netdev->irq, rc);
+       rc = ibmveth_setup_rx_interrupts(adapter);
+       if (rc)
                goto out_free_buffer_pools;
-       }
 
        netdev_dbg(netdev, "initial replenish cycle\n");
-       ibmveth_interrupt(netdev->irq, netdev);
+       ibmveth_schedule_rx_queue(adapter, 0);
 
        netif_tx_start_all_queues(netdev);
 
@@ -1104,30 +1416,20 @@ static int ibmveth_close(struct net_device *netdev)
 
        netdev_dbg(netdev, "close starting\n");
 
-       napi_disable(&adapter->napi[0]);
-       /* napi_disable() returns once ibmveth_poll() has called
-        * napi_complete_done(), but the poll still re-enables the
-        * interrupt and reads the RX queue after that.
-        */
-       synchronize_net();
-
        netif_tx_disable(netdev);
 
-       h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE);
+       ibmveth_cleanup_rx_interrupts(adapter);
 
+       /* Full LAN teardown (subordinates arrive with register helpers). */
        do {
                lpar_rc = h_free_logical_lan(adapter->vdev->unit_address);
        } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY));
-
        if (lpar_rc != H_SUCCESS) {
-               netdev_err(netdev, "h_free_logical_lan failed with %lx, "
-                          "continuing with close\n", lpar_rc);
+               netdev_err(adapter->netdev,
+                          "h_free_logical_lan failed with %lx, continuing\n",
+                          lpar_rc);
        }
-
-       free_irq(netdev->irq, netdev);
-
        ibmveth_update_rx_no_buffer(adapter);
-
        ibmveth_free_buffer_pools(adapter);
        ibmveth_cleanup_rx_resources(adapter);
        ibmveth_free_filter_list(adapter);
@@ -1814,7 +2116,6 @@ static int ibmveth_poll(struct napi_struct *napi, int 
budget)
                        container_of(napi, struct ibmveth_adapter, napi[0]);
        struct net_device *netdev = adapter->netdev;
        int frames_processed = 0;
-       unsigned long lpar_rc;
        u64 correlator;
        u16 mss = 0;
        int rc;
@@ -1962,18 +2263,17 @@ static int ibmveth_poll(struct napi_struct *napi, int 
budget)
        /* We think we are done - reenable interrupts,
         * then check once more to make sure we are done.
         */
-       lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_ENABLE);
-       if (WARN_ON(lpar_rc != H_SUCCESS)) {
+       rc = ibmveth_enable_irq(adapter, 0);
+       if (rc) {
                schedule_work(&adapter->work);
                goto out;
        }
 
-       /* Mask before queueing, as ibmveth_interrupt() does: a threaded
+       /* Mask before queueing, as schedule_rx_queue() does: a threaded
         * NAPI can start polling, and unmask, as soon as it is queued.
         */
        if (ibmveth_rxq_pending_buffer(adapter) && napi_schedule_prep(napi)) {
-               lpar_rc = h_vio_signal(adapter->vdev->unit_address,
-                                      VIO_IRQ_DISABLE);
+               ibmveth_disable_irq(adapter, 0);
                __napi_schedule(napi);
        }
 
@@ -1983,16 +2283,20 @@ static int ibmveth_poll(struct napi_struct *napi, int 
budget)
 
 static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance)
 {
-       struct net_device *netdev = dev_instance;
+       struct napi_struct *napi = dev_instance;
+       struct net_device *netdev = napi->dev;
        struct ibmveth_adapter *adapter = netdev_priv(netdev);
-       unsigned long lpar_rc;
+       int qindex;
 
-       if (napi_schedule_prep(&adapter->napi[0])) {
-               lpar_rc = h_vio_signal(adapter->vdev->unit_address,
-                                      VIO_IRQ_DISABLE);
-               WARN_ON(lpar_rc != H_SUCCESS);
-               __napi_schedule(&adapter->napi[0]);
-       }
+       qindex = napi - adapter->napi;
+       /*
+        * Quiet on out-of-range: teardown can leave a residual IRQ after the
+        * live count drops. Do not WARN-storm; return IRQ_NONE until free_irq.
+        */
+       if (qindex < 0 || qindex >= adapter->num_rx_queues)
+               return IRQ_NONE;
+
+       ibmveth_schedule_rx_queue(adapter, qindex);
        return IRQ_HANDLED;
 }
 
@@ -2536,8 +2840,8 @@ static ssize_t veth_pool_store(struct kobject *kobj, 
struct attribute *attr,
        }
        rtnl_unlock();
 
-       /* kick the interrupt handler to allocate/deallocate pools */
-       ibmveth_interrupt(netdev->irq, netdev);
+       /* kick RX processing to allocate/deallocate pools */
+       ibmveth_schedule_rx_queue(adapter, 0);
        return count;
 
 unlock_err:
@@ -2577,7 +2881,9 @@ static struct kobj_type ktype_veth_pool = {
 static int ibmveth_resume(struct device *dev)
 {
        struct net_device *netdev = dev_get_drvdata(dev);
-       ibmveth_interrupt(netdev->irq, netdev);
+       struct ibmveth_adapter *adapter = netdev_priv(netdev);
+
+       ibmveth_schedule_rx_queue(adapter, 0);
        return 0;
 }
 
diff --git a/drivers/net/ethernet/ibm/ibmveth.h 
b/drivers/net/ethernet/ibm/ibmveth.h
index df5a3b69a157..50be6c95457e 100644
--- a/drivers/net/ethernet/ibm/ibmveth.h
+++ b/drivers/net/ethernet/ibm/ibmveth.h
@@ -325,6 +325,8 @@ struct ibmveth_adapter {
        unsigned int queue_irq[IBMVETH_MAX_RX_QUEUES];
        int multi_queue;
        unsigned int num_rx_queues;
+       /* Lifetime: true while RX IRQ handlers / NAPI are installed. */
+       bool rx_irq_setup;
        int rx_csum;
        int large_send;
        bool is_active_trunk;
-- 
2.50.1 (Apple Git-155)


Reply via email to