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/types.h> 98c2ecf20Sopenharmony_ci#include <linux/errno.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/u64_stats_sync.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 208c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 218c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 228c2ecf20Sopenharmony_ci#include <asm/barrier.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "hinic_common.h" 258c2ecf20Sopenharmony_ci#include "hinic_hw_if.h" 268c2ecf20Sopenharmony_ci#include "hinic_hw_wqe.h" 278c2ecf20Sopenharmony_ci#include "hinic_hw_wq.h" 288c2ecf20Sopenharmony_ci#include "hinic_hw_qp.h" 298c2ecf20Sopenharmony_ci#include "hinic_hw_dev.h" 308c2ecf20Sopenharmony_ci#include "hinic_rx.h" 318c2ecf20Sopenharmony_ci#include "hinic_dev.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define RX_IRQ_NO_PENDING 0 348c2ecf20Sopenharmony_ci#define RX_IRQ_NO_COALESC 0 358c2ecf20Sopenharmony_ci#define RX_IRQ_NO_LLI_TIMER 0 368c2ecf20Sopenharmony_ci#define RX_IRQ_NO_CREDIT 0 378c2ecf20Sopenharmony_ci#define RX_IRQ_NO_RESEND_TIMER 0 388c2ecf20Sopenharmony_ci#define HINIC_RX_BUFFER_WRITE 16 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define HINIC_RX_IPV6_PKT 7 418c2ecf20Sopenharmony_ci#define LRO_PKT_HDR_LEN_IPV4 66 428c2ecf20Sopenharmony_ci#define LRO_PKT_HDR_LEN_IPV6 86 438c2ecf20Sopenharmony_ci#define LRO_REPLENISH_THLD 256 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define LRO_PKT_HDR_LEN(cqe) \ 468c2ecf20Sopenharmony_ci (HINIC_GET_RX_PKT_TYPE(be32_to_cpu((cqe)->offload_type)) == \ 478c2ecf20Sopenharmony_ci HINIC_RX_IPV6_PKT ? LRO_PKT_HDR_LEN_IPV6 : LRO_PKT_HDR_LEN_IPV4) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/** 508c2ecf20Sopenharmony_ci * hinic_rxq_clean_stats - Clean the statistics of specific queue 518c2ecf20Sopenharmony_ci * @rxq: Logical Rx Queue 528c2ecf20Sopenharmony_ci **/ 538c2ecf20Sopenharmony_civoid hinic_rxq_clean_stats(struct hinic_rxq *rxq) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci u64_stats_update_begin(&rxq_stats->syncp); 588c2ecf20Sopenharmony_ci rxq_stats->pkts = 0; 598c2ecf20Sopenharmony_ci rxq_stats->bytes = 0; 608c2ecf20Sopenharmony_ci rxq_stats->errors = 0; 618c2ecf20Sopenharmony_ci rxq_stats->csum_errors = 0; 628c2ecf20Sopenharmony_ci rxq_stats->other_errors = 0; 638c2ecf20Sopenharmony_ci u64_stats_update_end(&rxq_stats->syncp); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/** 678c2ecf20Sopenharmony_ci * hinic_rxq_get_stats - get statistics of Rx Queue 688c2ecf20Sopenharmony_ci * @rxq: Logical Rx Queue 698c2ecf20Sopenharmony_ci * @stats: return updated stats here 708c2ecf20Sopenharmony_ci **/ 718c2ecf20Sopenharmony_civoid hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; 748c2ecf20Sopenharmony_ci unsigned int start; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci do { 778c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&rxq_stats->syncp); 788c2ecf20Sopenharmony_ci stats->pkts = rxq_stats->pkts; 798c2ecf20Sopenharmony_ci stats->bytes = rxq_stats->bytes; 808c2ecf20Sopenharmony_ci stats->errors = rxq_stats->csum_errors + 818c2ecf20Sopenharmony_ci rxq_stats->other_errors; 828c2ecf20Sopenharmony_ci stats->csum_errors = rxq_stats->csum_errors; 838c2ecf20Sopenharmony_ci stats->other_errors = rxq_stats->other_errors; 848c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&rxq_stats->syncp, start)); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/** 888c2ecf20Sopenharmony_ci * rxq_stats_init - Initialize the statistics of specific queue 898c2ecf20Sopenharmony_ci * @rxq: Logical Rx Queue 908c2ecf20Sopenharmony_ci **/ 918c2ecf20Sopenharmony_cistatic void rxq_stats_init(struct hinic_rxq *rxq) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci u64_stats_init(&rxq_stats->syncp); 968c2ecf20Sopenharmony_ci hinic_rxq_clean_stats(rxq); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void rx_csum(struct hinic_rxq *rxq, u32 status, 1008c2ecf20Sopenharmony_ci struct sk_buff *skb) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct net_device *netdev = rxq->netdev; 1038c2ecf20Sopenharmony_ci u32 csum_err; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!(netdev->features & NETIF_F_RXCSUM)) 1088c2ecf20Sopenharmony_ci return; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!csum_err) { 1118c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci if (!(csum_err & (HINIC_RX_CSUM_HW_CHECK_NONE | 1148c2ecf20Sopenharmony_ci HINIC_RX_CSUM_IPSU_OTHER_ERR))) 1158c2ecf20Sopenharmony_ci rxq->rxq_stats.csum_errors++; 1168c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci/** 1208c2ecf20Sopenharmony_ci * rx_alloc_skb - allocate skb and map it to dma address 1218c2ecf20Sopenharmony_ci * @rxq: rx queue 1228c2ecf20Sopenharmony_ci * @dma_addr: returned dma address for the skb 1238c2ecf20Sopenharmony_ci * 1248c2ecf20Sopenharmony_ci * Return skb 1258c2ecf20Sopenharmony_ci **/ 1268c2ecf20Sopenharmony_cistatic struct sk_buff *rx_alloc_skb(struct hinic_rxq *rxq, 1278c2ecf20Sopenharmony_ci dma_addr_t *dma_addr) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); 1308c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 1318c2ecf20Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 1328c2ecf20Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 1338c2ecf20Sopenharmony_ci struct sk_buff *skb; 1348c2ecf20Sopenharmony_ci dma_addr_t addr; 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(rxq->netdev, rxq->rq->buf_sz); 1388c2ecf20Sopenharmony_ci if (!skb) { 1398c2ecf20Sopenharmony_ci netdev_err(rxq->netdev, "Failed to allocate Rx SKB\n"); 1408c2ecf20Sopenharmony_ci return NULL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci addr = dma_map_single(&pdev->dev, skb->data, rxq->rq->buf_sz, 1448c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 1458c2ecf20Sopenharmony_ci err = dma_mapping_error(&pdev->dev, addr); 1468c2ecf20Sopenharmony_ci if (err) { 1478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to map Rx DMA, err = %d\n", err); 1488c2ecf20Sopenharmony_ci goto err_rx_map; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci *dma_addr = addr; 1528c2ecf20Sopenharmony_ci return skb; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cierr_rx_map: 1558c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1568c2ecf20Sopenharmony_ci return NULL; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * rx_unmap_skb - unmap the dma address of the skb 1618c2ecf20Sopenharmony_ci * @rxq: rx queue 1628c2ecf20Sopenharmony_ci * @dma_addr: dma address of the skb 1638c2ecf20Sopenharmony_ci **/ 1648c2ecf20Sopenharmony_cistatic void rx_unmap_skb(struct hinic_rxq *rxq, dma_addr_t dma_addr) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); 1678c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 1688c2ecf20Sopenharmony_ci struct hinic_hwif *hwif = hwdev->hwif; 1698c2ecf20Sopenharmony_ci struct pci_dev *pdev = hwif->pdev; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dma_unmap_single(&pdev->dev, dma_addr, rxq->rq->buf_sz, 1728c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * rx_free_skb - unmap and free skb 1778c2ecf20Sopenharmony_ci * @rxq: rx queue 1788c2ecf20Sopenharmony_ci * @skb: skb to free 1798c2ecf20Sopenharmony_ci * @dma_addr: dma address of the skb 1808c2ecf20Sopenharmony_ci **/ 1818c2ecf20Sopenharmony_cistatic void rx_free_skb(struct hinic_rxq *rxq, struct sk_buff *skb, 1828c2ecf20Sopenharmony_ci dma_addr_t dma_addr) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci rx_unmap_skb(rxq, dma_addr); 1858c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/** 1898c2ecf20Sopenharmony_ci * rx_alloc_pkts - allocate pkts in rx queue 1908c2ecf20Sopenharmony_ci * @rxq: rx queue 1918c2ecf20Sopenharmony_ci * 1928c2ecf20Sopenharmony_ci * Return number of skbs allocated 1938c2ecf20Sopenharmony_ci **/ 1948c2ecf20Sopenharmony_cistatic int rx_alloc_pkts(struct hinic_rxq *rxq) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); 1978c2ecf20Sopenharmony_ci struct hinic_rq_wqe *rq_wqe; 1988c2ecf20Sopenharmony_ci unsigned int free_wqebbs; 1998c2ecf20Sopenharmony_ci struct hinic_sge sge; 2008c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 2018c2ecf20Sopenharmony_ci struct sk_buff *skb; 2028c2ecf20Sopenharmony_ci u16 prod_idx; 2038c2ecf20Sopenharmony_ci int i; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* Limit the allocation chunks */ 2088c2ecf20Sopenharmony_ci if (free_wqebbs > nic_dev->rx_weight) 2098c2ecf20Sopenharmony_ci free_wqebbs = nic_dev->rx_weight; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < free_wqebbs; i++) { 2128c2ecf20Sopenharmony_ci skb = rx_alloc_skb(rxq, &dma_addr); 2138c2ecf20Sopenharmony_ci if (!skb) { 2148c2ecf20Sopenharmony_ci netdev_err(rxq->netdev, "Failed to alloc Rx skb\n"); 2158c2ecf20Sopenharmony_ci goto skb_out; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci hinic_set_sge(&sge, dma_addr, skb->len); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci rq_wqe = hinic_rq_get_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, 2218c2ecf20Sopenharmony_ci &prod_idx); 2228c2ecf20Sopenharmony_ci if (!rq_wqe) { 2238c2ecf20Sopenharmony_ci rx_free_skb(rxq, skb, dma_addr); 2248c2ecf20Sopenharmony_ci goto skb_out; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci hinic_rq_prepare_wqe(rxq->rq, prod_idx, rq_wqe, &sge); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci hinic_rq_write_wqe(rxq->rq, prod_idx, rq_wqe, skb); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciskb_out: 2338c2ecf20Sopenharmony_ci if (i) { 2348c2ecf20Sopenharmony_ci wmb(); /* write all the wqes before update PI */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci hinic_rq_update(rxq->rq, prod_idx); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return i; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/** 2438c2ecf20Sopenharmony_ci * free_all_rx_skbs - free all skbs in rx queue 2448c2ecf20Sopenharmony_ci * @rxq: rx queue 2458c2ecf20Sopenharmony_ci **/ 2468c2ecf20Sopenharmony_cistatic void free_all_rx_skbs(struct hinic_rxq *rxq) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct hinic_rq *rq = rxq->rq; 2498c2ecf20Sopenharmony_ci struct hinic_hw_wqe *hw_wqe; 2508c2ecf20Sopenharmony_ci struct hinic_sge sge; 2518c2ecf20Sopenharmony_ci u16 ci; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci while ((hw_wqe = hinic_read_wqe(rq->wq, HINIC_RQ_WQE_SIZE, &ci))) { 2548c2ecf20Sopenharmony_ci if (IS_ERR(hw_wqe)) 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci hinic_rq_get_sge(rq, &hw_wqe->rq_wqe, ci, &sge); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci hinic_put_wqe(rq->wq, HINIC_RQ_WQE_SIZE); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci rx_free_skb(rxq, rq->saved_skb[ci], hinic_sge_to_dma(&sge)); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/** 2668c2ecf20Sopenharmony_ci * rx_recv_jumbo_pkt - Rx handler for jumbo pkt 2678c2ecf20Sopenharmony_ci * @rxq: rx queue 2688c2ecf20Sopenharmony_ci * @head_skb: the first skb in the list 2698c2ecf20Sopenharmony_ci * @left_pkt_len: left size of the pkt exclude head skb 2708c2ecf20Sopenharmony_ci * @ci: consumer index 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * Return number of wqes that used for the left of the pkt 2738c2ecf20Sopenharmony_ci **/ 2748c2ecf20Sopenharmony_cistatic int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb, 2758c2ecf20Sopenharmony_ci unsigned int left_pkt_len, u16 ci) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct sk_buff *skb, *curr_skb = head_skb; 2788c2ecf20Sopenharmony_ci struct hinic_rq_wqe *rq_wqe; 2798c2ecf20Sopenharmony_ci unsigned int curr_len; 2808c2ecf20Sopenharmony_ci struct hinic_sge sge; 2818c2ecf20Sopenharmony_ci int num_wqes = 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci while (left_pkt_len > 0) { 2848c2ecf20Sopenharmony_ci rq_wqe = hinic_rq_read_next_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, 2858c2ecf20Sopenharmony_ci &skb, &ci); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci num_wqes++; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci prefetch(skb->data); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci curr_len = (left_pkt_len > HINIC_RX_BUF_SZ) ? HINIC_RX_BUF_SZ : 2968c2ecf20Sopenharmony_ci left_pkt_len; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci left_pkt_len -= curr_len; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci __skb_put(skb, curr_len); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (curr_skb == head_skb) 3038c2ecf20Sopenharmony_ci skb_shinfo(head_skb)->frag_list = skb; 3048c2ecf20Sopenharmony_ci else 3058c2ecf20Sopenharmony_ci curr_skb->next = skb; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci head_skb->len += skb->len; 3088c2ecf20Sopenharmony_ci head_skb->data_len += skb->len; 3098c2ecf20Sopenharmony_ci head_skb->truesize += skb->truesize; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci curr_skb = skb; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return num_wqes; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void hinic_copy_lp_data(struct hinic_dev *nic_dev, 3188c2ecf20Sopenharmony_ci struct sk_buff *skb) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct net_device *netdev = nic_dev->netdev; 3218c2ecf20Sopenharmony_ci u8 *lb_buf = nic_dev->lb_test_rx_buf; 3228c2ecf20Sopenharmony_ci int lb_len = nic_dev->lb_pkt_len; 3238c2ecf20Sopenharmony_ci int pkt_offset, frag_len, i; 3248c2ecf20Sopenharmony_ci void *frag_data = NULL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) { 3278c2ecf20Sopenharmony_ci nic_dev->lb_test_rx_idx = 0; 3288c2ecf20Sopenharmony_ci netif_warn(nic_dev, drv, netdev, "Loopback test warning, receive too more test pkts\n"); 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (skb->len != nic_dev->lb_pkt_len) { 3328c2ecf20Sopenharmony_ci netif_warn(nic_dev, drv, netdev, "Wrong packet length\n"); 3338c2ecf20Sopenharmony_ci nic_dev->lb_test_rx_idx++; 3348c2ecf20Sopenharmony_ci return; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci pkt_offset = nic_dev->lb_test_rx_idx * lb_len; 3388c2ecf20Sopenharmony_ci frag_len = (int)skb_headlen(skb); 3398c2ecf20Sopenharmony_ci memcpy(lb_buf + pkt_offset, skb->data, frag_len); 3408c2ecf20Sopenharmony_ci pkt_offset += frag_len; 3418c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 3428c2ecf20Sopenharmony_ci frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]); 3438c2ecf20Sopenharmony_ci frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]); 3448c2ecf20Sopenharmony_ci memcpy((lb_buf + pkt_offset), frag_data, frag_len); 3458c2ecf20Sopenharmony_ci pkt_offset += frag_len; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci nic_dev->lb_test_rx_idx++; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/** 3518c2ecf20Sopenharmony_ci * rxq_recv - Rx handler 3528c2ecf20Sopenharmony_ci * @rxq: rx queue 3538c2ecf20Sopenharmony_ci * @budget: maximum pkts to process 3548c2ecf20Sopenharmony_ci * 3558c2ecf20Sopenharmony_ci * Return number of pkts received 3568c2ecf20Sopenharmony_ci **/ 3578c2ecf20Sopenharmony_cistatic int rxq_recv(struct hinic_rxq *rxq, int budget) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq); 3608c2ecf20Sopenharmony_ci struct net_device *netdev = rxq->netdev; 3618c2ecf20Sopenharmony_ci u64 pkt_len = 0, rx_bytes = 0; 3628c2ecf20Sopenharmony_ci struct hinic_rq *rq = rxq->rq; 3638c2ecf20Sopenharmony_ci struct hinic_rq_wqe *rq_wqe; 3648c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev; 3658c2ecf20Sopenharmony_ci unsigned int free_wqebbs; 3668c2ecf20Sopenharmony_ci struct hinic_rq_cqe *cqe; 3678c2ecf20Sopenharmony_ci int num_wqes, pkts = 0; 3688c2ecf20Sopenharmony_ci struct hinic_sge sge; 3698c2ecf20Sopenharmony_ci unsigned int status; 3708c2ecf20Sopenharmony_ci struct sk_buff *skb; 3718c2ecf20Sopenharmony_ci u32 offload_type; 3728c2ecf20Sopenharmony_ci u16 ci, num_lro; 3738c2ecf20Sopenharmony_ci u16 num_wqe = 0; 3748c2ecf20Sopenharmony_ci u32 vlan_len; 3758c2ecf20Sopenharmony_ci u16 vid; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci nic_dev = netdev_priv(netdev); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci while (pkts < budget) { 3808c2ecf20Sopenharmony_ci num_wqes = 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci rq_wqe = hinic_rq_read_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, &skb, 3838c2ecf20Sopenharmony_ci &ci); 3848c2ecf20Sopenharmony_ci if (!rq_wqe) 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* make sure we read rx_done before packet length */ 3888c2ecf20Sopenharmony_ci dma_rmb(); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci cqe = rq->cqe[ci]; 3918c2ecf20Sopenharmony_ci status = be32_to_cpu(cqe->status); 3928c2ecf20Sopenharmony_ci hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci rx_csum(rxq, status, skb); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci prefetch(skb->data); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci pkt_len = sge.len; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (pkt_len <= HINIC_RX_BUF_SZ) { 4038c2ecf20Sopenharmony_ci __skb_put(skb, pkt_len); 4048c2ecf20Sopenharmony_ci } else { 4058c2ecf20Sopenharmony_ci __skb_put(skb, HINIC_RX_BUF_SZ); 4068c2ecf20Sopenharmony_ci num_wqes = rx_recv_jumbo_pkt(rxq, skb, pkt_len - 4078c2ecf20Sopenharmony_ci HINIC_RX_BUF_SZ, ci); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci hinic_rq_put_wqe(rq, ci, 4118c2ecf20Sopenharmony_ci (num_wqes + 1) * HINIC_RQ_WQE_SIZE); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci offload_type = be32_to_cpu(cqe->offload_type); 4148c2ecf20Sopenharmony_ci vlan_len = be32_to_cpu(cqe->len); 4158c2ecf20Sopenharmony_ci if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && 4168c2ecf20Sopenharmony_ci HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)) { 4178c2ecf20Sopenharmony_ci vid = HINIC_GET_RX_VLAN_TAG(vlan_len); 4188c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (unlikely(nic_dev->flags & HINIC_LP_TEST)) 4228c2ecf20Sopenharmony_ci hinic_copy_lp_data(nic_dev, skb); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, qp->q_id); 4258c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, rxq->netdev); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci napi_gro_receive(&rxq->napi, skb); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci pkts++; 4308c2ecf20Sopenharmony_ci rx_bytes += pkt_len; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci num_lro = HINIC_GET_RX_NUM_LRO(status); 4338c2ecf20Sopenharmony_ci if (num_lro) { 4348c2ecf20Sopenharmony_ci rx_bytes += ((num_lro - 1) * 4358c2ecf20Sopenharmony_ci LRO_PKT_HDR_LEN(cqe)); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci num_wqe += 4388c2ecf20Sopenharmony_ci (u16)(pkt_len >> rxq->rx_buff_shift) + 4398c2ecf20Sopenharmony_ci ((pkt_len & (rxq->buf_len - 1)) ? 1 : 0); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci cqe->status = 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (num_wqe >= LRO_REPLENISH_THLD) 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq); 4498c2ecf20Sopenharmony_ci if (free_wqebbs > HINIC_RX_BUFFER_WRITE) 4508c2ecf20Sopenharmony_ci rx_alloc_pkts(rxq); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci u64_stats_update_begin(&rxq->rxq_stats.syncp); 4538c2ecf20Sopenharmony_ci rxq->rxq_stats.pkts += pkts; 4548c2ecf20Sopenharmony_ci rxq->rxq_stats.bytes += rx_bytes; 4558c2ecf20Sopenharmony_ci u64_stats_update_end(&rxq->rxq_stats.syncp); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return pkts; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int rx_poll(struct napi_struct *napi, int budget) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct hinic_rxq *rxq = container_of(napi, struct hinic_rxq, napi); 4638c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); 4648c2ecf20Sopenharmony_ci struct hinic_rq *rq = rxq->rq; 4658c2ecf20Sopenharmony_ci int pkts; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci pkts = rxq_recv(rxq, budget); 4688c2ecf20Sopenharmony_ci if (pkts >= budget) 4698c2ecf20Sopenharmony_ci return budget; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci napi_complete(napi); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 4748c2ecf20Sopenharmony_ci hinic_hwdev_set_msix_state(nic_dev->hwdev, 4758c2ecf20Sopenharmony_ci rq->msix_entry, 4768c2ecf20Sopenharmony_ci HINIC_MSIX_ENABLE); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return pkts; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void rx_add_napi(struct hinic_rxq *rxq) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci netif_napi_add(rxq->netdev, &rxq->napi, rx_poll, nic_dev->rx_weight); 4868c2ecf20Sopenharmony_ci napi_enable(&rxq->napi); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void rx_del_napi(struct hinic_rxq *rxq) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci napi_disable(&rxq->napi); 4928c2ecf20Sopenharmony_ci netif_napi_del(&rxq->napi); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic irqreturn_t rx_irq(int irq, void *data) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct hinic_rxq *rxq = (struct hinic_rxq *)data; 4988c2ecf20Sopenharmony_ci struct hinic_rq *rq = rxq->rq; 4998c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* Disable the interrupt until napi will be completed */ 5028c2ecf20Sopenharmony_ci nic_dev = netdev_priv(rxq->netdev); 5038c2ecf20Sopenharmony_ci if (!HINIC_IS_VF(nic_dev->hwdev->hwif)) 5048c2ecf20Sopenharmony_ci hinic_hwdev_set_msix_state(nic_dev->hwdev, 5058c2ecf20Sopenharmony_ci rq->msix_entry, 5068c2ecf20Sopenharmony_ci HINIC_MSIX_DISABLE); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci nic_dev = netdev_priv(rxq->netdev); 5098c2ecf20Sopenharmony_ci hinic_hwdev_msix_cnt_set(nic_dev->hwdev, rq->msix_entry); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci napi_schedule(&rxq->napi); 5128c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic int rx_request_irq(struct hinic_rxq *rxq) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); 5188c2ecf20Sopenharmony_ci struct hinic_msix_config interrupt_info = {0}; 5198c2ecf20Sopenharmony_ci struct hinic_intr_coal_info *intr_coal = NULL; 5208c2ecf20Sopenharmony_ci struct hinic_hwdev *hwdev = nic_dev->hwdev; 5218c2ecf20Sopenharmony_ci struct hinic_rq *rq = rxq->rq; 5228c2ecf20Sopenharmony_ci struct hinic_qp *qp; 5238c2ecf20Sopenharmony_ci int err; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci qp = container_of(rq, struct hinic_qp, rq); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci rx_add_napi(rxq); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci hinic_hwdev_msix_set(hwdev, rq->msix_entry, 5308c2ecf20Sopenharmony_ci RX_IRQ_NO_PENDING, RX_IRQ_NO_COALESC, 5318c2ecf20Sopenharmony_ci RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT, 5328c2ecf20Sopenharmony_ci RX_IRQ_NO_RESEND_TIMER); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci intr_coal = &nic_dev->rx_intr_coalesce[qp->q_id]; 5358c2ecf20Sopenharmony_ci interrupt_info.msix_index = rq->msix_entry; 5368c2ecf20Sopenharmony_ci interrupt_info.coalesce_timer_cnt = intr_coal->coalesce_timer_cfg; 5378c2ecf20Sopenharmony_ci interrupt_info.pending_cnt = intr_coal->pending_limt; 5388c2ecf20Sopenharmony_ci interrupt_info.resend_timer_cnt = intr_coal->resend_timer_cfg; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci err = hinic_set_interrupt_cfg(hwdev, &interrupt_info); 5418c2ecf20Sopenharmony_ci if (err) { 5428c2ecf20Sopenharmony_ci netif_err(nic_dev, drv, rxq->netdev, 5438c2ecf20Sopenharmony_ci "Failed to set RX interrupt coalescing attribute\n"); 5448c2ecf20Sopenharmony_ci goto err_req_irq; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq); 5488c2ecf20Sopenharmony_ci if (err) 5498c2ecf20Sopenharmony_ci goto err_req_irq; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci cpumask_set_cpu(qp->q_id % num_online_cpus(), &rq->affinity_mask); 5528c2ecf20Sopenharmony_ci err = irq_set_affinity_hint(rq->irq, &rq->affinity_mask); 5538c2ecf20Sopenharmony_ci if (err) 5548c2ecf20Sopenharmony_ci goto err_irq_affinity; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cierr_irq_affinity: 5598c2ecf20Sopenharmony_ci free_irq(rq->irq, rxq); 5608c2ecf20Sopenharmony_cierr_req_irq: 5618c2ecf20Sopenharmony_ci rx_del_napi(rxq); 5628c2ecf20Sopenharmony_ci return err; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic void rx_free_irq(struct hinic_rxq *rxq) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct hinic_rq *rq = rxq->rq; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci irq_set_affinity_hint(rq->irq, NULL); 5708c2ecf20Sopenharmony_ci free_irq(rq->irq, rxq); 5718c2ecf20Sopenharmony_ci rx_del_napi(rxq); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/** 5758c2ecf20Sopenharmony_ci * hinic_init_rxq - Initialize the Rx Queue 5768c2ecf20Sopenharmony_ci * @rxq: Logical Rx Queue 5778c2ecf20Sopenharmony_ci * @rq: Hardware Rx Queue to connect the Logical queue with 5788c2ecf20Sopenharmony_ci * @netdev: network device to connect the Logical queue with 5798c2ecf20Sopenharmony_ci * 5808c2ecf20Sopenharmony_ci * Return 0 - Success, negative - Failure 5818c2ecf20Sopenharmony_ci **/ 5828c2ecf20Sopenharmony_ciint hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, 5838c2ecf20Sopenharmony_ci struct net_device *netdev) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct hinic_qp *qp = container_of(rq, struct hinic_qp, rq); 5868c2ecf20Sopenharmony_ci int err, pkts; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci rxq->netdev = netdev; 5898c2ecf20Sopenharmony_ci rxq->rq = rq; 5908c2ecf20Sopenharmony_ci rxq->buf_len = HINIC_RX_BUF_SZ; 5918c2ecf20Sopenharmony_ci rxq->rx_buff_shift = ilog2(HINIC_RX_BUF_SZ); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci rxq_stats_init(rxq); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci rxq->irq_name = devm_kasprintf(&netdev->dev, GFP_KERNEL, 5968c2ecf20Sopenharmony_ci "%s_rxq%d", netdev->name, qp->q_id); 5978c2ecf20Sopenharmony_ci if (!rxq->irq_name) 5988c2ecf20Sopenharmony_ci return -ENOMEM; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci pkts = rx_alloc_pkts(rxq); 6018c2ecf20Sopenharmony_ci if (!pkts) { 6028c2ecf20Sopenharmony_ci err = -ENOMEM; 6038c2ecf20Sopenharmony_ci goto err_rx_pkts; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci err = rx_request_irq(rxq); 6078c2ecf20Sopenharmony_ci if (err) { 6088c2ecf20Sopenharmony_ci netdev_err(netdev, "Failed to request Rx irq\n"); 6098c2ecf20Sopenharmony_ci goto err_req_rx_irq; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci return 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cierr_req_rx_irq: 6158c2ecf20Sopenharmony_cierr_rx_pkts: 6168c2ecf20Sopenharmony_ci free_all_rx_skbs(rxq); 6178c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, rxq->irq_name); 6188c2ecf20Sopenharmony_ci return err; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/** 6228c2ecf20Sopenharmony_ci * hinic_clean_rxq - Clean the Rx Queue 6238c2ecf20Sopenharmony_ci * @rxq: Logical Rx Queue 6248c2ecf20Sopenharmony_ci **/ 6258c2ecf20Sopenharmony_civoid hinic_clean_rxq(struct hinic_rxq *rxq) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct net_device *netdev = rxq->netdev; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci rx_free_irq(rxq); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci free_all_rx_skbs(rxq); 6328c2ecf20Sopenharmony_ci devm_kfree(&netdev->dev, rxq->irq_name); 6338c2ecf20Sopenharmony_ci} 634