18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT) 28c2ecf20Sopenharmony_ci/* Google virtual Ethernet (gve) driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015-2019 Google, Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "gve.h" 88c2ecf20Sopenharmony_ci#include "gve_adminq.h" 98c2ecf20Sopenharmony_ci#include <linux/ip.h> 108c2ecf20Sopenharmony_ci#include <linux/tcp.h> 118c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 128c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic inline void gve_tx_put_doorbell(struct gve_priv *priv, 158c2ecf20Sopenharmony_ci struct gve_queue_resources *q_resources, 168c2ecf20Sopenharmony_ci u32 val) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci iowrite32be(val, &priv->db_bar2[be32_to_cpu(q_resources->db_index)]); 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* gvnic can only transmit from a Registered Segment. 228c2ecf20Sopenharmony_ci * We copy skb payloads into the registered segment before writing Tx 238c2ecf20Sopenharmony_ci * descriptors and ringing the Tx doorbell. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * gve_tx_fifo_* manages the Registered Segment as a FIFO - clients must 268c2ecf20Sopenharmony_ci * free allocations in the order they were allocated. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int gve_tx_fifo_init(struct gve_priv *priv, struct gve_tx_fifo *fifo) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci fifo->base = vmap(fifo->qpl->pages, fifo->qpl->num_entries, VM_MAP, 328c2ecf20Sopenharmony_ci PAGE_KERNEL); 338c2ecf20Sopenharmony_ci if (unlikely(!fifo->base)) { 348c2ecf20Sopenharmony_ci netif_err(priv, drv, priv->dev, "Failed to vmap fifo, qpl_id = %d\n", 358c2ecf20Sopenharmony_ci fifo->qpl->id); 368c2ecf20Sopenharmony_ci return -ENOMEM; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci fifo->size = fifo->qpl->num_entries * PAGE_SIZE; 408c2ecf20Sopenharmony_ci atomic_set(&fifo->available, fifo->size); 418c2ecf20Sopenharmony_ci fifo->head = 0; 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void gve_tx_fifo_release(struct gve_priv *priv, struct gve_tx_fifo *fifo) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci WARN(atomic_read(&fifo->available) != fifo->size, 488c2ecf20Sopenharmony_ci "Releasing non-empty fifo"); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci vunmap(fifo->base); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int gve_tx_fifo_pad_alloc_one_frag(struct gve_tx_fifo *fifo, 548c2ecf20Sopenharmony_ci size_t bytes) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci return (fifo->head + bytes < fifo->size) ? 0 : fifo->size - fifo->head; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool gve_tx_fifo_can_alloc(struct gve_tx_fifo *fifo, size_t bytes) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return (atomic_read(&fifo->available) <= bytes) ? false : true; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* gve_tx_alloc_fifo - Allocate fragment(s) from Tx FIFO 658c2ecf20Sopenharmony_ci * @fifo: FIFO to allocate from 668c2ecf20Sopenharmony_ci * @bytes: Allocation size 678c2ecf20Sopenharmony_ci * @iov: Scatter-gather elements to fill with allocation fragment base/len 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * Returns number of valid elements in iov[] or negative on error. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * Allocations from a given FIFO must be externally synchronized but concurrent 728c2ecf20Sopenharmony_ci * allocation and frees are allowed. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic int gve_tx_alloc_fifo(struct gve_tx_fifo *fifo, size_t bytes, 758c2ecf20Sopenharmony_ci struct gve_tx_iovec iov[2]) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci size_t overflow, padding; 788c2ecf20Sopenharmony_ci u32 aligned_head; 798c2ecf20Sopenharmony_ci int nfrags = 0; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (!bytes) 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* This check happens before we know how much padding is needed to 858c2ecf20Sopenharmony_ci * align to a cacheline boundary for the payload, but that is fine, 868c2ecf20Sopenharmony_ci * because the FIFO head always start aligned, and the FIFO's boundaries 878c2ecf20Sopenharmony_ci * are aligned, so if there is space for the data, there is space for 888c2ecf20Sopenharmony_ci * the padding to the next alignment. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci WARN(!gve_tx_fifo_can_alloc(fifo, bytes), 918c2ecf20Sopenharmony_ci "Reached %s when there's not enough space in the fifo", __func__); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci nfrags++; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci iov[0].iov_offset = fifo->head; 968c2ecf20Sopenharmony_ci iov[0].iov_len = bytes; 978c2ecf20Sopenharmony_ci fifo->head += bytes; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (fifo->head > fifo->size) { 1008c2ecf20Sopenharmony_ci /* If the allocation did not fit in the tail fragment of the 1018c2ecf20Sopenharmony_ci * FIFO, also use the head fragment. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci nfrags++; 1048c2ecf20Sopenharmony_ci overflow = fifo->head - fifo->size; 1058c2ecf20Sopenharmony_ci iov[0].iov_len -= overflow; 1068c2ecf20Sopenharmony_ci iov[1].iov_offset = 0; /* Start of fifo*/ 1078c2ecf20Sopenharmony_ci iov[1].iov_len = overflow; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci fifo->head = overflow; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Re-align to a cacheline boundary */ 1138c2ecf20Sopenharmony_ci aligned_head = L1_CACHE_ALIGN(fifo->head); 1148c2ecf20Sopenharmony_ci padding = aligned_head - fifo->head; 1158c2ecf20Sopenharmony_ci iov[nfrags - 1].iov_padding = padding; 1168c2ecf20Sopenharmony_ci atomic_sub(bytes + padding, &fifo->available); 1178c2ecf20Sopenharmony_ci fifo->head = aligned_head; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (fifo->head == fifo->size) 1208c2ecf20Sopenharmony_ci fifo->head = 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return nfrags; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* gve_tx_free_fifo - Return space to Tx FIFO 1268c2ecf20Sopenharmony_ci * @fifo: FIFO to return fragments to 1278c2ecf20Sopenharmony_ci * @bytes: Bytes to free 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cistatic void gve_tx_free_fifo(struct gve_tx_fifo *fifo, size_t bytes) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci atomic_add(bytes, &fifo->available); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void gve_tx_remove_from_block(struct gve_priv *priv, int queue_idx) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct gve_notify_block *block = 1378c2ecf20Sopenharmony_ci &priv->ntfy_blocks[gve_tx_idx_to_ntfy(priv, queue_idx)]; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci block->tx = NULL; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int gve_clean_tx_done(struct gve_priv *priv, struct gve_tx_ring *tx, 1438c2ecf20Sopenharmony_ci u32 to_do, bool try_to_wake); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void gve_tx_free_ring(struct gve_priv *priv, int idx) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct gve_tx_ring *tx = &priv->tx[idx]; 1488c2ecf20Sopenharmony_ci struct device *hdev = &priv->pdev->dev; 1498c2ecf20Sopenharmony_ci size_t bytes; 1508c2ecf20Sopenharmony_ci u32 slots; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci gve_tx_remove_from_block(priv, idx); 1538c2ecf20Sopenharmony_ci slots = tx->mask + 1; 1548c2ecf20Sopenharmony_ci gve_clean_tx_done(priv, tx, tx->req, false); 1558c2ecf20Sopenharmony_ci netdev_tx_reset_queue(tx->netdev_txq); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci dma_free_coherent(hdev, sizeof(*tx->q_resources), 1588c2ecf20Sopenharmony_ci tx->q_resources, tx->q_resources_bus); 1598c2ecf20Sopenharmony_ci tx->q_resources = NULL; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci gve_tx_fifo_release(priv, &tx->tx_fifo); 1628c2ecf20Sopenharmony_ci gve_unassign_qpl(priv, tx->tx_fifo.qpl->id); 1638c2ecf20Sopenharmony_ci tx->tx_fifo.qpl = NULL; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci bytes = sizeof(*tx->desc) * slots; 1668c2ecf20Sopenharmony_ci dma_free_coherent(hdev, bytes, tx->desc, tx->bus); 1678c2ecf20Sopenharmony_ci tx->desc = NULL; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci vfree(tx->info); 1708c2ecf20Sopenharmony_ci tx->info = NULL; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci netif_dbg(priv, drv, priv->dev, "freed tx queue %d\n", idx); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void gve_tx_add_to_block(struct gve_priv *priv, int queue_idx) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int ntfy_idx = gve_tx_idx_to_ntfy(priv, queue_idx); 1788c2ecf20Sopenharmony_ci struct gve_notify_block *block = &priv->ntfy_blocks[ntfy_idx]; 1798c2ecf20Sopenharmony_ci struct gve_tx_ring *tx = &priv->tx[queue_idx]; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci block->tx = tx; 1828c2ecf20Sopenharmony_ci tx->ntfy_id = ntfy_idx; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int gve_tx_alloc_ring(struct gve_priv *priv, int idx) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct gve_tx_ring *tx = &priv->tx[idx]; 1888c2ecf20Sopenharmony_ci struct device *hdev = &priv->pdev->dev; 1898c2ecf20Sopenharmony_ci u32 slots = priv->tx_desc_cnt; 1908c2ecf20Sopenharmony_ci size_t bytes; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* Make sure everything is zeroed to start */ 1938c2ecf20Sopenharmony_ci memset(tx, 0, sizeof(*tx)); 1948c2ecf20Sopenharmony_ci tx->q_num = idx; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci tx->mask = slots - 1; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* alloc metadata */ 1998c2ecf20Sopenharmony_ci tx->info = vzalloc(sizeof(*tx->info) * slots); 2008c2ecf20Sopenharmony_ci if (!tx->info) 2018c2ecf20Sopenharmony_ci return -ENOMEM; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* alloc tx queue */ 2048c2ecf20Sopenharmony_ci bytes = sizeof(*tx->desc) * slots; 2058c2ecf20Sopenharmony_ci tx->desc = dma_alloc_coherent(hdev, bytes, &tx->bus, GFP_KERNEL); 2068c2ecf20Sopenharmony_ci if (!tx->desc) 2078c2ecf20Sopenharmony_ci goto abort_with_info; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci tx->tx_fifo.qpl = gve_assign_tx_qpl(priv); 2108c2ecf20Sopenharmony_ci if (!tx->tx_fifo.qpl) 2118c2ecf20Sopenharmony_ci goto abort_with_desc; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* map Tx FIFO */ 2148c2ecf20Sopenharmony_ci if (gve_tx_fifo_init(priv, &tx->tx_fifo)) 2158c2ecf20Sopenharmony_ci goto abort_with_qpl; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci tx->q_resources = 2188c2ecf20Sopenharmony_ci dma_alloc_coherent(hdev, 2198c2ecf20Sopenharmony_ci sizeof(*tx->q_resources), 2208c2ecf20Sopenharmony_ci &tx->q_resources_bus, 2218c2ecf20Sopenharmony_ci GFP_KERNEL); 2228c2ecf20Sopenharmony_ci if (!tx->q_resources) 2238c2ecf20Sopenharmony_ci goto abort_with_fifo; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci netif_dbg(priv, drv, priv->dev, "tx[%d]->bus=%lx\n", idx, 2268c2ecf20Sopenharmony_ci (unsigned long)tx->bus); 2278c2ecf20Sopenharmony_ci tx->netdev_txq = netdev_get_tx_queue(priv->dev, idx); 2288c2ecf20Sopenharmony_ci gve_tx_add_to_block(priv, idx); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciabort_with_fifo: 2338c2ecf20Sopenharmony_ci gve_tx_fifo_release(priv, &tx->tx_fifo); 2348c2ecf20Sopenharmony_ciabort_with_qpl: 2358c2ecf20Sopenharmony_ci gve_unassign_qpl(priv, tx->tx_fifo.qpl->id); 2368c2ecf20Sopenharmony_ciabort_with_desc: 2378c2ecf20Sopenharmony_ci dma_free_coherent(hdev, bytes, tx->desc, tx->bus); 2388c2ecf20Sopenharmony_ci tx->desc = NULL; 2398c2ecf20Sopenharmony_ciabort_with_info: 2408c2ecf20Sopenharmony_ci vfree(tx->info); 2418c2ecf20Sopenharmony_ci tx->info = NULL; 2428c2ecf20Sopenharmony_ci return -ENOMEM; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciint gve_tx_alloc_rings(struct gve_priv *priv) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int err = 0; 2488c2ecf20Sopenharmony_ci int i; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci for (i = 0; i < priv->tx_cfg.num_queues; i++) { 2518c2ecf20Sopenharmony_ci err = gve_tx_alloc_ring(priv, i); 2528c2ecf20Sopenharmony_ci if (err) { 2538c2ecf20Sopenharmony_ci netif_err(priv, drv, priv->dev, 2548c2ecf20Sopenharmony_ci "Failed to alloc tx ring=%d: err=%d\n", 2558c2ecf20Sopenharmony_ci i, err); 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci /* Unallocate if there was an error */ 2608c2ecf20Sopenharmony_ci if (err) { 2618c2ecf20Sopenharmony_ci int j; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 2648c2ecf20Sopenharmony_ci gve_tx_free_ring(priv, j); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid gve_tx_free_rings(struct gve_priv *priv) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int i; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci for (i = 0; i < priv->tx_cfg.num_queues; i++) 2748c2ecf20Sopenharmony_ci gve_tx_free_ring(priv, i); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* gve_tx_avail - Calculates the number of slots available in the ring 2788c2ecf20Sopenharmony_ci * @tx: tx ring to check 2798c2ecf20Sopenharmony_ci * 2808c2ecf20Sopenharmony_ci * Returns the number of slots available 2818c2ecf20Sopenharmony_ci * 2828c2ecf20Sopenharmony_ci * The capacity of the queue is mask + 1. We don't need to reserve an entry. 2838c2ecf20Sopenharmony_ci **/ 2848c2ecf20Sopenharmony_cistatic inline u32 gve_tx_avail(struct gve_tx_ring *tx) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci return tx->mask + 1 - (tx->req - tx->done); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic inline int gve_skb_fifo_bytes_required(struct gve_tx_ring *tx, 2908c2ecf20Sopenharmony_ci struct sk_buff *skb) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci int pad_bytes, align_hdr_pad; 2938c2ecf20Sopenharmony_ci int bytes; 2948c2ecf20Sopenharmony_ci int hlen; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci hlen = skb_is_gso(skb) ? skb_checksum_start_offset(skb) + 2978c2ecf20Sopenharmony_ci tcp_hdrlen(skb) : skb_headlen(skb); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci pad_bytes = gve_tx_fifo_pad_alloc_one_frag(&tx->tx_fifo, 3008c2ecf20Sopenharmony_ci hlen); 3018c2ecf20Sopenharmony_ci /* We need to take into account the header alignment padding. */ 3028c2ecf20Sopenharmony_ci align_hdr_pad = L1_CACHE_ALIGN(hlen) - hlen; 3038c2ecf20Sopenharmony_ci bytes = align_hdr_pad + pad_bytes + skb->len; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return bytes; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/* The most descriptors we could need are 3 - 1 for the headers, 1 for 3098c2ecf20Sopenharmony_ci * the beginning of the payload at the end of the FIFO, and 1 if the 3108c2ecf20Sopenharmony_ci * payload wraps to the beginning of the FIFO. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci#define MAX_TX_DESC_NEEDED 3 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* Check if sufficient resources (descriptor ring space, FIFO space) are 3158c2ecf20Sopenharmony_ci * available to transmit the given number of bytes. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic inline bool gve_can_tx(struct gve_tx_ring *tx, int bytes_required) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci return (gve_tx_avail(tx) >= MAX_TX_DESC_NEEDED && 3208c2ecf20Sopenharmony_ci gve_tx_fifo_can_alloc(&tx->tx_fifo, bytes_required)); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* Stops the queue if the skb cannot be transmitted. */ 3248c2ecf20Sopenharmony_cistatic int gve_maybe_stop_tx(struct gve_tx_ring *tx, struct sk_buff *skb) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci int bytes_required; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci bytes_required = gve_skb_fifo_bytes_required(tx, skb); 3298c2ecf20Sopenharmony_ci if (likely(gve_can_tx(tx, bytes_required))) 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* No space, so stop the queue */ 3338c2ecf20Sopenharmony_ci tx->stop_queue++; 3348c2ecf20Sopenharmony_ci netif_tx_stop_queue(tx->netdev_txq); 3358c2ecf20Sopenharmony_ci smp_mb(); /* sync with restarting queue in gve_clean_tx_done() */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Now check for resources again, in case gve_clean_tx_done() freed 3388c2ecf20Sopenharmony_ci * resources after we checked and we stopped the queue after 3398c2ecf20Sopenharmony_ci * gve_clean_tx_done() checked. 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * gve_maybe_stop_tx() gve_clean_tx_done() 3428c2ecf20Sopenharmony_ci * nsegs/can_alloc test failed 3438c2ecf20Sopenharmony_ci * gve_tx_free_fifo() 3448c2ecf20Sopenharmony_ci * if (tx queue stopped) 3458c2ecf20Sopenharmony_ci * netif_tx_queue_wake() 3468c2ecf20Sopenharmony_ci * netif_tx_stop_queue() 3478c2ecf20Sopenharmony_ci * Need to check again for space here! 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci if (likely(!gve_can_tx(tx, bytes_required))) 3508c2ecf20Sopenharmony_ci return -EBUSY; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci netif_tx_start_queue(tx->netdev_txq); 3538c2ecf20Sopenharmony_ci tx->wake_queue++; 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void gve_tx_fill_pkt_desc(union gve_tx_desc *pkt_desc, 3588c2ecf20Sopenharmony_ci struct sk_buff *skb, bool is_gso, 3598c2ecf20Sopenharmony_ci int l4_hdr_offset, u32 desc_cnt, 3608c2ecf20Sopenharmony_ci u16 hlen, u64 addr) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci /* l4_hdr_offset and csum_offset are in units of 16-bit words */ 3638c2ecf20Sopenharmony_ci if (is_gso) { 3648c2ecf20Sopenharmony_ci pkt_desc->pkt.type_flags = GVE_TXD_TSO | GVE_TXF_L4CSUM; 3658c2ecf20Sopenharmony_ci pkt_desc->pkt.l4_csum_offset = skb->csum_offset >> 1; 3668c2ecf20Sopenharmony_ci pkt_desc->pkt.l4_hdr_offset = l4_hdr_offset >> 1; 3678c2ecf20Sopenharmony_ci } else if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { 3688c2ecf20Sopenharmony_ci pkt_desc->pkt.type_flags = GVE_TXD_STD | GVE_TXF_L4CSUM; 3698c2ecf20Sopenharmony_ci pkt_desc->pkt.l4_csum_offset = skb->csum_offset >> 1; 3708c2ecf20Sopenharmony_ci pkt_desc->pkt.l4_hdr_offset = l4_hdr_offset >> 1; 3718c2ecf20Sopenharmony_ci } else { 3728c2ecf20Sopenharmony_ci pkt_desc->pkt.type_flags = GVE_TXD_STD; 3738c2ecf20Sopenharmony_ci pkt_desc->pkt.l4_csum_offset = 0; 3748c2ecf20Sopenharmony_ci pkt_desc->pkt.l4_hdr_offset = 0; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci pkt_desc->pkt.desc_cnt = desc_cnt; 3778c2ecf20Sopenharmony_ci pkt_desc->pkt.len = cpu_to_be16(skb->len); 3788c2ecf20Sopenharmony_ci pkt_desc->pkt.seg_len = cpu_to_be16(hlen); 3798c2ecf20Sopenharmony_ci pkt_desc->pkt.seg_addr = cpu_to_be64(addr); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void gve_tx_fill_seg_desc(union gve_tx_desc *seg_desc, 3838c2ecf20Sopenharmony_ci struct sk_buff *skb, bool is_gso, 3848c2ecf20Sopenharmony_ci u16 len, u64 addr) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci seg_desc->seg.type_flags = GVE_TXD_SEG; 3878c2ecf20Sopenharmony_ci if (is_gso) { 3888c2ecf20Sopenharmony_ci if (skb_is_gso_v6(skb)) 3898c2ecf20Sopenharmony_ci seg_desc->seg.type_flags |= GVE_TXSF_IPV6; 3908c2ecf20Sopenharmony_ci seg_desc->seg.l3_offset = skb_network_offset(skb) >> 1; 3918c2ecf20Sopenharmony_ci seg_desc->seg.mss = cpu_to_be16(skb_shinfo(skb)->gso_size); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci seg_desc->seg.seg_len = cpu_to_be16(len); 3948c2ecf20Sopenharmony_ci seg_desc->seg.seg_addr = cpu_to_be64(addr); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void gve_dma_sync_for_device(struct device *dev, dma_addr_t *page_buses, 3988c2ecf20Sopenharmony_ci u64 iov_offset, u64 iov_len) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci u64 last_page = (iov_offset + iov_len - 1) / PAGE_SIZE; 4018c2ecf20Sopenharmony_ci u64 first_page = iov_offset / PAGE_SIZE; 4028c2ecf20Sopenharmony_ci dma_addr_t dma; 4038c2ecf20Sopenharmony_ci u64 page; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci for (page = first_page; page <= last_page; page++) { 4068c2ecf20Sopenharmony_ci dma = page_buses[page]; 4078c2ecf20Sopenharmony_ci dma_sync_single_for_device(dev, dma, PAGE_SIZE, DMA_TO_DEVICE); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int gve_tx_add_skb(struct gve_tx_ring *tx, struct sk_buff *skb, 4128c2ecf20Sopenharmony_ci struct device *dev) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci int pad_bytes, hlen, hdr_nfrags, payload_nfrags, l4_hdr_offset; 4158c2ecf20Sopenharmony_ci union gve_tx_desc *pkt_desc, *seg_desc; 4168c2ecf20Sopenharmony_ci struct gve_tx_buffer_state *info; 4178c2ecf20Sopenharmony_ci bool is_gso = skb_is_gso(skb); 4188c2ecf20Sopenharmony_ci u32 idx = tx->req & tx->mask; 4198c2ecf20Sopenharmony_ci int payload_iov = 2; 4208c2ecf20Sopenharmony_ci int copy_offset; 4218c2ecf20Sopenharmony_ci u32 next_idx; 4228c2ecf20Sopenharmony_ci int i; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci info = &tx->info[idx]; 4258c2ecf20Sopenharmony_ci pkt_desc = &tx->desc[idx]; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci l4_hdr_offset = skb_checksum_start_offset(skb); 4288c2ecf20Sopenharmony_ci /* If the skb is gso, then we want the tcp header in the first segment 4298c2ecf20Sopenharmony_ci * otherwise we want the linear portion of the skb (which will contain 4308c2ecf20Sopenharmony_ci * the checksum because skb->csum_start and skb->csum_offset are given 4318c2ecf20Sopenharmony_ci * relative to skb->head) in the first segment. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci hlen = is_gso ? l4_hdr_offset + tcp_hdrlen(skb) : 4348c2ecf20Sopenharmony_ci skb_headlen(skb); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci info->skb = skb; 4378c2ecf20Sopenharmony_ci /* We don't want to split the header, so if necessary, pad to the end 4388c2ecf20Sopenharmony_ci * of the fifo and then put the header at the beginning of the fifo. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci pad_bytes = gve_tx_fifo_pad_alloc_one_frag(&tx->tx_fifo, hlen); 4418c2ecf20Sopenharmony_ci hdr_nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, hlen + pad_bytes, 4428c2ecf20Sopenharmony_ci &info->iov[0]); 4438c2ecf20Sopenharmony_ci WARN(!hdr_nfrags, "hdr_nfrags should never be 0!"); 4448c2ecf20Sopenharmony_ci payload_nfrags = gve_tx_alloc_fifo(&tx->tx_fifo, skb->len - hlen, 4458c2ecf20Sopenharmony_ci &info->iov[payload_iov]); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci gve_tx_fill_pkt_desc(pkt_desc, skb, is_gso, l4_hdr_offset, 4488c2ecf20Sopenharmony_ci 1 + payload_nfrags, hlen, 4498c2ecf20Sopenharmony_ci info->iov[hdr_nfrags - 1].iov_offset); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci skb_copy_bits(skb, 0, 4528c2ecf20Sopenharmony_ci tx->tx_fifo.base + info->iov[hdr_nfrags - 1].iov_offset, 4538c2ecf20Sopenharmony_ci hlen); 4548c2ecf20Sopenharmony_ci gve_dma_sync_for_device(dev, tx->tx_fifo.qpl->page_buses, 4558c2ecf20Sopenharmony_ci info->iov[hdr_nfrags - 1].iov_offset, 4568c2ecf20Sopenharmony_ci info->iov[hdr_nfrags - 1].iov_len); 4578c2ecf20Sopenharmony_ci copy_offset = hlen; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci for (i = payload_iov; i < payload_nfrags + payload_iov; i++) { 4608c2ecf20Sopenharmony_ci next_idx = (tx->req + 1 + i - payload_iov) & tx->mask; 4618c2ecf20Sopenharmony_ci seg_desc = &tx->desc[next_idx]; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci gve_tx_fill_seg_desc(seg_desc, skb, is_gso, 4648c2ecf20Sopenharmony_ci info->iov[i].iov_len, 4658c2ecf20Sopenharmony_ci info->iov[i].iov_offset); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci skb_copy_bits(skb, copy_offset, 4688c2ecf20Sopenharmony_ci tx->tx_fifo.base + info->iov[i].iov_offset, 4698c2ecf20Sopenharmony_ci info->iov[i].iov_len); 4708c2ecf20Sopenharmony_ci gve_dma_sync_for_device(dev, tx->tx_fifo.qpl->page_buses, 4718c2ecf20Sopenharmony_ci info->iov[i].iov_offset, 4728c2ecf20Sopenharmony_ci info->iov[i].iov_len); 4738c2ecf20Sopenharmony_ci copy_offset += info->iov[i].iov_len; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return 1 + payload_nfrags; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cinetdev_tx_t gve_tx(struct sk_buff *skb, struct net_device *dev) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct gve_priv *priv = netdev_priv(dev); 4828c2ecf20Sopenharmony_ci struct gve_tx_ring *tx; 4838c2ecf20Sopenharmony_ci int nsegs; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci WARN(skb_get_queue_mapping(skb) >= priv->tx_cfg.num_queues, 4868c2ecf20Sopenharmony_ci "skb queue index out of range"); 4878c2ecf20Sopenharmony_ci tx = &priv->tx[skb_get_queue_mapping(skb)]; 4888c2ecf20Sopenharmony_ci if (unlikely(gve_maybe_stop_tx(tx, skb))) { 4898c2ecf20Sopenharmony_ci /* We need to ring the txq doorbell -- we have stopped the Tx 4908c2ecf20Sopenharmony_ci * queue for want of resources, but prior calls to gve_tx() 4918c2ecf20Sopenharmony_ci * may have added descriptors without ringing the doorbell. 4928c2ecf20Sopenharmony_ci */ 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci gve_tx_put_doorbell(priv, tx->q_resources, tx->req); 4958c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci nsegs = gve_tx_add_skb(tx, skb, &priv->pdev->dev); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci netdev_tx_sent_queue(tx->netdev_txq, skb->len); 5008c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* give packets to NIC */ 5038c2ecf20Sopenharmony_ci tx->req += nsegs; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (!netif_xmit_stopped(tx->netdev_txq) && netdev_xmit_more()) 5068c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci gve_tx_put_doorbell(priv, tx->q_resources, tx->req); 5098c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci#define GVE_TX_START_THRESH PAGE_SIZE 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic int gve_clean_tx_done(struct gve_priv *priv, struct gve_tx_ring *tx, 5158c2ecf20Sopenharmony_ci u32 to_do, bool try_to_wake) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct gve_tx_buffer_state *info; 5188c2ecf20Sopenharmony_ci u64 pkts = 0, bytes = 0; 5198c2ecf20Sopenharmony_ci size_t space_freed = 0; 5208c2ecf20Sopenharmony_ci struct sk_buff *skb; 5218c2ecf20Sopenharmony_ci int i, j; 5228c2ecf20Sopenharmony_ci u32 idx; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for (j = 0; j < to_do; j++) { 5258c2ecf20Sopenharmony_ci idx = tx->done & tx->mask; 5268c2ecf20Sopenharmony_ci netif_info(priv, tx_done, priv->dev, 5278c2ecf20Sopenharmony_ci "[%d] %s: idx=%d (req=%u done=%u)\n", 5288c2ecf20Sopenharmony_ci tx->q_num, __func__, idx, tx->req, tx->done); 5298c2ecf20Sopenharmony_ci info = &tx->info[idx]; 5308c2ecf20Sopenharmony_ci skb = info->skb; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* Mark as free */ 5338c2ecf20Sopenharmony_ci if (skb) { 5348c2ecf20Sopenharmony_ci info->skb = NULL; 5358c2ecf20Sopenharmony_ci bytes += skb->len; 5368c2ecf20Sopenharmony_ci pkts++; 5378c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 5388c2ecf20Sopenharmony_ci /* FIFO free */ 5398c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(info->iov); i++) { 5408c2ecf20Sopenharmony_ci space_freed += info->iov[i].iov_len + 5418c2ecf20Sopenharmony_ci info->iov[i].iov_padding; 5428c2ecf20Sopenharmony_ci info->iov[i].iov_len = 0; 5438c2ecf20Sopenharmony_ci info->iov[i].iov_padding = 0; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci tx->done++; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci gve_tx_free_fifo(&tx->tx_fifo, space_freed); 5508c2ecf20Sopenharmony_ci u64_stats_update_begin(&tx->statss); 5518c2ecf20Sopenharmony_ci tx->bytes_done += bytes; 5528c2ecf20Sopenharmony_ci tx->pkt_done += pkts; 5538c2ecf20Sopenharmony_ci u64_stats_update_end(&tx->statss); 5548c2ecf20Sopenharmony_ci netdev_tx_completed_queue(tx->netdev_txq, pkts, bytes); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* start the queue if we've stopped it */ 5578c2ecf20Sopenharmony_ci#ifndef CONFIG_BQL 5588c2ecf20Sopenharmony_ci /* Make sure that the doorbells are synced */ 5598c2ecf20Sopenharmony_ci smp_mb(); 5608c2ecf20Sopenharmony_ci#endif 5618c2ecf20Sopenharmony_ci if (try_to_wake && netif_tx_queue_stopped(tx->netdev_txq) && 5628c2ecf20Sopenharmony_ci likely(gve_can_tx(tx, GVE_TX_START_THRESH))) { 5638c2ecf20Sopenharmony_ci tx->wake_queue++; 5648c2ecf20Sopenharmony_ci netif_tx_wake_queue(tx->netdev_txq); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return pkts; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci__be32 gve_tx_load_event_counter(struct gve_priv *priv, 5718c2ecf20Sopenharmony_ci struct gve_tx_ring *tx) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci u32 counter_index = be32_to_cpu((tx->q_resources->counter_index)); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return READ_ONCE(priv->counter_array[counter_index]); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cibool gve_tx_poll(struct gve_notify_block *block, int budget) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct gve_priv *priv = block->priv; 5818c2ecf20Sopenharmony_ci struct gve_tx_ring *tx = block->tx; 5828c2ecf20Sopenharmony_ci bool repoll = false; 5838c2ecf20Sopenharmony_ci u32 nic_done; 5848c2ecf20Sopenharmony_ci u32 to_do; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* If budget is 0, do all the work */ 5878c2ecf20Sopenharmony_ci if (budget == 0) 5888c2ecf20Sopenharmony_ci budget = INT_MAX; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Find out how much work there is to be done */ 5918c2ecf20Sopenharmony_ci tx->last_nic_done = gve_tx_load_event_counter(priv, tx); 5928c2ecf20Sopenharmony_ci nic_done = be32_to_cpu(tx->last_nic_done); 5938c2ecf20Sopenharmony_ci if (budget > 0) { 5948c2ecf20Sopenharmony_ci /* Do as much work as we have that the budget will 5958c2ecf20Sopenharmony_ci * allow 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci to_do = min_t(u32, (nic_done - tx->done), budget); 5988c2ecf20Sopenharmony_ci gve_clean_tx_done(priv, tx, to_do, true); 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci /* If we still have work we want to repoll */ 6018c2ecf20Sopenharmony_ci repoll |= (nic_done != tx->done); 6028c2ecf20Sopenharmony_ci return repoll; 6038c2ecf20Sopenharmony_ci} 604