162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Huawei HiNIC PCI Express Linux driver 462306a36Sopenharmony_ci * Copyright(c) 2017 Huawei Technologies Co., Ltd 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/if_vlan.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/netdevice.h> 1062306a36Sopenharmony_ci#include <linux/u64_stats_sync.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/pci.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci#include <linux/smp.h> 2062306a36Sopenharmony_ci#include <asm/byteorder.h> 2162306a36Sopenharmony_ci#include <linux/ip.h> 2262306a36Sopenharmony_ci#include <linux/tcp.h> 2362306a36Sopenharmony_ci#include <linux/sctp.h> 2462306a36Sopenharmony_ci#include <linux/ipv6.h> 2562306a36Sopenharmony_ci#include <net/ipv6.h> 2662306a36Sopenharmony_ci#include <net/checksum.h> 2762306a36Sopenharmony_ci#include <net/ip6_checksum.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "hinic_common.h" 3062306a36Sopenharmony_ci#include "hinic_hw_if.h" 3162306a36Sopenharmony_ci#include "hinic_hw_wqe.h" 3262306a36Sopenharmony_ci#include "hinic_hw_wq.h" 3362306a36Sopenharmony_ci#include "hinic_hw_qp.h" 3462306a36Sopenharmony_ci#include "hinic_hw_dev.h" 3562306a36Sopenharmony_ci#include "hinic_dev.h" 3662306a36Sopenharmony_ci#include "hinic_tx.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define TX_IRQ_NO_PENDING 0 3962306a36Sopenharmony_ci#define TX_IRQ_NO_COALESC 0 4062306a36Sopenharmony_ci#define TX_IRQ_NO_LLI_TIMER 0 4162306a36Sopenharmony_ci#define TX_IRQ_NO_CREDIT 0 4262306a36Sopenharmony_ci#define TX_IRQ_NO_RESEND_TIMER 0 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define CI_UPDATE_NO_PENDING 0 4562306a36Sopenharmony_ci#define CI_UPDATE_NO_COALESC 0 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define HW_CONS_IDX(sq) be16_to_cpu(*(u16 *)((sq)->hw_ci_addr)) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define MIN_SKB_LEN 32 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define MAX_PAYLOAD_OFFSET 221 5262306a36Sopenharmony_ci#define TRANSPORT_OFFSET(l4_hdr, skb) ((u32)((l4_hdr) - (skb)->data)) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ciunion hinic_l3 { 5562306a36Sopenharmony_ci struct iphdr *v4; 5662306a36Sopenharmony_ci struct ipv6hdr *v6; 5762306a36Sopenharmony_ci unsigned char *hdr; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciunion hinic_l4 { 6162306a36Sopenharmony_ci struct tcphdr *tcp; 6262306a36Sopenharmony_ci struct udphdr *udp; 6362306a36Sopenharmony_ci unsigned char *hdr; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cienum hinic_offload_type { 6762306a36Sopenharmony_ci TX_OFFLOAD_TSO = BIT(0), 6862306a36Sopenharmony_ci TX_OFFLOAD_CSUM = BIT(1), 6962306a36Sopenharmony_ci TX_OFFLOAD_VLAN = BIT(2), 7062306a36Sopenharmony_ci TX_OFFLOAD_INVALID = BIT(3), 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/** 7462306a36Sopenharmony_ci * hinic_txq_clean_stats - Clean the statistics of specific queue 7562306a36Sopenharmony_ci * @txq: Logical Tx Queue 7662306a36Sopenharmony_ci **/ 7762306a36Sopenharmony_cistatic void hinic_txq_clean_stats(struct hinic_txq *txq) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct hinic_txq_stats *txq_stats = &txq->txq_stats; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci u64_stats_update_begin(&txq_stats->syncp); 8262306a36Sopenharmony_ci txq_stats->pkts = 0; 8362306a36Sopenharmony_ci txq_stats->bytes = 0; 8462306a36Sopenharmony_ci txq_stats->tx_busy = 0; 8562306a36Sopenharmony_ci txq_stats->tx_wake = 0; 8662306a36Sopenharmony_ci txq_stats->tx_dropped = 0; 8762306a36Sopenharmony_ci txq_stats->big_frags_pkts = 0; 8862306a36Sopenharmony_ci u64_stats_update_end(&txq_stats->syncp); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * hinic_txq_get_stats - get statistics of Tx Queue 9362306a36Sopenharmony_ci * @txq: Logical Tx Queue 9462306a36Sopenharmony_ci * @stats: return updated stats here 9562306a36Sopenharmony_ci **/ 9662306a36Sopenharmony_civoid hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct hinic_txq_stats *txq_stats = &txq->txq_stats; 9962306a36Sopenharmony_ci unsigned int start; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci do { 10262306a36Sopenharmony_ci start = u64_stats_fetch_begin(&txq_stats->syncp); 10362306a36Sopenharmony_ci stats->pkts = txq_stats->pkts; 10462306a36Sopenharmony_ci stats->bytes = txq_stats->bytes; 10562306a36Sopenharmony_ci stats->tx_busy = txq_stats->tx_busy; 10662306a36Sopenharmony_ci stats->tx_wake = txq_stats->tx_wake; 10762306a36Sopenharmony_ci stats->tx_dropped = txq_stats->tx_dropped; 10862306a36Sopenharmony_ci stats->big_frags_pkts = txq_stats->big_frags_pkts; 10962306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/** 11362306a36Sopenharmony_ci * txq_stats_init - Initialize the statistics of specific queue 11462306a36Sopenharmony_ci * @txq: Logical Tx Queue 11562306a36Sopenharmony_ci **/ 11662306a36Sopenharmony_cistatic void txq_stats_init(struct hinic_txq *txq) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct hinic_txq_stats *txq_stats = &txq->txq_stats; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci u64_stats_init(&txq_stats->syncp); 12162306a36Sopenharmony_ci hinic_txq_clean_stats(txq); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * tx_map_skb - dma mapping for skb and return sges 12662306a36Sopenharmony_ci * @nic_dev: nic device 12762306a36Sopenharmony_ci * @skb: the skb 12862306a36Sopenharmony_ci * @sges: returned sges 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Return 0 - Success, negative - Failure 13162306a36Sopenharmony_ci **/ 13262306a36Sopenharmony_cistatic int tx_map_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, 13362306a36Sopenharmony_ci struct hinic_sge *sges) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 13662306a36Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 13762306a36Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 13862306a36Sopenharmony_ci skb_frag_t *frag; 13962306a36Sopenharmony_ci dma_addr_t dma_addr; 14062306a36Sopenharmony_ci int i, j; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci dma_addr = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), 14362306a36Sopenharmony_ci DMA_TO_DEVICE); 14462306a36Sopenharmony_ci if (dma_mapping_error(&pdev->dev, dma_addr)) { 14562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map Tx skb data\n"); 14662306a36Sopenharmony_ci return -EFAULT; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci hinic_set_sge(&sges[0], dma_addr, skb_headlen(skb)); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci for (i = 0 ; i < skb_shinfo(skb)->nr_frags; i++) { 15262306a36Sopenharmony_ci frag = &skb_shinfo(skb)->frags[i]; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dma_addr = skb_frag_dma_map(&pdev->dev, frag, 0, 15562306a36Sopenharmony_ci skb_frag_size(frag), 15662306a36Sopenharmony_ci DMA_TO_DEVICE); 15762306a36Sopenharmony_ci if (dma_mapping_error(&pdev->dev, dma_addr)) { 15862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to map Tx skb frag\n"); 15962306a36Sopenharmony_ci goto err_tx_map; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci hinic_set_sge(&sges[i + 1], dma_addr, skb_frag_size(frag)); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cierr_tx_map: 16862306a36Sopenharmony_ci for (j = 0; j < i; j++) 16962306a36Sopenharmony_ci dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[j + 1]), 17062306a36Sopenharmony_ci sges[j + 1].len, DMA_TO_DEVICE); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, 17362306a36Sopenharmony_ci DMA_TO_DEVICE); 17462306a36Sopenharmony_ci return -EFAULT; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * tx_unmap_skb - unmap the dma address of the skb 17962306a36Sopenharmony_ci * @nic_dev: nic device 18062306a36Sopenharmony_ci * @skb: the skb 18162306a36Sopenharmony_ci * @sges: the sges that are connected to the skb 18262306a36Sopenharmony_ci **/ 18362306a36Sopenharmony_cistatic void tx_unmap_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, 18462306a36Sopenharmony_ci struct hinic_sge *sges) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 18762306a36Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 18862306a36Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 18962306a36Sopenharmony_ci int i; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags ; i++) 19262306a36Sopenharmony_ci dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[i + 1]), 19362306a36Sopenharmony_ci sges[i + 1].len, DMA_TO_DEVICE); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, 19662306a36Sopenharmony_ci DMA_TO_DEVICE); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic void get_inner_l3_l4_type(struct sk_buff *skb, union hinic_l3 *ip, 20062306a36Sopenharmony_ci union hinic_l4 *l4, 20162306a36Sopenharmony_ci enum hinic_offload_type offload_type, 20262306a36Sopenharmony_ci enum hinic_l3_offload_type *l3_type, 20362306a36Sopenharmony_ci u8 *l4_proto) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u8 *exthdr; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (ip->v4->version == 4) { 20862306a36Sopenharmony_ci *l3_type = (offload_type == TX_OFFLOAD_CSUM) ? 20962306a36Sopenharmony_ci IPV4_PKT_NO_CHKSUM_OFFLOAD : 21062306a36Sopenharmony_ci IPV4_PKT_WITH_CHKSUM_OFFLOAD; 21162306a36Sopenharmony_ci *l4_proto = ip->v4->protocol; 21262306a36Sopenharmony_ci } else if (ip->v4->version == 6) { 21362306a36Sopenharmony_ci *l3_type = IPV6_PKT; 21462306a36Sopenharmony_ci exthdr = ip->hdr + sizeof(*ip->v6); 21562306a36Sopenharmony_ci *l4_proto = ip->v6->nexthdr; 21662306a36Sopenharmony_ci if (exthdr != l4->hdr) { 21762306a36Sopenharmony_ci int start = exthdr - skb->data; 21862306a36Sopenharmony_ci __be16 frag_off; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ipv6_skip_exthdr(skb, start, l4_proto, &frag_off); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci *l3_type = L3TYPE_UNKNOWN; 22462306a36Sopenharmony_ci *l4_proto = 0; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void get_inner_l4_info(struct sk_buff *skb, union hinic_l4 *l4, 22962306a36Sopenharmony_ci enum hinic_offload_type offload_type, u8 l4_proto, 23062306a36Sopenharmony_ci enum hinic_l4_offload_type *l4_offload, 23162306a36Sopenharmony_ci u32 *l4_len, u32 *offset) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci *l4_offload = OFFLOAD_DISABLE; 23462306a36Sopenharmony_ci *offset = 0; 23562306a36Sopenharmony_ci *l4_len = 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci switch (l4_proto) { 23862306a36Sopenharmony_ci case IPPROTO_TCP: 23962306a36Sopenharmony_ci *l4_offload = TCP_OFFLOAD_ENABLE; 24062306a36Sopenharmony_ci /* doff in unit of 4B */ 24162306a36Sopenharmony_ci *l4_len = l4->tcp->doff * 4; 24262306a36Sopenharmony_ci *offset = *l4_len + TRANSPORT_OFFSET(l4->hdr, skb); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci case IPPROTO_UDP: 24662306a36Sopenharmony_ci *l4_offload = UDP_OFFLOAD_ENABLE; 24762306a36Sopenharmony_ci *l4_len = sizeof(struct udphdr); 24862306a36Sopenharmony_ci *offset = TRANSPORT_OFFSET(l4->hdr, skb); 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci case IPPROTO_SCTP: 25262306a36Sopenharmony_ci /* only csum offload support sctp */ 25362306a36Sopenharmony_ci if (offload_type != TX_OFFLOAD_CSUM) 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci *l4_offload = SCTP_OFFLOAD_ENABLE; 25762306a36Sopenharmony_ci *l4_len = sizeof(struct sctphdr); 25862306a36Sopenharmony_ci *offset = TRANSPORT_OFFSET(l4->hdr, skb); 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci default: 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic __sum16 csum_magic(union hinic_l3 *ip, unsigned short proto) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci return (ip->v4->version == 4) ? 26962306a36Sopenharmony_ci csum_tcpudp_magic(ip->v4->saddr, ip->v4->daddr, 0, proto, 0) : 27062306a36Sopenharmony_ci csum_ipv6_magic(&ip->v6->saddr, &ip->v6->daddr, 0, proto, 0); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int offload_tso(struct hinic_sq_task *task, u32 *queue_info, 27462306a36Sopenharmony_ci struct sk_buff *skb) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci u32 offset, l4_len, ip_identify, network_hdr_len; 27762306a36Sopenharmony_ci enum hinic_l3_offload_type l3_offload; 27862306a36Sopenharmony_ci enum hinic_l4_offload_type l4_offload; 27962306a36Sopenharmony_ci union hinic_l3 ip; 28062306a36Sopenharmony_ci union hinic_l4 l4; 28162306a36Sopenharmony_ci u8 l4_proto; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!skb_is_gso(skb)) 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (skb_cow_head(skb, 0) < 0) 28762306a36Sopenharmony_ci return -EPROTONOSUPPORT; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (skb->encapsulation) { 29062306a36Sopenharmony_ci u32 gso_type = skb_shinfo(skb)->gso_type; 29162306a36Sopenharmony_ci u32 tunnel_type = 0; 29262306a36Sopenharmony_ci u32 l4_tunnel_len; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ip.hdr = skb_network_header(skb); 29562306a36Sopenharmony_ci l4.hdr = skb_transport_header(skb); 29662306a36Sopenharmony_ci network_hdr_len = skb_inner_network_header_len(skb); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (ip.v4->version == 4) { 29962306a36Sopenharmony_ci ip.v4->tot_len = 0; 30062306a36Sopenharmony_ci l3_offload = IPV4_PKT_WITH_CHKSUM_OFFLOAD; 30162306a36Sopenharmony_ci } else if (ip.v4->version == 6) { 30262306a36Sopenharmony_ci l3_offload = IPV6_PKT; 30362306a36Sopenharmony_ci } else { 30462306a36Sopenharmony_ci l3_offload = 0; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci hinic_task_set_outter_l3(task, l3_offload, 30862306a36Sopenharmony_ci skb_network_header_len(skb)); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (gso_type & SKB_GSO_UDP_TUNNEL_CSUM) { 31162306a36Sopenharmony_ci l4.udp->check = ~csum_magic(&ip, IPPROTO_UDP); 31262306a36Sopenharmony_ci tunnel_type = TUNNEL_UDP_CSUM; 31362306a36Sopenharmony_ci } else if (gso_type & SKB_GSO_UDP_TUNNEL) { 31462306a36Sopenharmony_ci tunnel_type = TUNNEL_UDP_NO_CSUM; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci l4_tunnel_len = skb_inner_network_offset(skb) - 31862306a36Sopenharmony_ci skb_transport_offset(skb); 31962306a36Sopenharmony_ci hinic_task_set_tunnel_l4(task, tunnel_type, l4_tunnel_len); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 32262306a36Sopenharmony_ci l4.hdr = skb_inner_transport_header(skb); 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci ip.hdr = skb_network_header(skb); 32562306a36Sopenharmony_ci l4.hdr = skb_transport_header(skb); 32662306a36Sopenharmony_ci network_hdr_len = skb_network_header_len(skb); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* initialize inner IP header fields */ 33062306a36Sopenharmony_ci if (ip.v4->version == 4) 33162306a36Sopenharmony_ci ip.v4->tot_len = 0; 33262306a36Sopenharmony_ci else 33362306a36Sopenharmony_ci ip.v6->payload_len = 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci get_inner_l3_l4_type(skb, &ip, &l4, TX_OFFLOAD_TSO, &l3_offload, 33662306a36Sopenharmony_ci &l4_proto); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci hinic_task_set_inner_l3(task, l3_offload, network_hdr_len); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ip_identify = 0; 34162306a36Sopenharmony_ci if (l4_proto == IPPROTO_TCP) 34262306a36Sopenharmony_ci l4.tcp->check = ~csum_magic(&ip, IPPROTO_TCP); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci get_inner_l4_info(skb, &l4, TX_OFFLOAD_TSO, l4_proto, &l4_offload, 34562306a36Sopenharmony_ci &l4_len, &offset); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci hinic_set_tso_inner_l4(task, queue_info, l4_offload, l4_len, offset, 34862306a36Sopenharmony_ci ip_identify, skb_shinfo(skb)->gso_size); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci return 1; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic int offload_csum(struct hinic_sq_task *task, u32 *queue_info, 35462306a36Sopenharmony_ci struct sk_buff *skb) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci enum hinic_l4_offload_type l4_offload; 35762306a36Sopenharmony_ci u32 offset, l4_len, network_hdr_len; 35862306a36Sopenharmony_ci enum hinic_l3_offload_type l3_type; 35962306a36Sopenharmony_ci u32 tunnel_type = NOT_TUNNEL; 36062306a36Sopenharmony_ci union hinic_l3 ip; 36162306a36Sopenharmony_ci union hinic_l4 l4; 36262306a36Sopenharmony_ci u8 l4_proto; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (skb->encapsulation) { 36862306a36Sopenharmony_ci u32 l4_tunnel_len; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci tunnel_type = TUNNEL_UDP_NO_CSUM; 37162306a36Sopenharmony_ci ip.hdr = skb_network_header(skb); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (ip.v4->version == 4) { 37462306a36Sopenharmony_ci l3_type = IPV4_PKT_NO_CHKSUM_OFFLOAD; 37562306a36Sopenharmony_ci l4_proto = ip.v4->protocol; 37662306a36Sopenharmony_ci } else if (ip.v4->version == 6) { 37762306a36Sopenharmony_ci unsigned char *exthdr; 37862306a36Sopenharmony_ci __be16 frag_off; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci l3_type = IPV6_PKT; 38162306a36Sopenharmony_ci tunnel_type = TUNNEL_UDP_CSUM; 38262306a36Sopenharmony_ci exthdr = ip.hdr + sizeof(*ip.v6); 38362306a36Sopenharmony_ci l4_proto = ip.v6->nexthdr; 38462306a36Sopenharmony_ci l4.hdr = skb_transport_header(skb); 38562306a36Sopenharmony_ci if (l4.hdr != exthdr) 38662306a36Sopenharmony_ci ipv6_skip_exthdr(skb, exthdr - skb->data, 38762306a36Sopenharmony_ci &l4_proto, &frag_off); 38862306a36Sopenharmony_ci } else { 38962306a36Sopenharmony_ci l3_type = L3TYPE_UNKNOWN; 39062306a36Sopenharmony_ci l4_proto = IPPROTO_RAW; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci hinic_task_set_outter_l3(task, l3_type, 39462306a36Sopenharmony_ci skb_network_header_len(skb)); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci switch (l4_proto) { 39762306a36Sopenharmony_ci case IPPROTO_UDP: 39862306a36Sopenharmony_ci l4_tunnel_len = skb_inner_network_offset(skb) - 39962306a36Sopenharmony_ci skb_transport_offset(skb); 40062306a36Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 40162306a36Sopenharmony_ci l4.hdr = skb_inner_transport_header(skb); 40262306a36Sopenharmony_ci network_hdr_len = skb_inner_network_header_len(skb); 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case IPPROTO_IPIP: 40562306a36Sopenharmony_ci case IPPROTO_IPV6: 40662306a36Sopenharmony_ci tunnel_type = NOT_TUNNEL; 40762306a36Sopenharmony_ci l4_tunnel_len = 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ip.hdr = skb_inner_network_header(skb); 41062306a36Sopenharmony_ci l4.hdr = skb_transport_header(skb); 41162306a36Sopenharmony_ci network_hdr_len = skb_network_header_len(skb); 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci default: 41462306a36Sopenharmony_ci /* Unsupported tunnel packet, disable csum offload */ 41562306a36Sopenharmony_ci skb_checksum_help(skb); 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci hinic_task_set_tunnel_l4(task, tunnel_type, l4_tunnel_len); 42062306a36Sopenharmony_ci } else { 42162306a36Sopenharmony_ci ip.hdr = skb_network_header(skb); 42262306a36Sopenharmony_ci l4.hdr = skb_transport_header(skb); 42362306a36Sopenharmony_ci network_hdr_len = skb_network_header_len(skb); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci get_inner_l3_l4_type(skb, &ip, &l4, TX_OFFLOAD_CSUM, &l3_type, 42762306a36Sopenharmony_ci &l4_proto); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci hinic_task_set_inner_l3(task, l3_type, network_hdr_len); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci get_inner_l4_info(skb, &l4, TX_OFFLOAD_CSUM, l4_proto, &l4_offload, 43262306a36Sopenharmony_ci &l4_len, &offset); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci hinic_set_cs_inner_l4(task, queue_info, l4_offload, l4_len, offset); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return 1; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void offload_vlan(struct hinic_sq_task *task, u32 *queue_info, 44062306a36Sopenharmony_ci u16 vlan_tag, u16 vlan_pri) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(vlan_tag, VLAN_TAG) | 44362306a36Sopenharmony_ci HINIC_SQ_TASK_INFO0_SET(1U, VLAN_OFFLOAD); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci *queue_info |= HINIC_SQ_CTRL_SET(vlan_pri, QUEUE_INFO_PRI); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int hinic_tx_offload(struct sk_buff *skb, struct hinic_sq_task *task, 44962306a36Sopenharmony_ci u32 *queue_info) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci enum hinic_offload_type offload = 0; 45262306a36Sopenharmony_ci u16 vlan_tag; 45362306a36Sopenharmony_ci int enabled; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci enabled = offload_tso(task, queue_info, skb); 45662306a36Sopenharmony_ci if (enabled > 0) { 45762306a36Sopenharmony_ci offload |= TX_OFFLOAD_TSO; 45862306a36Sopenharmony_ci } else if (enabled == 0) { 45962306a36Sopenharmony_ci enabled = offload_csum(task, queue_info, skb); 46062306a36Sopenharmony_ci if (enabled) 46162306a36Sopenharmony_ci offload |= TX_OFFLOAD_CSUM; 46262306a36Sopenharmony_ci } else { 46362306a36Sopenharmony_ci return -EPROTONOSUPPORT; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (unlikely(skb_vlan_tag_present(skb))) { 46762306a36Sopenharmony_ci vlan_tag = skb_vlan_tag_get(skb); 46862306a36Sopenharmony_ci offload_vlan(task, queue_info, vlan_tag, 46962306a36Sopenharmony_ci vlan_tag >> VLAN_PRIO_SHIFT); 47062306a36Sopenharmony_ci offload |= TX_OFFLOAD_VLAN; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (offload) 47462306a36Sopenharmony_ci hinic_task_set_l2hdr(task, skb_network_offset(skb)); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* payload offset should not more than 221 */ 47762306a36Sopenharmony_ci if (HINIC_SQ_CTRL_GET(*queue_info, QUEUE_INFO_PLDOFF) > 47862306a36Sopenharmony_ci MAX_PAYLOAD_OFFSET) { 47962306a36Sopenharmony_ci return -EPROTONOSUPPORT; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* mss should not less than 80 */ 48362306a36Sopenharmony_ci if (HINIC_SQ_CTRL_GET(*queue_info, QUEUE_INFO_MSS) < HINIC_MSS_MIN) { 48462306a36Sopenharmony_ci *queue_info = HINIC_SQ_CTRL_CLEAR(*queue_info, QUEUE_INFO_MSS); 48562306a36Sopenharmony_ci *queue_info |= HINIC_SQ_CTRL_SET(HINIC_MSS_MIN, QUEUE_INFO_MSS); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cinetdev_tx_t hinic_lb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 49462306a36Sopenharmony_ci u16 prod_idx, q_id = skb->queue_mapping; 49562306a36Sopenharmony_ci struct netdev_queue *netdev_txq; 49662306a36Sopenharmony_ci int nr_sges, err = NETDEV_TX_OK; 49762306a36Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 49862306a36Sopenharmony_ci unsigned int wqe_size; 49962306a36Sopenharmony_ci struct hinic_txq *txq; 50062306a36Sopenharmony_ci struct hinic_qp *qp; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci txq = &nic_dev->txqs[q_id]; 50362306a36Sopenharmony_ci qp = container_of(txq->sq, struct hinic_qp, sq); 50462306a36Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci err = tx_map_skb(nic_dev, skb, txq->sges); 50762306a36Sopenharmony_ci if (err) 50862306a36Sopenharmony_ci goto skb_error; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 51362306a36Sopenharmony_ci if (!sq_wqe) { 51462306a36Sopenharmony_ci netif_stop_subqueue(netdev, qp->q_id); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 51762306a36Sopenharmony_ci if (sq_wqe) { 51862306a36Sopenharmony_ci netif_wake_subqueue(nic_dev->netdev, qp->q_id); 51962306a36Sopenharmony_ci goto process_sq_wqe; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci tx_unmap_skb(nic_dev, skb, txq->sges); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 52562306a36Sopenharmony_ci txq->txq_stats.tx_busy++; 52662306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 52762306a36Sopenharmony_ci err = NETDEV_TX_BUSY; 52862306a36Sopenharmony_ci wqe_size = 0; 52962306a36Sopenharmony_ci goto flush_skbs; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ciprocess_sq_wqe: 53362306a36Sopenharmony_ci hinic_sq_prepare_wqe(txq->sq, sq_wqe, txq->sges, nr_sges); 53462306a36Sopenharmony_ci hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ciflush_skbs: 53762306a36Sopenharmony_ci netdev_txq = netdev_get_tx_queue(netdev, q_id); 53862306a36Sopenharmony_ci if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq))) 53962306a36Sopenharmony_ci hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return err; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciskb_error: 54462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 54562306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 54662306a36Sopenharmony_ci txq->txq_stats.tx_dropped++; 54762306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return NETDEV_TX_OK; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cinetdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 55562306a36Sopenharmony_ci u16 prod_idx, q_id = skb->queue_mapping; 55662306a36Sopenharmony_ci struct netdev_queue *netdev_txq; 55762306a36Sopenharmony_ci int nr_sges, err = NETDEV_TX_OK; 55862306a36Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 55962306a36Sopenharmony_ci unsigned int wqe_size; 56062306a36Sopenharmony_ci struct hinic_txq *txq; 56162306a36Sopenharmony_ci struct hinic_qp *qp; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci txq = &nic_dev->txqs[q_id]; 56462306a36Sopenharmony_ci qp = container_of(txq->sq, struct hinic_qp, sq); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (skb->len < MIN_SKB_LEN) { 56762306a36Sopenharmony_ci if (skb_pad(skb, MIN_SKB_LEN - skb->len)) { 56862306a36Sopenharmony_ci netdev_err(netdev, "Failed to pad skb\n"); 56962306a36Sopenharmony_ci goto update_error_stats; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci skb->len = MIN_SKB_LEN; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 57662306a36Sopenharmony_ci if (nr_sges > 17) { 57762306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 57862306a36Sopenharmony_ci txq->txq_stats.big_frags_pkts++; 57962306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (nr_sges > txq->max_sges) { 58362306a36Sopenharmony_ci netdev_err(netdev, "Too many Tx sges\n"); 58462306a36Sopenharmony_ci goto skb_error; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci err = tx_map_skb(nic_dev, skb, txq->sges); 58862306a36Sopenharmony_ci if (err) 58962306a36Sopenharmony_ci goto skb_error; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 59462306a36Sopenharmony_ci if (!sq_wqe) { 59562306a36Sopenharmony_ci netif_stop_subqueue(netdev, qp->q_id); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Check for the case free_tx_poll is called in another cpu 59862306a36Sopenharmony_ci * and we stopped the subqueue after free_tx_poll check. 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); 60162306a36Sopenharmony_ci if (sq_wqe) { 60262306a36Sopenharmony_ci netif_wake_subqueue(nic_dev->netdev, qp->q_id); 60362306a36Sopenharmony_ci goto process_sq_wqe; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci tx_unmap_skb(nic_dev, skb, txq->sges); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 60962306a36Sopenharmony_ci txq->txq_stats.tx_busy++; 61062306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 61162306a36Sopenharmony_ci err = NETDEV_TX_BUSY; 61262306a36Sopenharmony_ci wqe_size = 0; 61362306a36Sopenharmony_ci goto flush_skbs; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciprocess_sq_wqe: 61762306a36Sopenharmony_ci hinic_sq_prepare_wqe(txq->sq, sq_wqe, txq->sges, nr_sges); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci err = hinic_tx_offload(skb, &sq_wqe->task, &sq_wqe->ctrl.queue_info); 62062306a36Sopenharmony_ci if (err) 62162306a36Sopenharmony_ci goto offload_error; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciflush_skbs: 62662306a36Sopenharmony_ci netdev_txq = netdev_get_tx_queue(netdev, q_id); 62762306a36Sopenharmony_ci if ((!netdev_xmit_more()) || (netif_xmit_stopped(netdev_txq))) 62862306a36Sopenharmony_ci hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return err; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cioffload_error: 63362306a36Sopenharmony_ci hinic_sq_return_wqe(txq->sq, wqe_size); 63462306a36Sopenharmony_ci tx_unmap_skb(nic_dev, skb, txq->sges); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ciskb_error: 63762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciupdate_error_stats: 64062306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 64162306a36Sopenharmony_ci txq->txq_stats.tx_dropped++; 64262306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return NETDEV_TX_OK; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/** 64862306a36Sopenharmony_ci * tx_free_skb - unmap and free skb 64962306a36Sopenharmony_ci * @nic_dev: nic device 65062306a36Sopenharmony_ci * @skb: the skb 65162306a36Sopenharmony_ci * @sges: the sges that are connected to the skb 65262306a36Sopenharmony_ci **/ 65362306a36Sopenharmony_cistatic void tx_free_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, 65462306a36Sopenharmony_ci struct hinic_sge *sges) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci tx_unmap_skb(nic_dev, skb, sges); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/** 66262306a36Sopenharmony_ci * free_all_tx_skbs - free all skbs in tx queue 66362306a36Sopenharmony_ci * @txq: tx queue 66462306a36Sopenharmony_ci **/ 66562306a36Sopenharmony_cistatic void free_all_tx_skbs(struct hinic_txq *txq) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(txq->netdev); 66862306a36Sopenharmony_ci struct hinic_sq *sq = txq->sq; 66962306a36Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 67062306a36Sopenharmony_ci unsigned int wqe_size; 67162306a36Sopenharmony_ci struct sk_buff *skb; 67262306a36Sopenharmony_ci int nr_sges; 67362306a36Sopenharmony_ci u16 ci; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci while ((sq_wqe = hinic_sq_read_wqebb(sq, &skb, &wqe_size, &ci))) { 67662306a36Sopenharmony_ci sq_wqe = hinic_sq_read_wqe(sq, &skb, wqe_size, &ci); 67762306a36Sopenharmony_ci if (!sq_wqe) 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci hinic_sq_put_wqe(sq, wqe_size); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci tx_free_skb(nic_dev, skb, txq->free_sges); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci/** 69162306a36Sopenharmony_ci * free_tx_poll - free finished tx skbs in tx queue that connected to napi 69262306a36Sopenharmony_ci * @napi: napi 69362306a36Sopenharmony_ci * @budget: number of tx 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * Return 0 - Success, negative - Failure 69662306a36Sopenharmony_ci **/ 69762306a36Sopenharmony_cistatic int free_tx_poll(struct napi_struct *napi, int budget) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct hinic_txq *txq = container_of(napi, struct hinic_txq, napi); 70062306a36Sopenharmony_ci struct hinic_qp *qp = container_of(txq->sq, struct hinic_qp, sq); 70162306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(txq->netdev); 70262306a36Sopenharmony_ci struct netdev_queue *netdev_txq; 70362306a36Sopenharmony_ci struct hinic_sq *sq = txq->sq; 70462306a36Sopenharmony_ci struct hinic_wq *wq = sq->wq; 70562306a36Sopenharmony_ci struct hinic_sq_wqe *sq_wqe; 70662306a36Sopenharmony_ci unsigned int wqe_size; 70762306a36Sopenharmony_ci int nr_sges, pkts = 0; 70862306a36Sopenharmony_ci struct sk_buff *skb; 70962306a36Sopenharmony_ci u64 tx_bytes = 0; 71062306a36Sopenharmony_ci u16 hw_ci, sw_ci; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci do { 71362306a36Sopenharmony_ci hw_ci = HW_CONS_IDX(sq) & wq->mask; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci dma_rmb(); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* Reading a WQEBB to get real WQE size and consumer index. */ 71862306a36Sopenharmony_ci sq_wqe = hinic_sq_read_wqebb(sq, &skb, &wqe_size, &sw_ci); 71962306a36Sopenharmony_ci if (!sq_wqe || 72062306a36Sopenharmony_ci (((hw_ci - sw_ci) & wq->mask) * wq->wqebb_size < wqe_size)) 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* If this WQE have multiple WQEBBs, we will read again to get 72462306a36Sopenharmony_ci * full size WQE. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci if (wqe_size > wq->wqebb_size) { 72762306a36Sopenharmony_ci sq_wqe = hinic_sq_read_wqe(sq, &skb, wqe_size, &sw_ci); 72862306a36Sopenharmony_ci if (unlikely(!sq_wqe)) 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci tx_bytes += skb->len; 73362306a36Sopenharmony_ci pkts++; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci nr_sges = skb_shinfo(skb)->nr_frags + 1; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci hinic_sq_put_wqe(sq, wqe_size); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci tx_free_skb(nic_dev, skb, txq->free_sges); 74262306a36Sopenharmony_ci } while (pkts < budget); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (__netif_subqueue_stopped(nic_dev->netdev, qp->q_id) && 74562306a36Sopenharmony_ci hinic_get_sq_free_wqebbs(sq) >= HINIC_MIN_TX_NUM_WQEBBS(sq)) { 74662306a36Sopenharmony_ci netdev_txq = netdev_get_tx_queue(txq->netdev, qp->q_id); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci __netif_tx_lock(netdev_txq, smp_processor_id()); 74962306a36Sopenharmony_ci if (!netif_testing(nic_dev->netdev)) 75062306a36Sopenharmony_ci netif_wake_subqueue(nic_dev->netdev, qp->q_id); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci __netif_tx_unlock(netdev_txq); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 75562306a36Sopenharmony_ci txq->txq_stats.tx_wake++; 75662306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci u64_stats_update_begin(&txq->txq_stats.syncp); 76062306a36Sopenharmony_ci txq->txq_stats.bytes += tx_bytes; 76162306a36Sopenharmony_ci txq->txq_stats.pkts += pkts; 76262306a36Sopenharmony_ci u64_stats_update_end(&txq->txq_stats.syncp); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (pkts < budget) { 76562306a36Sopenharmony_ci napi_complete(napi); 76662306a36Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 76762306a36Sopenharmony_ci hinic_hwdev_set_msix_state(nic_dev->hwdev, 76862306a36Sopenharmony_ci sq->msix_entry, 76962306a36Sopenharmony_ci HINIC_MSIX_ENABLE); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return pkts; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci return budget; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic irqreturn_t tx_irq(int irq, void *data) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct hinic_txq *txq = data; 78062306a36Sopenharmony_ci struct hinic_dev *nic_dev; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci nic_dev = netdev_priv(txq->netdev); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 78562306a36Sopenharmony_ci /* Disable the interrupt until napi will be completed */ 78662306a36Sopenharmony_ci hinic_hwdev_set_msix_state(nic_dev->hwdev, 78762306a36Sopenharmony_ci txq->sq->msix_entry, 78862306a36Sopenharmony_ci HINIC_MSIX_DISABLE); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci hinic_hwdev_msix_cnt_set(nic_dev->hwdev, txq->sq->msix_entry); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci napi_schedule(&txq->napi); 79362306a36Sopenharmony_ci return IRQ_HANDLED; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic int tx_request_irq(struct hinic_txq *txq) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(txq->netdev); 79962306a36Sopenharmony_ci struct hinic_msix_config interrupt_info = {0}; 80062306a36Sopenharmony_ci struct hinic_intr_coal_info *intr_coal = NULL; 80162306a36Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 80262306a36Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 80362306a36Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 80462306a36Sopenharmony_ci struct hinic_sq *sq = txq->sq; 80562306a36Sopenharmony_ci struct hinic_qp *qp; 80662306a36Sopenharmony_ci int err; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci qp = container_of(sq, struct hinic_qp, sq); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci netif_napi_add_weight(txq->netdev, &txq->napi, free_tx_poll, 81162306a36Sopenharmony_ci nic_dev->tx_weight); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry, 81462306a36Sopenharmony_ci TX_IRQ_NO_PENDING, TX_IRQ_NO_COALESC, 81562306a36Sopenharmony_ci TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT, 81662306a36Sopenharmony_ci TX_IRQ_NO_RESEND_TIMER); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci intr_coal = &nic_dev->tx_intr_coalesce[qp->q_id]; 81962306a36Sopenharmony_ci interrupt_info.msix_index = sq->msix_entry; 82062306a36Sopenharmony_ci interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; 82162306a36Sopenharmony_ci interrupt_info.pending_cnt = intr_coal->pending_limt; 82262306a36Sopenharmony_ci interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci err = hinic_set_interrupt_cfg(hwdev, &interrupt_info); 82562306a36Sopenharmony_ci if (err) { 82662306a36Sopenharmony_ci netif_err(nic_dev, drv, txq->netdev, 82762306a36Sopenharmony_ci "Failed to set TX interrupt coalescing attribute\n"); 82862306a36Sopenharmony_ci netif_napi_del(&txq->napi); 82962306a36Sopenharmony_ci return err; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq); 83362306a36Sopenharmony_ci if (err) { 83462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request Tx irq\n"); 83562306a36Sopenharmony_ci netif_napi_del(&txq->napi); 83662306a36Sopenharmony_ci return err; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return 0; 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic void tx_free_irq(struct hinic_txq *txq) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct hinic_sq *sq = txq->sq; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci free_irq(sq->irq, txq); 84762306a36Sopenharmony_ci netif_napi_del(&txq->napi); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci/** 85162306a36Sopenharmony_ci * hinic_init_txq - Initialize the Tx Queue 85262306a36Sopenharmony_ci * @txq: Logical Tx Queue 85362306a36Sopenharmony_ci * @sq: Hardware Tx Queue to connect the Logical queue with 85462306a36Sopenharmony_ci * @netdev: network device to connect the Logical queue with 85562306a36Sopenharmony_ci * 85662306a36Sopenharmony_ci * Return 0 - Success, negative - Failure 85762306a36Sopenharmony_ci **/ 85862306a36Sopenharmony_ciint hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, 85962306a36Sopenharmony_ci struct net_device *netdev) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); 86262306a36Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(netdev); 86362306a36Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 86462306a36Sopenharmony_ci int err, irqname_len; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci txq->netdev = netdev; 86762306a36Sopenharmony_ci txq->sq = sq; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci txq_stats_init(txq); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci txq->max_sges = HINIC_MAX_SQ_BUFDESCS; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci txq->sges = devm_kcalloc(&netdev->dev, txq->max_sges, 87462306a36Sopenharmony_ci sizeof(*txq->sges), GFP_KERNEL); 87562306a36Sopenharmony_ci if (!txq->sges) 87662306a36Sopenharmony_ci return -ENOMEM; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci txq->free_sges = devm_kcalloc(&netdev->dev, txq->max_sges, 87962306a36Sopenharmony_ci sizeof(*txq->free_sges), GFP_KERNEL); 88062306a36Sopenharmony_ci if (!txq->free_sges) { 88162306a36Sopenharmony_ci err = -ENOMEM; 88262306a36Sopenharmony_ci goto err_alloc_free_sges; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci irqname_len = snprintf(NULL, 0, "%s_txq%d", netdev->name, qp->q_id) + 1; 88662306a36Sopenharmony_ci txq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); 88762306a36Sopenharmony_ci if (!txq->irq_name) { 88862306a36Sopenharmony_ci err = -ENOMEM; 88962306a36Sopenharmony_ci goto err_alloc_irqname; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci sprintf(txq->irq_name, "%s_txq%d", netdev->name, qp->q_id); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci err = hinic_hwdev_hw_ci_addr_set(hwdev, sq, CI_UPDATE_NO_PENDING, 89562306a36Sopenharmony_ci CI_UPDATE_NO_COALESC); 89662306a36Sopenharmony_ci if (err) 89762306a36Sopenharmony_ci goto err_hw_ci; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci err = tx_request_irq(txq); 90062306a36Sopenharmony_ci if (err) { 90162306a36Sopenharmony_ci netdev_err(netdev, "Failed to request Tx irq\n"); 90262306a36Sopenharmony_ci goto err_req_tx_irq; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return 0; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cierr_req_tx_irq: 90862306a36Sopenharmony_cierr_hw_ci: 90962306a36Sopenharmony_ci devm_kfree(&netdev->dev, txq->irq_name); 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cierr_alloc_irqname: 91262306a36Sopenharmony_ci devm_kfree(&netdev->dev, txq->free_sges); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cierr_alloc_free_sges: 91562306a36Sopenharmony_ci devm_kfree(&netdev->dev, txq->sges); 91662306a36Sopenharmony_ci return err; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci/** 92062306a36Sopenharmony_ci * hinic_clean_txq - Clean the Tx Queue 92162306a36Sopenharmony_ci * @txq: Logical Tx Queue 92262306a36Sopenharmony_ci **/ 92362306a36Sopenharmony_civoid hinic_clean_txq(struct hinic_txq *txq) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct net_device *netdev = txq->netdev; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci tx_free_irq(txq); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci free_all_tx_skbs(txq); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci devm_kfree(&netdev->dev, txq->irq_name); 93262306a36Sopenharmony_ci devm_kfree(&netdev->dev, txq->free_sges); 93362306a36Sopenharmony_ci devm_kfree(&netdev->dev, txq->sges); 93462306a36Sopenharmony_ci} 935