gve: DQO: Add ring allocation and initialization
authorBailey Forrest <bcf@google.com>
Thu, 24 Jun 2021 18:06:29 +0000 (11:06 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 Jun 2021 19:47:38 +0000 (12:47 -0700)
Allocate the buffer and completion ring structures. Do not populate the
rings yet. That will happen in the respective rx and tx datapath
follow-on patches

Signed-off-by: Bailey Forrest <bcf@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Catherine Sullivan <csully@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/google/gve/gve.h
drivers/net/ethernet/google/gve/gve_dqo.h
drivers/net/ethernet/google/gve/gve_main.c
drivers/net/ethernet/google/gve/gve_rx.c
drivers/net/ethernet/google/gve/gve_rx_dqo.c
drivers/net/ethernet/google/gve/gve_tx.c
drivers/net/ethernet/google/gve/gve_tx_dqo.c

index d6bf046..30978a1 100644 (file)
@@ -204,6 +204,10 @@ struct gve_rx_ring {
        struct gve_queue_resources *q_resources; /* head and tail pointer idx */
        dma_addr_t q_resources_bus; /* dma address for the queue resources */
        struct u64_stats_sync statss; /* sync stats for 32bit archs */
+
+       /* head and tail of skb chain for the current packet or NULL if none */
+       struct sk_buff *skb_head;
+       struct sk_buff *skb_tail;
 };
 
 /* A TX desc ring entry */
@@ -816,14 +820,14 @@ void gve_free_page(struct device *dev, struct page *page, dma_addr_t dma,
 netdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev);
 bool gve_tx_poll(struct gve_notify_block *block, int budget);
 int gve_tx_alloc_rings(struct gve_priv *priv);
-void gve_tx_free_rings(struct gve_priv *priv);
+void gve_tx_free_rings_gqi(struct gve_priv *priv);
 __be32 gve_tx_load_event_counter(struct gve_priv *priv,
                                 struct gve_tx_ring *tx);
 /* rx handling */
 void gve_rx_write_doorbell(struct gve_priv *priv, struct gve_rx_ring *rx);
 bool gve_rx_poll(struct gve_notify_block *block, int budget);
 int gve_rx_alloc_rings(struct gve_priv *priv);
-void gve_rx_free_rings(struct gve_priv *priv);
+void gve_rx_free_rings_gqi(struct gve_priv *priv);
 bool gve_clean_rx_done(struct gve_rx_ring *rx, int budget,
                       netdev_features_t feat);
 /* Reset */
index cff4e6e..9877a33 100644 (file)
 netdev_tx_t gve_tx_dqo(struct sk_buff *skb, struct net_device *dev);
 bool gve_tx_poll_dqo(struct gve_notify_block *block, bool do_clean);
 int gve_rx_poll_dqo(struct gve_notify_block *block, int budget);
+int gve_tx_alloc_rings_dqo(struct gve_priv *priv);
+void gve_tx_free_rings_dqo(struct gve_priv *priv);
+int gve_rx_alloc_rings_dqo(struct gve_priv *priv);
+void gve_rx_free_rings_dqo(struct gve_priv *priv);
+int gve_clean_tx_done_dqo(struct gve_priv *priv, struct gve_tx_ring *tx,
+                         struct napi_struct *napi);
+void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx);
+void gve_rx_write_doorbell_dqo(const struct gve_priv *priv, int queue_idx);
+
+static inline void
+gve_tx_put_doorbell_dqo(const struct gve_priv *priv,
+                       const struct gve_queue_resources *q_resources, u32 val)
+{
+       u64 index;
+
+       index = be32_to_cpu(q_resources->db_index);
+       iowrite32(val, &priv->db_bar2[index]);
+}
 
 static inline void
 gve_write_irq_doorbell_dqo(const struct gve_priv *priv,
index 579f867..cddf19c 100644 (file)
@@ -571,13 +571,21 @@ static int gve_create_rings(struct gve_priv *priv)
        netif_dbg(priv, drv, priv->dev, "created %d rx queues\n",
                  priv->rx_cfg.num_queues);
 
-       /* Rx data ring has been prefilled with packet buffers at queue
-        * allocation time.
-        * Write the doorbell to provide descriptor slots and packet buffers
-        * to the NIC.
-        */
-       for (i = 0; i < priv->rx_cfg.num_queues; i++)
-               gve_rx_write_doorbell(priv, &priv->rx[i]);
+       if (gve_is_gqi(priv)) {
+               /* Rx data ring has been prefilled with packet buffers at queue
+                * allocation time.
+                *
+                * Write the doorbell to provide descriptor slots and packet
+                * buffers to the NIC.
+                */
+               for (i = 0; i < priv->rx_cfg.num_queues; i++)
+                       gve_rx_write_doorbell(priv, &priv->rx[i]);
+       } else {
+               for (i = 0; i < priv->rx_cfg.num_queues; i++) {
+                       /* Post buffers and ring doorbell. */
+                       gve_rx_post_buffers_dqo(&priv->rx[i]);
+               }
+       }
 
        return 0;
 }
