18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/ip.h> 58c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 68c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 78c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "ionic.h" 108c2ecf20Sopenharmony_ci#include "ionic_lif.h" 118c2ecf20Sopenharmony_ci#include "ionic_txrx.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic void ionic_rx_clean(struct ionic_queue *q, 148c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info, 158c2ecf20Sopenharmony_ci struct ionic_cq_info *cq_info, 168c2ecf20Sopenharmony_ci void *cb_arg); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell, 238c2ecf20Sopenharmony_ci ionic_desc_cb cb_func, void *cb_arg) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci DEBUG_STATS_TXQ_POST(q, ring_dbell); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci ionic_q_post(q, ring_dbell, cb_func, cb_arg); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell, 318c2ecf20Sopenharmony_ci ionic_desc_cb cb_func, void *cb_arg) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci ionic_q_post(q, ring_dbell, cb_func, cb_arg); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci DEBUG_STATS_RX_BUFF_CNT(q); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline struct netdev_queue *q_to_ndq(struct ionic_queue *q) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return netdev_get_tx_queue(q->lif->netdev, q->index); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct sk_buff *ionic_rx_skb_alloc(struct ionic_queue *q, 448c2ecf20Sopenharmony_ci unsigned int len, bool frags) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct ionic_lif *lif = q->lif; 478c2ecf20Sopenharmony_ci struct ionic_rx_stats *stats; 488c2ecf20Sopenharmony_ci struct net_device *netdev; 498c2ecf20Sopenharmony_ci struct sk_buff *skb; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci netdev = lif->netdev; 528c2ecf20Sopenharmony_ci stats = &q->lif->rxqstats[q->index]; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (frags) 558c2ecf20Sopenharmony_ci skb = napi_get_frags(&q_to_qcq(q)->napi); 568c2ecf20Sopenharmony_ci else 578c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, len); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 608c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: SKB alloc failed on %s!\n", 618c2ecf20Sopenharmony_ci netdev->name, q->name); 628c2ecf20Sopenharmony_ci stats->alloc_err++; 638c2ecf20Sopenharmony_ci return NULL; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return skb; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic struct sk_buff *ionic_rx_frags(struct ionic_queue *q, 708c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info, 718c2ecf20Sopenharmony_ci struct ionic_cq_info *cq_info) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct ionic_rxq_comp *comp = cq_info->cq_desc; 748c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 758c2ecf20Sopenharmony_ci struct ionic_page_info *page_info; 768c2ecf20Sopenharmony_ci struct sk_buff *skb; 778c2ecf20Sopenharmony_ci unsigned int i; 788c2ecf20Sopenharmony_ci u16 frag_len; 798c2ecf20Sopenharmony_ci u16 len; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci page_info = &desc_info->pages[0]; 828c2ecf20Sopenharmony_ci len = le16_to_cpu(comp->len); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci prefetch(page_address(page_info->page) + NET_IP_ALIGN); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci skb = ionic_rx_skb_alloc(q, len, true); 878c2ecf20Sopenharmony_ci if (unlikely(!skb)) 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci i = comp->num_sg_elems + 1; 918c2ecf20Sopenharmony_ci do { 928c2ecf20Sopenharmony_ci if (unlikely(!page_info->page)) { 938c2ecf20Sopenharmony_ci struct napi_struct *napi = &q_to_qcq(q)->napi; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci napi->skb = NULL; 968c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci frag_len = min(len, (u16)PAGE_SIZE); 1018c2ecf20Sopenharmony_ci len -= frag_len; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma_unmap_addr(page_info, dma_addr), 1048c2ecf20Sopenharmony_ci PAGE_SIZE, DMA_FROM_DEVICE); 1058c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 1068c2ecf20Sopenharmony_ci page_info->page, 0, frag_len, PAGE_SIZE); 1078c2ecf20Sopenharmony_ci page_info->page = NULL; 1088c2ecf20Sopenharmony_ci page_info++; 1098c2ecf20Sopenharmony_ci i--; 1108c2ecf20Sopenharmony_ci } while (i > 0); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return skb; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, 1168c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info, 1178c2ecf20Sopenharmony_ci struct ionic_cq_info *cq_info) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct ionic_rxq_comp *comp = cq_info->cq_desc; 1208c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 1218c2ecf20Sopenharmony_ci struct ionic_page_info *page_info; 1228c2ecf20Sopenharmony_ci struct sk_buff *skb; 1238c2ecf20Sopenharmony_ci u16 len; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci page_info = &desc_info->pages[0]; 1268c2ecf20Sopenharmony_ci len = le16_to_cpu(comp->len); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci skb = ionic_rx_skb_alloc(q, len, false); 1298c2ecf20Sopenharmony_ci if (unlikely(!skb)) 1308c2ecf20Sopenharmony_ci return NULL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (unlikely(!page_info->page)) { 1338c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 1348c2ecf20Sopenharmony_ci return NULL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(dev, dma_unmap_addr(page_info, dma_addr), 1388c2ecf20Sopenharmony_ci len, DMA_FROM_DEVICE); 1398c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, page_address(page_info->page), len); 1408c2ecf20Sopenharmony_ci dma_sync_single_for_device(dev, dma_unmap_addr(page_info, dma_addr), 1418c2ecf20Sopenharmony_ci len, DMA_FROM_DEVICE); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci skb_put(skb, len); 1448c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, q->lif->netdev); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return skb; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void ionic_rx_clean(struct ionic_queue *q, 1508c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info, 1518c2ecf20Sopenharmony_ci struct ionic_cq_info *cq_info, 1528c2ecf20Sopenharmony_ci void *cb_arg) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct ionic_rxq_comp *comp = cq_info->cq_desc; 1558c2ecf20Sopenharmony_ci struct ionic_qcq *qcq = q_to_qcq(q); 1568c2ecf20Sopenharmony_ci struct ionic_rx_stats *stats; 1578c2ecf20Sopenharmony_ci struct net_device *netdev; 1588c2ecf20Sopenharmony_ci struct sk_buff *skb; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci stats = q_to_rx_stats(q); 1618c2ecf20Sopenharmony_ci netdev = q->lif->netdev; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (comp->status) { 1648c2ecf20Sopenharmony_ci stats->dropped++; 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci stats->pkts++; 1698c2ecf20Sopenharmony_ci stats->bytes += le16_to_cpu(comp->len); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) 1728c2ecf20Sopenharmony_ci skb = ionic_rx_copybreak(q, desc_info, cq_info); 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci skb = ionic_rx_frags(q, desc_info, cq_info); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 1778c2ecf20Sopenharmony_ci stats->dropped++; 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, q->index); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (likely(netdev->features & NETIF_F_RXHASH)) { 1848c2ecf20Sopenharmony_ci switch (comp->pkt_type_color & IONIC_RXQ_COMP_PKT_TYPE_MASK) { 1858c2ecf20Sopenharmony_ci case IONIC_PKT_TYPE_IPV4: 1868c2ecf20Sopenharmony_ci case IONIC_PKT_TYPE_IPV6: 1878c2ecf20Sopenharmony_ci skb_set_hash(skb, le32_to_cpu(comp->rss_hash), 1888c2ecf20Sopenharmony_ci PKT_HASH_TYPE_L3); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci case IONIC_PKT_TYPE_IPV4_TCP: 1918c2ecf20Sopenharmony_ci case IONIC_PKT_TYPE_IPV6_TCP: 1928c2ecf20Sopenharmony_ci case IONIC_PKT_TYPE_IPV4_UDP: 1938c2ecf20Sopenharmony_ci case IONIC_PKT_TYPE_IPV6_UDP: 1948c2ecf20Sopenharmony_ci skb_set_hash(skb, le32_to_cpu(comp->rss_hash), 1958c2ecf20Sopenharmony_ci PKT_HASH_TYPE_L4); 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (likely(netdev->features & NETIF_F_RXCSUM) && 2018c2ecf20Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_CALC)) { 2028c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 2038c2ecf20Sopenharmony_ci skb->csum = (__force __wsum)le16_to_cpu(comp->csum); 2048c2ecf20Sopenharmony_ci stats->csum_complete++; 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci stats->csum_none++; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (unlikely((comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_TCP_BAD) || 2108c2ecf20Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_UDP_BAD) || 2118c2ecf20Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_IP_BAD))) 2128c2ecf20Sopenharmony_ci stats->csum_error++; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (likely(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && 2158c2ecf20Sopenharmony_ci (comp->csum_flags & IONIC_RXQ_COMP_CSUM_F_VLAN)) { 2168c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), 2178c2ecf20Sopenharmony_ci le16_to_cpu(comp->vlan_tci)); 2188c2ecf20Sopenharmony_ci stats->vlan_stripped++; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (le16_to_cpu(comp->len) <= q->lif->rx_copybreak) 2228c2ecf20Sopenharmony_ci napi_gro_receive(&qcq->napi, skb); 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci napi_gro_frags(&qcq->napi); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct ionic_rxq_comp *comp = cq_info->cq_desc; 2308c2ecf20Sopenharmony_ci struct ionic_queue *q = cq->bound_q; 2318c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (!color_match(comp->pkt_type_color, cq->done_color)) 2348c2ecf20Sopenharmony_ci return false; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* check for empty queue */ 2378c2ecf20Sopenharmony_ci if (q->tail_idx == q->head_idx) 2388c2ecf20Sopenharmony_ci return false; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (q->tail_idx != le16_to_cpu(comp->comp_index)) 2418c2ecf20Sopenharmony_ci return false; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 2448c2ecf20Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* clean the related q entry, only one per qc completion */ 2478c2ecf20Sopenharmony_ci ionic_rx_clean(q, desc_info, cq_info, desc_info->cb_arg); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci desc_info->cb = NULL; 2508c2ecf20Sopenharmony_ci desc_info->cb_arg = NULL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return true; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int ionic_rx_page_alloc(struct ionic_queue *q, 2568c2ecf20Sopenharmony_ci struct ionic_page_info *page_info) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct ionic_lif *lif = q->lif; 2598c2ecf20Sopenharmony_ci struct ionic_rx_stats *stats; 2608c2ecf20Sopenharmony_ci struct net_device *netdev; 2618c2ecf20Sopenharmony_ci struct device *dev; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci netdev = lif->netdev; 2648c2ecf20Sopenharmony_ci dev = lif->ionic->dev; 2658c2ecf20Sopenharmony_ci stats = q_to_rx_stats(q); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (unlikely(!page_info)) { 2688c2ecf20Sopenharmony_ci net_err_ratelimited("%s: %s invalid page_info in alloc\n", 2698c2ecf20Sopenharmony_ci netdev->name, q->name); 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci page_info->page = dev_alloc_page(); 2748c2ecf20Sopenharmony_ci if (unlikely(!page_info->page)) { 2758c2ecf20Sopenharmony_ci net_err_ratelimited("%s: %s page alloc failed\n", 2768c2ecf20Sopenharmony_ci netdev->name, q->name); 2778c2ecf20Sopenharmony_ci stats->alloc_err++; 2788c2ecf20Sopenharmony_ci return -ENOMEM; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci page_info->dma_addr = dma_map_page(dev, page_info->page, 0, PAGE_SIZE, 2828c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2838c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(dev, page_info->dma_addr))) { 2848c2ecf20Sopenharmony_ci put_page(page_info->page); 2858c2ecf20Sopenharmony_ci page_info->dma_addr = 0; 2868c2ecf20Sopenharmony_ci page_info->page = NULL; 2878c2ecf20Sopenharmony_ci net_err_ratelimited("%s: %s dma map failed\n", 2888c2ecf20Sopenharmony_ci netdev->name, q->name); 2898c2ecf20Sopenharmony_ci stats->dma_map_err++; 2908c2ecf20Sopenharmony_ci return -EIO; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void ionic_rx_page_free(struct ionic_queue *q, 2978c2ecf20Sopenharmony_ci struct ionic_page_info *page_info) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct ionic_lif *lif = q->lif; 3008c2ecf20Sopenharmony_ci struct net_device *netdev; 3018c2ecf20Sopenharmony_ci struct device *dev; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci netdev = lif->netdev; 3048c2ecf20Sopenharmony_ci dev = lif->ionic->dev; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (unlikely(!page_info)) { 3078c2ecf20Sopenharmony_ci net_err_ratelimited("%s: %s invalid page_info in free\n", 3088c2ecf20Sopenharmony_ci netdev->name, q->name); 3098c2ecf20Sopenharmony_ci return; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (unlikely(!page_info->page)) { 3138c2ecf20Sopenharmony_ci net_err_ratelimited("%s: %s invalid page in free\n", 3148c2ecf20Sopenharmony_ci netdev->name, q->name); 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci dma_unmap_page(dev, page_info->dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci put_page(page_info->page); 3218c2ecf20Sopenharmony_ci page_info->dma_addr = 0; 3228c2ecf20Sopenharmony_ci page_info->page = NULL; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_civoid ionic_rx_fill(struct ionic_queue *q) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct net_device *netdev = q->lif->netdev; 3288c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 3298c2ecf20Sopenharmony_ci struct ionic_page_info *page_info; 3308c2ecf20Sopenharmony_ci struct ionic_rxq_sg_desc *sg_desc; 3318c2ecf20Sopenharmony_ci struct ionic_rxq_sg_elem *sg_elem; 3328c2ecf20Sopenharmony_ci struct ionic_rxq_desc *desc; 3338c2ecf20Sopenharmony_ci unsigned int remain_len; 3348c2ecf20Sopenharmony_ci unsigned int seg_len; 3358c2ecf20Sopenharmony_ci unsigned int nfrags; 3368c2ecf20Sopenharmony_ci unsigned int i, j; 3378c2ecf20Sopenharmony_ci unsigned int len; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci len = netdev->mtu + ETH_HLEN + VLAN_HLEN; 3408c2ecf20Sopenharmony_ci nfrags = round_up(len, PAGE_SIZE) / PAGE_SIZE; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci for (i = ionic_q_space_avail(q); i; i--) { 3438c2ecf20Sopenharmony_ci remain_len = len; 3448c2ecf20Sopenharmony_ci desc_info = &q->info[q->head_idx]; 3458c2ecf20Sopenharmony_ci desc = desc_info->desc; 3468c2ecf20Sopenharmony_ci sg_desc = desc_info->sg_desc; 3478c2ecf20Sopenharmony_ci page_info = &desc_info->pages[0]; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (page_info->page) { /* recycle the buffer */ 3508c2ecf20Sopenharmony_ci ionic_rxq_post(q, false, ionic_rx_clean, NULL); 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* fill main descriptor - pages[0] */ 3558c2ecf20Sopenharmony_ci desc->opcode = (nfrags > 1) ? IONIC_RXQ_DESC_OPCODE_SG : 3568c2ecf20Sopenharmony_ci IONIC_RXQ_DESC_OPCODE_SIMPLE; 3578c2ecf20Sopenharmony_ci desc_info->npages = nfrags; 3588c2ecf20Sopenharmony_ci if (unlikely(ionic_rx_page_alloc(q, page_info))) { 3598c2ecf20Sopenharmony_ci desc->addr = 0; 3608c2ecf20Sopenharmony_ci desc->len = 0; 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci desc->addr = cpu_to_le64(page_info->dma_addr); 3648c2ecf20Sopenharmony_ci seg_len = min_t(unsigned int, PAGE_SIZE, len); 3658c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(seg_len); 3668c2ecf20Sopenharmony_ci remain_len -= seg_len; 3678c2ecf20Sopenharmony_ci page_info++; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* fill sg descriptors - pages[1..n] */ 3708c2ecf20Sopenharmony_ci for (j = 0; j < nfrags - 1; j++) { 3718c2ecf20Sopenharmony_ci if (page_info->page) /* recycle the sg buffer */ 3728c2ecf20Sopenharmony_ci continue; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci sg_elem = &sg_desc->elems[j]; 3758c2ecf20Sopenharmony_ci if (unlikely(ionic_rx_page_alloc(q, page_info))) { 3768c2ecf20Sopenharmony_ci sg_elem->addr = 0; 3778c2ecf20Sopenharmony_ci sg_elem->len = 0; 3788c2ecf20Sopenharmony_ci return; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci sg_elem->addr = cpu_to_le64(page_info->dma_addr); 3818c2ecf20Sopenharmony_ci seg_len = min_t(unsigned int, PAGE_SIZE, remain_len); 3828c2ecf20Sopenharmony_ci sg_elem->len = cpu_to_le16(seg_len); 3838c2ecf20Sopenharmony_ci remain_len -= seg_len; 3848c2ecf20Sopenharmony_ci page_info++; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci ionic_rxq_post(q, false, ionic_rx_clean, NULL); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type, 3918c2ecf20Sopenharmony_ci q->dbval | q->head_idx); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void ionic_rx_fill_cb(void *arg) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci ionic_rx_fill(arg); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_civoid ionic_rx_empty(struct ionic_queue *q) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 4028c2ecf20Sopenharmony_ci struct ionic_page_info *page_info; 4038c2ecf20Sopenharmony_ci unsigned int i, j; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci for (i = 0; i < q->num_descs; i++) { 4068c2ecf20Sopenharmony_ci desc_info = &q->info[i]; 4078c2ecf20Sopenharmony_ci for (j = 0; j < IONIC_RX_MAX_SG_ELEMS + 1; j++) { 4088c2ecf20Sopenharmony_ci page_info = &desc_info->pages[j]; 4098c2ecf20Sopenharmony_ci if (page_info->page) 4108c2ecf20Sopenharmony_ci ionic_rx_page_free(q, page_info); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci desc_info->npages = 0; 4148c2ecf20Sopenharmony_ci desc_info->cb = NULL; 4158c2ecf20Sopenharmony_ci desc_info->cb_arg = NULL; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic void ionic_dim_update(struct ionic_qcq *qcq, int napi_mode) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct dim_sample dim_sample; 4228c2ecf20Sopenharmony_ci struct ionic_lif *lif; 4238c2ecf20Sopenharmony_ci unsigned int qi; 4248c2ecf20Sopenharmony_ci u64 pkts, bytes; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!qcq->intr.dim_coal_hw) 4278c2ecf20Sopenharmony_ci return; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci lif = qcq->q.lif; 4308c2ecf20Sopenharmony_ci qi = qcq->cq.bound_q->index; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci switch (napi_mode) { 4338c2ecf20Sopenharmony_ci case IONIC_LIF_F_TX_DIM_INTR: 4348c2ecf20Sopenharmony_ci pkts = lif->txqstats[qi].pkts; 4358c2ecf20Sopenharmony_ci bytes = lif->txqstats[qi].bytes; 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci case IONIC_LIF_F_RX_DIM_INTR: 4388c2ecf20Sopenharmony_ci pkts = lif->rxqstats[qi].pkts; 4398c2ecf20Sopenharmony_ci bytes = lif->rxqstats[qi].bytes; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci default: 4428c2ecf20Sopenharmony_ci pkts = lif->txqstats[qi].pkts + lif->rxqstats[qi].pkts; 4438c2ecf20Sopenharmony_ci bytes = lif->txqstats[qi].bytes + lif->rxqstats[qi].bytes; 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci dim_update_sample(qcq->cq.bound_intr->rearm_count, 4488c2ecf20Sopenharmony_ci pkts, bytes, &dim_sample); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci net_dim(&qcq->dim, dim_sample); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciint ionic_tx_napi(struct napi_struct *napi, int budget) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct ionic_qcq *qcq = napi_to_qcq(napi); 4568c2ecf20Sopenharmony_ci struct ionic_cq *cq = napi_to_cq(napi); 4578c2ecf20Sopenharmony_ci struct ionic_dev *idev; 4588c2ecf20Sopenharmony_ci struct ionic_lif *lif; 4598c2ecf20Sopenharmony_ci u32 work_done = 0; 4608c2ecf20Sopenharmony_ci u32 flags = 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci lif = cq->bound_q->lif; 4638c2ecf20Sopenharmony_ci idev = &lif->ionic->idev; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci work_done = ionic_cq_service(cq, budget, 4668c2ecf20Sopenharmony_ci ionic_tx_service, NULL, NULL); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (work_done < budget && napi_complete_done(napi, work_done)) { 4698c2ecf20Sopenharmony_ci ionic_dim_update(qcq, IONIC_LIF_F_TX_DIM_INTR); 4708c2ecf20Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 4718c2ecf20Sopenharmony_ci cq->bound_intr->rearm_count++; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (work_done || flags) { 4758c2ecf20Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 4768c2ecf20Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, 4778c2ecf20Sopenharmony_ci cq->bound_intr->index, 4788c2ecf20Sopenharmony_ci work_done, flags); 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci DEBUG_STATS_NAPI_POLL(qcq, work_done); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return work_done; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ciint ionic_rx_napi(struct napi_struct *napi, int budget) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct ionic_qcq *qcq = napi_to_qcq(napi); 4898c2ecf20Sopenharmony_ci struct ionic_cq *cq = napi_to_cq(napi); 4908c2ecf20Sopenharmony_ci struct ionic_dev *idev; 4918c2ecf20Sopenharmony_ci struct ionic_lif *lif; 4928c2ecf20Sopenharmony_ci u32 work_done = 0; 4938c2ecf20Sopenharmony_ci u32 flags = 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci lif = cq->bound_q->lif; 4968c2ecf20Sopenharmony_ci idev = &lif->ionic->idev; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci work_done = ionic_cq_service(cq, budget, 4998c2ecf20Sopenharmony_ci ionic_rx_service, NULL, NULL); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (work_done) 5028c2ecf20Sopenharmony_ci ionic_rx_fill(cq->bound_q); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (work_done < budget && napi_complete_done(napi, work_done)) { 5058c2ecf20Sopenharmony_ci ionic_dim_update(qcq, IONIC_LIF_F_RX_DIM_INTR); 5068c2ecf20Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 5078c2ecf20Sopenharmony_ci cq->bound_intr->rearm_count++; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (work_done || flags) { 5118c2ecf20Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 5128c2ecf20Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, 5138c2ecf20Sopenharmony_ci cq->bound_intr->index, 5148c2ecf20Sopenharmony_ci work_done, flags); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci DEBUG_STATS_NAPI_POLL(qcq, work_done); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci return work_done; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ciint ionic_txrx_napi(struct napi_struct *napi, int budget) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct ionic_qcq *qcq = napi_to_qcq(napi); 5258c2ecf20Sopenharmony_ci struct ionic_cq *rxcq = napi_to_cq(napi); 5268c2ecf20Sopenharmony_ci unsigned int qi = rxcq->bound_q->index; 5278c2ecf20Sopenharmony_ci struct ionic_dev *idev; 5288c2ecf20Sopenharmony_ci struct ionic_lif *lif; 5298c2ecf20Sopenharmony_ci struct ionic_cq *txcq; 5308c2ecf20Sopenharmony_ci u32 rx_work_done = 0; 5318c2ecf20Sopenharmony_ci u32 tx_work_done = 0; 5328c2ecf20Sopenharmony_ci u32 flags = 0; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci lif = rxcq->bound_q->lif; 5358c2ecf20Sopenharmony_ci idev = &lif->ionic->idev; 5368c2ecf20Sopenharmony_ci txcq = &lif->txqcqs[qi]->cq; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci tx_work_done = ionic_cq_service(txcq, lif->tx_budget, 5398c2ecf20Sopenharmony_ci ionic_tx_service, NULL, NULL); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci rx_work_done = ionic_cq_service(rxcq, budget, 5428c2ecf20Sopenharmony_ci ionic_rx_service, NULL, NULL); 5438c2ecf20Sopenharmony_ci if (rx_work_done) 5448c2ecf20Sopenharmony_ci ionic_rx_fill_cb(rxcq->bound_q); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) { 5478c2ecf20Sopenharmony_ci ionic_dim_update(qcq, 0); 5488c2ecf20Sopenharmony_ci flags |= IONIC_INTR_CRED_UNMASK; 5498c2ecf20Sopenharmony_ci rxcq->bound_intr->rearm_count++; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (rx_work_done || flags) { 5538c2ecf20Sopenharmony_ci flags |= IONIC_INTR_CRED_RESET_COALESCE; 5548c2ecf20Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, rxcq->bound_intr->index, 5558c2ecf20Sopenharmony_ci tx_work_done + rx_work_done, flags); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci DEBUG_STATS_NAPI_POLL(qcq, rx_work_done); 5598c2ecf20Sopenharmony_ci DEBUG_STATS_NAPI_POLL(qcq, tx_work_done); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return rx_work_done; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic dma_addr_t ionic_tx_map_single(struct ionic_queue *q, 5658c2ecf20Sopenharmony_ci void *data, size_t len) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 5688c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 5698c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci dma_addr = dma_map_single(dev, data, len, DMA_TO_DEVICE); 5728c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 5738c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: DMA single map failed on %s!\n", 5748c2ecf20Sopenharmony_ci q->lif->netdev->name, q->name); 5758c2ecf20Sopenharmony_ci stats->dma_map_err++; 5768c2ecf20Sopenharmony_ci return 0; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci return dma_addr; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic dma_addr_t ionic_tx_map_frag(struct ionic_queue *q, 5828c2ecf20Sopenharmony_ci const skb_frag_t *frag, 5838c2ecf20Sopenharmony_ci size_t offset, size_t len) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 5868c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 5878c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci dma_addr = skb_frag_dma_map(dev, frag, offset, len, DMA_TO_DEVICE); 5908c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) { 5918c2ecf20Sopenharmony_ci net_warn_ratelimited("%s: DMA frag map failed on %s!\n", 5928c2ecf20Sopenharmony_ci q->lif->netdev->name, q->name); 5938c2ecf20Sopenharmony_ci stats->dma_map_err++; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci return dma_addr; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void ionic_tx_clean(struct ionic_queue *q, 5998c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info, 6008c2ecf20Sopenharmony_ci struct ionic_cq_info *cq_info, 6018c2ecf20Sopenharmony_ci void *cb_arg) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct ionic_txq_sg_desc *sg_desc = desc_info->sg_desc; 6048c2ecf20Sopenharmony_ci struct ionic_txq_sg_elem *elem = sg_desc->elems; 6058c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 6068c2ecf20Sopenharmony_ci struct ionic_txq_desc *desc = desc_info->desc; 6078c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 6088c2ecf20Sopenharmony_ci u8 opcode, flags, nsge; 6098c2ecf20Sopenharmony_ci u16 queue_index; 6108c2ecf20Sopenharmony_ci unsigned int i; 6118c2ecf20Sopenharmony_ci u64 addr; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci decode_txq_desc_cmd(le64_to_cpu(desc->cmd), 6148c2ecf20Sopenharmony_ci &opcode, &flags, &nsge, &addr); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* use unmap_single only if either this is not TSO, 6178c2ecf20Sopenharmony_ci * or this is first descriptor of a TSO 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci if (opcode != IONIC_TXQ_DESC_OPCODE_TSO || 6208c2ecf20Sopenharmony_ci flags & IONIC_TXQ_DESC_FLAG_TSO_SOT) 6218c2ecf20Sopenharmony_ci dma_unmap_single(dev, (dma_addr_t)addr, 6228c2ecf20Sopenharmony_ci le16_to_cpu(desc->len), DMA_TO_DEVICE); 6238c2ecf20Sopenharmony_ci else 6248c2ecf20Sopenharmony_ci dma_unmap_page(dev, (dma_addr_t)addr, 6258c2ecf20Sopenharmony_ci le16_to_cpu(desc->len), DMA_TO_DEVICE); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci for (i = 0; i < nsge; i++, elem++) 6288c2ecf20Sopenharmony_ci dma_unmap_page(dev, (dma_addr_t)le64_to_cpu(elem->addr), 6298c2ecf20Sopenharmony_ci le16_to_cpu(elem->len), DMA_TO_DEVICE); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (cb_arg) { 6328c2ecf20Sopenharmony_ci struct sk_buff *skb = cb_arg; 6338c2ecf20Sopenharmony_ci u32 len = skb->len; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci queue_index = skb_get_queue_mapping(skb); 6368c2ecf20Sopenharmony_ci if (unlikely(__netif_subqueue_stopped(q->lif->netdev, 6378c2ecf20Sopenharmony_ci queue_index))) { 6388c2ecf20Sopenharmony_ci netif_wake_subqueue(q->lif->netdev, queue_index); 6398c2ecf20Sopenharmony_ci q->wake++; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6428c2ecf20Sopenharmony_ci stats->clean++; 6438c2ecf20Sopenharmony_ci netdev_tx_completed_queue(q_to_ndq(q), 1, len); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct ionic_txq_comp *comp = cq_info->cq_desc; 6508c2ecf20Sopenharmony_ci struct ionic_queue *q = cq->bound_q; 6518c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 6528c2ecf20Sopenharmony_ci u16 index; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (!color_match(comp->color, cq->done_color)) 6558c2ecf20Sopenharmony_ci return false; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* clean the related q entries, there could be 6588c2ecf20Sopenharmony_ci * several q entries completed for each cq completion 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci do { 6618c2ecf20Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 6628c2ecf20Sopenharmony_ci index = q->tail_idx; 6638c2ecf20Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 6648c2ecf20Sopenharmony_ci ionic_tx_clean(q, desc_info, cq_info, desc_info->cb_arg); 6658c2ecf20Sopenharmony_ci desc_info->cb = NULL; 6668c2ecf20Sopenharmony_ci desc_info->cb_arg = NULL; 6678c2ecf20Sopenharmony_ci } while (index != le16_to_cpu(comp->comp_index)); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return true; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_civoid ionic_tx_flush(struct ionic_cq *cq) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct ionic_dev *idev = &cq->lif->ionic->idev; 6758c2ecf20Sopenharmony_ci u32 work_done; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci work_done = ionic_cq_service(cq, cq->num_descs, 6788c2ecf20Sopenharmony_ci ionic_tx_service, NULL, NULL); 6798c2ecf20Sopenharmony_ci if (work_done) 6808c2ecf20Sopenharmony_ci ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index, 6818c2ecf20Sopenharmony_ci work_done, IONIC_INTR_CRED_RESET_COALESCE); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_civoid ionic_tx_empty(struct ionic_queue *q) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* walk the not completed tx entries, if any */ 6898c2ecf20Sopenharmony_ci while (q->head_idx != q->tail_idx) { 6908c2ecf20Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 6918c2ecf20Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 6928c2ecf20Sopenharmony_ci ionic_tx_clean(q, desc_info, NULL, desc_info->cb_arg); 6938c2ecf20Sopenharmony_ci desc_info->cb = NULL; 6948c2ecf20Sopenharmony_ci desc_info->cb_arg = NULL; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci int err; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci err = skb_cow_head(skb, 0); 7038c2ecf20Sopenharmony_ci if (err) 7048c2ecf20Sopenharmony_ci return err; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_IP)) { 7078c2ecf20Sopenharmony_ci inner_ip_hdr(skb)->check = 0; 7088c2ecf20Sopenharmony_ci inner_tcp_hdr(skb)->check = 7098c2ecf20Sopenharmony_ci ~csum_tcpudp_magic(inner_ip_hdr(skb)->saddr, 7108c2ecf20Sopenharmony_ci inner_ip_hdr(skb)->daddr, 7118c2ecf20Sopenharmony_ci 0, IPPROTO_TCP, 0); 7128c2ecf20Sopenharmony_ci } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) { 7138c2ecf20Sopenharmony_ci inner_tcp_hdr(skb)->check = 7148c2ecf20Sopenharmony_ci ~csum_ipv6_magic(&inner_ipv6_hdr(skb)->saddr, 7158c2ecf20Sopenharmony_ci &inner_ipv6_hdr(skb)->daddr, 7168c2ecf20Sopenharmony_ci 0, IPPROTO_TCP, 0); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci return 0; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic int ionic_tx_tcp_pseudo_csum(struct sk_buff *skb) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci int err; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci err = skb_cow_head(skb, 0); 7278c2ecf20Sopenharmony_ci if (err) 7288c2ecf20Sopenharmony_ci return err; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_IP)) { 7318c2ecf20Sopenharmony_ci ip_hdr(skb)->check = 0; 7328c2ecf20Sopenharmony_ci tcp_hdr(skb)->check = 7338c2ecf20Sopenharmony_ci ~csum_tcpudp_magic(ip_hdr(skb)->saddr, 7348c2ecf20Sopenharmony_ci ip_hdr(skb)->daddr, 7358c2ecf20Sopenharmony_ci 0, IPPROTO_TCP, 0); 7368c2ecf20Sopenharmony_ci } else if (skb->protocol == cpu_to_be16(ETH_P_IPV6)) { 7378c2ecf20Sopenharmony_ci tcp_v6_gso_csum_prep(skb); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return 0; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void ionic_tx_tso_post(struct ionic_queue *q, struct ionic_txq_desc *desc, 7448c2ecf20Sopenharmony_ci struct sk_buff *skb, 7458c2ecf20Sopenharmony_ci dma_addr_t addr, u8 nsge, u16 len, 7468c2ecf20Sopenharmony_ci unsigned int hdrlen, unsigned int mss, 7478c2ecf20Sopenharmony_ci bool outer_csum, 7488c2ecf20Sopenharmony_ci u16 vlan_tci, bool has_vlan, 7498c2ecf20Sopenharmony_ci bool start, bool done) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci u8 flags = 0; 7528c2ecf20Sopenharmony_ci u64 cmd; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0; 7558c2ecf20Sopenharmony_ci flags |= outer_csum ? IONIC_TXQ_DESC_FLAG_ENCAP : 0; 7568c2ecf20Sopenharmony_ci flags |= start ? IONIC_TXQ_DESC_FLAG_TSO_SOT : 0; 7578c2ecf20Sopenharmony_ci flags |= done ? IONIC_TXQ_DESC_FLAG_TSO_EOT : 0; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_TSO, flags, nsge, addr); 7608c2ecf20Sopenharmony_ci desc->cmd = cpu_to_le64(cmd); 7618c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(len); 7628c2ecf20Sopenharmony_ci desc->vlan_tci = cpu_to_le16(vlan_tci); 7638c2ecf20Sopenharmony_ci desc->hdr_len = cpu_to_le16(hdrlen); 7648c2ecf20Sopenharmony_ci desc->mss = cpu_to_le16(mss); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (done) { 7678c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 7688c2ecf20Sopenharmony_ci netdev_tx_sent_queue(q_to_ndq(q), skb->len); 7698c2ecf20Sopenharmony_ci ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb); 7708c2ecf20Sopenharmony_ci } else { 7718c2ecf20Sopenharmony_ci ionic_txq_post(q, false, ionic_tx_clean, NULL); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic struct ionic_txq_desc *ionic_tx_tso_next(struct ionic_queue *q, 7768c2ecf20Sopenharmony_ci struct ionic_txq_sg_elem **elem) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct ionic_txq_sg_desc *sg_desc = q->info[q->head_idx].txq_sg_desc; 7798c2ecf20Sopenharmony_ci struct ionic_txq_desc *desc = q->info[q->head_idx].txq_desc; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci *elem = sg_desc->elems; 7828c2ecf20Sopenharmony_ci return desc; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 7888c2ecf20Sopenharmony_ci struct ionic_desc_info *rewind_desc_info; 7898c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 7908c2ecf20Sopenharmony_ci struct ionic_txq_sg_elem *elem; 7918c2ecf20Sopenharmony_ci struct ionic_txq_desc *desc; 7928c2ecf20Sopenharmony_ci unsigned int frag_left = 0; 7938c2ecf20Sopenharmony_ci unsigned int offset = 0; 7948c2ecf20Sopenharmony_ci u16 abort = q->head_idx; 7958c2ecf20Sopenharmony_ci unsigned int len_left; 7968c2ecf20Sopenharmony_ci dma_addr_t desc_addr; 7978c2ecf20Sopenharmony_ci unsigned int hdrlen; 7988c2ecf20Sopenharmony_ci unsigned int nfrags; 7998c2ecf20Sopenharmony_ci unsigned int seglen; 8008c2ecf20Sopenharmony_ci u64 total_bytes = 0; 8018c2ecf20Sopenharmony_ci u64 total_pkts = 0; 8028c2ecf20Sopenharmony_ci u16 rewind = abort; 8038c2ecf20Sopenharmony_ci unsigned int left; 8048c2ecf20Sopenharmony_ci unsigned int len; 8058c2ecf20Sopenharmony_ci unsigned int mss; 8068c2ecf20Sopenharmony_ci skb_frag_t *frag; 8078c2ecf20Sopenharmony_ci bool start, done; 8088c2ecf20Sopenharmony_ci bool outer_csum; 8098c2ecf20Sopenharmony_ci dma_addr_t addr; 8108c2ecf20Sopenharmony_ci bool has_vlan; 8118c2ecf20Sopenharmony_ci u16 desc_len; 8128c2ecf20Sopenharmony_ci u8 desc_nsge; 8138c2ecf20Sopenharmony_ci u16 vlan_tci; 8148c2ecf20Sopenharmony_ci bool encap; 8158c2ecf20Sopenharmony_ci int err; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci mss = skb_shinfo(skb)->gso_size; 8188c2ecf20Sopenharmony_ci nfrags = skb_shinfo(skb)->nr_frags; 8198c2ecf20Sopenharmony_ci len_left = skb->len - skb_headlen(skb); 8208c2ecf20Sopenharmony_ci outer_csum = (skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM) || 8218c2ecf20Sopenharmony_ci (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); 8228c2ecf20Sopenharmony_ci has_vlan = !!skb_vlan_tag_present(skb); 8238c2ecf20Sopenharmony_ci vlan_tci = skb_vlan_tag_get(skb); 8248c2ecf20Sopenharmony_ci encap = skb->encapsulation; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* Preload inner-most TCP csum field with IP pseudo hdr 8278c2ecf20Sopenharmony_ci * calculated with IP length set to zero. HW will later 8288c2ecf20Sopenharmony_ci * add in length to each TCP segment resulting from the TSO. 8298c2ecf20Sopenharmony_ci */ 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (encap) 8328c2ecf20Sopenharmony_ci err = ionic_tx_tcp_inner_pseudo_csum(skb); 8338c2ecf20Sopenharmony_ci else 8348c2ecf20Sopenharmony_ci err = ionic_tx_tcp_pseudo_csum(skb); 8358c2ecf20Sopenharmony_ci if (err) 8368c2ecf20Sopenharmony_ci return err; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (encap) 8398c2ecf20Sopenharmony_ci hdrlen = skb_inner_transport_header(skb) - skb->data + 8408c2ecf20Sopenharmony_ci inner_tcp_hdrlen(skb); 8418c2ecf20Sopenharmony_ci else 8428c2ecf20Sopenharmony_ci hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci seglen = hdrlen + mss; 8458c2ecf20Sopenharmony_ci left = skb_headlen(skb); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci desc = ionic_tx_tso_next(q, &elem); 8488c2ecf20Sopenharmony_ci start = true; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci /* Chop skb->data up into desc segments */ 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci while (left > 0) { 8538c2ecf20Sopenharmony_ci len = min(seglen, left); 8548c2ecf20Sopenharmony_ci frag_left = seglen - len; 8558c2ecf20Sopenharmony_ci desc_addr = ionic_tx_map_single(q, skb->data + offset, len); 8568c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, desc_addr)) 8578c2ecf20Sopenharmony_ci goto err_out_abort; 8588c2ecf20Sopenharmony_ci desc_len = len; 8598c2ecf20Sopenharmony_ci desc_nsge = 0; 8608c2ecf20Sopenharmony_ci left -= len; 8618c2ecf20Sopenharmony_ci offset += len; 8628c2ecf20Sopenharmony_ci if (nfrags > 0 && frag_left > 0) 8638c2ecf20Sopenharmony_ci continue; 8648c2ecf20Sopenharmony_ci done = (nfrags == 0 && left == 0); 8658c2ecf20Sopenharmony_ci ionic_tx_tso_post(q, desc, skb, 8668c2ecf20Sopenharmony_ci desc_addr, desc_nsge, desc_len, 8678c2ecf20Sopenharmony_ci hdrlen, mss, 8688c2ecf20Sopenharmony_ci outer_csum, 8698c2ecf20Sopenharmony_ci vlan_tci, has_vlan, 8708c2ecf20Sopenharmony_ci start, done); 8718c2ecf20Sopenharmony_ci total_pkts++; 8728c2ecf20Sopenharmony_ci total_bytes += start ? len : len + hdrlen; 8738c2ecf20Sopenharmony_ci desc = ionic_tx_tso_next(q, &elem); 8748c2ecf20Sopenharmony_ci start = false; 8758c2ecf20Sopenharmony_ci seglen = mss; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* Chop skb frags into desc segments */ 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci for (frag = skb_shinfo(skb)->frags; len_left; frag++) { 8818c2ecf20Sopenharmony_ci offset = 0; 8828c2ecf20Sopenharmony_ci left = skb_frag_size(frag); 8838c2ecf20Sopenharmony_ci len_left -= left; 8848c2ecf20Sopenharmony_ci nfrags--; 8858c2ecf20Sopenharmony_ci stats->frags++; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci while (left > 0) { 8888c2ecf20Sopenharmony_ci if (frag_left > 0) { 8898c2ecf20Sopenharmony_ci len = min(frag_left, left); 8908c2ecf20Sopenharmony_ci frag_left -= len; 8918c2ecf20Sopenharmony_ci addr = ionic_tx_map_frag(q, frag, offset, len); 8928c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, addr)) 8938c2ecf20Sopenharmony_ci goto err_out_abort; 8948c2ecf20Sopenharmony_ci elem->addr = cpu_to_le64(addr); 8958c2ecf20Sopenharmony_ci elem->len = cpu_to_le16(len); 8968c2ecf20Sopenharmony_ci elem++; 8978c2ecf20Sopenharmony_ci desc_nsge++; 8988c2ecf20Sopenharmony_ci left -= len; 8998c2ecf20Sopenharmony_ci offset += len; 9008c2ecf20Sopenharmony_ci if (nfrags > 0 && frag_left > 0) 9018c2ecf20Sopenharmony_ci continue; 9028c2ecf20Sopenharmony_ci done = (nfrags == 0 && left == 0); 9038c2ecf20Sopenharmony_ci ionic_tx_tso_post(q, desc, skb, desc_addr, 9048c2ecf20Sopenharmony_ci desc_nsge, desc_len, 9058c2ecf20Sopenharmony_ci hdrlen, mss, outer_csum, 9068c2ecf20Sopenharmony_ci vlan_tci, has_vlan, 9078c2ecf20Sopenharmony_ci start, done); 9088c2ecf20Sopenharmony_ci total_pkts++; 9098c2ecf20Sopenharmony_ci total_bytes += start ? len : len + hdrlen; 9108c2ecf20Sopenharmony_ci desc = ionic_tx_tso_next(q, &elem); 9118c2ecf20Sopenharmony_ci start = false; 9128c2ecf20Sopenharmony_ci } else { 9138c2ecf20Sopenharmony_ci len = min(mss, left); 9148c2ecf20Sopenharmony_ci frag_left = mss - len; 9158c2ecf20Sopenharmony_ci desc_addr = ionic_tx_map_frag(q, frag, 9168c2ecf20Sopenharmony_ci offset, len); 9178c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, desc_addr)) 9188c2ecf20Sopenharmony_ci goto err_out_abort; 9198c2ecf20Sopenharmony_ci desc_len = len; 9208c2ecf20Sopenharmony_ci desc_nsge = 0; 9218c2ecf20Sopenharmony_ci left -= len; 9228c2ecf20Sopenharmony_ci offset += len; 9238c2ecf20Sopenharmony_ci if (nfrags > 0 && frag_left > 0) 9248c2ecf20Sopenharmony_ci continue; 9258c2ecf20Sopenharmony_ci done = (nfrags == 0 && left == 0); 9268c2ecf20Sopenharmony_ci ionic_tx_tso_post(q, desc, skb, desc_addr, 9278c2ecf20Sopenharmony_ci desc_nsge, desc_len, 9288c2ecf20Sopenharmony_ci hdrlen, mss, outer_csum, 9298c2ecf20Sopenharmony_ci vlan_tci, has_vlan, 9308c2ecf20Sopenharmony_ci start, done); 9318c2ecf20Sopenharmony_ci total_pkts++; 9328c2ecf20Sopenharmony_ci total_bytes += start ? len : len + hdrlen; 9338c2ecf20Sopenharmony_ci desc = ionic_tx_tso_next(q, &elem); 9348c2ecf20Sopenharmony_ci start = false; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci stats->pkts += total_pkts; 9408c2ecf20Sopenharmony_ci stats->bytes += total_bytes; 9418c2ecf20Sopenharmony_ci stats->tso++; 9428c2ecf20Sopenharmony_ci stats->tso_bytes += total_bytes; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci return 0; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cierr_out_abort: 9478c2ecf20Sopenharmony_ci while (rewind != q->head_idx) { 9488c2ecf20Sopenharmony_ci rewind_desc_info = &q->info[rewind]; 9498c2ecf20Sopenharmony_ci ionic_tx_clean(q, rewind_desc_info, NULL, NULL); 9508c2ecf20Sopenharmony_ci rewind = (rewind + 1) & (q->num_descs - 1); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci q->head_idx = abort; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return -ENOMEM; 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct ionic_txq_desc *desc = q->info[q->head_idx].txq_desc; 9608c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 9618c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 9628c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 9638c2ecf20Sopenharmony_ci bool has_vlan; 9648c2ecf20Sopenharmony_ci u8 flags = 0; 9658c2ecf20Sopenharmony_ci bool encap; 9668c2ecf20Sopenharmony_ci u64 cmd; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci has_vlan = !!skb_vlan_tag_present(skb); 9698c2ecf20Sopenharmony_ci encap = skb->encapsulation; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci dma_addr = ionic_tx_map_single(q, skb->data, skb_headlen(skb)); 9728c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) 9738c2ecf20Sopenharmony_ci return -ENOMEM; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0; 9768c2ecf20Sopenharmony_ci flags |= encap ? IONIC_TXQ_DESC_FLAG_ENCAP : 0; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_PARTIAL, 9798c2ecf20Sopenharmony_ci flags, skb_shinfo(skb)->nr_frags, dma_addr); 9808c2ecf20Sopenharmony_ci desc->cmd = cpu_to_le64(cmd); 9818c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(skb_headlen(skb)); 9828c2ecf20Sopenharmony_ci desc->csum_start = cpu_to_le16(skb_checksum_start_offset(skb)); 9838c2ecf20Sopenharmony_ci desc->csum_offset = cpu_to_le16(skb->csum_offset); 9848c2ecf20Sopenharmony_ci if (has_vlan) { 9858c2ecf20Sopenharmony_ci desc->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb)); 9868c2ecf20Sopenharmony_ci stats->vlan_inserted++; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (skb_csum_is_sctp(skb)) 9908c2ecf20Sopenharmony_ci stats->crc32_csum++; 9918c2ecf20Sopenharmony_ci else 9928c2ecf20Sopenharmony_ci stats->csum++; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return 0; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int ionic_tx_calc_no_csum(struct ionic_queue *q, struct sk_buff *skb) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci struct ionic_txq_desc *desc = q->info[q->head_idx].txq_desc; 10008c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 10018c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 10028c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 10038c2ecf20Sopenharmony_ci bool has_vlan; 10048c2ecf20Sopenharmony_ci u8 flags = 0; 10058c2ecf20Sopenharmony_ci bool encap; 10068c2ecf20Sopenharmony_ci u64 cmd; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci has_vlan = !!skb_vlan_tag_present(skb); 10098c2ecf20Sopenharmony_ci encap = skb->encapsulation; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci dma_addr = ionic_tx_map_single(q, skb->data, skb_headlen(skb)); 10128c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) 10138c2ecf20Sopenharmony_ci return -ENOMEM; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci flags |= has_vlan ? IONIC_TXQ_DESC_FLAG_VLAN : 0; 10168c2ecf20Sopenharmony_ci flags |= encap ? IONIC_TXQ_DESC_FLAG_ENCAP : 0; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci cmd = encode_txq_desc_cmd(IONIC_TXQ_DESC_OPCODE_CSUM_NONE, 10198c2ecf20Sopenharmony_ci flags, skb_shinfo(skb)->nr_frags, dma_addr); 10208c2ecf20Sopenharmony_ci desc->cmd = cpu_to_le64(cmd); 10218c2ecf20Sopenharmony_ci desc->len = cpu_to_le16(skb_headlen(skb)); 10228c2ecf20Sopenharmony_ci if (has_vlan) { 10238c2ecf20Sopenharmony_ci desc->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb)); 10248c2ecf20Sopenharmony_ci stats->vlan_inserted++; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci stats->csum_none++; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci struct ionic_txq_sg_desc *sg_desc = q->info[q->head_idx].txq_sg_desc; 10358c2ecf20Sopenharmony_ci unsigned int len_left = skb->len - skb_headlen(skb); 10368c2ecf20Sopenharmony_ci struct ionic_txq_sg_elem *elem = sg_desc->elems; 10378c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 10388c2ecf20Sopenharmony_ci struct device *dev = q->lif->ionic->dev; 10398c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 10408c2ecf20Sopenharmony_ci skb_frag_t *frag; 10418c2ecf20Sopenharmony_ci u16 len; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci for (frag = skb_shinfo(skb)->frags; len_left; frag++, elem++) { 10448c2ecf20Sopenharmony_ci len = skb_frag_size(frag); 10458c2ecf20Sopenharmony_ci elem->len = cpu_to_le16(len); 10468c2ecf20Sopenharmony_ci dma_addr = ionic_tx_map_frag(q, frag, 0, len); 10478c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, dma_addr)) 10488c2ecf20Sopenharmony_ci return -ENOMEM; 10498c2ecf20Sopenharmony_ci elem->addr = cpu_to_le64(dma_addr); 10508c2ecf20Sopenharmony_ci len_left -= len; 10518c2ecf20Sopenharmony_ci stats->frags++; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci return 0; 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 10608c2ecf20Sopenharmony_ci int err; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* set up the initial descriptor */ 10638c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 10648c2ecf20Sopenharmony_ci err = ionic_tx_calc_csum(q, skb); 10658c2ecf20Sopenharmony_ci else 10668c2ecf20Sopenharmony_ci err = ionic_tx_calc_no_csum(q, skb); 10678c2ecf20Sopenharmony_ci if (err) 10688c2ecf20Sopenharmony_ci return err; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* add frags */ 10718c2ecf20Sopenharmony_ci err = ionic_tx_skb_frags(q, skb); 10728c2ecf20Sopenharmony_ci if (err) 10738c2ecf20Sopenharmony_ci return err; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 10768c2ecf20Sopenharmony_ci stats->pkts++; 10778c2ecf20Sopenharmony_ci stats->bytes += skb->len; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci netdev_tx_sent_queue(q_to_ndq(q), skb->len); 10808c2ecf20Sopenharmony_ci ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci int sg_elems = q->lif->qtype_info[IONIC_QTYPE_TXQ].max_sg_elems; 10888c2ecf20Sopenharmony_ci struct ionic_tx_stats *stats = q_to_tx_stats(q); 10898c2ecf20Sopenharmony_ci int ndescs; 10908c2ecf20Sopenharmony_ci int err; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* Each desc is mss long max, so a descriptor for each gso_seg */ 10938c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) 10948c2ecf20Sopenharmony_ci ndescs = skb_shinfo(skb)->gso_segs; 10958c2ecf20Sopenharmony_ci else 10968c2ecf20Sopenharmony_ci ndescs = 1; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->nr_frags <= sg_elems) 10998c2ecf20Sopenharmony_ci return ndescs; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* Too many frags, so linearize */ 11028c2ecf20Sopenharmony_ci err = skb_linearize(skb); 11038c2ecf20Sopenharmony_ci if (err) 11048c2ecf20Sopenharmony_ci return err; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci stats->linearize++; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci return ndescs; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci int stopped = 0; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (unlikely(!ionic_q_has_space(q, ndescs))) { 11168c2ecf20Sopenharmony_ci netif_stop_subqueue(q->lif->netdev, q->index); 11178c2ecf20Sopenharmony_ci q->stop++; 11188c2ecf20Sopenharmony_ci stopped = 1; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* Might race with ionic_tx_clean, check again */ 11218c2ecf20Sopenharmony_ci smp_rmb(); 11228c2ecf20Sopenharmony_ci if (ionic_q_has_space(q, ndescs)) { 11238c2ecf20Sopenharmony_ci netif_wake_subqueue(q->lif->netdev, q->index); 11248c2ecf20Sopenharmony_ci stopped = 0; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci return stopped; 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cinetdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci u16 queue_index = skb_get_queue_mapping(skb); 11348c2ecf20Sopenharmony_ci struct ionic_lif *lif = netdev_priv(netdev); 11358c2ecf20Sopenharmony_ci struct ionic_queue *q; 11368c2ecf20Sopenharmony_ci int ndescs; 11378c2ecf20Sopenharmony_ci int err; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (unlikely(!test_bit(IONIC_LIF_F_UP, lif->state))) { 11408c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 11418c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (unlikely(queue_index >= lif->nxqs)) 11458c2ecf20Sopenharmony_ci queue_index = 0; 11468c2ecf20Sopenharmony_ci q = &lif->txqcqs[queue_index]->q; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci ndescs = ionic_tx_descs_needed(q, skb); 11498c2ecf20Sopenharmony_ci if (ndescs < 0) 11508c2ecf20Sopenharmony_ci goto err_out_drop; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (unlikely(ionic_maybe_stop_tx(q, ndescs))) 11538c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) 11568c2ecf20Sopenharmony_ci err = ionic_tx_tso(q, skb); 11578c2ecf20Sopenharmony_ci else 11588c2ecf20Sopenharmony_ci err = ionic_tx(q, skb); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (err) 11618c2ecf20Sopenharmony_ci goto err_out_drop; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* Stop the queue if there aren't descriptors for the next packet. 11648c2ecf20Sopenharmony_ci * Since our SG lists per descriptor take care of most of the possible 11658c2ecf20Sopenharmony_ci * fragmentation, we don't need to have many descriptors available. 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci ionic_maybe_stop_tx(q, 4); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cierr_out_drop: 11728c2ecf20Sopenharmony_ci q->stop++; 11738c2ecf20Sopenharmony_ci q->drop++; 11748c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 11758c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11768c2ecf20Sopenharmony_ci} 1177