eth: fbnic: support ring channel set while up
authorJakub Kicinski <kuba@kernel.org>
Fri, 20 Dec 2024 02:52:41 +0000 (18:52 -0800)
committerJakub Kicinski <kuba@kernel.org>
Mon, 23 Dec 2024 18:35:56 +0000 (10:35 -0800)
Implement the channel count changes. Copy the netdev priv,
allocate new channels using it. Stop, swap, start.
Then free the copy of the priv along with the channels it
holds, which are now the channels that used to be on the
real priv.

Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Link: https://patch.msgid.link/20241220025241.1522781-11-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/meta/fbnic/fbnic.h
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
drivers/net/ethernet/meta/fbnic/fbnic_irq.c
drivers/net/ethernet/meta/fbnic/fbnic_netdev.h
drivers/net/ethernet/meta/fbnic/fbnic_pci.c
drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
drivers/net/ethernet/meta/fbnic/fbnic_txrx.h

index ed52720..14751f1 100644 (file)
@@ -162,6 +162,7 @@ int fbnic_napi_request_irq(struct fbnic_dev *fbd,
                           struct fbnic_napi_vector *nv);
 void fbnic_napi_free_irq(struct fbnic_dev *fbd,
                         struct fbnic_napi_vector *nv);
+void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr);
 int fbnic_request_irq(struct fbnic_dev *dev, int nr, irq_handler_t handler,
                      unsigned long flags, const char *name, void *data);
 void fbnic_free_irq(struct fbnic_dev *dev, int nr, void *data);
index d2fe97a..20cd9f5 100644 (file)
@@ -65,6 +65,76 @@ static void fbnic_get_regs(struct net_device *netdev,
        fbnic_csr_get_regs(fbn->fbd, data, &regs->version);
 }
 
+static struct fbnic_net *fbnic_clone_create(struct fbnic_net *orig)
+{
+       struct fbnic_net *clone;
+
+       clone = kmemdup(orig, sizeof(*orig), GFP_KERNEL);
+       if (!clone)
+               return NULL;
+
+       memset(clone->tx, 0, sizeof(clone->tx));
+       memset(clone->rx, 0, sizeof(clone->rx));
+       memset(clone->napi, 0, sizeof(clone->napi));
+       return clone;
+}
+
+static void fbnic_clone_swap_cfg(struct fbnic_net *orig,
+                                struct fbnic_net *clone)
+{
+       swap(clone->rcq_size, orig->rcq_size);
+       swap(clone->hpq_size, orig->hpq_size);
+       swap(clone->ppq_size, orig->ppq_size);
+       swap(clone->txq_size, orig->txq_size);
+       swap(clone->num_rx_queues, orig->num_rx_queues);
+       swap(clone->num_tx_queues, orig->num_tx_queues);
+       swap(clone->num_napi, orig->num_napi);
+}
+
+static void fbnic_aggregate_vector_counters(struct fbnic_net *fbn,
+                                           struct fbnic_napi_vector *nv)
+{
+       int i, j;
+
+       for (i = 0; i < nv->txt_count; i++) {
+               fbnic_aggregate_ring_tx_counters(fbn, &nv->qt[i].sub0);
+               fbnic_aggregate_ring_tx_counters(fbn, &nv->qt[i].sub1);
+               fbnic_aggregate_ring_tx_counters(fbn, &nv->qt[i].cmpl);
+       }
+
+       for (j = 0; j < nv->rxt_count; j++, i++) {
+               fbnic_aggregate_ring_rx_counters(fbn, &nv->qt[i].sub0);
+               fbnic_aggregate_ring_rx_counters(fbn, &nv->qt[i].sub1);
+               fbnic_aggregate_ring_rx_counters(fbn, &nv->qt[i].cmpl);
+       }
+}
+
+static void fbnic_clone_swap(struct fbnic_net *orig,
+                            struct fbnic_net *clone)
+{
+       struct fbnic_dev *fbd = orig->fbd;
+       unsigned int i;
+
+       for (i = 0; i < max(clone->num_napi, orig->num_napi); i++)
+               fbnic_synchronize_irq(fbd, FBNIC_NON_NAPI_VECTORS + i);
+       for (i = 0; i < orig->num_napi; i++)
+               fbnic_aggregate_vector_counters(orig, orig->napi[i]);
+
+       fbnic_clone_swap_cfg(orig, clone);
+
+       for (i = 0; i < ARRAY_SIZE(orig->napi); i++)
+               swap(clone->napi[i], orig->napi[i]);
+       for (i = 0; i < ARRAY_SIZE(orig->tx); i++)
+               swap(clone->tx[i], orig->tx[i]);
+       for (i = 0; i < ARRAY_SIZE(orig->rx); i++)
+               swap(clone->rx[i], orig->rx[i]);
+}
+
+static void fbnic_clone_free(struct fbnic_net *clone)
+{
+       kfree(clone);
+}
+
 static void fbnic_get_strings(struct net_device *dev, u32 sset, u8 *data)
 {
        int i;
@@ -342,6 +412,8 @@ static int fbnic_set_channels(struct net_device *netdev,
        struct fbnic_net *fbn = netdev_priv(netdev);
        unsigned int max_napis, standalone;
        struct fbnic_dev *fbd = fbn->fbd;
+       struct fbnic_net *clone;
+       int err;
 
        max_napis = fbd->num_irqs - FBNIC_NON_NAPI_VECTORS;
        standalone = ch->rx_count + ch->tx_count;
@@ -363,7 +435,54 @@ static int fbnic_set_channels(struct net_device *netdev,
                return 0;
        }
 
-       return -EBUSY;
+       clone = fbnic_clone_create(fbn);
+       if (!clone)
+               return -ENOMEM;
+
+       fbnic_set_queues(clone, ch, max_napis);
+
+       err = fbnic_alloc_napi_vectors(clone);
+       if (err)
+               goto err_free_clone;
+
+       err = fbnic_alloc_resources(clone);
+       if (err)
+               goto err_free_napis;
+
+       fbnic_down_noidle(fbn);
+       err = fbnic_wait_all_queues_idle(fbn->fbd, true);
+       if (err)
+               goto err_start_stack;
+
+       err = fbnic_set_netif_queues(clone);
+       if (err)
+               goto err_start_stack;
+
+       /* Nothing can fail past this point */
+       fbnic_flush(fbn);
+
+       fbnic_clone_swap(fbn, clone);
+
+       /* Reset RSS indirection table */
+       fbnic_reset_indir_tbl(fbn);
+
+       fbnic_up(fbn);
+
+       fbnic_free_resources(clone);
+       fbnic_free_napi_vectors(clone);
+       fbnic_clone_free(clone);
+
+       return 0;
+
+err_start_stack:
+       fbnic_flush(fbn);
+       fbnic_up(fbn);
+       fbnic_free_resources(clone);
+err_free_napis:
+       fbnic_free_napi_vectors(clone);
+err_free_clone:
+       fbnic_clone_free(clone);
+       return err;
 }
 
 static int
index a8ea7b6..1bbc0e5 100644 (file)
@@ -146,6 +146,17 @@ void fbnic_pcs_irq_disable(struct fbnic_dev *fbd)
        free_irq(fbd->pcs_msix_vector, fbd);
 }
 