@@ -606,6 +614,15 @@ static void add_napi_init_sync_stats(struct gve_priv *priv,
        }
 }
 
+static void gve_tx_free_rings(struct gve_priv *priv)
+{
+       if (gve_is_gqi(priv)) {
+               gve_tx_free_rings_gqi(priv);
+       } else {
+               gve_tx_free_rings_dqo(priv);
+       }
+}
+
 static int gve_alloc_rings(struct gve_priv *priv)
 {
        int err;
@@ -615,9 +632,14 @@ static int gve_alloc_rings(struct gve_priv *priv)
                            GFP_KERNEL);
        if (!priv->tx)
                return -ENOMEM;
-       err = gve_tx_alloc_rings(priv);
+
+       if (gve_is_gqi(priv))
+               err = gve_tx_alloc_rings(priv);
+       else
+               err = gve_tx_alloc_rings_dqo(priv);
        if (err)
                goto free_tx;
+
        /* Setup rx rings */
        priv->rx = kvzalloc(priv->rx_cfg.num_queues * sizeof(*priv->rx),
                            GFP_KERNEL);
@@ -625,7 +647,11 @@ static int gve_alloc_rings(struct gve_priv *priv)
                err = -ENOMEM;
                goto free_tx_queue;
        }
-       err = gve_rx_alloc_rings(priv);
+
+       if (gve_is_gqi(priv))
+               err = gve_rx_alloc_rings(priv);
+       else
+               err = gve_rx_alloc_rings_dqo(priv);
        if (err)
                goto free_rx;
 
@@ -670,6 +696,14 @@ static int gve_destroy_rings(struct gve_priv *priv)
        return 0;
 }
 
+static inline void gve_rx_free_rings(struct gve_priv *priv)
+{
+       if (gve_is_gqi(priv))
+               gve_rx_free_rings_gqi(priv);
+       else
+               gve_rx_free_rings_dqo(priv);
+}
+
 static void gve_free_rings(struct gve_priv *priv)
 {
        int ntfy_idx;
@@ -869,6 +903,7 @@ static int gve_open(struct net_device *dev)
        err = gve_alloc_qpls(priv);
        if (err)
                return err;
+
        err = gve_alloc_rings(priv);
        if (err)
                goto free_qpls;
index 15a64e4..bb82613 100644 (file)
@@ -238,7 +238,7 @@ int gve_rx_alloc_rings(struct gve_priv *priv)
        return err;
 }
 
