18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Huawei HiNIC PCI Express Linux driver 48c2ecf20Sopenharmony_ci * Copyright(c) 2017 Huawei Technologies Co., Ltd 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 98c2ecf20Sopenharmony_ci#include <linux/u64_stats_sync.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/smp.h> 198c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 208c2ecf20Sopenharmony_ci#include <linux/ip.h> 218c2ecf20Sopenharmony_ci#include <linux/tcp.h> 228c2ecf20Sopenharmony_ci#include <linux/sctp.h> 238c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 248c2ecf20Sopenharmony_ci#include <net/ipv6.h> 258c2ecf20Sopenharmony_ci#include <net/checksum.h> 268c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "hinic_common.h" 298c2ecf20Sopenharmony_ci#include "hinic_hw_if.h" 308c2ecf20Sopenharmony_ci#include "hinic_hw_wqe.h" 318c2ecf20Sopenharmony_ci#include "hinic_hw_wq.h" 328c2ecf20Sopenharmony_ci#include "hinic_hw_qp.h" 338c2ecf20Sopenharmony_ci#include "hinic_hw_dev.h" 348c2ecf20Sopenharmony_ci#include "hinic_dev.h" 358c2ecf20Sopenharmony_ci#include "hinic_tx.h" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define TX_IRQ_NO_PENDING 0 388c2ecf20Sopenharmony_ci#define TX_IRQ_NO_COALESC 0 398c2ecf20Sopenharmony_ci#define TX_IRQ_NO_LLI_TIMER 0 408c2ecf20Sopenharmony_ci#define TX_IRQ_NO_CREDIT 0 418c2ecf20Sopenharmony_ci#define TX_IRQ_NO_RESEND_TIMER 0 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define CI_UPDATE_NO_PENDING 0 448c2ecf20Sopenharmony_ci#define CI_UPDATE_NO_COALESC 0 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define HW_CONS_IDX(sq) be16_to_cpu(*(u16 *)((sq)->hw_ci_addr)) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define MIN_SKB_LEN 32 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define MAX_PAYLOAD_OFFSET 221 518c2ecf20Sopenharmony_ci#define TRANSPORT_OFFSET(l4_hdr, skb) ((u32)((l4_hdr) - (skb)->data)) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ciunion hinic_l3 { 548c2ecf20Sopenharmony_ci struct iphdr *v4; 558c2ecf20Sopenharmony_ci struct ipv6hdr *v6; 568c2ecf20Sopenharmony_ci unsigned char *hdr; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciunion hinic_l4 { 608c2ecf20Sopenharmony_ci struct tcphdr *tcp; 618c2ecf20Sopenharmony_ci struct udphdr *udp; 628c2ecf20Sopenharmony_ci unsigned char *hdr; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cienum hinic_offload_type { 668c2ecf20Sopenharmony_ci TX_OFFLOAD_TSO = BIT(0), 678c2ecf20Sopenharmony_ci TX_OFFLOAD_CSUM = BIT(1), 688c2ecf20Sopenharmony_ci TX_OFFLOAD_VLAN = BIT(2), 698c2ecf20Sopenharmony_ci TX_OFFLOAD_INVALID = BIT(3), 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/** 738c2ecf20Sopenharmony_ci * hinic_txq_clean_stats - Clean the statistics of specific queue 748c2ecf20Sopenharmony_ci * @txq: Logical Tx Queue 758c2ecf20Sopenharmony_ci **/ 768c2ecf20Sopenharmony_civoid hinic_txq_clean_stats(struct hinic_txq *txq) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct hinic_txq_stats *txq_stats = &txq->txq_stats; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq_stats->syncp); 818c2ecf20Sopenharmony_ci txq_stats->pkts = 0; 828c2ecf20Sopenharmony_ci txq_stats->bytes = 0; 838c2ecf20Sopenharmony_ci txq_stats->tx_busy = 0; 848c2ecf20Sopenharmony_ci txq_stats->tx_wake = 0; 858c2ecf20Sopenharmony_ci txq_stats->tx_dropped = 0; 868c2ecf20Sopenharmony_ci txq_stats->big_frags_pkts = 0; 878c2ecf20Sopenharmony_ci u64_stats_update_end(&txq_stats->syncp); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * hinic_txq_get_stats - get statistics of Tx Queue 928c2ecf20Sopenharmony_ci * @txq: Logical Tx Queue 938c2ecf20Sopenharmony_ci * @stats: return updated stats here 948c2ecf20Sopenharmony_ci **/ 958c2ecf20Sopenharmony_civoid hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct hinic_txq_stats *txq_stats = &txq->txq_stats; 988c2ecf20Sopenharmony_ci unsigned int start; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci do { 1018c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&txq_stats->syncp); 1028c2ecf20Sopenharmony_ci stats->pkts = txq_stats->pkts; 1038c2ecf20Sopenharmony_ci stats->bytes = txq_stats->bytes; 1048c2ecf20Sopenharmony_ci stats->tx_busy = txq_stats->tx_busy; 1058c2ecf20Sopenharmony_ci stats->tx_wake = txq_stats->tx_wake; 1068c2ecf20Sopenharmony_ci stats->tx_dropped = txq_stats->tx_dropped; 1078c2ecf20Sopenharmony_ci stats->big_frags_pkts = txq_stats->big_frags_pkts; 1088c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&txq_stats->syncp, start)); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * txq_stats_init - Initialize the statistics of specific queue 1138c2ecf20Sopenharmony_ci * @txq: Logical Tx Queue 1148c2ecf20Sopenharmony_ci **/ 1158c2ecf20Sopenharmony_cistatic void txq_stats_init(struct hinic_txq *txq) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct hinic_txq_stats *txq_stats = &txq->txq_stats; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci u64_stats_init(&txq_stats->syncp); 1208c2ecf20Sopenharmony_ci hinic_txq_clean_stats(txq); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/** 1248c2ecf20Sopenharmony_ci * tx_map_skb - dma mapping for skb and return sges 1258c2ecf20Sopenharmony_ci * @nic_dev: nic device 1268c2ecf20Sopenharmony_ci * @skb: the skb 1278c2ecf20Sopenharmony_ci * @sges: returned sges 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Return 0 - Success, negative - Failure 1308c2ecf20Sopenharmony_ci **/ 1318c2ecf20Sopenharmony_cistatic int tx_map_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, 1328c2ecf20Sopenharmony_ci struct hinic_sge *sges) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 1358c2ecf20Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 1368c2ecf20Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 1378c2ecf20Sopenharmony_ci skb_frag_t *frag; 1388c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 1398c2ecf20Sopenharmony_ci int i, j; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci dma_addr = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), 1428c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 1438c2ecf20Sopenharmony_ci if (dma_mapping_error(&pdev->dev, dma_addr)) { 1448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to map Tx skb data\n"); 1458c2ecf20Sopenharmony_ci return -EFAULT; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci hinic_set_sge(&sges[0], dma_addr, skb_headlen(skb)); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci for (i = 0 ; i < skb_shinfo(skb)->nr_frags; i++) { 1518c2ecf20Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci dma_addr = skb_frag_dma_map(&pdev->dev, frag, 0, 1548c2ecf20Sopenharmony_ci skb_frag_size(frag), 1558c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 1568c2ecf20Sopenharmony_ci if (dma_mapping_error(&pdev->dev, dma_addr)) { 1578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to map Tx skb frag\n"); 1588c2ecf20Sopenharmony_ci goto err_tx_map; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci hinic_set_sge(&sges[i + 1], dma_addr, skb_frag_size(frag)); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cierr_tx_map: 1678c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 1688c2ecf20Sopenharmony_ci dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[j + 1]), 1698c2ecf20Sopenharmony_ci sges[j + 1].len, DMA_TO_DEVICE); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, 1728c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 1738c2ecf20Sopenharmony_ci return -EFAULT; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * tx_unmap_skb - unmap the dma address of the skb 1788c2ecf20Sopenharmony_ci * @nic_dev: nic device 1798c2ecf20Sopenharmony_ci * @skb: the skb 1808c2ecf20Sopenharmony_ci * @sges: the sges that are connected to the skb 1818c2ecf20Sopenharmony_ci **/ 1828c2ecf20Sopenharmony_cistatic void tx_unmap_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, 1838c2ecf20Sopenharmony_ci struct hinic_sge *sges) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 1868c2ecf20Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 1878c2ecf20Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 1888c2ecf20Sopenharmony_ci int i; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags ; i++) 1918c2ecf20Sopenharmony_ci dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[i + 1]), 1928c2ecf20Sopenharmony_ci sges[i + 1].len, DMA_TO_DEVICE); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, 1958c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void get_inner_l3_l4_type(struct sk_buff *skb, union hinic_l3 *ip, 1998c2ecf20Sopenharmony_ci union hinic_l4 *l4, 2008c2ecf20Sopenharmony_ci enum hinic_offload_type offload_type, 2018c2ecf20Sopenharmony_ci enum hinic_l3_offload_type *l3_type, 2028c2ecf20Sopenharmony_ci u8 *l4_proto) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci u8 *exthdr; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (ip->v4->version == 4) { 2078c2ecf20Sopenharmony_ci *l3_type = (offload_type == TX_OFFLOAD_CSUM) ? 2088c2ecf20Sopenharmony_ci IPV4_PKT_NO_CHKSUM_OFFLOAD : 2098c2ecf20Sopenharmony_ci IPV4_PKT_WITH_CHKSUM_OFFLOAD; 2108c2ecf20Sopenharmony_ci *l4_proto = ip->v4->protocol; 2118c2ecf20Sopenharmony_ci } else if (ip->v4->version == 6) { 2128c2ecf20Sopenharmony_ci *l3_type = IPV6_PKT; 2138c2ecf20Sopenharmony_ci exthdr = ip->hdr + sizeof(*ip->v6); 2148c2ecf20Sopenharmony_ci *l4_proto = ip->v6->nexthdr; 2158c2ecf20Sopenharmony_ci if (exthdr != l4->hdr) { 2168c2ecf20Sopenharmony_ci int start = exthdr - skb->data; 2178c2ecf20Sopenharmony_ci __be16 frag_off; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci ipv6_skip_exthdr(skb, start, l4_proto, &frag_off); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci *l3_type = L3TYPE_UNKNOWN; 2238c2ecf20Sopenharmony_ci *l4_proto = 0; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void get_inner_l4_info(struct sk_buff *skb, union hinic_l4 *l4, 2288c2ecf20Sopenharmony_ci enum hinic_offload_type offload_type, u8 l4_proto, 2298c2ecf20Sopenharmony_ci enum hinic_l4_offload_type *l4_offload, 2308c2ecf20Sopenharmony_ci u32 *l4_len, u32 *offset) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci *l4_offload = OFFLOAD_DISABLE; 2338c2ecf20Sopenharmony_ci *offset = 0; 2348c2ecf20Sopenharmony_ci *l4_len = 0; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci switch (l4_proto) { 2378c2ecf20Sopenharmony_ci case IPPROTO_TCP: 2388c2ecf20Sopenharmony_ci *l4_offload = TCP_OFFLOAD_ENABLE; 2398c2ecf20Sopenharmony_ci /* doff in unit of 4B */ 2408c2ecf20Sopenharmony_ci *l4_len = l4->tcp->doff * 4; 2418c2ecf20Sopenharmony_ci *offset = *l4_len + TRANSPORT_OFFSET(l4->hdr, skb); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci case IPPROTO_UDP: 2458c2ecf20Sopenharmony_ci *l4_offload = UDP_OFFLOAD_ENABLE; 2468c2ecf20Sopenharmony_ci *l4_len = sizeof(struct udphdr); 2478c2ecf20Sopenharmony_ci *offset = TRANSPORT_OFFSET(l4->hdr, skb); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci case IPPROTO_SCTP: 2518c2ecf20Sopenharmony_ci /* only csum offload support sctp */ 2528c2ecf20Sopenharmony_ci if (offload_type != TX_OFFLOAD_CSUM) 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci *l4_offload = SCTP_OFFLOAD_ENABLE; 2568c2ecf20Sopenharmony_ci *l4_len = sizeof(struct sctphdr); 2578c2ecf20Sopenharmony_ci *offset = TRANSPORT_OFFSET(l4->hdr, skb); 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci default: 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic __sum16 csum_magic(union hinic_l3 *ip, unsigned short proto) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci return (ip->v4->version == 4) ? 2688c2ecf20Sopenharmony_ci csum_tcpudp_magic(ip->v4->saddr, ip->v4->daddr, 0, proto, 0) : 2698c2ecf20Sopenharmony_ci csum_ipv6_magic(&ip->v6->saddr, &ip->v6->daddr, 0, proto, 0); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int offload_tso(struct hinic_sq_task *task, u32 *queue_info, 2738c2ecf20Sopenharmony_ci struct sk_buff *skb) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci u32 offset, l4_len, ip_identify, network_hdr_len; 2768c2ecf20Sopenharmony_ci enum hinic_l3_offload_type l3_offload; 2778c2ecf20Sopenharmony_ci enum hinic_l4_offload_type l4_offload; 2788c2ecf20Sopenharmony_ci union hinic_l3 ip; 2798c2ecf20Sopenharmony_ci union hinic_l4 l4; 2808c2ecf20Sopenharmony_ci u8 l4_proto; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!skb_is_gso(skb)) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (skb_cow_head(skb, 0) < 0) 2868c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (skb->encapsulation) { 2898c2ecf20Sopenharmony_ci u32 gso_type = skb_shinfo(skb)->gso_type; 2908c2ecf20Sopenharmony_ci u32 tunnel_type = 0; 2918c2ecf20Sopenharmony_ci u32 l4_tunnel_len; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ip.hdr = skb_network_header(skb); 2948c2ecf20Sopenharmony_ci l4.hdr = skb_transport_header(skb); 2958c2ecf20Sopenharmony_ci network_hdr_len = skb_inner_network_header_len(skb); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (ip.v4->version == 4) { 2988c2ecf20Sopenharmony_ci ip.v4->tot_len = 0; 2998c2ecf20Sopenharmony_ci l3_offload = IPV4_PKT_WITH_CHKSUM_OFFLOAD; 3008c2ecf20Sopenharmony_ci } else if (ip.v4->version == 6) { 3018c2ecf20Sopenharmony_ci l3_offload = IPV6_PKT; 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci l3_offload = 0; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci hinic_task_set_outter_l3(task, l3_offload, 3078c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (gso_type & SKB_GSO_UDP_TUNNEL_CSUM) { 3108c2ecf20Sopenharmony_ci l4.udp->check = ~csum_magic(&ip, IPPROTO_UDP); 3118c2ecf20Sopenharmony_ci tunnel_type = TUNNEL_UDP_CSUM; 3128c2ecf20Sopenharmony_ci } else if (gso_type & SKB_GSO_UDP_TUNNEL) { 3138c2ecf20Sopenharmony_ci tunnel_type = TUNNEL_UDP_NO_CSUM; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci l4_tunnel_len = skb_inner_network_offset(skb) - 3178c2ecf20Sopenharmony_ci skb_transport_offset(skb); 3188c2ecf20Sopenharmony_ci hinic_task_set_tunnel_l4(task, tunnel_type, l4_tunnel_len); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 3218c2ecf20Sopenharmony_ci l4.hdr = skb_inner_transport_header(skb); 3228c2ecf20Sopenharmony_ci } else { 3238c2ecf20Sopenharmony_ci ip.hdr = skb_network_header(skb); 3248c2ecf20Sopenharmony_ci l4.hdr = skb_transport_header(skb); 3258c2ecf20Sopenharmony_ci network_hdr_len = skb_network_header_len(skb); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* initialize inner IP header fields */ 3298c2ecf20Sopenharmony_ci if (ip.v4->version == 4) 3308c2ecf20Sopenharmony_ci ip.v4->tot_len = 0; 3318c2ecf20Sopenharmony_ci else 3328c2ecf20Sopenharmony_ci ip.v6->payload_len = 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci get_inner_l3_l4_type(skb, &ip, &l4, TX_OFFLOAD_TSO, &l3_offload, 3358c2ecf20Sopenharmony_ci &l4_proto); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci hinic_task_set_inner_l3(task, l3_offload, network_hdr_len); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ip_identify = 0; 3408c2ecf20Sopenharmony_ci if (l4_proto == IPPROTO_TCP) 3418c2ecf20Sopenharmony_ci l4.tcp->check = ~csum_magic(&ip, IPPROTO_TCP); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci get_inner_l4_info(skb, &l4, TX_OFFLOAD_TSO, l4_proto, &l4_offload, 3448c2ecf20Sopenharmony_ci &l4_len, &offset); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci hinic_set_tso_inner_l4(task, queue_info, l4_offload, l4_len, offset, 3478c2ecf20Sopenharmony_ci ip_identify, skb_shinfo(skb)->gso_size); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci return 1; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int offload_csum(struct hinic_sq_task *task, u32 *queue_info, 3538c2ecf20Sopenharmony_ci struct sk_buff *skb) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci enum hinic_l4_offload_type l4_offload; 3568c2ecf20Sopenharmony_ci u32 offset, l4_len, network_hdr_len; 3578c2ecf20Sopenharmony_ci enum hinic_l3_offload_type l3_type; 3588c2ecf20Sopenharmony_ci u32 tunnel_type = NOT_TUNNEL; 3598c2ecf20Sopenharmony_ci union hinic_l3 ip; 3608c2ecf20Sopenharmony_ci union hinic_l4 l4; 3618c2ecf20Sopenharmony_ci u8 l4_proto; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (skb->encapsulation) { 3678c2ecf20Sopenharmony_ci u32 l4_tunnel_len; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci tunnel_type = TUNNEL_UDP_NO_CSUM; 3708c2ecf20Sopenharmony_ci ip.hdr = skb_network_header(skb); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (ip.v4->version == 4) { 3738c2ecf20Sopenharmony_ci l3_type = IPV4_PKT_NO_CHKSUM_OFFLOAD; 3748c2ecf20Sopenharmony_ci l4_proto = ip.v4->protocol; 3758c2ecf20Sopenharmony_ci } else if (ip.v4->version == 6) { 3768c2ecf20Sopenharmony_ci unsigned char *exthdr; 3778c2ecf20Sopenharmony_ci __be16 frag_off; 3788c2ecf20Sopenharmony_ci l3_type = IPV6_PKT; 3798c2ecf20Sopenharmony_ci tunnel_type = TUNNEL_UDP_CSUM; 3808c2ecf20Sopenharmony_ci exthdr = ip.hdr + sizeof(*ip.v6); 3818c2ecf20Sopenharmony_ci l4_proto = ip.v6->nexthdr; 3828c2ecf20Sopenharmony_ci l4.hdr = skb_transport_header(skb); 3838c2ecf20Sopenharmony_ci if (l4.hdr != exthdr) 3848c2ecf20Sopenharmony_ci ipv6_skip_exthdr(skb, exthdr - skb->data, 3858c2ecf20Sopenharmony_ci &l4_proto, &frag_off); 3868c2ecf20Sopenharmony_ci } else { 3878c2ecf20Sopenharmony_ci l3_type = L3TYPE_UNKNOWN; 3888c2ecf20Sopenharmony_ci l4_proto = IPPROTO_RAW; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci hinic_task_set_outter_l3(task, l3_type, 3928c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci switch (l4_proto) { 3958c2ecf20Sopenharmony_ci case IPPROTO_UDP: 3968c2ecf20Sopenharmony_ci l4_tunnel_len = skb_inner_network_offset(skb) - 3978c2ecf20Sopenharmony_ci skb_transport_offset(skb); 3988c2ecf20Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 3998c2ecf20Sopenharmony_ci l4.hdr = skb_inner_transport_header(skb); 4008c2ecf20Sopenharmony_ci network_hdr_len = skb_inner_network_header_len(skb); 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case IPPROTO_IPIP: 4038c2ecf20Sopenharmony_ci case IPPROTO_IPV6: 4048c2ecf20Sopenharmony_ci tunnel_type = NOT_TUNNEL; 4058c2ecf20Sopenharmony_ci l4_tunnel_len = 0; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 4088c2ecf20Sopenharmony_ci l4.hdr = skb_transport_header(skb); 4098c2ecf20Sopenharmony_ci network_hdr_len = skb_network_header_len(skb); 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci default: 4128c2ecf20Sopenharmony_ci /* Unsupported tunnel packet, disable csum offload */ 4138c2ecf20Sopenharmony_ci skb_checksum_help(skb); 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci hinic_task_set_tunnel_l4(task, tunnel_type, l4_tunnel_len); 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci ip.hdr = skb_network_header(skb); 4208c2ecf20Sopenharmony_ci l4.hdr = skb_transport_header(skb); 4218c2ecf20Sopenharmony_ci network_hdr_len = skb_network_header_len(skb); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci get_inner_l3_l4_type(skb, &ip, &l4, TX_OFFLOAD_CSUM, &l3_type, 4258c2ecf20Sopenharmony_ci &l4_proto); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci hinic_task_set_inner_l3(task, l3_type, network_hdr_len); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci get_inner_l4_info(skb, &l4, TX_OFFLOAD_CSUM, l4_proto, &l4_offload, 4308c2ecf20Sopenharmony_ci &l4_len, &offset); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci hinic_set_cs_inner_l4(task, queue_info, l4_offload, l4_len, offset); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 1; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void offload_vlan(struct hinic_sq_task *task, u32 *queue_info, 4388c2ecf20Sopenharmony_ci u16 vlan_tag, u16 vlan_pri) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(vlan_tag, VLAN_TAG) | 4418c2ecf20Sopenharmony_ci HINIC_SQ_TASK_INFO0_SET(1U, VLAN_OFFLOAD); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci *queue_info |= HINIC_SQ_CTRL_SET(vlan_pri, QUEUE_INFO_PRI); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task, 4478c2ecf20Sopenharmony_ci u32 *queue_info) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci enum hinic_offload_type offload = 0; 4508c2ecf20Sopenharmony_ci u16 vlan_tag; 4518c2ecf20Sopenharmony_ci int enabled; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci enabled = offload_tso(task, queue_info, skb); 4548c2ecf20Sopenharmony_ci if (enabled > 0) { 4558c2ecf20Sopenharmony_ci offload |= TX_OFFLOAD_TSO; 4568c2ecf20Sopenharmony_ci } else if (enabled == 0) { 4578c2ecf20Sopenharmony_ci enabled = offload_csum(task, queue_info, skb); 4588c2ecf20Sopenharmony_ci if (enabled) 4598c2ecf20Sopenharmony_ci offload |= TX_OFFLOAD_CSUM; 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (unlikely(skb_vlan_tag_present(skb))) { 4658c2ecf20Sopenharmony_ci vlan_tag = skb_vlan_tag_get(skb); 4668c2ecf20Sopenharmony_ci offload_vlan(task, queue_info, vlan_tag, 4678c2ecf20Sopenharmony_ci vlan_tag >> VLAN_PRIO_SHIFT); 4688c2ecf20Sopenharmony_ci offload |= TX_OFFLOAD_VLAN; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (offload) 4728c2ecf20Sopenharmony_ci hinic_task_set_l2hdr(task, skb_network_offset(skb)); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* payload offset should not more than 221 */ 4758c2ecf20Sopenharmony_ci if (HINIC_SQ_CTRL_GET(*queue_info, QUEUE_INFO_PLDOFF) > 4768c2ecf20Sopenharmony_ci MAX_PAYLOAD_OFFSET) { 4778c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* mss should not less than 80 */ 4818c2ecf20Sopenharmony_ci if (HINIC_SQ_CTRL_GET(*queue_info, QUEUE_INFO_MSS) < HINIC_MSS_MIN) { 4828c2ecf20Sopenharmony_ci *queue_info = HINIC_SQ_CTRL_CLEAR(*queue_info, QUEUE_INFO_MSS); 4838c2ecf20Sopenharmony_ci *queue_info |= HINIC_SQ_CTRL_SET(HINIC_MSS_MIN, QUEUE_INFO_MSS); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cinetdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 4928c2ecf20Sopenharmony_ci u16 prod_idx, q_id = skb->queue_mapping; 4938c2ecf20Sopenharmony_ci struct netdev_queue *netdev_txq; 4948c2ecf20Sopenharmony_ci int nr_sges, err = NETDEV_TX_OK; 4958c2ecf20Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 4968c2ecf20Sopenharmony_ci unsigned int wqe_size; 4978c2ecf20Sopenharmony_ci struct hinic_txq *txq; 4988c2ecf20Sopenharmony_ci struct hinic_qp *qp; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci txq = &nic_dev->txqs[q_id]; 5018c2ecf20Sopenharmony_ci qp = container_of(txq->sq, struct hinic_qp, sq); 5028c2ecf20Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci err = tx_map_skb(nic_dev, skb, txq->sges); 5058c2ecf20Sopenharmony_ci if (err) 5068c2ecf20Sopenharmony_ci goto skb_error; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 5118c2ecf20Sopenharmony_ci if (!sq_wqe) { 5128c2ecf20Sopenharmony_ci netif_stop_subqueue(netdev, qp->q_id); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 5158c2ecf20Sopenharmony_ci if (sq_wqe) { 5168c2ecf20Sopenharmony_ci netif_wake_subqueue(nic_dev->netdev, qp->q_id); 5178c2ecf20Sopenharmony_ci goto process_sq_wqe; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci tx_unmap_skb(nic_dev, skb, txq->sges); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 5238c2ecf20Sopenharmony_ci txq->txq_stats.tx_busy++; 5248c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 5258c2ecf20Sopenharmony_ci err = NETDEV_TX_BUSY; 5268c2ecf20Sopenharmony_ci wqe_size = 0; 5278c2ecf20Sopenharmony_ci goto flush_skbs; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciprocess_sq_wqe: 5318c2ecf20Sopenharmony_ci hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); 5328c2ecf20Sopenharmony_ci hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ciflush_skbs: 5358c2ecf20Sopenharmony_ci netdev_txq = netdev_get_tx_queue(netdev, q_id); 5368c2ecf20Sopenharmony_ci if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq))) 5378c2ecf20Sopenharmony_ci hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return err; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ciskb_error: 5428c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5438c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 5448c2ecf20Sopenharmony_ci txq->txq_stats.tx_dropped++; 5458c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cinetdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 5538c2ecf20Sopenharmony_ci u16 prod_idx, q_id = skb->queue_mapping; 5548c2ecf20Sopenharmony_ci struct netdev_queue *netdev_txq; 5558c2ecf20Sopenharmony_ci int nr_sges, err = NETDEV_TX_OK; 5568c2ecf20Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 5578c2ecf20Sopenharmony_ci unsigned int wqe_size; 5588c2ecf20Sopenharmony_ci struct hinic_txq *txq; 5598c2ecf20Sopenharmony_ci struct hinic_qp *qp; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci txq = &nic_dev->txqs[q_id]; 5628c2ecf20Sopenharmony_ci qp = container_of(txq->sq, struct hinic_qp, sq); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (skb->len < MIN_SKB_LEN) { 5658c2ecf20Sopenharmony_ci if (skb_pad(skb, MIN_SKB_LEN - skb->len)) { 5668c2ecf20Sopenharmony_ci netdev_err(netdev, "Failed to pad skb\n"); 5678c2ecf20Sopenharmony_ci goto update_error_stats; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci skb->len = MIN_SKB_LEN; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 5748c2ecf20Sopenharmony_ci if (nr_sges > 17) { 5758c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 5768c2ecf20Sopenharmony_ci txq->txq_stats.big_frags_pkts++; 5778c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci if (nr_sges > txq->max_sges) { 5818c2ecf20Sopenharmony_ci netdev_err(netdev, "Too many Tx sges\n"); 5828c2ecf20Sopenharmony_ci goto skb_error; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci err = tx_map_skb(nic_dev, skb, txq->sges); 5868c2ecf20Sopenharmony_ci if (err) 5878c2ecf20Sopenharmony_ci goto skb_error; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 5928c2ecf20Sopenharmony_ci if (!sq_wqe) { 5938c2ecf20Sopenharmony_ci netif_stop_subqueue(netdev, qp->q_id); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Check for the case free_tx_poll is called in another cpu 5968c2ecf20Sopenharmony_ci * and we stopped the subqueue after free_tx_poll check. 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 5998c2ecf20Sopenharmony_ci if (sq_wqe) { 6008c2ecf20Sopenharmony_ci netif_wake_subqueue(nic_dev->netdev, qp->q_id); 6018c2ecf20Sopenharmony_ci goto process_sq_wqe; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci tx_unmap_skb(nic_dev, skb, txq->sges); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 6078c2ecf20Sopenharmony_ci txq->txq_stats.tx_busy++; 6088c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 6098c2ecf20Sopenharmony_ci err = NETDEV_TX_BUSY; 6108c2ecf20Sopenharmony_ci wqe_size = 0; 6118c2ecf20Sopenharmony_ci goto flush_skbs; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ciprocess_sq_wqe: 6158c2ecf20Sopenharmony_ci hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci err = hinic_tx_offload(skb, &sq_wqe->task, &sq_wqe->ctrl.queue_info); 6188c2ecf20Sopenharmony_ci if (err) 6198c2ecf20Sopenharmony_ci goto offload_error; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ciflush_skbs: 6248c2ecf20Sopenharmony_ci netdev_txq = netdev_get_tx_queue(netdev, q_id); 6258c2ecf20Sopenharmony_ci if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq))) 6268c2ecf20Sopenharmony_ci hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci return err; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cioffload_error: 6318c2ecf20Sopenharmony_ci hinic_sq_return_wqe(txq->sq, wqe_size); 6328c2ecf20Sopenharmony_ci tx_unmap_skb(nic_dev, skb, txq->sges); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ciskb_error: 6358c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ciupdate_error_stats: 6388c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 6398c2ecf20Sopenharmony_ci txq->txq_stats.tx_dropped++; 6408c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/** 6468c2ecf20Sopenharmony_ci * tx_free_skb - unmap and free skb 6478c2ecf20Sopenharmony_ci * @nic_dev: nic device 6488c2ecf20Sopenharmony_ci * @skb: the skb 6498c2ecf20Sopenharmony_ci * @sges: the sges that are connected to the skb 6508c2ecf20Sopenharmony_ci **/ 6518c2ecf20Sopenharmony_cistatic void tx_free_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, 6528c2ecf20Sopenharmony_ci struct hinic_sge *sges) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci tx_unmap_skb(nic_dev, skb, sges); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/** 6608c2ecf20Sopenharmony_ci * free_all_rx_skbs - free all skbs in tx queue 6618c2ecf20Sopenharmony_ci * @txq: tx queue 6628c2ecf20Sopenharmony_ci **/ 6638c2ecf20Sopenharmony_cistatic void free_all_tx_skbs(struct hinic_txq *txq) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(txq->netdev); 6668c2ecf20Sopenharmony_ci struct hinic_sq *sq = txq->sq; 6678c2ecf20Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 6688c2ecf20Sopenharmony_ci unsigned int wqe_size; 6698c2ecf20Sopenharmony_ci struct sk_buff *skb; 6708c2ecf20Sopenharmony_ci int nr_sges; 6718c2ecf20Sopenharmony_ci u16 ci; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci while ((sq_wqe = hinic_sq_read_wqebb(sq, &skb, &wqe_size, &ci))) { 6748c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_read_wqe(sq, &skb, wqe_size, &ci); 6758c2ecf20Sopenharmony_ci if (!sq_wqe) 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci hinic_sq_put_wqe(sq, wqe_size); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci tx_free_skb(nic_dev, skb, txq->free_sges); 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/** 6898c2ecf20Sopenharmony_ci * free_tx_poll - free finished tx skbs in tx queue that connected to napi 6908c2ecf20Sopenharmony_ci * @napi: napi 6918c2ecf20Sopenharmony_ci * @budget: number of tx 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * Return 0 - Success, negative - Failure 6948c2ecf20Sopenharmony_ci **/ 6958c2ecf20Sopenharmony_cistatic int free_tx_poll(struct napi_struct *napi, int budget) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct hinic_txq *txq = container_of(napi, struct hinic_txq, napi); 6988c2ecf20Sopenharmony_ci struct hinic_qp *qp = container_of(txq->sq, struct hinic_qp, sq); 6998c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(txq->netdev); 7008c2ecf20Sopenharmony_ci struct netdev_queue *netdev_txq; 7018c2ecf20Sopenharmony_ci struct hinic_sq *sq = txq->sq; 7028c2ecf20Sopenharmony_ci struct hinic_wq *wq = sq->wq; 7038c2ecf20Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 7048c2ecf20Sopenharmony_ci unsigned int wqe_size; 7058c2ecf20Sopenharmony_ci int nr_sges, pkts = 0; 7068c2ecf20Sopenharmony_ci struct sk_buff *skb; 7078c2ecf20Sopenharmony_ci u64 tx_bytes = 0; 7088c2ecf20Sopenharmony_ci u16 hw_ci, sw_ci; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci do { 7118c2ecf20Sopenharmony_ci hw_ci = HW_CONS_IDX(sq) & wq->mask; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci dma_rmb(); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Reading a WQEBB to get real WQE size and consumer index. */ 7168c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_read_wqebb(sq, &skb, &wqe_size, &sw_ci); 7178c2ecf20Sopenharmony_ci if ((!sq_wqe) || 7188c2ecf20Sopenharmony_ci (((hw_ci - sw_ci) & wq->mask) * wq->wqebb_size < wqe_size)) 7198c2ecf20Sopenharmony_ci break; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* If this WQE have multiple WQEBBs, we will read again to get 7228c2ecf20Sopenharmony_ci * full size WQE. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci if (wqe_size > wq->wqebb_size) { 7258c2ecf20Sopenharmony_ci sq_wqe = hinic_sq_read_wqe(sq, &skb, wqe_size, &sw_ci); 7268c2ecf20Sopenharmony_ci if (unlikely(!sq_wqe)) 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci tx_bytes += skb->len; 7318c2ecf20Sopenharmony_ci pkts++; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci hinic_sq_put_wqe(sq, wqe_size); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci tx_free_skb(nic_dev, skb, txq->free_sges); 7408c2ecf20Sopenharmony_ci } while (pkts < budget); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (__netif_subqueue_stopped(nic_dev->netdev, qp->q_id) && 7438c2ecf20Sopenharmony_ci hinic_get_sq_free_wqebbs(sq) >= HINIC_MIN_TX_NUM_WQEBBS(sq)) { 7448c2ecf20Sopenharmony_ci netdev_txq = netdev_get_tx_queue(txq->netdev, qp->q_id); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci __netif_tx_lock(netdev_txq, smp_processor_id()); 7478c2ecf20Sopenharmony_ci if (!netif_testing(nic_dev->netdev)) 7488c2ecf20Sopenharmony_ci netif_wake_subqueue(nic_dev->netdev, qp->q_id); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci __netif_tx_unlock(netdev_txq); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 7538c2ecf20Sopenharmony_ci txq->txq_stats.tx_wake++; 7548c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 7588c2ecf20Sopenharmony_ci txq->txq_stats.bytes += tx_bytes; 7598c2ecf20Sopenharmony_ci txq->txq_stats.pkts += pkts; 7608c2ecf20Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (pkts < budget) { 7638c2ecf20Sopenharmony_ci napi_complete(napi); 7648c2ecf20Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 7658c2ecf20Sopenharmony_ci hinic_hwdev_set_msix_state(nic_dev->hwdev, 7668c2ecf20Sopenharmony_ci sq->msix_entry, 7678c2ecf20Sopenharmony_ci HINIC_MSIX_ENABLE); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci return pkts; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return budget; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic irqreturn_t tx_irq(int irq, void *data) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct hinic_txq *txq = data; 7788c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci nic_dev = netdev_priv(txq->netdev); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 7838c2ecf20Sopenharmony_ci /* Disable the interrupt until napi will be completed */ 7848c2ecf20Sopenharmony_ci hinic_hwdev_set_msix_state(nic_dev->hwdev, 7858c2ecf20Sopenharmony_ci txq->sq->msix_entry, 7868c2ecf20Sopenharmony_ci HINIC_MSIX_DISABLE); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci hinic_hwdev_msix_cnt_set(nic_dev->hwdev, txq->sq->msix_entry); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci napi_schedule(&txq->napi); 7918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic int tx_request_irq(struct hinic_txq *txq) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(txq->netdev); 7978c2ecf20Sopenharmony_ci struct hinic_msix_config interrupt_info = {0}; 7988c2ecf20Sopenharmony_ci struct hinic_intr_coal_info *intr_coal = NULL; 7998c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 8008c2ecf20Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 8018c2ecf20Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 8028c2ecf20Sopenharmony_ci struct hinic_sq *sq = txq->sq; 8038c2ecf20Sopenharmony_ci struct hinic_qp *qp; 8048c2ecf20Sopenharmony_ci int err; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci qp = container_of(sq, struct hinic_qp, sq); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci netif_napi_add(txq->netdev, &txq->napi, free_tx_poll, nic_dev->tx_weight); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry, 8118c2ecf20Sopenharmony_ci TX_IRQ_NO_PENDING, TX_IRQ_NO_COALESC, 8128c2ecf20Sopenharmony_ci TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT, 8138c2ecf20Sopenharmony_ci TX_IRQ_NO_RESEND_TIMER); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci intr_coal = &nic_dev->tx_intr_coalesce[qp->q_id]; 8168c2ecf20Sopenharmony_ci interrupt_info.msix_index = sq->msix_entry; 8178c2ecf20Sopenharmony_ci interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; 8188c2ecf20Sopenharmony_ci interrupt_info.pending_cnt = intr_coal->pending_limt; 8198c2ecf20Sopenharmony_ci interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci err = hinic_set_interrupt_cfg(hwdev, &interrupt_info); 8228c2ecf20Sopenharmony_ci if (err) { 8238c2ecf20Sopenharmony_ci netif_err(nic_dev, drv, txq->netdev, 8248c2ecf20Sopenharmony_ci "Failed to set TX interrupt coalescing attribute\n"); 8258c2ecf20Sopenharmony_ci netif_napi_del(&txq->napi); 8268c2ecf20Sopenharmony_ci return err; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq); 8308c2ecf20Sopenharmony_ci if (err) { 8318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request Tx irq\n"); 8328c2ecf20Sopenharmony_ci netif_napi_del(&txq->napi); 8338c2ecf20Sopenharmony_ci return err; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci return 0; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic void tx_free_irq(struct hinic_txq *txq) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct hinic_sq *sq = txq->sq; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci free_irq(sq->irq, txq); 8448c2ecf20Sopenharmony_ci netif_napi_del(&txq->napi); 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci/** 8488c2ecf20Sopenharmony_ci * hinic_init_txq - Initialize the Tx Queue 8498c2ecf20Sopenharmony_ci * @txq: Logical Tx Queue 8508c2ecf20Sopenharmony_ci * @sq: Hardware Tx Queue to connect the Logical queue with 8518c2ecf20Sopenharmony_ci * @netdev: network device to connect the Logical queue with 8528c2ecf20Sopenharmony_ci * 8538c2ecf20Sopenharmony_ci * Return 0 - Success, negative - Failure 8548c2ecf20Sopenharmony_ci **/ 8558c2ecf20Sopenharmony_ciint hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, 8568c2ecf20Sopenharmony_ci struct net_device *netdev) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); 8598c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 8608c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 8618c2ecf20Sopenharmony_ci int err, irqname_len; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci txq->netdev = netdev; 8648c2ecf20Sopenharmony_ci txq->sq = sq; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci txq_stats_init(txq); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci txq->max_sges = HINIC_MAX_SQ_BUFDESCS; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci txq->sges = devm_kcalloc(&netdev->dev, txq->max_sges, 8718c2ecf20Sopenharmony_ci sizeof(*txq->sges), GFP_KERNEL); 8728c2ecf20Sopenharmony_ci if (!txq->sges) 8738c2ecf20Sopenharmony_ci return -ENOMEM; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci txq->free_sges = devm_kcalloc(&netdev->dev, txq->max_sges, 8768c2ecf20Sopenharmony_ci sizeof(*txq->free_sges), GFP_KERNEL); 8778c2ecf20Sopenharmony_ci if (!txq->free_sges) { 8788c2ecf20Sopenharmony_ci err = -ENOMEM; 8798c2ecf20Sopenharmony_ci goto err_alloc_free_sges; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci irqname_len = snprintf(NULL, 0, "%s_txq%d", netdev->name, qp->q_id) + 1; 8838c2ecf20Sopenharmony_ci txq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); 8848c2ecf20Sopenharmony_ci if (!txq->irq_name) { 8858c2ecf20Sopenharmony_ci err = -ENOMEM; 8868c2ecf20Sopenharmony_ci goto err_alloc_irqname; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci sprintf(txq->irq_name, "%s_txq%d", netdev->name, qp->q_id); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci err = hinic_hwdev_hw_ci_addr_set(hwdev, sq, CI_UPDATE_NO_PENDING, 8928c2ecf20Sopenharmony_ci CI_UPDATE_NO_COALESC); 8938c2ecf20Sopenharmony_ci if (err) 8948c2ecf20Sopenharmony_ci goto err_hw_ci; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci err = tx_request_irq(txq); 8978c2ecf20Sopenharmony_ci if (err) { 8988c2ecf20Sopenharmony_ci netdev_err(netdev, "Failed to request Tx irq\n"); 8998c2ecf20Sopenharmony_ci goto err_req_tx_irq; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return 0; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cierr_req_tx_irq: 9058c2ecf20Sopenharmony_cierr_hw_ci: 9068c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, txq->irq_name); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cierr_alloc_irqname: 9098c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, txq->free_sges); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cierr_alloc_free_sges: 9128c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, txq->sges); 9138c2ecf20Sopenharmony_ci return err; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/** 9178c2ecf20Sopenharmony_ci * hinic_clean_txq - Clean the Tx Queue 9188c2ecf20Sopenharmony_ci * @txq: Logical Tx Queue 9198c2ecf20Sopenharmony_ci **/ 9208c2ecf20Sopenharmony_civoid hinic_clean_txq(struct hinic_txq *txq) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci struct net_device *netdev = txq->netdev; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci tx_free_irq(txq); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci free_all_tx_skbs(txq); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, txq->irq_name); 9298c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, txq->free_sges); 9308c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, txq->sges); 9318c2ecf20Sopenharmony_ci} 932