18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2007 Mellanox Technologies. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 58c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 118c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 128c2ecf20Sopenharmony_ci * conditions are met: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 158c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 168c2ecf20Sopenharmony_ci * disclaimer. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 198c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 208c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 218c2ecf20Sopenharmony_ci * provided with the distribution. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 308c2ecf20Sopenharmony_ci * SOFTWARE. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <asm/page.h> 358c2ecf20Sopenharmony_ci#include <linux/mlx4/cq.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci#include <linux/mlx4/qp.h> 388c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 398c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 408c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 418c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 428c2ecf20Sopenharmony_ci#include <linux/tcp.h> 438c2ecf20Sopenharmony_ci#include <linux/ip.h> 448c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 458c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 468c2ecf20Sopenharmony_ci#include <linux/indirect_call_wrapper.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "mlx4_en.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciint mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, 518c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring **pring, u32 size, 528c2ecf20Sopenharmony_ci u16 stride, int node, int queue_index) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 558c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring; 568c2ecf20Sopenharmony_ci int tmp; 578c2ecf20Sopenharmony_ci int err; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node); 608c2ecf20Sopenharmony_ci if (!ring) { 618c2ecf20Sopenharmony_ci en_err(priv, "Failed allocating TX ring\n"); 628c2ecf20Sopenharmony_ci return -ENOMEM; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ring->size = size; 668c2ecf20Sopenharmony_ci ring->size_mask = size - 1; 678c2ecf20Sopenharmony_ci ring->sp_stride = stride; 688c2ecf20Sopenharmony_ci ring->full_size = ring->size - HEADROOM - MAX_DESC_TXBBS; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci tmp = size * sizeof(struct mlx4_en_tx_info); 718c2ecf20Sopenharmony_ci ring->tx_info = kvmalloc_node(tmp, GFP_KERNEL, node); 728c2ecf20Sopenharmony_ci if (!ring->tx_info) { 738c2ecf20Sopenharmony_ci err = -ENOMEM; 748c2ecf20Sopenharmony_ci goto err_ring; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci en_dbg(DRV, priv, "Allocated tx_info ring at addr:%p size:%d\n", 788c2ecf20Sopenharmony_ci ring->tx_info, tmp); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci ring->bounce_buf = kmalloc_node(MAX_DESC_SIZE, GFP_KERNEL, node); 818c2ecf20Sopenharmony_ci if (!ring->bounce_buf) { 828c2ecf20Sopenharmony_ci ring->bounce_buf = kmalloc(MAX_DESC_SIZE, GFP_KERNEL); 838c2ecf20Sopenharmony_ci if (!ring->bounce_buf) { 848c2ecf20Sopenharmony_ci err = -ENOMEM; 858c2ecf20Sopenharmony_ci goto err_info; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci ring->buf_size = ALIGN(size * ring->sp_stride, MLX4_EN_PAGE_SIZE); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Allocate HW buffers on provided NUMA node */ 918c2ecf20Sopenharmony_ci set_dev_node(&mdev->dev->persist->pdev->dev, node); 928c2ecf20Sopenharmony_ci err = mlx4_alloc_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size); 938c2ecf20Sopenharmony_ci set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); 948c2ecf20Sopenharmony_ci if (err) { 958c2ecf20Sopenharmony_ci en_err(priv, "Failed allocating hwq resources\n"); 968c2ecf20Sopenharmony_ci goto err_bounce; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ring->buf = ring->sp_wqres.buf.direct.buf; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci en_dbg(DRV, priv, "Allocated TX ring (addr:%p) - buf:%p size:%d buf_size:%d dma:%llx\n", 1028c2ecf20Sopenharmony_ci ring, ring->buf, ring->size, ring->buf_size, 1038c2ecf20Sopenharmony_ci (unsigned long long) ring->sp_wqres.buf.direct.map); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci err = mlx4_qp_reserve_range(mdev->dev, 1, 1, &ring->qpn, 1068c2ecf20Sopenharmony_ci MLX4_RESERVE_ETH_BF_QP, 1078c2ecf20Sopenharmony_ci MLX4_RES_USAGE_DRIVER); 1088c2ecf20Sopenharmony_ci if (err) { 1098c2ecf20Sopenharmony_ci en_err(priv, "failed reserving qp for TX ring\n"); 1108c2ecf20Sopenharmony_ci goto err_hwq_res; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->sp_qp); 1148c2ecf20Sopenharmony_ci if (err) { 1158c2ecf20Sopenharmony_ci en_err(priv, "Failed allocating qp %d\n", ring->qpn); 1168c2ecf20Sopenharmony_ci goto err_reserve; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci ring->sp_qp.event = mlx4_en_sqp_event; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci err = mlx4_bf_alloc(mdev->dev, &ring->bf, node); 1218c2ecf20Sopenharmony_ci if (err) { 1228c2ecf20Sopenharmony_ci en_dbg(DRV, priv, "working without blueflame (%d)\n", err); 1238c2ecf20Sopenharmony_ci ring->bf.uar = &mdev->priv_uar; 1248c2ecf20Sopenharmony_ci ring->bf.uar->map = mdev->uar_map; 1258c2ecf20Sopenharmony_ci ring->bf_enabled = false; 1268c2ecf20Sopenharmony_ci ring->bf_alloced = false; 1278c2ecf20Sopenharmony_ci priv->pflags &= ~MLX4_EN_PRIV_FLAGS_BLUEFLAME; 1288c2ecf20Sopenharmony_ci } else { 1298c2ecf20Sopenharmony_ci ring->bf_alloced = true; 1308c2ecf20Sopenharmony_ci ring->bf_enabled = !!(priv->pflags & 1318c2ecf20Sopenharmony_ci MLX4_EN_PRIV_FLAGS_BLUEFLAME); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ring->hwtstamp_tx_type = priv->hwtstamp_config.tx_type; 1358c2ecf20Sopenharmony_ci ring->queue_index = queue_index; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (queue_index < priv->num_tx_rings_p_up) 1388c2ecf20Sopenharmony_ci cpumask_set_cpu(cpumask_local_spread(queue_index, 1398c2ecf20Sopenharmony_ci priv->mdev->dev->numa_node), 1408c2ecf20Sopenharmony_ci &ring->sp_affinity_mask); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci *pring = ring; 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cierr_reserve: 1468c2ecf20Sopenharmony_ci mlx4_qp_release_range(mdev->dev, ring->qpn, 1); 1478c2ecf20Sopenharmony_cierr_hwq_res: 1488c2ecf20Sopenharmony_ci mlx4_free_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size); 1498c2ecf20Sopenharmony_cierr_bounce: 1508c2ecf20Sopenharmony_ci kfree(ring->bounce_buf); 1518c2ecf20Sopenharmony_ci ring->bounce_buf = NULL; 1528c2ecf20Sopenharmony_cierr_info: 1538c2ecf20Sopenharmony_ci kvfree(ring->tx_info); 1548c2ecf20Sopenharmony_ci ring->tx_info = NULL; 1558c2ecf20Sopenharmony_cierr_ring: 1568c2ecf20Sopenharmony_ci kfree(ring); 1578c2ecf20Sopenharmony_ci *pring = NULL; 1588c2ecf20Sopenharmony_ci return err; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_civoid mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, 1628c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring **pring) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 1658c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring = *pring; 1668c2ecf20Sopenharmony_ci en_dbg(DRV, priv, "Destroying tx ring, qpn: %d\n", ring->qpn); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (ring->bf_alloced) 1698c2ecf20Sopenharmony_ci mlx4_bf_free(mdev->dev, &ring->bf); 1708c2ecf20Sopenharmony_ci mlx4_qp_remove(mdev->dev, &ring->sp_qp); 1718c2ecf20Sopenharmony_ci mlx4_qp_free(mdev->dev, &ring->sp_qp); 1728c2ecf20Sopenharmony_ci mlx4_qp_release_range(priv->mdev->dev, ring->qpn, 1); 1738c2ecf20Sopenharmony_ci mlx4_free_hwq_res(mdev->dev, &ring->sp_wqres, ring->buf_size); 1748c2ecf20Sopenharmony_ci kfree(ring->bounce_buf); 1758c2ecf20Sopenharmony_ci ring->bounce_buf = NULL; 1768c2ecf20Sopenharmony_ci kvfree(ring->tx_info); 1778c2ecf20Sopenharmony_ci ring->tx_info = NULL; 1788c2ecf20Sopenharmony_ci kfree(ring); 1798c2ecf20Sopenharmony_ci *pring = NULL; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ciint mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, 1838c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, 1848c2ecf20Sopenharmony_ci int cq, int user_prio) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 1878c2ecf20Sopenharmony_ci int err; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ring->sp_cqn = cq; 1908c2ecf20Sopenharmony_ci ring->prod = 0; 1918c2ecf20Sopenharmony_ci ring->cons = 0xffffffff; 1928c2ecf20Sopenharmony_ci ring->last_nr_txbb = 1; 1938c2ecf20Sopenharmony_ci memset(ring->tx_info, 0, ring->size * sizeof(struct mlx4_en_tx_info)); 1948c2ecf20Sopenharmony_ci memset(ring->buf, 0, ring->buf_size); 1958c2ecf20Sopenharmony_ci ring->free_tx_desc = mlx4_en_free_tx_desc; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ring->sp_qp_state = MLX4_QP_STATE_RST; 1988c2ecf20Sopenharmony_ci ring->doorbell_qpn = cpu_to_be32(ring->sp_qp.qpn << 8); 1998c2ecf20Sopenharmony_ci ring->mr_key = cpu_to_be32(mdev->mr.key); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mlx4_en_fill_qp_context(priv, ring->size, ring->sp_stride, 1, 0, ring->qpn, 2028c2ecf20Sopenharmony_ci ring->sp_cqn, user_prio, &ring->sp_context); 2038c2ecf20Sopenharmony_ci if (ring->bf_alloced) 2048c2ecf20Sopenharmony_ci ring->sp_context.usr_page = 2058c2ecf20Sopenharmony_ci cpu_to_be32(mlx4_to_hw_uar_index(mdev->dev, 2068c2ecf20Sopenharmony_ci ring->bf.uar->index)); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci err = mlx4_qp_to_ready(mdev->dev, &ring->sp_wqres.mtt, &ring->sp_context, 2098c2ecf20Sopenharmony_ci &ring->sp_qp, &ring->sp_qp_state); 2108c2ecf20Sopenharmony_ci if (!cpumask_empty(&ring->sp_affinity_mask)) 2118c2ecf20Sopenharmony_ci netif_set_xps_queue(priv->dev, &ring->sp_affinity_mask, 2128c2ecf20Sopenharmony_ci ring->queue_index); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return err; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_civoid mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv, 2188c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mlx4_qp_modify(mdev->dev, NULL, ring->sp_qp_state, 2238c2ecf20Sopenharmony_ci MLX4_QP_STATE_RST, NULL, 0, 0, &ring->sp_qp); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic inline bool mlx4_en_is_tx_ring_full(struct mlx4_en_tx_ring *ring) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci return ring->prod - ring->cons > ring->full_size; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void mlx4_en_stamp_wqe(struct mlx4_en_priv *priv, 2328c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, int index, 2338c2ecf20Sopenharmony_ci u8 owner) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci __be32 stamp = cpu_to_be32(STAMP_VAL | (!!owner << STAMP_SHIFT)); 2368c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc = ring->buf + (index << LOG_TXBB_SIZE); 2378c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info = &ring->tx_info[index]; 2388c2ecf20Sopenharmony_ci void *end = ring->buf + ring->buf_size; 2398c2ecf20Sopenharmony_ci __be32 *ptr = (__be32 *)tx_desc; 2408c2ecf20Sopenharmony_ci int i; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Optimize the common case when there are no wraparounds */ 2438c2ecf20Sopenharmony_ci if (likely((void *)tx_desc + 2448c2ecf20Sopenharmony_ci (tx_info->nr_txbb << LOG_TXBB_SIZE) <= end)) { 2458c2ecf20Sopenharmony_ci /* Stamp the freed descriptor */ 2468c2ecf20Sopenharmony_ci for (i = 0; i < tx_info->nr_txbb << LOG_TXBB_SIZE; 2478c2ecf20Sopenharmony_ci i += STAMP_STRIDE) { 2488c2ecf20Sopenharmony_ci *ptr = stamp; 2498c2ecf20Sopenharmony_ci ptr += STAMP_DWORDS; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } else { 2528c2ecf20Sopenharmony_ci /* Stamp the freed descriptor */ 2538c2ecf20Sopenharmony_ci for (i = 0; i < tx_info->nr_txbb << LOG_TXBB_SIZE; 2548c2ecf20Sopenharmony_ci i += STAMP_STRIDE) { 2558c2ecf20Sopenharmony_ci *ptr = stamp; 2568c2ecf20Sopenharmony_ci ptr += STAMP_DWORDS; 2578c2ecf20Sopenharmony_ci if ((void *)ptr >= end) { 2588c2ecf20Sopenharmony_ci ptr = ring->buf; 2598c2ecf20Sopenharmony_ci stamp ^= cpu_to_be32(0x80000000); 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(u32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, 2668c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, 2678c2ecf20Sopenharmony_ci int index, u64 timestamp, 2688c2ecf20Sopenharmony_ci int napi_mode)); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciu32 mlx4_en_free_tx_desc(struct mlx4_en_priv *priv, 2718c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, 2728c2ecf20Sopenharmony_ci int index, u64 timestamp, 2738c2ecf20Sopenharmony_ci int napi_mode) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info = &ring->tx_info[index]; 2768c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc = ring->buf + (index << LOG_TXBB_SIZE); 2778c2ecf20Sopenharmony_ci struct mlx4_wqe_data_seg *data = (void *) tx_desc + tx_info->data_offset; 2788c2ecf20Sopenharmony_ci void *end = ring->buf + ring->buf_size; 2798c2ecf20Sopenharmony_ci struct sk_buff *skb = tx_info->skb; 2808c2ecf20Sopenharmony_ci int nr_maps = tx_info->nr_maps; 2818c2ecf20Sopenharmony_ci int i; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* We do not touch skb here, so prefetch skb->users location 2848c2ecf20Sopenharmony_ci * to speedup consume_skb() 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci prefetchw(&skb->users); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (unlikely(timestamp)) { 2898c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps hwts; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci mlx4_en_fill_hwtstamps(priv->mdev, &hwts, timestamp); 2928c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, &hwts); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (!tx_info->inl) { 2968c2ecf20Sopenharmony_ci if (tx_info->linear) 2978c2ecf20Sopenharmony_ci dma_unmap_single(priv->ddev, 2988c2ecf20Sopenharmony_ci tx_info->map0_dma, 2998c2ecf20Sopenharmony_ci tx_info->map0_byte_count, 3008c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 3018c2ecf20Sopenharmony_ci else 3028c2ecf20Sopenharmony_ci dma_unmap_page(priv->ddev, 3038c2ecf20Sopenharmony_ci tx_info->map0_dma, 3048c2ecf20Sopenharmony_ci tx_info->map0_byte_count, 3058c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 3068c2ecf20Sopenharmony_ci /* Optimize the common case when there are no wraparounds */ 3078c2ecf20Sopenharmony_ci if (likely((void *)tx_desc + 3088c2ecf20Sopenharmony_ci (tx_info->nr_txbb << LOG_TXBB_SIZE) <= end)) { 3098c2ecf20Sopenharmony_ci for (i = 1; i < nr_maps; i++) { 3108c2ecf20Sopenharmony_ci data++; 3118c2ecf20Sopenharmony_ci dma_unmap_page(priv->ddev, 3128c2ecf20Sopenharmony_ci (dma_addr_t)be64_to_cpu(data->addr), 3138c2ecf20Sopenharmony_ci be32_to_cpu(data->byte_count), 3148c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } else { 3178c2ecf20Sopenharmony_ci if ((void *)data >= end) 3188c2ecf20Sopenharmony_ci data = ring->buf + ((void *)data - end); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci for (i = 1; i < nr_maps; i++) { 3218c2ecf20Sopenharmony_ci data++; 3228c2ecf20Sopenharmony_ci /* Check for wraparound before unmapping */ 3238c2ecf20Sopenharmony_ci if ((void *) data >= end) 3248c2ecf20Sopenharmony_ci data = ring->buf; 3258c2ecf20Sopenharmony_ci dma_unmap_page(priv->ddev, 3268c2ecf20Sopenharmony_ci (dma_addr_t)be64_to_cpu(data->addr), 3278c2ecf20Sopenharmony_ci be32_to_cpu(data->byte_count), 3288c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci napi_consume_skb(skb, napi_mode); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return tx_info->nr_txbb; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciINDIRECT_CALLABLE_DECLARE(u32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv, 3388c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, 3398c2ecf20Sopenharmony_ci int index, u64 timestamp, 3408c2ecf20Sopenharmony_ci int napi_mode)); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciu32 mlx4_en_recycle_tx_desc(struct mlx4_en_priv *priv, 3438c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, 3448c2ecf20Sopenharmony_ci int index, u64 timestamp, 3458c2ecf20Sopenharmony_ci int napi_mode) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info = &ring->tx_info[index]; 3488c2ecf20Sopenharmony_ci struct mlx4_en_rx_alloc frame = { 3498c2ecf20Sopenharmony_ci .page = tx_info->page, 3508c2ecf20Sopenharmony_ci .dma = tx_info->map0_dma, 3518c2ecf20Sopenharmony_ci }; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!napi_mode || !mlx4_en_rx_recycle(ring->recycle_ring, &frame)) { 3548c2ecf20Sopenharmony_ci dma_unmap_page(priv->ddev, tx_info->map0_dma, 3558c2ecf20Sopenharmony_ci PAGE_SIZE, priv->dma_dir); 3568c2ecf20Sopenharmony_ci put_page(tx_info->page); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return tx_info->nr_txbb; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ciint mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 3658c2ecf20Sopenharmony_ci int cnt = 0; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Skip last polled descriptor */ 3688c2ecf20Sopenharmony_ci ring->cons += ring->last_nr_txbb; 3698c2ecf20Sopenharmony_ci en_dbg(DRV, priv, "Freeing Tx buf - cons:0x%x prod:0x%x\n", 3708c2ecf20Sopenharmony_ci ring->cons, ring->prod); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if ((u32) (ring->prod - ring->cons) > ring->size) { 3738c2ecf20Sopenharmony_ci if (netif_msg_tx_err(priv)) 3748c2ecf20Sopenharmony_ci en_warn(priv, "Tx consumer passed producer!\n"); 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci while (ring->cons != ring->prod) { 3798c2ecf20Sopenharmony_ci ring->last_nr_txbb = ring->free_tx_desc(priv, ring, 3808c2ecf20Sopenharmony_ci ring->cons & ring->size_mask, 3818c2ecf20Sopenharmony_ci 0, 0 /* Non-NAPI caller */); 3828c2ecf20Sopenharmony_ci ring->cons += ring->last_nr_txbb; 3838c2ecf20Sopenharmony_ci cnt++; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (ring->tx_queue) 3878c2ecf20Sopenharmony_ci netdev_tx_reset_queue(ring->tx_queue); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (cnt) 3908c2ecf20Sopenharmony_ci en_dbg(DRV, priv, "Freed %d uncompleted tx descriptors\n", cnt); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return cnt; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void mlx4_en_handle_err_cqe(struct mlx4_en_priv *priv, struct mlx4_err_cqe *err_cqe, 3968c2ecf20Sopenharmony_ci u16 cqe_index, struct mlx4_en_tx_ring *ring) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct mlx4_en_dev *mdev = priv->mdev; 3998c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info; 4008c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc; 4018c2ecf20Sopenharmony_ci u16 wqe_index; 4028c2ecf20Sopenharmony_ci int desc_size; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci en_err(priv, "CQE error - cqn 0x%x, ci 0x%x, vendor syndrome: 0x%x syndrome: 0x%x\n", 4058c2ecf20Sopenharmony_ci ring->sp_cqn, cqe_index, err_cqe->vendor_err_syndrome, err_cqe->syndrome); 4068c2ecf20Sopenharmony_ci print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, err_cqe, sizeof(*err_cqe), 4078c2ecf20Sopenharmony_ci false); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci wqe_index = be16_to_cpu(err_cqe->wqe_index) & ring->size_mask; 4108c2ecf20Sopenharmony_ci tx_info = &ring->tx_info[wqe_index]; 4118c2ecf20Sopenharmony_ci desc_size = tx_info->nr_txbb << LOG_TXBB_SIZE; 4128c2ecf20Sopenharmony_ci en_err(priv, "Related WQE - qpn 0x%x, wqe index 0x%x, wqe size 0x%x\n", ring->qpn, 4138c2ecf20Sopenharmony_ci wqe_index, desc_size); 4148c2ecf20Sopenharmony_ci tx_desc = ring->buf + (wqe_index << LOG_TXBB_SIZE); 4158c2ecf20Sopenharmony_ci print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, tx_desc, desc_size, false); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (test_and_set_bit(MLX4_EN_STATE_FLAG_RESTARTING, &priv->state)) 4188c2ecf20Sopenharmony_ci return; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci en_err(priv, "Scheduling port restart\n"); 4218c2ecf20Sopenharmony_ci queue_work(mdev->workqueue, &priv->restart_task); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciint mlx4_en_process_tx_cq(struct net_device *dev, 4258c2ecf20Sopenharmony_ci struct mlx4_en_cq *cq, int napi_budget) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 4288c2ecf20Sopenharmony_ci struct mlx4_cq *mcq = &cq->mcq; 4298c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring = priv->tx_ring[cq->type][cq->ring]; 4308c2ecf20Sopenharmony_ci struct mlx4_cqe *cqe; 4318c2ecf20Sopenharmony_ci u16 index, ring_index, stamp_index; 4328c2ecf20Sopenharmony_ci u32 txbbs_skipped = 0; 4338c2ecf20Sopenharmony_ci u32 txbbs_stamp = 0; 4348c2ecf20Sopenharmony_ci u32 cons_index = mcq->cons_index; 4358c2ecf20Sopenharmony_ci int size = cq->size; 4368c2ecf20Sopenharmony_ci u32 size_mask = ring->size_mask; 4378c2ecf20Sopenharmony_ci struct mlx4_cqe *buf = cq->buf; 4388c2ecf20Sopenharmony_ci u32 packets = 0; 4398c2ecf20Sopenharmony_ci u32 bytes = 0; 4408c2ecf20Sopenharmony_ci int factor = priv->cqe_factor; 4418c2ecf20Sopenharmony_ci int done = 0; 4428c2ecf20Sopenharmony_ci int budget = priv->tx_work_limit; 4438c2ecf20Sopenharmony_ci u32 last_nr_txbb; 4448c2ecf20Sopenharmony_ci u32 ring_cons; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (unlikely(!priv->port_up)) 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci netdev_txq_bql_complete_prefetchw(ring->tx_queue); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci index = cons_index & size_mask; 4528c2ecf20Sopenharmony_ci cqe = mlx4_en_get_cqe(buf, index, priv->cqe_size) + factor; 4538c2ecf20Sopenharmony_ci last_nr_txbb = READ_ONCE(ring->last_nr_txbb); 4548c2ecf20Sopenharmony_ci ring_cons = READ_ONCE(ring->cons); 4558c2ecf20Sopenharmony_ci ring_index = ring_cons & size_mask; 4568c2ecf20Sopenharmony_ci stamp_index = ring_index; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Process all completed CQEs */ 4598c2ecf20Sopenharmony_ci while (XNOR(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK, 4608c2ecf20Sopenharmony_ci cons_index & size) && (done < budget)) { 4618c2ecf20Sopenharmony_ci u16 new_index; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* 4648c2ecf20Sopenharmony_ci * make sure we read the CQE after we read the 4658c2ecf20Sopenharmony_ci * ownership bit 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci dma_rmb(); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) == 4708c2ecf20Sopenharmony_ci MLX4_CQE_OPCODE_ERROR)) 4718c2ecf20Sopenharmony_ci if (!test_and_set_bit(MLX4_EN_TX_RING_STATE_RECOVERING, &ring->state)) 4728c2ecf20Sopenharmony_ci mlx4_en_handle_err_cqe(priv, (struct mlx4_err_cqe *)cqe, index, 4738c2ecf20Sopenharmony_ci ring); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* Skip over last polled CQE */ 4768c2ecf20Sopenharmony_ci new_index = be16_to_cpu(cqe->wqe_index) & size_mask; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci do { 4798c2ecf20Sopenharmony_ci u64 timestamp = 0; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci txbbs_skipped += last_nr_txbb; 4828c2ecf20Sopenharmony_ci ring_index = (ring_index + last_nr_txbb) & size_mask; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (unlikely(ring->tx_info[ring_index].ts_requested)) 4858c2ecf20Sopenharmony_ci timestamp = mlx4_en_get_cqe_ts(cqe); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* free next descriptor */ 4888c2ecf20Sopenharmony_ci last_nr_txbb = INDIRECT_CALL_2(ring->free_tx_desc, 4898c2ecf20Sopenharmony_ci mlx4_en_free_tx_desc, 4908c2ecf20Sopenharmony_ci mlx4_en_recycle_tx_desc, 4918c2ecf20Sopenharmony_ci priv, ring, ring_index, 4928c2ecf20Sopenharmony_ci timestamp, napi_budget); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci mlx4_en_stamp_wqe(priv, ring, stamp_index, 4958c2ecf20Sopenharmony_ci !!((ring_cons + txbbs_stamp) & 4968c2ecf20Sopenharmony_ci ring->size)); 4978c2ecf20Sopenharmony_ci stamp_index = ring_index; 4988c2ecf20Sopenharmony_ci txbbs_stamp = txbbs_skipped; 4998c2ecf20Sopenharmony_ci packets++; 5008c2ecf20Sopenharmony_ci bytes += ring->tx_info[ring_index].nr_bytes; 5018c2ecf20Sopenharmony_ci } while ((++done < budget) && (ring_index != new_index)); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ++cons_index; 5048c2ecf20Sopenharmony_ci index = cons_index & size_mask; 5058c2ecf20Sopenharmony_ci cqe = mlx4_en_get_cqe(buf, index, priv->cqe_size) + factor; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* 5098c2ecf20Sopenharmony_ci * To prevent CQ overflow we first update CQ consumer and only then 5108c2ecf20Sopenharmony_ci * the ring consumer. 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_ci mcq->cons_index = cons_index; 5138c2ecf20Sopenharmony_ci mlx4_cq_set_ci(mcq); 5148c2ecf20Sopenharmony_ci wmb(); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* we want to dirty this cache line once */ 5178c2ecf20Sopenharmony_ci WRITE_ONCE(ring->last_nr_txbb, last_nr_txbb); 5188c2ecf20Sopenharmony_ci WRITE_ONCE(ring->cons, ring_cons + txbbs_skipped); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (cq->type == TX_XDP) 5218c2ecf20Sopenharmony_ci return done; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci netdev_tx_completed_queue(ring->tx_queue, packets, bytes); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* Wakeup Tx queue if this stopped, and ring is not full. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(ring->tx_queue) && 5288c2ecf20Sopenharmony_ci !mlx4_en_is_tx_ring_full(ring)) { 5298c2ecf20Sopenharmony_ci netif_tx_wake_queue(ring->tx_queue); 5308c2ecf20Sopenharmony_ci ring->wake_queue++; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return done; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_civoid mlx4_en_tx_irq(struct mlx4_cq *mcq) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct mlx4_en_cq *cq = container_of(mcq, struct mlx4_en_cq, mcq); 5398c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(cq->dev); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (likely(priv->port_up)) 5428c2ecf20Sopenharmony_ci napi_schedule_irqoff(&cq->napi); 5438c2ecf20Sopenharmony_ci else 5448c2ecf20Sopenharmony_ci mlx4_en_arm_cq(priv, cq); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* TX CQ polling - called by NAPI */ 5488c2ecf20Sopenharmony_ciint mlx4_en_poll_tx_cq(struct napi_struct *napi, int budget) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); 5518c2ecf20Sopenharmony_ci struct net_device *dev = cq->dev; 5528c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 5538c2ecf20Sopenharmony_ci int work_done; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci work_done = mlx4_en_process_tx_cq(dev, cq, budget); 5568c2ecf20Sopenharmony_ci if (work_done >= budget) 5578c2ecf20Sopenharmony_ci return budget; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (napi_complete_done(napi, work_done)) 5608c2ecf20Sopenharmony_ci mlx4_en_arm_cq(priv, cq); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic struct mlx4_en_tx_desc *mlx4_en_bounce_to_desc(struct mlx4_en_priv *priv, 5668c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring, 5678c2ecf20Sopenharmony_ci u32 index, 5688c2ecf20Sopenharmony_ci unsigned int desc_size) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci u32 copy = (ring->size - index) << LOG_TXBB_SIZE; 5718c2ecf20Sopenharmony_ci int i; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci for (i = desc_size - copy - 4; i >= 0; i -= 4) { 5748c2ecf20Sopenharmony_ci if ((i & (TXBB_SIZE - 1)) == 0) 5758c2ecf20Sopenharmony_ci wmb(); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci *((u32 *) (ring->buf + i)) = 5788c2ecf20Sopenharmony_ci *((u32 *) (ring->bounce_buf + copy + i)); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci for (i = copy - 4; i >= 4 ; i -= 4) { 5828c2ecf20Sopenharmony_ci if ((i & (TXBB_SIZE - 1)) == 0) 5838c2ecf20Sopenharmony_ci wmb(); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci *((u32 *)(ring->buf + (index << LOG_TXBB_SIZE) + i)) = 5868c2ecf20Sopenharmony_ci *((u32 *) (ring->bounce_buf + i)); 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* Return real descriptor location */ 5908c2ecf20Sopenharmony_ci return ring->buf + (index << LOG_TXBB_SIZE); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/* Decide if skb can be inlined in tx descriptor to avoid dma mapping 5948c2ecf20Sopenharmony_ci * 5958c2ecf20Sopenharmony_ci * It seems strange we do not simply use skb_copy_bits(). 5968c2ecf20Sopenharmony_ci * This would allow to inline all skbs iff skb->len <= inline_thold 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * Note that caller already checked skb was not a gso packet 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_cistatic bool is_inline(int inline_thold, const struct sk_buff *skb, 6018c2ecf20Sopenharmony_ci const struct skb_shared_info *shinfo, 6028c2ecf20Sopenharmony_ci void **pfrag) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci void *ptr; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (skb->len > inline_thold || !inline_thold) 6078c2ecf20Sopenharmony_ci return false; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (shinfo->nr_frags == 1) { 6108c2ecf20Sopenharmony_ci ptr = skb_frag_address_safe(&shinfo->frags[0]); 6118c2ecf20Sopenharmony_ci if (unlikely(!ptr)) 6128c2ecf20Sopenharmony_ci return false; 6138c2ecf20Sopenharmony_ci *pfrag = ptr; 6148c2ecf20Sopenharmony_ci return true; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci if (shinfo->nr_frags) 6178c2ecf20Sopenharmony_ci return false; 6188c2ecf20Sopenharmony_ci return true; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic int inline_size(const struct sk_buff *skb) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci if (skb->len + CTRL_SIZE + sizeof(struct mlx4_wqe_inline_seg) 6248c2ecf20Sopenharmony_ci <= MLX4_INLINE_ALIGN) 6258c2ecf20Sopenharmony_ci return ALIGN(skb->len + CTRL_SIZE + 6268c2ecf20Sopenharmony_ci sizeof(struct mlx4_wqe_inline_seg), 16); 6278c2ecf20Sopenharmony_ci else 6288c2ecf20Sopenharmony_ci return ALIGN(skb->len + CTRL_SIZE + 2 * 6298c2ecf20Sopenharmony_ci sizeof(struct mlx4_wqe_inline_seg), 16); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int get_real_size(const struct sk_buff *skb, 6338c2ecf20Sopenharmony_ci const struct skb_shared_info *shinfo, 6348c2ecf20Sopenharmony_ci struct net_device *dev, 6358c2ecf20Sopenharmony_ci int *lso_header_size, 6368c2ecf20Sopenharmony_ci bool *inline_ok, 6378c2ecf20Sopenharmony_ci void **pfrag) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 6408c2ecf20Sopenharmony_ci int real_size; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (shinfo->gso_size) { 6438c2ecf20Sopenharmony_ci *inline_ok = false; 6448c2ecf20Sopenharmony_ci if (skb->encapsulation) 6458c2ecf20Sopenharmony_ci *lso_header_size = (skb_inner_transport_header(skb) - skb->data) + inner_tcp_hdrlen(skb); 6468c2ecf20Sopenharmony_ci else 6478c2ecf20Sopenharmony_ci *lso_header_size = skb_transport_offset(skb) + tcp_hdrlen(skb); 6488c2ecf20Sopenharmony_ci real_size = CTRL_SIZE + shinfo->nr_frags * DS_SIZE + 6498c2ecf20Sopenharmony_ci ALIGN(*lso_header_size + 4, DS_SIZE); 6508c2ecf20Sopenharmony_ci if (unlikely(*lso_header_size != skb_headlen(skb))) { 6518c2ecf20Sopenharmony_ci /* We add a segment for the skb linear buffer only if 6528c2ecf20Sopenharmony_ci * it contains data */ 6538c2ecf20Sopenharmony_ci if (*lso_header_size < skb_headlen(skb)) 6548c2ecf20Sopenharmony_ci real_size += DS_SIZE; 6558c2ecf20Sopenharmony_ci else { 6568c2ecf20Sopenharmony_ci if (netif_msg_tx_err(priv)) 6578c2ecf20Sopenharmony_ci en_warn(priv, "Non-linear headers\n"); 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci } else { 6628c2ecf20Sopenharmony_ci *lso_header_size = 0; 6638c2ecf20Sopenharmony_ci *inline_ok = is_inline(priv->prof->inline_thold, skb, 6648c2ecf20Sopenharmony_ci shinfo, pfrag); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (*inline_ok) 6678c2ecf20Sopenharmony_ci real_size = inline_size(skb); 6688c2ecf20Sopenharmony_ci else 6698c2ecf20Sopenharmony_ci real_size = CTRL_SIZE + 6708c2ecf20Sopenharmony_ci (shinfo->nr_frags + 1) * DS_SIZE; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return real_size; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, 6778c2ecf20Sopenharmony_ci const struct sk_buff *skb, 6788c2ecf20Sopenharmony_ci const struct skb_shared_info *shinfo, 6798c2ecf20Sopenharmony_ci void *fragptr) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct mlx4_wqe_inline_seg *inl = &tx_desc->inl; 6828c2ecf20Sopenharmony_ci int spc = MLX4_INLINE_ALIGN - CTRL_SIZE - sizeof(*inl); 6838c2ecf20Sopenharmony_ci unsigned int hlen = skb_headlen(skb); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (skb->len <= spc) { 6868c2ecf20Sopenharmony_ci if (likely(skb->len >= MIN_PKT_LEN)) { 6878c2ecf20Sopenharmony_ci inl->byte_count = cpu_to_be32(1 << 31 | skb->len); 6888c2ecf20Sopenharmony_ci } else { 6898c2ecf20Sopenharmony_ci inl->byte_count = cpu_to_be32(1 << 31 | MIN_PKT_LEN); 6908c2ecf20Sopenharmony_ci memset(((void *)(inl + 1)) + skb->len, 0, 6918c2ecf20Sopenharmony_ci MIN_PKT_LEN - skb->len); 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, inl + 1, hlen); 6948c2ecf20Sopenharmony_ci if (shinfo->nr_frags) 6958c2ecf20Sopenharmony_ci memcpy(((void *)(inl + 1)) + hlen, fragptr, 6968c2ecf20Sopenharmony_ci skb_frag_size(&shinfo->frags[0])); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci } else { 6998c2ecf20Sopenharmony_ci inl->byte_count = cpu_to_be32(1 << 31 | spc); 7008c2ecf20Sopenharmony_ci if (hlen <= spc) { 7018c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, inl + 1, hlen); 7028c2ecf20Sopenharmony_ci if (hlen < spc) { 7038c2ecf20Sopenharmony_ci memcpy(((void *)(inl + 1)) + hlen, 7048c2ecf20Sopenharmony_ci fragptr, spc - hlen); 7058c2ecf20Sopenharmony_ci fragptr += spc - hlen; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci inl = (void *) (inl + 1) + spc; 7088c2ecf20Sopenharmony_ci memcpy(((void *)(inl + 1)), fragptr, skb->len - spc); 7098c2ecf20Sopenharmony_ci } else { 7108c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, inl + 1, spc); 7118c2ecf20Sopenharmony_ci inl = (void *) (inl + 1) + spc; 7128c2ecf20Sopenharmony_ci skb_copy_from_linear_data_offset(skb, spc, inl + 1, 7138c2ecf20Sopenharmony_ci hlen - spc); 7148c2ecf20Sopenharmony_ci if (shinfo->nr_frags) 7158c2ecf20Sopenharmony_ci memcpy(((void *)(inl + 1)) + hlen - spc, 7168c2ecf20Sopenharmony_ci fragptr, 7178c2ecf20Sopenharmony_ci skb_frag_size(&shinfo->frags[0])); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci dma_wmb(); 7218c2ecf20Sopenharmony_ci inl->byte_count = cpu_to_be32(1 << 31 | (skb->len - spc)); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ciu16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb, 7268c2ecf20Sopenharmony_ci struct net_device *sb_dev) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 7298c2ecf20Sopenharmony_ci u16 rings_p_up = priv->num_tx_rings_p_up; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (netdev_get_num_tc(dev)) 7328c2ecf20Sopenharmony_ci return netdev_pick_tx(dev, skb, NULL); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return netdev_pick_tx(dev, skb, NULL) % rings_p_up; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic void mlx4_bf_copy(void __iomem *dst, const void *src, 7388c2ecf20Sopenharmony_ci unsigned int bytecnt) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci __iowrite64_copy(dst, src, bytecnt / 8); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_civoid mlx4_en_xmit_doorbell(struct mlx4_en_tx_ring *ring) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci wmb(); 7468c2ecf20Sopenharmony_ci /* Since there is no iowrite*_native() that writes the 7478c2ecf20Sopenharmony_ci * value as is, without byteswapping - using the one 7488c2ecf20Sopenharmony_ci * the doesn't do byteswapping in the relevant arch 7498c2ecf20Sopenharmony_ci * endianness. 7508c2ecf20Sopenharmony_ci */ 7518c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN) 7528c2ecf20Sopenharmony_ci iowrite32( 7538c2ecf20Sopenharmony_ci#else 7548c2ecf20Sopenharmony_ci iowrite32be( 7558c2ecf20Sopenharmony_ci#endif 7568c2ecf20Sopenharmony_ci (__force u32)ring->doorbell_qpn, 7578c2ecf20Sopenharmony_ci ring->bf.uar->map + MLX4_SEND_DOORBELL); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic void mlx4_en_tx_write_desc(struct mlx4_en_tx_ring *ring, 7618c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc, 7628c2ecf20Sopenharmony_ci union mlx4_wqe_qpn_vlan qpn_vlan, 7638c2ecf20Sopenharmony_ci int desc_size, int bf_index, 7648c2ecf20Sopenharmony_ci __be32 op_own, bool bf_ok, 7658c2ecf20Sopenharmony_ci bool send_doorbell) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci tx_desc->ctrl.qpn_vlan = qpn_vlan; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (bf_ok) { 7708c2ecf20Sopenharmony_ci op_own |= htonl((bf_index & 0xffff) << 8); 7718c2ecf20Sopenharmony_ci /* Ensure new descriptor hits memory 7728c2ecf20Sopenharmony_ci * before setting ownership of this descriptor to HW 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_ci dma_wmb(); 7758c2ecf20Sopenharmony_ci tx_desc->ctrl.owner_opcode = op_own; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci wmb(); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci mlx4_bf_copy(ring->bf.reg + ring->bf.offset, &tx_desc->ctrl, 7808c2ecf20Sopenharmony_ci desc_size); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci wmb(); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ring->bf.offset ^= ring->bf.buf_size; 7858c2ecf20Sopenharmony_ci } else { 7868c2ecf20Sopenharmony_ci /* Ensure new descriptor hits memory 7878c2ecf20Sopenharmony_ci * before setting ownership of this descriptor to HW 7888c2ecf20Sopenharmony_ci */ 7898c2ecf20Sopenharmony_ci dma_wmb(); 7908c2ecf20Sopenharmony_ci tx_desc->ctrl.owner_opcode = op_own; 7918c2ecf20Sopenharmony_ci if (send_doorbell) 7928c2ecf20Sopenharmony_ci mlx4_en_xmit_doorbell(ring); 7938c2ecf20Sopenharmony_ci else 7948c2ecf20Sopenharmony_ci ring->xmit_more++; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci} 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic bool mlx4_en_build_dma_wqe(struct mlx4_en_priv *priv, 7998c2ecf20Sopenharmony_ci struct skb_shared_info *shinfo, 8008c2ecf20Sopenharmony_ci struct mlx4_wqe_data_seg *data, 8018c2ecf20Sopenharmony_ci struct sk_buff *skb, 8028c2ecf20Sopenharmony_ci int lso_header_size, 8038c2ecf20Sopenharmony_ci __be32 mr_key, 8048c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct device *ddev = priv->ddev; 8078c2ecf20Sopenharmony_ci dma_addr_t dma = 0; 8088c2ecf20Sopenharmony_ci u32 byte_count = 0; 8098c2ecf20Sopenharmony_ci int i_frag; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* Map fragments if any */ 8128c2ecf20Sopenharmony_ci for (i_frag = shinfo->nr_frags - 1; i_frag >= 0; i_frag--) { 8138c2ecf20Sopenharmony_ci const skb_frag_t *frag = &shinfo->frags[i_frag]; 8148c2ecf20Sopenharmony_ci byte_count = skb_frag_size(frag); 8158c2ecf20Sopenharmony_ci dma = skb_frag_dma_map(ddev, frag, 8168c2ecf20Sopenharmony_ci 0, byte_count, 8178c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 8188c2ecf20Sopenharmony_ci if (dma_mapping_error(ddev, dma)) 8198c2ecf20Sopenharmony_ci goto tx_drop_unmap; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci data->addr = cpu_to_be64(dma); 8228c2ecf20Sopenharmony_ci data->lkey = mr_key; 8238c2ecf20Sopenharmony_ci dma_wmb(); 8248c2ecf20Sopenharmony_ci data->byte_count = cpu_to_be32(byte_count); 8258c2ecf20Sopenharmony_ci --data; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Map linear part if needed */ 8298c2ecf20Sopenharmony_ci if (tx_info->linear) { 8308c2ecf20Sopenharmony_ci byte_count = skb_headlen(skb) - lso_header_size; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci dma = dma_map_single(ddev, skb->data + 8338c2ecf20Sopenharmony_ci lso_header_size, byte_count, 8348c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 8358c2ecf20Sopenharmony_ci if (dma_mapping_error(ddev, dma)) 8368c2ecf20Sopenharmony_ci goto tx_drop_unmap; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci data->addr = cpu_to_be64(dma); 8398c2ecf20Sopenharmony_ci data->lkey = mr_key; 8408c2ecf20Sopenharmony_ci dma_wmb(); 8418c2ecf20Sopenharmony_ci data->byte_count = cpu_to_be32(byte_count); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci /* tx completion can avoid cache line miss for common cases */ 8448c2ecf20Sopenharmony_ci tx_info->map0_dma = dma; 8458c2ecf20Sopenharmony_ci tx_info->map0_byte_count = byte_count; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return true; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_citx_drop_unmap: 8508c2ecf20Sopenharmony_ci en_err(priv, "DMA mapping error\n"); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci while (++i_frag < shinfo->nr_frags) { 8538c2ecf20Sopenharmony_ci ++data; 8548c2ecf20Sopenharmony_ci dma_unmap_page(ddev, (dma_addr_t)be64_to_cpu(data->addr), 8558c2ecf20Sopenharmony_ci be32_to_cpu(data->byte_count), 8568c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci return false; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cinetdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct skb_shared_info *shinfo = skb_shinfo(skb); 8658c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv = netdev_priv(dev); 8668c2ecf20Sopenharmony_ci union mlx4_wqe_qpn_vlan qpn_vlan = {}; 8678c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring; 8688c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc; 8698c2ecf20Sopenharmony_ci struct mlx4_wqe_data_seg *data; 8708c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info; 8718c2ecf20Sopenharmony_ci u32 __maybe_unused ring_cons; 8728c2ecf20Sopenharmony_ci int tx_ind; 8738c2ecf20Sopenharmony_ci int nr_txbb; 8748c2ecf20Sopenharmony_ci int desc_size; 8758c2ecf20Sopenharmony_ci int real_size; 8768c2ecf20Sopenharmony_ci u32 index, bf_index; 8778c2ecf20Sopenharmony_ci __be32 op_own; 8788c2ecf20Sopenharmony_ci int lso_header_size; 8798c2ecf20Sopenharmony_ci void *fragptr = NULL; 8808c2ecf20Sopenharmony_ci bool bounce = false; 8818c2ecf20Sopenharmony_ci bool send_doorbell; 8828c2ecf20Sopenharmony_ci bool stop_queue; 8838c2ecf20Sopenharmony_ci bool inline_ok; 8848c2ecf20Sopenharmony_ci u8 data_offset; 8858c2ecf20Sopenharmony_ci bool bf_ok; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci tx_ind = skb_get_queue_mapping(skb); 8888c2ecf20Sopenharmony_ci ring = priv->tx_ring[TX][tx_ind]; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (unlikely(!priv->port_up)) 8918c2ecf20Sopenharmony_ci goto tx_drop; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* fetch ring->cons far ahead before needing it to avoid stall */ 8948c2ecf20Sopenharmony_ci ring_cons = READ_ONCE(ring->cons); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci real_size = get_real_size(skb, shinfo, dev, &lso_header_size, 8978c2ecf20Sopenharmony_ci &inline_ok, &fragptr); 8988c2ecf20Sopenharmony_ci if (unlikely(!real_size)) 8998c2ecf20Sopenharmony_ci goto tx_drop_count; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Align descriptor to TXBB size */ 9028c2ecf20Sopenharmony_ci desc_size = ALIGN(real_size, TXBB_SIZE); 9038c2ecf20Sopenharmony_ci nr_txbb = desc_size >> LOG_TXBB_SIZE; 9048c2ecf20Sopenharmony_ci if (unlikely(nr_txbb > MAX_DESC_TXBBS)) { 9058c2ecf20Sopenharmony_ci if (netif_msg_tx_err(priv)) 9068c2ecf20Sopenharmony_ci en_warn(priv, "Oversized header or SG list\n"); 9078c2ecf20Sopenharmony_ci goto tx_drop_count; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci bf_ok = ring->bf_enabled; 9118c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 9128c2ecf20Sopenharmony_ci u16 vlan_proto; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci qpn_vlan.vlan_tag = cpu_to_be16(skb_vlan_tag_get(skb)); 9158c2ecf20Sopenharmony_ci vlan_proto = be16_to_cpu(skb->vlan_proto); 9168c2ecf20Sopenharmony_ci if (vlan_proto == ETH_P_8021AD) 9178c2ecf20Sopenharmony_ci qpn_vlan.ins_vlan = MLX4_WQE_CTRL_INS_SVLAN; 9188c2ecf20Sopenharmony_ci else if (vlan_proto == ETH_P_8021Q) 9198c2ecf20Sopenharmony_ci qpn_vlan.ins_vlan = MLX4_WQE_CTRL_INS_CVLAN; 9208c2ecf20Sopenharmony_ci else 9218c2ecf20Sopenharmony_ci qpn_vlan.ins_vlan = 0; 9228c2ecf20Sopenharmony_ci bf_ok = false; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci netdev_txq_bql_enqueue_prefetchw(ring->tx_queue); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* Track current inflight packets for performance analysis */ 9288c2ecf20Sopenharmony_ci AVG_PERF_COUNTER(priv->pstats.inflight_avg, 9298c2ecf20Sopenharmony_ci (u32)(ring->prod - ring_cons - 1)); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Packet is good - grab an index and transmit it */ 9328c2ecf20Sopenharmony_ci index = ring->prod & ring->size_mask; 9338c2ecf20Sopenharmony_ci bf_index = ring->prod; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* See if we have enough space for whole descriptor TXBB for setting 9368c2ecf20Sopenharmony_ci * SW ownership on next descriptor; if not, use a bounce buffer. */ 9378c2ecf20Sopenharmony_ci if (likely(index + nr_txbb <= ring->size)) 9388c2ecf20Sopenharmony_ci tx_desc = ring->buf + (index << LOG_TXBB_SIZE); 9398c2ecf20Sopenharmony_ci else { 9408c2ecf20Sopenharmony_ci tx_desc = (struct mlx4_en_tx_desc *) ring->bounce_buf; 9418c2ecf20Sopenharmony_ci bounce = true; 9428c2ecf20Sopenharmony_ci bf_ok = false; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Save skb in tx_info ring */ 9468c2ecf20Sopenharmony_ci tx_info = &ring->tx_info[index]; 9478c2ecf20Sopenharmony_ci tx_info->skb = skb; 9488c2ecf20Sopenharmony_ci tx_info->nr_txbb = nr_txbb; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (!lso_header_size) { 9518c2ecf20Sopenharmony_ci data = &tx_desc->data; 9528c2ecf20Sopenharmony_ci data_offset = offsetof(struct mlx4_en_tx_desc, data); 9538c2ecf20Sopenharmony_ci } else { 9548c2ecf20Sopenharmony_ci int lso_align = ALIGN(lso_header_size + 4, DS_SIZE); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci data = (void *)&tx_desc->lso + lso_align; 9578c2ecf20Sopenharmony_ci data_offset = offsetof(struct mlx4_en_tx_desc, lso) + lso_align; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* valid only for none inline segments */ 9618c2ecf20Sopenharmony_ci tx_info->data_offset = data_offset; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci tx_info->inl = inline_ok; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci tx_info->linear = lso_header_size < skb_headlen(skb) && !inline_ok; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci tx_info->nr_maps = shinfo->nr_frags + tx_info->linear; 9688c2ecf20Sopenharmony_ci data += tx_info->nr_maps - 1; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (!tx_info->inl) 9718c2ecf20Sopenharmony_ci if (!mlx4_en_build_dma_wqe(priv, shinfo, data, skb, 9728c2ecf20Sopenharmony_ci lso_header_size, ring->mr_key, 9738c2ecf20Sopenharmony_ci tx_info)) 9748c2ecf20Sopenharmony_ci goto tx_drop_count; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* 9778c2ecf20Sopenharmony_ci * For timestamping add flag to skb_shinfo and 9788c2ecf20Sopenharmony_ci * set flag for further reference 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_ci tx_info->ts_requested = 0; 9818c2ecf20Sopenharmony_ci if (unlikely(ring->hwtstamp_tx_type == HWTSTAMP_TX_ON && 9828c2ecf20Sopenharmony_ci shinfo->tx_flags & SKBTX_HW_TSTAMP)) { 9838c2ecf20Sopenharmony_ci shinfo->tx_flags |= SKBTX_IN_PROGRESS; 9848c2ecf20Sopenharmony_ci tx_info->ts_requested = 1; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* Prepare ctrl segement apart opcode+ownership, which depends on 9888c2ecf20Sopenharmony_ci * whether LSO is used */ 9898c2ecf20Sopenharmony_ci tx_desc->ctrl.srcrb_flags = priv->ctrl_flags; 9908c2ecf20Sopenharmony_ci if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { 9918c2ecf20Sopenharmony_ci if (!skb->encapsulation) 9928c2ecf20Sopenharmony_ci tx_desc->ctrl.srcrb_flags |= cpu_to_be32(MLX4_WQE_CTRL_IP_CSUM | 9938c2ecf20Sopenharmony_ci MLX4_WQE_CTRL_TCP_UDP_CSUM); 9948c2ecf20Sopenharmony_ci else 9958c2ecf20Sopenharmony_ci tx_desc->ctrl.srcrb_flags |= cpu_to_be32(MLX4_WQE_CTRL_IP_CSUM); 9968c2ecf20Sopenharmony_ci ring->tx_csum++; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (priv->flags & MLX4_EN_FLAG_ENABLE_HW_LOOPBACK) { 10008c2ecf20Sopenharmony_ci struct ethhdr *ethh; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* Copy dst mac address to wqe. This allows loopback in eSwitch, 10038c2ecf20Sopenharmony_ci * so that VFs and PF can communicate with each other 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_ci ethh = (struct ethhdr *)skb->data; 10068c2ecf20Sopenharmony_ci tx_desc->ctrl.srcrb_flags16[0] = get_unaligned((__be16 *)ethh->h_dest); 10078c2ecf20Sopenharmony_ci tx_desc->ctrl.imm = get_unaligned((__be32 *)(ethh->h_dest + 2)); 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Handle LSO (TSO) packets */ 10118c2ecf20Sopenharmony_ci if (lso_header_size) { 10128c2ecf20Sopenharmony_ci int i; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* Mark opcode as LSO */ 10158c2ecf20Sopenharmony_ci op_own = cpu_to_be32(MLX4_OPCODE_LSO | (1 << 6)) | 10168c2ecf20Sopenharmony_ci ((ring->prod & ring->size) ? 10178c2ecf20Sopenharmony_ci cpu_to_be32(MLX4_EN_BIT_DESC_OWN) : 0); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Fill in the LSO prefix */ 10208c2ecf20Sopenharmony_ci tx_desc->lso.mss_hdr_size = cpu_to_be32( 10218c2ecf20Sopenharmony_ci shinfo->gso_size << 16 | lso_header_size); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* Copy headers; 10248c2ecf20Sopenharmony_ci * note that we already verified that it is linear */ 10258c2ecf20Sopenharmony_ci memcpy(tx_desc->lso.header, skb->data, lso_header_size); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci ring->tso_packets++; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci i = shinfo->gso_segs; 10308c2ecf20Sopenharmony_ci tx_info->nr_bytes = skb->len + (i - 1) * lso_header_size; 10318c2ecf20Sopenharmony_ci ring->packets += i; 10328c2ecf20Sopenharmony_ci } else { 10338c2ecf20Sopenharmony_ci /* Normal (Non LSO) packet */ 10348c2ecf20Sopenharmony_ci op_own = cpu_to_be32(MLX4_OPCODE_SEND) | 10358c2ecf20Sopenharmony_ci ((ring->prod & ring->size) ? 10368c2ecf20Sopenharmony_ci cpu_to_be32(MLX4_EN_BIT_DESC_OWN) : 0); 10378c2ecf20Sopenharmony_ci tx_info->nr_bytes = max_t(unsigned int, skb->len, ETH_ZLEN); 10388c2ecf20Sopenharmony_ci ring->packets++; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci ring->bytes += tx_info->nr_bytes; 10418c2ecf20Sopenharmony_ci AVG_PERF_COUNTER(priv->pstats.tx_pktsz_avg, skb->len); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (tx_info->inl) 10448c2ecf20Sopenharmony_ci build_inline_wqe(tx_desc, skb, shinfo, fragptr); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (skb->encapsulation) { 10478c2ecf20Sopenharmony_ci union { 10488c2ecf20Sopenharmony_ci struct iphdr *v4; 10498c2ecf20Sopenharmony_ci struct ipv6hdr *v6; 10508c2ecf20Sopenharmony_ci unsigned char *hdr; 10518c2ecf20Sopenharmony_ci } ip; 10528c2ecf20Sopenharmony_ci u8 proto; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 10558c2ecf20Sopenharmony_ci proto = (ip.v4->version == 4) ? ip.v4->protocol : 10568c2ecf20Sopenharmony_ci ip.v6->nexthdr; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (proto == IPPROTO_TCP || proto == IPPROTO_UDP) 10598c2ecf20Sopenharmony_ci op_own |= cpu_to_be32(MLX4_WQE_CTRL_IIP | MLX4_WQE_CTRL_ILP); 10608c2ecf20Sopenharmony_ci else 10618c2ecf20Sopenharmony_ci op_own |= cpu_to_be32(MLX4_WQE_CTRL_IIP); 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci ring->prod += nr_txbb; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* If we used a bounce buffer then copy descriptor back into place */ 10678c2ecf20Sopenharmony_ci if (unlikely(bounce)) 10688c2ecf20Sopenharmony_ci tx_desc = mlx4_en_bounce_to_desc(priv, ring, index, desc_size); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* Check available TXBBs And 2K spare for prefetch */ 10738c2ecf20Sopenharmony_ci stop_queue = mlx4_en_is_tx_ring_full(ring); 10748c2ecf20Sopenharmony_ci if (unlikely(stop_queue)) { 10758c2ecf20Sopenharmony_ci netif_tx_stop_queue(ring->tx_queue); 10768c2ecf20Sopenharmony_ci ring->queue_stopped++; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci send_doorbell = __netdev_tx_sent_queue(ring->tx_queue, 10808c2ecf20Sopenharmony_ci tx_info->nr_bytes, 10818c2ecf20Sopenharmony_ci netdev_xmit_more()); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci real_size = (real_size / 16) & 0x3f; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci bf_ok &= desc_size <= MAX_BF && send_doorbell; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (bf_ok) 10888c2ecf20Sopenharmony_ci qpn_vlan.bf_qpn = ring->doorbell_qpn | cpu_to_be32(real_size); 10898c2ecf20Sopenharmony_ci else 10908c2ecf20Sopenharmony_ci qpn_vlan.fence_size = real_size; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci mlx4_en_tx_write_desc(ring, tx_desc, qpn_vlan, desc_size, bf_index, 10938c2ecf20Sopenharmony_ci op_own, bf_ok, send_doorbell); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (unlikely(stop_queue)) { 10968c2ecf20Sopenharmony_ci /* If queue was emptied after the if (stop_queue) , and before 10978c2ecf20Sopenharmony_ci * the netif_tx_stop_queue() - need to wake the queue, 10988c2ecf20Sopenharmony_ci * or else it will remain stopped forever. 10998c2ecf20Sopenharmony_ci * Need a memory barrier to make sure ring->cons was not 11008c2ecf20Sopenharmony_ci * updated before queue was stopped. 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_ci smp_rmb(); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci if (unlikely(!mlx4_en_is_tx_ring_full(ring))) { 11058c2ecf20Sopenharmony_ci netif_tx_wake_queue(ring->tx_queue); 11068c2ecf20Sopenharmony_ci ring->wake_queue++; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_citx_drop_count: 11128c2ecf20Sopenharmony_ci ring->tx_dropped++; 11138c2ecf20Sopenharmony_citx_drop: 11148c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11158c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci#define MLX4_EN_XDP_TX_NRTXBB 1 11198c2ecf20Sopenharmony_ci#define MLX4_EN_XDP_TX_REAL_SZ (((CTRL_SIZE + MLX4_EN_XDP_TX_NRTXBB * DS_SIZE) \ 11208c2ecf20Sopenharmony_ci / 16) & 0x3f) 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_civoid mlx4_en_init_tx_xdp_ring_descs(struct mlx4_en_priv *priv, 11238c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci int i; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci for (i = 0; i < ring->size; i++) { 11288c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info = &ring->tx_info[i]; 11298c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc = ring->buf + 11308c2ecf20Sopenharmony_ci (i << LOG_TXBB_SIZE); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci tx_info->map0_byte_count = PAGE_SIZE; 11338c2ecf20Sopenharmony_ci tx_info->nr_txbb = MLX4_EN_XDP_TX_NRTXBB; 11348c2ecf20Sopenharmony_ci tx_info->data_offset = offsetof(struct mlx4_en_tx_desc, data); 11358c2ecf20Sopenharmony_ci tx_info->ts_requested = 0; 11368c2ecf20Sopenharmony_ci tx_info->nr_maps = 1; 11378c2ecf20Sopenharmony_ci tx_info->linear = 1; 11388c2ecf20Sopenharmony_ci tx_info->inl = 0; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci tx_desc->data.lkey = ring->mr_key; 11418c2ecf20Sopenharmony_ci tx_desc->ctrl.qpn_vlan.fence_size = MLX4_EN_XDP_TX_REAL_SZ; 11428c2ecf20Sopenharmony_ci tx_desc->ctrl.srcrb_flags = priv->ctrl_flags; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cinetdev_tx_t mlx4_en_xmit_frame(struct mlx4_en_rx_ring *rx_ring, 11478c2ecf20Sopenharmony_ci struct mlx4_en_rx_alloc *frame, 11488c2ecf20Sopenharmony_ci struct mlx4_en_priv *priv, unsigned int length, 11498c2ecf20Sopenharmony_ci int tx_ind, bool *doorbell_pending) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct mlx4_en_tx_desc *tx_desc; 11528c2ecf20Sopenharmony_ci struct mlx4_en_tx_info *tx_info; 11538c2ecf20Sopenharmony_ci struct mlx4_wqe_data_seg *data; 11548c2ecf20Sopenharmony_ci struct mlx4_en_tx_ring *ring; 11558c2ecf20Sopenharmony_ci dma_addr_t dma; 11568c2ecf20Sopenharmony_ci __be32 op_own; 11578c2ecf20Sopenharmony_ci int index; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (unlikely(!priv->port_up)) 11608c2ecf20Sopenharmony_ci goto tx_drop; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci ring = priv->tx_ring[TX_XDP][tx_ind]; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (unlikely(mlx4_en_is_tx_ring_full(ring))) 11658c2ecf20Sopenharmony_ci goto tx_drop_count; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci index = ring->prod & ring->size_mask; 11688c2ecf20Sopenharmony_ci tx_info = &ring->tx_info[index]; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* Track current inflight packets for performance analysis */ 11718c2ecf20Sopenharmony_ci AVG_PERF_COUNTER(priv->pstats.inflight_avg, 11728c2ecf20Sopenharmony_ci (u32)(ring->prod - READ_ONCE(ring->cons) - 1)); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci tx_desc = ring->buf + (index << LOG_TXBB_SIZE); 11758c2ecf20Sopenharmony_ci data = &tx_desc->data; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci dma = frame->dma; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci tx_info->page = frame->page; 11808c2ecf20Sopenharmony_ci frame->page = NULL; 11818c2ecf20Sopenharmony_ci tx_info->map0_dma = dma; 11828c2ecf20Sopenharmony_ci tx_info->nr_bytes = max_t(unsigned int, length, ETH_ZLEN); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci dma_sync_single_range_for_device(priv->ddev, dma, frame->page_offset, 11858c2ecf20Sopenharmony_ci length, PCI_DMA_TODEVICE); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci data->addr = cpu_to_be64(dma + frame->page_offset); 11888c2ecf20Sopenharmony_ci dma_wmb(); 11898c2ecf20Sopenharmony_ci data->byte_count = cpu_to_be32(length); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* tx completion can avoid cache line miss for common cases */ 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci op_own = cpu_to_be32(MLX4_OPCODE_SEND) | 11948c2ecf20Sopenharmony_ci ((ring->prod & ring->size) ? 11958c2ecf20Sopenharmony_ci cpu_to_be32(MLX4_EN_BIT_DESC_OWN) : 0); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci rx_ring->xdp_tx++; 11988c2ecf20Sopenharmony_ci AVG_PERF_COUNTER(priv->pstats.tx_pktsz_avg, length); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci ring->prod += MLX4_EN_XDP_TX_NRTXBB; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* Ensure new descriptor hits memory 12038c2ecf20Sopenharmony_ci * before setting ownership of this descriptor to HW 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_ci dma_wmb(); 12068c2ecf20Sopenharmony_ci tx_desc->ctrl.owner_opcode = op_own; 12078c2ecf20Sopenharmony_ci ring->xmit_more++; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci *doorbell_pending = true; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_citx_drop_count: 12148c2ecf20Sopenharmony_ci rx_ring->xdp_tx_full++; 12158c2ecf20Sopenharmony_ci *doorbell_pending = true; 12168c2ecf20Sopenharmony_citx_drop: 12178c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 12188c2ecf20Sopenharmony_ci} 1219