-void gve_rx_free_rings(struct gve_priv *priv)
+void gve_rx_free_rings_gqi(struct gve_priv *priv)
 {
        int i;
 
index 808e097..1073a82 100644 (file)
 #include <net/ipv6.h>
 #include <net/tcp.h>
 
+static void gve_free_page_dqo(struct gve_priv *priv,
+                             struct gve_rx_buf_state_dqo *bs)
+{
+}
+
+static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx)
+{
+       struct gve_rx_ring *rx = &priv->rx[idx];
+       struct device *hdev = &priv->pdev->dev;
+       size_t completion_queue_slots;
+       size_t buffer_queue_slots;
+       size_t size;
+       int i;
+
+       completion_queue_slots = rx->dqo.complq.mask + 1;
+       buffer_queue_slots = rx->dqo.bufq.mask + 1;
+
+       gve_rx_remove_from_block(priv, idx);
+
+       if (rx->q_resources) {
+               dma_free_coherent(hdev, sizeof(*rx->q_resources),
+                                 rx->q_resources, rx->q_resources_bus);
+               rx->q_resources = NULL;
+       }
+
+       for (i = 0; i < rx->dqo.num_buf_states; i++) {
+               struct gve_rx_buf_state_dqo *bs = &rx->dqo.buf_states[i];
+
+               if (bs->page_info.page)
+                       gve_free_page_dqo(priv, bs);
+       }
+
+       if (rx->dqo.bufq.desc_ring) {
+               size = sizeof(rx->dqo.bufq.desc_ring[0]) * buffer_queue_slots;
+               dma_free_coherent(hdev, size, rx->dqo.bufq.desc_ring,
+                                 rx->dqo.bufq.bus);
+               rx->dqo.bufq.desc_ring = NULL;
+       }
+
+       if (rx->dqo.complq.desc_ring) {
+               size = sizeof(rx->dqo.complq.desc_ring[0]) *
+                       completion_queue_slots;
+               dma_free_coherent(hdev, size, rx->dqo.complq.desc_ring,
+                                 rx->dqo.complq.bus);
+               rx->dqo.complq.desc_ring = NULL;
+       }
+
+       kvfree(rx->dqo.buf_states);
+       rx->dqo.buf_states = NULL;
+
+       netif_dbg(priv, drv, priv->dev, "freed rx ring %d\n", idx);
+}
+
+static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx)
+{
+       struct gve_rx_ring *rx = &priv->rx[idx];
+       struct device *hdev = &priv->pdev->dev;
+       size_t size;
+       int i;
+
+       const u32 buffer_queue_slots =
+               priv->options_dqo_rda.rx_buff_ring_entries;
+       const u32 completion_queue_slots = priv->rx_desc_cnt;
+
+       netif_dbg(priv, drv, priv->dev, "allocating rx ring DQO\n");
+
+       memset(rx, 0, sizeof(*rx));
+       rx->gve = priv;
+       rx->q_num = idx;
+       rx->dqo.bufq.mask = buffer_queue_slots - 1;
+       rx->dqo.complq.num_free_slots = completion_queue_slots;
+       rx->dqo.complq.mask = completion_queue_slots - 1;
+       rx->skb_head = NULL;
+       rx->skb_tail = NULL;
+
+       rx->dqo.num_buf_states = min_t(s16, S16_MAX, buffer_queue_slots * 4);
+       rx->dqo.buf_states = kvcalloc(rx->dqo.num_buf_states,
+                                     sizeof(rx->dqo.buf_states[0]),
+                                     GFP_KERNEL);
+       if (!rx->dqo.buf_states)
+               return -ENOMEM;
+
+       /* Set up linked list of buffer IDs */
+       for (i = 0; i < rx->dqo.num_buf_states - 1; i++)
+               rx->dqo.buf_states[i].next = i + 1;
+
+       rx->dqo.buf_states[rx->dqo.num_buf_states - 1].next = -1;
+       rx->dqo.recycled_buf_states.head = -1;
+       rx->dqo.recycled_buf_states.tail = -1;
+       rx->dqo.used_buf_states.head = -1;
+       rx->dqo.used_buf_states.tail = -1;
+
+       /* Allocate RX completion queue */
+       size = sizeof(rx->dqo.complq.desc_ring[0]) *
+               completion_queue_slots;
+       rx->dqo.complq.desc_ring =
+               dma_alloc_coherent(hdev, size, &rx->dqo.complq.bus, GFP_KERNEL);
+       if (!rx->dqo.complq.desc_ring)
+               goto err;
+
+       /* Allocate RX buffer queue */
+       size = sizeof(rx->dqo.bufq.desc_ring[0]) * buffer_queue_slots;
+       rx->dqo.bufq.desc_ring =
+               dma_alloc_coherent(hdev, size, &rx->dqo.bufq.bus, GFP_KERNEL);
+       if (!rx->dqo.bufq.desc_ring)
+               goto err;
+
+       rx->q_resources = dma_alloc_coherent(hdev, sizeof(*rx->q_resources),
+                                            &rx->q_resources_bus, GFP_KERNEL);
+       if (!rx->q_resources)
+               goto err;
+
+       gve_rx_add_to_block(priv, idx);
+
+       return 0;
+
+err:
+       gve_rx_free_ring_dqo(priv, idx);
+       return -ENOMEM;
+}
+
+int gve_rx_alloc_rings_dqo(struct gve_priv *priv)
+{
+       int err = 0;
+       int i;
+
+       for (i = 0; i < priv->rx_cfg.num_queues; i++) {
+               err = gve_rx_alloc_ring_dqo(priv, i);
+               if (err) {
+                       netif_err(priv, drv, priv->dev,
+                                 "Failed to alloc rx ring=%d: err=%d\n",
+                                 i, err);
+                       goto err;
+               }
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               gve_rx_free_ring_dqo(priv, i);
+
+       return err;
+}
+
+void gve_rx_free_rings_dqo(struct gve_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < priv->rx_cfg.num_queues; i++)
+               gve_rx_free_ring_dqo(priv, i);
+}
+
+void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx)
+{
+}
+
 int gve_rx_poll_dqo(struct gve_notify_block *block, int budget)
 {
        u32 work_done = 0;
index 75930bb..665ac79 100644 (file)
@@ -256,7 +256,7 @@ int gve_tx_alloc_rings(struct gve_priv *priv)
        return err;
 }
 