+void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr)
+{
+       struct pci_dev *pdev = to_pci_dev(fbd->dev);
+       int irq = pci_irq_vector(pdev, nr);
+
+       if (irq < 0)
+               return;
+
+       synchronize_irq(irq);
+}
+
 int fbnic_request_irq(struct fbnic_dev *fbd, int nr, irq_handler_t handler,
                      unsigned long flags, const char *name, void *data)
 {
index 0986c8f..a392ac1 100644 (file)
@@ -65,6 +65,7 @@ struct fbnic_net {
 int __fbnic_open(struct fbnic_net *fbn);
 void fbnic_up(struct fbnic_net *fbn);
 void fbnic_down(struct fbnic_net *fbn);
+void fbnic_down_noidle(struct fbnic_net *fbn);
 
 struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd);
 void fbnic_netdev_free(struct fbnic_dev *fbd);
index 32702dc..6cbbc2e 100644 (file)
@@ -145,7 +145,7 @@ void fbnic_up(struct fbnic_net *fbn)
        fbnic_service_task_start(fbn);
 }
 
-static void fbnic_down_noidle(struct fbnic_net *fbn)
+void fbnic_down_noidle(struct fbnic_net *fbn)
 {
        fbnic_service_task_stop(fbn);
 
index 92fc1ad..bb54ce5 100644 (file)
@@ -1045,8 +1045,8 @@ irqreturn_t fbnic_msix_clean_rings(int __always_unused irq, void *data)
        return IRQ_HANDLED;
 }
 
-static void fbnic_aggregate_ring_rx_counters(struct fbnic_net *fbn,
-                                            struct fbnic_ring *rxr)
+void fbnic_aggregate_ring_rx_counters(struct fbnic_net *fbn,
+                                     struct fbnic_ring *rxr)
 {
        struct fbnic_queue_stats *stats = &rxr->stats;
 
@@ -1056,8 +1056,8 @@ static void fbnic_aggregate_ring_rx_counters(struct fbnic_net *fbn,
        fbn->rx_stats.dropped += stats->dropped;
 }
 
-static void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn,
-                                            struct fbnic_ring *txr)
+void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn,
+                                     struct fbnic_ring *txr)
 {
        struct fbnic_queue_stats *stats = &txr->stats;
 
index 92c6711..c2a94f3 100644 (file)
@@ -120,6 +120,11 @@ netdev_features_t
 fbnic_features_check(struct sk_buff *skb, struct net_device *dev,
                     netdev_features_t features);
 
+void fbnic_aggregate_ring_rx_counters(struct fbnic_net *fbn,
+                                     struct fbnic_ring *rxr);
+void fbnic_aggregate_ring_tx_counters(struct fbnic_net *fbn,
+                                     struct fbnic_ring *txr);
+
 int fbnic_alloc_napi_vectors(struct fbnic_net *fbn);
 void fbnic_free_napi_vectors(struct fbnic_net *fbn);
 int fbnic_alloc_resources(struct fbnic_net *fbn);