Add helpers to register/deregister individual RX queues and scale
queue count up or down while the netdev stays up. Surviving queues
keep PHYP handles, buffer pools, and IRQ state across resize.

Signed-off-by: Mingming Cao <[email protected]>
Reviewed-by: Dave Marquardt <[email protected]>
---
 drivers/net/ethernet/ibm/ibmveth.c | 406 ++++++++++++++++++++++++++++-
 1 file changed, 401 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/ibm/ibmveth.c 
b/drivers/net/ethernet/ibm/ibmveth.c
index ecc472ee8f71..ac4d89a66a8d 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -589,6 +589,54 @@ ibmveth_cleanup_rx_interrupts(struct ibmveth_adapter 
*adapter)
        adapter->queue_irq[0] = 0;
 }
 
+/**
+ * ibmveth_setup_single_rx_interrupt - Setup interrupt for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to setup
+ *
+ * Registers the IRQ handler for one queue. Used during incremental
+ * scale-up when adding new RX queues; the caller enables NAPI via
+ * napi_enable() after ibmveth_enable_irq().
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_setup_single_rx_interrupt(struct ibmveth_adapter *adapter,
+                                 int queue_idx)
+{
+       struct net_device *netdev = adapter->netdev;
+       int rc;
+
+       rc = request_irq(adapter->queue_irq[queue_idx], ibmveth_interrupt,
+                        0, netdev->name, &adapter->napi[queue_idx]);
+       if (rc) {
+               netdev_err(netdev, "request_irq() failed for queue %d: %d\n",
+                          queue_idx, rc);
+               return rc;
+       }
+
+       netdev_dbg(netdev, "Setup IRQ %d for queue %d\n",
+                  adapter->queue_irq[queue_idx], queue_idx);
+       return 0;
+}
+
+/**
+ * ibmveth_cleanup_single_rx_interrupt - Cleanup interrupt for a single RX 
queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to cleanup
+ *
+ * Frees the IRQ handler for one queue. Used during incremental scale-down.
+ */
+static void
+ibmveth_cleanup_single_rx_interrupt(struct ibmveth_adapter *adapter,
+                                   int queue_idx)
+{
+       if (adapter->queue_irq[queue_idx]) {
+               free_irq(adapter->queue_irq[queue_idx], 
&adapter->napi[queue_idx]);
+               netdev_dbg(adapter->netdev, "Freed IRQ for queue %d\n", 
queue_idx);
+       }
+}
+
 /* 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,
@@ -897,18 +945,22 @@ static void ibmveth_replenish_task(struct ibmveth_adapter 
*adapter,
        unsigned long flags;
        int i;
 
-       if (queue_index >= adapter->num_rx_queues)
-               return;
-
        adapter->replenish_task_cycles++;
 
+       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;
+       }
+
        spin_lock_irqsave(&rxq->replenish_lock, flags);
 
        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 &&
+               if (pool->active && pool->free_map &&
                    (atomic_read(&pool->available) < pool->threshold))
                        ibmveth_replenish_buffer_pool(adapter, pool,
                                                      queue_index);
@@ -1080,6 +1132,138 @@ static void ibmveth_free_buffer_pools(struct 
ibmveth_adapter *adapter)
                   adapter->num_rx_queues);
 }
 
+/**
+ * ibmveth_alloc_single_rx_queue - Allocate resources for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to allocate
+ * @rxq_entries: Number of RX queue entries
+ *
+ * Allocates buffer list, RX queue, and per-queue buffer pools for one queue.
+ * Used during incremental scale-up without affecting existing queues.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_alloc_single_rx_queue(struct ibmveth_adapter *adapter, int queue_idx,
+                             int rxq_entries)
+{
+       struct device *dev = &adapter->vdev->dev;
+       struct net_device *netdev = adapter->netdev;
+       int i, rc = -ENOMEM;
+
+       adapter->buffer_list_addr[queue_idx] = (void 
*)get_zeroed_page(GFP_KERNEL);
+       if (!adapter->buffer_list_addr[queue_idx]) {
+               netdev_err(netdev, "unable to allocate buffer list for queue 
%d\n",
+                          queue_idx);
+               return -ENOMEM;
+       }
+
+       adapter->rx_queue[queue_idx].queue_len =
+               sizeof(struct ibmveth_rx_q_entry) * rxq_entries;
+       adapter->rx_queue[queue_idx].queue_addr =
+               dma_alloc_coherent(dev, adapter->rx_queue[queue_idx].queue_len,
+                                  &adapter->rx_queue[queue_idx].queue_dma,
+                                  GFP_KERNEL);
+       if (!adapter->rx_queue[queue_idx].queue_addr) {
+               netdev_err(netdev, "unable to allocate RX queue for queue %d\n",
+                          queue_idx);
+               goto out_free_buflist;
+       }
+
+       adapter->buffer_list_dma[queue_idx] =
+               dma_map_single(dev, adapter->buffer_list_addr[queue_idx],
+                              4096, DMA_BIDIRECTIONAL);
+       if (dma_mapping_error(dev, adapter->buffer_list_dma[queue_idx])) {
+               netdev_err(netdev, "unable to map buffer list for queue %d\n",
+                          queue_idx);
+               goto out_free_rxq;
+       }
+
+       for (i = 0; i < IBMVETH_NUM_BUFF_POOLS; i++) {
+               adapter->rx_buff_pool[queue_idx][i].size =
+                       adapter->rx_buff_pool[0][i].size;
+               adapter->rx_buff_pool[queue_idx][i].buff_size =
+                       adapter->rx_buff_pool[0][i].buff_size;
+               adapter->rx_buff_pool[queue_idx][i].threshold =
+                       adapter->rx_buff_pool[0][i].threshold;
+               adapter->rx_buff_pool[queue_idx][i].active =
+                       adapter->rx_buff_pool[0][i].active;
+       }
+
+       rc = ibmveth_alloc_queue_buffer_pools(adapter, queue_idx);
+       if (rc) {
+               netdev_err(netdev,
+                          "Failed to allocate buffer pools for queue %d\n",
+                          queue_idx);
+               goto out_unmap_buflist;
+       }
+
+       adapter->rx_queue[queue_idx].index = 0;
+       adapter->rx_queue[queue_idx].num_slots = rxq_entries;
+       adapter->rx_queue[queue_idx].toggle = 1;
+       spin_lock_init(&adapter->rx_queue[queue_idx].replenish_lock);
+
+       netdev_dbg(netdev,
+                  "Allocated queue %d: buffer_list @ %p (DMA: 0x%llx), 
rx_queue @ %p (DMA: 0x%llx), %d entries\n",
+                  queue_idx, adapter->buffer_list_addr[queue_idx],
+                  (unsigned long long)adapter->buffer_list_dma[queue_idx],
+                  adapter->rx_queue[queue_idx].queue_addr,
+                  (unsigned long long)adapter->rx_queue[queue_idx].queue_dma,
+                  rxq_entries);
+
+       return 0;
+
+out_unmap_buflist:
+       dma_unmap_single(dev, adapter->buffer_list_dma[queue_idx],
+                        4096, DMA_BIDIRECTIONAL);
+       adapter->buffer_list_dma[queue_idx] = 0;
+out_free_rxq:
+       dma_free_coherent(dev, adapter->rx_queue[queue_idx].queue_len,
+                         adapter->rx_queue[queue_idx].queue_addr,
+                         adapter->rx_queue[queue_idx].queue_dma);
+       adapter->rx_queue[queue_idx].queue_addr = NULL;
+out_free_buflist:
+       free_page((unsigned long)adapter->buffer_list_addr[queue_idx]);
+       adapter->buffer_list_addr[queue_idx] = NULL;
+       return rc;
+}
+
+/**
+ * ibmveth_free_single_rx_queue - Free resources for a single RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_idx: Queue index to free
+ *
+ * Frees buffer list, RX queue, and per-queue buffer pools for one queue.
+ * Used during incremental scale-down without affecting remaining queues.
+ */
+static void
+ibmveth_free_single_rx_queue(struct ibmveth_adapter *adapter, int queue_idx)
+{
+       struct device *dev = &adapter->vdev->dev;
+
+       ibmveth_free_queue_buffer_pools(adapter, queue_idx);
+
+       if (adapter->buffer_list_dma[queue_idx]) {
+               dma_unmap_single(dev, adapter->buffer_list_dma[queue_idx],
+                                4096, DMA_BIDIRECTIONAL);
+               adapter->buffer_list_dma[queue_idx] = 0;
+       }
+
+       if (adapter->rx_queue[queue_idx].queue_addr) {
+               dma_free_coherent(dev, adapter->rx_queue[queue_idx].queue_len,
+                                 adapter->rx_queue[queue_idx].queue_addr,
+                                 adapter->rx_queue[queue_idx].queue_dma);
+               adapter->rx_queue[queue_idx].queue_addr = NULL;
+       }
+
+       if (adapter->buffer_list_addr[queue_idx]) {
+               free_page((unsigned long)adapter->buffer_list_addr[queue_idx]);
+               adapter->buffer_list_addr[queue_idx] = NULL;
+       }
+
+       netdev_dbg(adapter->netdev, "Freed queue %d resources\n", queue_idx);
+}
+
 /**
  * ibmveth_remove_buffer_from_pool - remove a buffer from a pool
  * @adapter: adapter instance
@@ -1192,6 +1376,49 @@ static int ibmveth_rxq_harvest_buffer(struct 
ibmveth_adapter *adapter,
        return 0;
 }
 
+/**
+ * ibmveth_drain_rx_queue - Drain pending buffers from an RX queue
+ * @adapter: ibmveth adapter structure
+ * @queue_index: Queue index to drain
+ *
+ * Recycles all pending buffers back to the per-queue buffer pools.
+ * Must be called with NAPI disabled for this queue.
+ *
+ * Return: Number of buffers drained
+ */
+static int
+ibmveth_drain_rx_queue(struct ibmveth_adapter *adapter, int queue_index)
+{
+       struct net_device *netdev = adapter->netdev;
+       int drained = 0;
+       int limit = adapter->rx_queue[queue_index].num_slots;
+       int rc;
+
+       netdev_dbg(netdev, "Draining RX queue %d (limit: %d slots)\n",
+                  queue_index, limit);
+
+       while (drained < limit &&
+              ibmveth_rxq_pending_buffer(adapter, queue_index)) {
+               rc = ibmveth_rxq_harvest_buffer(adapter, queue_index, true);
+               if (rc) {
+                       netdev_err(netdev,
+                                  "Failed to harvest buffer from queue %d 
during drain: %d\n",
+                                  queue_index, rc);
+                       break;
+               }
+               drained++;
+       }
+
+       if (drained > 0)
+               netdev_dbg(netdev, "Drained %d buffer(s) from RX queue %d\n",
+                          drained, queue_index);
+       else
+               netdev_dbg(netdev, "No buffers to drain from RX queue %d\n",
+                          queue_index);
+
+       return drained;
+}
+
 static void ibmveth_free_tx_ltb(struct ibmveth_adapter *adapter, int idx)
 {
        dma_unmap_single(&adapter->vdev->dev, adapter->tx_ltb_dma[idx],
@@ -1459,7 +1686,7 @@ ibmveth_register_single_rx_queue(struct ibmveth_adapter 
*adapter,
  * the IRQ mapping for subordinate queues. Queue 0 is freed only through
  * ibmveth_free_all_queues() (H_FREE_LOGICAL_LAN).
  */
-static void __maybe_unused
+static void
 ibmveth_deregister_single_rx_queue(struct ibmveth_adapter *adapter,
                                   int queue_idx)
 {
@@ -1491,6 +1718,175 @@ ibmveth_deregister_single_rx_queue(struct 
ibmveth_adapter *adapter,
        netdev_dbg(adapter->netdev, "Deregistered queue %d\n", queue_idx);
 }
 
+/**
+ * ibmveth_resize_rx_queues_incremental - Resize RX queue count incrementally
+ * @adapter: ibmveth adapter structure
+ * @new_count: Target number of RX queues
+ * @rxq_entries: Number of entries per RX queue
+ *
+ * Adds or removes RX queues without tearing down the entire adapter.
+ * Active queues continue receiving during scale-up; scale-down drains
+ * excess queues before deregistering them with the hypervisor.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int
+ibmveth_resize_rx_queues_incremental(struct ibmveth_adapter *adapter,
+                                    int new_count, int rxq_entries)
+{
+       struct net_device *netdev = adapter->netdev;
+       u64 mac_address = ether_addr_to_u64(netdev->dev_addr);
+       int old_count = adapter->num_rx_queues;
+       int failed_queue;
+       int rc, i;
+
+       if (old_count == new_count) {
+               netdev_dbg(netdev, "RX queue count unchanged (%d), nothing to 
do\n",
+                          old_count);
+               return 0;
+       }
+
+       if (new_count < 1 || new_count > IBMVETH_MAX_RX_QUEUES) {
+               netdev_err(netdev, "Invalid RX queue count %d (must be 1-%d)\n",
+                          new_count, IBMVETH_MAX_RX_QUEUES);
+               return -EINVAL;
+       }
+
+       netdev_info(netdev, "Incrementally resizing RX queues: %d to %d\n",
+                   old_count, new_count);
+
+       if (new_count > old_count) {
+               netdev_dbg(netdev, "Scale-up: adding queues %d-%d\n",
+                          old_count, new_count - 1);
+
+               for (i = old_count; i < new_count; i++) {
+                       rc = ibmveth_alloc_single_rx_queue(adapter, i, 
rxq_entries);
+                       if (rc) {
+                               netdev_err(netdev, "Failed to allocate queue 
%d: %d\n",
+                                          i, rc);
+                               goto cleanup_new_queues;
+                       }
+
+                       rc = ibmveth_register_single_rx_queue(adapter, i,
+                                                             mac_address);
+                       if (rc) {
+                               netdev_err(netdev, "Failed to register queue 
%d: %d\n",
+                                          i, rc);
+                               ibmveth_free_single_rx_queue(adapter, i);
+                               goto cleanup_new_queues;
+                       }
+
+                       rc = ibmveth_setup_single_rx_interrupt(adapter, i);
+                       if (rc) {
+                               netdev_err(netdev,
+                                          "Failed to setup IRQ for queue %d: 
%d\n",
+                                          i, rc);
+                               ibmveth_deregister_single_rx_queue(adapter, i);
+                               ibmveth_free_single_rx_queue(adapter, i);
+                               goto cleanup_new_queues;
+                       }
+
+                       rc = ibmveth_enable_irq(adapter, i);
+                       if (rc) {
+                               netdev_err(netdev,
+                                          "Failed to enable IRQ for queue %d: 
%d\n",
+                                          i, rc);
+                               ibmveth_cleanup_single_rx_interrupt(adapter, i);
+                               ibmveth_deregister_single_rx_queue(adapter, i);
+                               ibmveth_free_single_rx_queue(adapter, i);
+                               goto cleanup_new_queues;
+                       }
+
+                       napi_enable(&adapter->napi[i]);
+               }
+
+               adapter->num_rx_queues = new_count;
+
+               for (i = old_count; i < new_count; i++)
+                       ibmveth_replenish_task(adapter, i);
+
+               rc = netif_set_real_num_rx_queues(netdev, new_count);
+               if (rc) {
+                       netdev_err(netdev, "Failed to set real RX queues to %d: 
%d\n",
+                                  new_count, rc);
+                       goto cleanup_new_queues;
+               }
+       } else {
+               netdev_dbg(netdev, "Scale-down: removing queues %d-%d\n",
+                          new_count, old_count - 1);
+
+               for (i = new_count; i < old_count; i++)
+                       napi_disable(&adapter->napi[i]);
+
+               for (i = new_count; i < old_count; i++)
+                       ibmveth_drain_rx_queue(adapter, i);
+
+               synchronize_net();
+
+               rc = netif_set_real_num_rx_queues(netdev, new_count);
+               if (rc) {
+                       netdev_err(netdev, "Failed to set real RX queues to %d: 
%d\n",
+                                  new_count, rc);
+                       for (i = new_count; i < old_count; i++)
+                               napi_enable(&adapter->napi[i]);
+                       return rc;
+               }
+
+               /* Disable hypervisor interrupts and wait for handlers to 
complete
+                * before updating num_rx_queues.
+                */
+               for (i = new_count; i < old_count; i++) {
+                       ibmveth_disable_irq(adapter, i);
+                       synchronize_irq(adapter->queue_irq[i]);
+               }
+
+               adapter->num_rx_queues = new_count;
+
+               for (i = new_count; i < old_count; i++) {
+                       ibmveth_cleanup_single_rx_interrupt(adapter, i);
+                       ibmveth_deregister_single_rx_queue(adapter, i);
+                       ibmveth_free_single_rx_queue(adapter, i);
+               }
+       }
+
+       netdev_info(netdev, "Successfully resized to %d RX queues 
(incremental)\n",
+                   adapter->num_rx_queues);
+
+       if (firmware_has_feature(FW_FEATURE_CMO))
+               vio_cmo_set_dev_desired(adapter->vdev,
+                                       ibmveth_get_desired_dma(adapter->vdev));
+
+       return 0;
+
+cleanup_new_queues:
+       failed_queue = i;
+       netdev_err(netdev,
+                  "Scale-up failed at queue %d, cleaning up queues %d-%d\n",
+                  failed_queue, old_count, failed_queue - 1);
+       for (i = old_count; i < failed_queue; i++)
+               napi_disable(&adapter->napi[i]);
+
+       for (i = old_count; i < failed_queue; i++)
+               ibmveth_drain_rx_queue(adapter, i);
+
+       synchronize_net();
+
+       for (i = old_count; i < failed_queue; i++) {
+               ibmveth_disable_irq(adapter, i);
+               synchronize_irq(adapter->queue_irq[i]);
+       }
+
+       for (i = old_count; i < failed_queue; i++) {
+               ibmveth_cleanup_single_rx_interrupt(adapter, i);
+               ibmveth_deregister_single_rx_queue(adapter, i);
+               ibmveth_free_single_rx_queue(adapter, i);
+       }
+       adapter->num_rx_queues = old_count;
+       netdev_warn(netdev, "Keeping %d queues after scale-up failure\n",
+                   old_count);
+       return rc;
+}
+
 /**
  * ibmveth_free_all_queues - Free all RX queues at once
  * @adapter: ibmveth adapter structure
-- 
2.39.3 (Apple Git-146)


Reply via email to