-void gve_tx_free_rings(struct gve_priv *priv)
+void gve_tx_free_rings_gqi(struct gve_priv *priv)
 {
        int i;
 
index 4b3319a..bde8f90 100644 (file)
 #include <linux/slab.h>
 #include <linux/skbuff.h>
 
+/* gve_tx_free_desc - Cleans up all pending tx requests and buffers.
+ */
+static void gve_tx_clean_pending_packets(struct gve_tx_ring *tx)
+{
+       int i;
+
+       for (i = 0; i < tx->dqo.num_pending_packets; i++) {
+               struct gve_tx_pending_packet_dqo *cur_state =
+                       &tx->dqo.pending_packets[i];
+               int j;
+
+               for (j = 0; j < cur_state->num_bufs; j++) {
+                       struct gve_tx_dma_buf *buf = &cur_state->bufs[j];
+
+                       if (j == 0) {
+                               dma_unmap_single(tx->dev,
+                                                dma_unmap_addr(buf, dma),
+                                                dma_unmap_len(buf, len),
+                                                DMA_TO_DEVICE);
+                       } else {
+                               dma_unmap_page(tx->dev,
+                                              dma_unmap_addr(buf, dma),
+                                              dma_unmap_len(buf, len),
+                                              DMA_TO_DEVICE);
+                       }
+               }
+               if (cur_state->skb) {
+                       dev_consume_skb_any(cur_state->skb);
+                       cur_state->skb = NULL;
+               }
+       }
+}
+
+static void gve_tx_free_ring_dqo(struct gve_priv *priv, int idx)
+{
+       struct gve_tx_ring *tx = &priv->tx[idx];
+       struct device *hdev = &priv->pdev->dev;
+       size_t bytes;
+
+       gve_tx_remove_from_block(priv, idx);
+
+       if (tx->q_resources) {
+               dma_free_coherent(hdev, sizeof(*tx->q_resources),
+                                 tx->q_resources, tx->q_resources_bus);
+               tx->q_resources = NULL;
+       }
+
+       if (tx->dqo.compl_ring) {
+               bytes = sizeof(tx->dqo.compl_ring[0]) *
+                       (tx->dqo.complq_mask + 1);
+               dma_free_coherent(hdev, bytes, tx->dqo.compl_ring,
+                                 tx->complq_bus_dqo);
+               tx->dqo.compl_ring = NULL;
+       }
+
+       if (tx->dqo.tx_ring) {
+               bytes = sizeof(tx->dqo.tx_ring[0]) * (tx->mask + 1);
+               dma_free_coherent(hdev, bytes, tx->dqo.tx_ring, tx->bus);
+               tx->dqo.tx_ring = NULL;
+       }
+
+       kvfree(tx->dqo.pending_packets);
+       tx->dqo.pending_packets = NULL;
+
+       netif_dbg(priv, drv, priv->dev, "freed tx queue %d\n", idx);
+}
+
+static int gve_tx_alloc_ring_dqo(struct gve_priv *priv, int idx)
+{
+       struct gve_tx_ring *tx = &priv->tx[idx];
+       struct device *hdev = &priv->pdev->dev;
+       int num_pending_packets;
+       size_t bytes;
+       int i;
+
+       memset(tx, 0, sizeof(*tx));
+       tx->q_num = idx;
+       tx->dev = &priv->pdev->dev;
+       tx->netdev_txq = netdev_get_tx_queue(priv->dev, idx);
+       atomic_set_release(&tx->dqo_compl.hw_tx_head, 0);
+
+       /* Queue sizes must be a power of 2 */
+       tx->mask = priv->tx_desc_cnt - 1;
+       tx->dqo.complq_mask = priv->options_dqo_rda.tx_comp_ring_entries - 1;
+
+       /* The max number of pending packets determines the maximum number of
+        * descriptors which maybe written to the completion queue.
+        *
+        * We must set the number small enough to make sure we never overrun the
+        * completion queue.
+        */
+       num_pending_packets = tx->dqo.complq_mask + 1;
+
+       /* Reserve space for descriptor completions, which will be reported at
+        * most every GVE_TX_MIN_RE_INTERVAL packets.
+        */
+       num_pending_packets -=
+               (tx->dqo.complq_mask + 1) / GVE_TX_MIN_RE_INTERVAL;
+
+       /* Each packet may have at most 2 buffer completions if it receives both
+        * a miss and reinjection completion.
+        */
+       num_pending_packets /= 2;
+
+       tx->dqo.num_pending_packets = min_t(int, num_pending_packets, S16_MAX);
+       tx->dqo.pending_packets = kvcalloc(tx->dqo.num_pending_packets,
+                                          sizeof(tx->dqo.pending_packets[0]),
+                                          GFP_KERNEL);
+       if (!tx->dqo.pending_packets)
+               goto err;
+
+       /* Set up linked list of pending packets */
+       for (i = 0; i < tx->dqo.num_pending_packets - 1; i++)
+               tx->dqo.pending_packets[i].next = i + 1;
+
+       tx->dqo.pending_packets[tx->dqo.num_pending_packets - 1].next = -1;
+       atomic_set_release(&tx->dqo_compl.free_pending_packets, -1);
+       tx->dqo_compl.miss_completions.head = -1;
+       tx->dqo_compl.miss_completions.tail = -1;
+       tx->dqo_compl.timed_out_completions.head = -1;
+       tx->dqo_compl.timed_out_completions.tail = -1;
+
+       bytes = sizeof(tx->dqo.tx_ring[0]) * (tx->mask + 1);
+       tx->dqo.tx_ring = dma_alloc_coherent(hdev, bytes, &tx->bus, GFP_KERNEL);
+       if (!tx->dqo.tx_ring)
+               goto err;
+
+       bytes = sizeof(tx->dqo.compl_ring[0]) * (tx->dqo.complq_mask + 1);
+       tx->dqo.compl_ring = dma_alloc_coherent(hdev, bytes,
+                                               &tx->complq_bus_dqo,
+                                               GFP_KERNEL);
+       if (!tx->dqo.compl_ring)
+               goto err;
+
+       tx->q_resources = dma_alloc_coherent(hdev, sizeof(*tx->q_resources),
+                                            &tx->q_resources_bus, GFP_KERNEL);
+       if (!tx->q_resources)
+               goto err;
+
+       gve_tx_add_to_block(priv, idx);
+
+       return 0;
+
+err:
+       gve_tx_free_ring_dqo(priv, idx);
+       return -ENOMEM;
+}
+
+int gve_tx_alloc_rings_dqo(struct gve_priv *priv)
+{
+       int err = 0;
+       int i;
+
+       for (i = 0; i < priv->tx_cfg.num_queues; i++) {
+               err = gve_tx_alloc_ring_dqo(priv, i);
+               if (err) {
+                       netif_err(priv, drv, priv->dev,
+                                 "Failed to alloc tx ring=%d: err=%d\n",
+                                 i, err);
+                       goto err;
+               }
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               gve_tx_free_ring_dqo(priv, i);
+
+       return err;
+}
+
+void gve_tx_free_rings_dqo(struct gve_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < priv->tx_cfg.num_queues; i++) {
+               struct gve_tx_ring *tx = &priv->tx[i];
+
+               gve_clean_tx_done_dqo(priv, tx, /*napi=*/NULL);
+               netdev_tx_reset_queue(tx->netdev_txq);
+               gve_tx_clean_pending_packets(tx);
+
+               gve_tx_free_ring_dqo(priv, i);
+       }
+}
+
 netdev_tx_t gve_tx_dqo(struct sk_buff *skb, struct net_device *dev)
 {
        return NETDEV_TX_OK;
 }
 
+int gve_clean_tx_done_dqo(struct gve_priv *priv, struct gve_tx_ring *tx,
+                         struct napi_struct *napi)
+{
+       return 0;
+}
+
 bool gve_tx_poll_dqo(struct gve_notify_block *block, bool do_clean)
 {
        return false;