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