// SPDX-License-Identifier: GPL-2.0-only
/*
* Huawei HiNIC PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/u64_stats_sync.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/smp.h>
#include <asm/byteorder.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/sctp.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include "hinic_common.h"
#include "hinic_hw_if.h"
#include "hinic_hw_wqe.h"
#include "hinic_hw_wq.h"
#include "hinic_hw_qp.h"
#include "hinic_hw_dev.h"
#include "hinic_dev.h"
#include "hinic_tx.h"
#define TX_IRQ_NO_PENDING 0
#define TX_IRQ_NO_COALESC 0
#define TX_IRQ_NO_LLI_TIMER 0
#define TX_IRQ_NO_CREDIT 0
#define TX_IRQ_NO_RESEND_TIMER 0
#define CI_UPDATE_NO_PENDING 0
#define CI_UPDATE_NO_COALESC 0
#define HW_CONS_IDX(sq) be16_to_cpu(*(u16 *)((sq)->hw_ci_addr))
#define MIN_SKB_LEN 32
#define MAX_PAYLOAD_OFFSET 221
#define TRANSPORT_OFFSET(l4_hdr, skb) ((u32)((l4_hdr) - (skb)->data))
union hinic_l3 {
struct iphdr *v4;
struct ipv6hdr *v6;
unsigned char *hdr;
};
union hinic_l4 {
struct tcphdr *tcp;
struct udphdr *udp;
unsigned char *hdr;
};
enum hinic_offload_type {
TX_OFFLOAD_TSO = BIT(0),
TX_OFFLOAD_CSUM = BIT(1),
TX_OFFLOAD_VLAN = BIT(2),
TX_OFFLOAD_INVALID = BIT(3),
};
/**
* hinic_txq_clean_stats - Clean the statistics of specific queue
* @txq: Logical Tx Queue
**/
void hinic_txq_clean_stats(struct hinic_txq *txq)
{
struct hinic_txq_stats *txq_stats = &txq->txq_stats;
u64_stats_update_begin(&txq_stats->syncp);
txq_stats->pkts = 0;
txq_stats->bytes = 0;
txq_stats->tx_busy = 0;
txq_stats->tx_wake = 0;
txq_stats->tx_dropped = 0;
txq_stats->big_frags_pkts = 0;
u64_stats_update_end(&txq_stats->syncp);
}
/**
* hinic_txq_get_stats - get statistics of Tx Queue
* @txq: Logical Tx Queue
* @stats: return updated stats here
**/
void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats)
{
struct hinic_txq_stats *txq_stats = &txq->txq_stats;
unsigned int start;
u64_stats_update_begin(&stats->syncp);
do {
start = u64_stats_fetch_begin(&txq_stats->syncp);
stats->pkts = txq_stats->pkts;
stats->bytes = txq_stats->bytes;
stats->tx_busy = txq_stats->tx_busy;
stats->tx_wake = txq_stats->tx_wake;
stats->tx_dropped = txq_stats->tx_dropped;
stats->big_frags_pkts = txq_stats->big_frags_pkts;
} while (u64_stats_fetch_retry(&txq_stats->syncp, start));
u64_stats_update_end(&stats->syncp);
}
/**
* txq_stats_init - Initialize the statistics of specific queue
* @txq: Logical Tx Queue
**/
static void txq_stats_init(struct hinic_txq *txq)
{
struct hinic_txq_stats *txq_stats = &txq->txq_stats;
u64_stats_init(&txq_stats->syncp);
hinic_txq_clean_stats(txq);
}
/**
* tx_map_skb - dma mapping for skb and return sges
* @nic_dev: nic device
* @skb: the skb
* @sges: returned sges
*
* Return 0 - Success, negative - Failure
**/
static int tx_map_skb(struct hinic_dev *nic_dev, struct sk_buff *skb,
struct hinic_sge *sges)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
skb_frag_t *frag;
dma_addr_t dma_addr;
int i, j;
dma_addr = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb),
DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, dma_addr)) {
dev_err(&pdev->dev, "Failed to map Tx skb data\n");
return -EFAULT;
}
hinic_set_sge(&sges[0], dma_addr, skb_headlen(skb));
for (i = 0 ; i < skb_shinfo(skb)->nr_frags; i++) {
frag = &skb_shinfo(skb)->frags[i];
dma_addr = skb_frag_dma_map(&pdev->dev, frag, 0,
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(&pdev->dev, dma_addr)) {
dev_err(&pdev->dev, "Failed to map Tx skb frag\n");
goto err_tx_map;
}
hinic_set_sge(&sges[i + 1], dma_addr, skb_frag_size(frag));
}
return 0;
err_tx_map:
for (j = 0; j < i; j++