2026-01-29 22:25:33 +08:00

1112 lines
27 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Huawei HiNIC PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": [NIC]" fmt
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/u64_stats_sync.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/sctp.h>
#include <linux/pkt_sched.h>
#include <linux/ipv6.h>
#include "ossl_knl.h"
#include "hinic_hw.h"
#include "hinic_hw_mgmt.h"
#include "hinic_nic_io.h"
#include "hinic_nic_cfg.h"
#include "hinic_nic_dev.h"
#include "hinic_qp.h"
#include "hinic_rx.h"
static void hinic_clear_rss_config_user(struct hinic_nic_dev *nic_dev);
#define HINIC_RX_HDR_SIZE 256
#define HINIC_RX_IPV6_PKT 7
#define HINIC_RX_VXLAN_PKT 0xb
#define RXQ_STATS_INC(rxq, field) \
{ \
u64_stats_update_begin(&(rxq)->rxq_stats.syncp); \
(rxq)->rxq_stats.field++; \
u64_stats_update_end(&(rxq)->rxq_stats.syncp); \
}
static bool rx_alloc_mapped_page(struct hinic_rxq *rxq,
struct hinic_rx_info *rx_info)
{
struct net_device *netdev = rxq->netdev;
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct pci_dev *pdev = nic_dev->pdev;
struct page *page = rx_info->page;
dma_addr_t dma = rx_info->buf_dma_addr;
if (likely(dma))
return true;
/* alloc new page for storage */
page = dev_alloc_pages(nic_dev->page_order);
if (unlikely(!page)) {
RXQ_STATS_INC(rxq, alloc_rx_buf_err);
return false;
}
/* map page for use */
dma = dma_map_page(&pdev->dev, page, 0, rxq->dma_rx_buff_size,
DMA_FROM_DEVICE);
/* if mapping failed free memory back to system since
* there isn't much point in holding memory we can't use
*/
if (unlikely(dma_mapping_error(&pdev->dev, dma))) {
RXQ_STATS_INC(rxq, map_rx_buf_err);
__free_pages(page, nic_dev->page_order);
return false;
}
rx_info->page = page;
rx_info->buf_dma_addr = dma;
rx_info->page_offset = 0;
return true;
}
static int hinic_rx_fill_wqe(struct hinic_rxq *rxq)
{
struct net_device *netdev = rxq->netdev;
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_rq_wqe *rq_wqe;
struct hinic_rx_info *rx_info;
dma_addr_t dma_addr = 0;
u16 pi = 0;
int rq_wqe_len;
int i;
for (i = 0; i < rxq->q_depth; i++) {
rx_info = &rxq->rx_info[i];
rq_wqe = hinic_get_rq_wqe(nic_dev->hwdev, rxq->q_id, &pi);
if (!rq_wqe) {
nicif_err(nic_dev, drv, netdev, "Failed to get rq wqe, rxq id: %d, wqe id: %d\n",
rxq->q_id, i);
break;
}
hinic_prepare_rq_wqe(rq_wqe, pi, dma_addr, rx_info->cqe_dma);
rq_wqe_len = sizeof(struct hinic_rq_wqe);
hinic_cpu_to_be32(rq_wqe, rq_wqe_len);
rx_info->rq_wqe = rq_wqe;
}
hinic_return_rq_wqe(nic_dev->hwdev, rxq->q_id, rxq->q_depth);
return i;
}
static int hinic_rx_fill_buffers(struct hinic_rxq *rxq)
{
struct net_device *netdev = rxq->netdev;
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct hinic_rq_wqe *rq_wqe;
struct hinic_rx_info *rx_info;
dma_addr_t dma_addr;
int i;
int free_wqebbs = rxq->delta - 1;
for (i = 0; i < free_wqebbs; i++) {
rx_info = &rxq->rx_info[rxq->next_to_update];
if (unlikely(!rx_alloc_mapped_page(rxq, rx_info)))
break;
dma_addr = rx_info->buf_dma_addr + rx_info->page_offset;
rq_wqe = rx_info->rq_wqe;
rq_wqe->buf_desc.addr_high =
cpu_to_be32(upper_32_bits(dma_addr));
rq_wqe->buf_desc.addr_low =
cpu_to_be32(lower_32_bits(dma_addr));
rxq->next_to_update = (rxq->next_to_update + 1) & rxq->q_mask;
}
if (likely(i)) {
/* Write all the wqes before pi update */
wmb();
hinic_update_rq_hw_pi(nic_dev->hwdev, rxq->q_id,
rxq->next_to_update);
rxq->delta -= i;
rxq->next_to_alloc = rxq->next_to_update;
} else if (free_wqebbs == rxq->q_depth - 1) {
RXQ_STATS_INC(rxq, rx_buf_empty);
}
return i;
}
void hinic_rx_free_buffers(struct hinic_rxq *rxq)
{
u16 i;
struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev);
struct hinic_rx_info *rx_info;
/* Free all the Rx ring sk_buffs */
for (i = 0; i < rxq->q_depth; i++) {
rx_info = &rxq->rx_info[i];
if (rx_info->buf_dma_addr) {
dma_unmap_page(rxq->dev, rx_info->buf_dma_addr,
rxq->dma_rx_buff_size,
DMA_FROM_DEVICE);
rx_info->buf_dma_addr = 0;
}
if (rx_info->page) {
__free_pages(rx_info->page, nic_dev->page_order);
rx_info->page = NULL;
}
}
}
static void hinic_reuse_rx_page(struct hinic_rxq *rxq,
struct hinic_rx_info *old_rx_info)
{
struct hinic_rx_info *new_rx_info;
u16 nta = rxq->next_to_alloc;
new_rx_info = &rxq->rx_info[nta];
/* update, and store next to alloc */
nta++;
rxq->next_to_alloc = (nta < rxq->q_depth) ? nta : 0;
new_rx_info->page = old_rx_info->page;
new_rx_info->page_offset = old_rx_info->page_offset;
new_rx_info->buf_dma_addr = old_rx_info->buf_dma_addr;
/* sync the buffer for use by the device */
dma_sync_single_range_for_device(rxq->dev, new_rx_info->buf_dma_addr,
new_rx_info->page_offset,
rxq->buf_len,
DMA_FROM_DEVICE);
}
static bool hinic_add_rx_frag(struct hinic_rxq *rxq,
struct hinic_rx_info *rx_info,
struct sk_buff *skb, u32 size)
{
struct page *page;
u8 *va;
page = rx_info->page;
va = (u8 *)page_address(page) + rx_info->page_offset;
prefetch(va);
#if L1_CACHE_BYTES < 128
prefetch(va + L1_CACHE_BYTES);
#endif
dma_sync_single_range_for_cpu(rxq->dev,
rx_info->buf_dma_addr,
rx_info->page_offset,
rxq->buf_len,
DMA_FROM_DEVICE);
if (size <= HINIC_RX_HDR_SIZE && !skb_is_nonlinear(skb)) {
memcpy(__skb_put(skb, size), va,
ALIGN(size, sizeof(long))); /*lint !e666*/
/* page is not reserved, we can reuse buffer as-is */
if (likely(page_to_nid(page) == numa_node_id()))
return true;
/* this page cannot be reused so discard it */
put_page(page);
return false;
}
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
(int)rx_info->page_offset, (int)size, rxq->buf_len);
/* avoid re-using remote pages */
if (unlikely(page_to_nid(page) != numa_node_id()))
return false;
/* if we are only owner of page we can reuse it */
if (unlikely(page_count(page) != 1))
return false;
/* flip page offset to other buffer */
rx_info->page_offset ^= rxq->buf_len;
page_ref_inc(page);
return true;
}
static void __packaging_skb(struct hinic_rxq *rxq, struct sk_buff *head_skb,
u8 sge_num, u32 pkt_len)
{
struct hinic_rx_info *rx_info;
struct sk_buff *skb;
u8 frag_num = 0;
u32 size;
u16 sw_ci;
sw_ci = ((u32)rxq->cons_idx) & rxq->q_mask;
skb = head_skb;
while (sge_num) {
rx_info = &rxq->rx_info[sw_ci];
sw_ci = (sw_ci + 1) & rxq->q_mask;
if (unlikely(pkt_len > rxq->buf_len)) {
size = rxq->buf_len;
pkt_len -= rxq->buf_len;
} else {
size = pkt_len;
}
if (unlikely(frag_num == MAX_SKB_FRAGS)) {
frag_num = 0;
if (skb == head_skb)
skb = skb_shinfo(skb)->frag_list;
else
skb = skb->next;
}
if (unlikely(skb != head_skb)) {
head_skb->len += size;
head_skb->data_len += size;
head_skb->truesize += rxq->buf_len;
}
if (likely(hinic_add_rx_frag(rxq, rx_info, skb, size))) {
hinic_reuse_rx_page(rxq, rx_info);
} else {
/* we are not reusing the buffer so unmap it */
dma_unmap_page(rxq->dev, rx_info->buf_dma_addr,
rxq->dma_rx_buff_size, DMA_FROM_DEVICE);
}
/* clear contents of buffer_info */
rx_info->buf_dma_addr = 0;
rx_info->page = NULL;
sge_num--;
frag_num++;
}
}
static struct sk_buff *hinic_fetch_rx_buffer(struct hinic_rxq *rxq, u32 pkt_len)
{
struct sk_buff *head_skb, *cur_skb, *skb = NULL;
struct net_device *netdev = rxq->netdev;
u8 sge_num, skb_num;
u16 wqebb_cnt = 0;
head_skb = netdev_alloc_skb_ip_align(netdev, HINIC_RX_HDR_SIZE);
if (unlikely(!head_skb))
return NULL;
sge_num = (u8)(pkt_len >> rxq->rx_buff_shift) +
((pkt_len & (rxq->buf_len - 1)) ? 1 : 0);
if (likely(sge_num <= MAX_SKB_FRAGS))
skb_num = 1;
else
skb_num = (sge_num / MAX_SKB_FRAGS) +
((sge_num % MAX_SKB_FRAGS) ? 1 : 0);
while (unlikely(skb_num > 1)) {
cur_skb = netdev_alloc_skb_ip_align(netdev, HINIC_RX_HDR_SIZE);
if (unlikely(!cur_skb))
goto alloc_skb_fail;
if (!skb) {
skb_shinfo(head_skb)->frag_list = cur_skb;
skb = cur_skb;
} else {
skb->next = cur_skb;
skb = cur_skb;
}
skb_num--;
}
prefetchw(head_skb->data);
wqebb_cnt = sge_num;
__packaging_skb(rxq, head_skb, sge_num, pkt_len);
rxq->cons_idx += wqebb_cnt;
rxq->delta += wqebb_cnt;
return head_skb;
alloc_skb_fail:
dev_kfree_skb_any(head_skb);
return NULL;
}
void hinic_rxq_get_stats(struct hinic_rxq *rxq,
struct hinic_rxq_stats *stats)
{
struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats;
unsigned int start;
u64_stats_update_begin(&stats->syncp);
do {
start = u64_stats_fetch_begin(&rxq_stats->syncp);
stats->bytes = rxq_stats->bytes;
stats->packets = rxq_stats->packets;
stats->errors = rxq_stats->csum_errors +
rxq_stats->other_errors;
stats->csum_errors = rxq_stats->csum_errors;
stats->other_errors = rxq_stats->other_errors;
stats->dropped = rxq_stats->dropped;
stats->rx_buf_empty = rxq_stats->rx_buf_empty;
} while (u64_stats_fetch_retry(&rxq_stats->syncp, start));
u64_stats_update_end(&stats->syncp);
}
void hinic_rxq_clean_stats(struct hinic_rxq_stats *rxq_stats)
{
u64_stats_update_begin(&rxq_stats->syncp);
rxq_stats->bytes = 0;
rxq_stats->packets = 0;
rxq_stats->errors = 0;
rxq_stats->csum_errors = 0;
rxq_stats->other_errors = 0;
rxq_stats->dropped = 0;
rxq_stats->alloc_skb_err = 0;
rxq_stats->alloc_rx_buf_err = 0;
rxq_stats->map_rx_buf_err = 0;
rxq_stats->rx_buf_empty = 0;
u64_stats_update_end(&rxq_stats->syncp);
}
static void rxq_stats_init(struct hinic_rxq *rxq)
{
struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats;
u64_stats_init(&rxq_stats->syncp);
hinic_rxq_clean_stats(rxq_stats);
}
static void hinic_pull_tail(struct sk_buff *skb)
{
skb_frag_t *frag = &skb_shinfo(skb)->frags[0];
unsigned char *va;
/* it is valid to use page_address instead of kmap since we are
* working with pages allocated out of the lomem pool per
* alloc_page(GFP_ATOMIC)
*/
va = skb_frag_address(frag);
/* align pull length to size of long to optimize memcpy performance */
skb_copy_to_linear_data(skb, va, HINIC_RX_HDR_SIZE);
/* update all of the pointers */
skb_frag_size_sub(frag, HINIC_RX_HDR_SIZE);
frag->bv_offset += HINIC_RX_HDR_SIZE;
skb->data_len -= HINIC_RX_HDR_SIZE;
skb->tail += HINIC_RX_HDR_SIZE;
}
static void hinic_rx_csum(struct hinic_rxq *rxq, u32 status,
struct sk_buff *skb)
{
struct net_device *netdev = rxq->netdev;
u32 csum_err;
csum_err = HINIC_GET_RX_CSUM_ERR(status);
if (unlikely(csum_err == HINIC_RX_CSUM_IPSU_OTHER_ERR))
rxq->rxq_stats.other_errors++;
if (!(netdev->features & NETIF_F_RXCSUM))
return;
if (!csum_err) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else {
/* pkt type is recognized by HW, and csum is err */
if (!(csum_err & (HINIC_RX_CSUM_HW_CHECK_NONE |
HINIC_RX_CSUM_IPSU_OTHER_ERR)))
rxq->rxq_stats.csum_errors++;
skb->ip_summed = CHECKSUM_NONE;
}
}
static void hinic_rx_gro(struct hinic_rxq *rxq, u32 offload_type,
struct sk_buff *skb)
{
struct net_device *netdev = rxq->netdev;
bool l2_tunnel;
if (!(netdev->features & NETIF_F_GRO))
return;
l2_tunnel = HINIC_GET_RX_PKT_TYPE(offload_type) == HINIC_RX_VXLAN_PKT ?
1 : 0;
if (l2_tunnel && skb->ip_summed == CHECKSUM_UNNECESSARY)
/* If we checked the outer header let the stack know */
skb->csum_level = 1;
}
static void hinic_copy_lp_data(struct hinic_nic_dev *nic_dev,
struct sk_buff *skb)
{
struct net_device *netdev = nic_dev->netdev;
u8 *lb_buf = nic_dev->lb_test_rx_buf;
void *frag_data;
int lb_len = nic_dev->lb_pkt_len;
int pkt_offset, frag_len, i;
if (nic_dev->lb_test_rx_idx == LP_PKT_CNT) {
nic_dev->lb_test_rx_idx = 0;
nicif_warn(nic_dev, rx_err, netdev, "Loopback test warning, recive too more test pkt\n");
}
if (skb->len != nic_dev->lb_pkt_len) {
nicif_warn(nic_dev, rx_err, netdev, "Wrong packet length\n");
nic_dev->lb_test_rx_idx++;
return;
}
pkt_offset = nic_dev->lb_test_rx_idx * lb_len;
frag_len = (int)skb_headlen(skb);
memcpy((lb_buf + pkt_offset), skb->data, frag_len);
pkt_offset += frag_len;
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
frag_data = skb_frag_address(&skb_shinfo(skb)->frags[i]);
frag_len = (int)skb_frag_size(&skb_shinfo(skb)->frags[i]);
memcpy((lb_buf + pkt_offset), frag_data, frag_len);
pkt_offset += frag_len;
}
nic_dev->lb_test_rx_idx++;
}
int recv_one_pkt(struct hinic_rxq *rxq, struct hinic_rq_cqe *rx_cqe,
u32 pkt_len, u32 vlan_len, u32 status)
{
struct sk_buff *skb;
struct net_device *netdev = rxq->netdev;
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u32 offload_type;
skb = hinic_fetch_rx_buffer(rxq, pkt_len);
if (unlikely(!skb)) {
RXQ_STATS_INC(rxq, alloc_skb_err);
return -ENOMEM;
}
/* place header in linear portion of buffer */
if (skb_is_nonlinear(skb))
hinic_pull_tail(skb);
hinic_rx_csum(rxq, status, skb);
offload_type = be32_to_cpu(rx_cqe->offload_type);
hinic_rx_gro(rxq, offload_type, skb);
if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
HINIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)) {
u16 vid = HINIC_GET_RX_VLAN_TAG(vlan_len);
/* if the packet is a vlan pkt, the vid may be 0 */
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
}
if (unlikely(test_bit(HINIC_LP_TEST, &nic_dev->flags)))
hinic_copy_lp_data(nic_dev, skb);
skb_record_rx_queue(skb, rxq->q_id);
skb->protocol = eth_type_trans(skb, netdev);
if (skb_has_frag_list(skb)) {
napi_gro_flush(&rxq->irq_cfg->napi, false);
netif_receive_skb(skb);
} else {
napi_gro_receive(&rxq->irq_cfg->napi, skb);
}
return 0;
}
void rx_pass_super_cqe(struct hinic_rxq *rxq, u32 index, u32 pkt_num,
struct hinic_rq_cqe *cqe)
{
u8 sge_num = 0;
u32 pkt_len;
while (index < pkt_num) {
pkt_len = hinic_get_pkt_len_for_super_cqe
(cqe, index == (pkt_num - 1));
sge_num += (u8)(pkt_len >> rxq->rx_buff_shift) +
((pkt_len & (rxq->buf_len - 1)) ? 1 : 0);
index++;
}
rxq->cons_idx += sge_num;
rxq->delta += sge_num;
}
static inline int __recv_supper_cqe(struct hinic_rxq *rxq,
struct hinic_rq_cqe *rx_cqe, u32 pkt_info,
u32 vlan_len, u32 status, int *pkts,
u64 *rx_bytes, u32 *dropped)
{
u32 pkt_len;
int i, pkt_num = 0;
pkt_num = HINIC_GET_RQ_CQE_PKT_NUM(pkt_info);
i = 0;
while (i < pkt_num) {
pkt_len = ((i == (pkt_num - 1)) ?
RQ_CQE_PKT_LEN_GET(pkt_info, LAST_LEN) :
RQ_CQE_PKT_LEN_GET(pkt_info, FIRST_LEN));
if (unlikely(recv_one_pkt(rxq, rx_cqe, pkt_len,
vlan_len, status))) {
if (i) {
rx_pass_super_cqe(rxq, i,
pkt_num,
rx_cqe);
*dropped += (pkt_num - i);
}
break;
}
*rx_bytes += pkt_len;
(*pkts)++;
i++;
}
if (!i)
return -EFAULT;
return 0;
}
#define LRO_PKT_HDR_LEN_IPV4 66
#define LRO_PKT_HDR_LEN_IPV6 86
#define LRO_PKT_HDR_LEN(cqe) \
(HINIC_GET_RX_PKT_TYPE(be32_to_cpu((cqe)->offload_type)) == \
HINIC_RX_IPV6_PKT ? LRO_PKT_HDR_LEN_IPV6 : LRO_PKT_HDR_LEN_IPV4)
int hinic_rx_poll(struct hinic_rxq *rxq, int budget)
{
struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev);
u32 status, pkt_len, vlan_len, pkt_info, dropped = 0;
struct hinic_rq_cqe *rx_cqe;
u64 rx_bytes = 0;
u16 sw_ci, num_lro;
int pkts = 0, nr_pkts = 0;
u16 num_wqe = 0;
while (likely(pkts < budget)) {
sw_ci = ((u32)rxq->cons_idx) & rxq->q_mask;
rx_cqe = rxq->rx_info[sw_ci].cqe;
status = be32_to_cpu(rx_cqe->status);
if (!HINIC_GET_RX_DONE(status))
break;
/* make sure we read rx_done before packet length */
rmb();
vlan_len = be32_to_cpu(rx_cqe->vlan_len);
pkt_info = be32_to_cpu(rx_cqe->pkt_info);
pkt_len = HINIC_GET_RX_PKT_LEN(vlan_len);
if (unlikely(HINIC_GET_SUPER_CQE_EN(pkt_info))) {
if (unlikely(__recv_supper_cqe(rxq, rx_cqe, pkt_info,
vlan_len, status, &pkts,
&rx_bytes, &dropped)))
break;
nr_pkts += (int)HINIC_GET_RQ_CQE_PKT_NUM(pkt_info);
} else {
if (recv_one_pkt(rxq, rx_cqe, pkt_len,
vlan_len, status))
break;
rx_bytes += pkt_len;
pkts++;
nr_pkts++;
num_lro = HINIC_GET_RX_NUM_LRO(status);
if (num_lro) {
rx_bytes += ((num_lro - 1) *
LRO_PKT_HDR_LEN(rx_cqe));
num_wqe +=
(u16)(pkt_len >> rxq->rx_buff_shift) +
((pkt_len & (rxq->buf_len - 1)) ? 1 : 0);
}
}
rx_cqe->status = 0;
if (num_wqe >= nic_dev->lro_replenish_thld)
break;
}
if (rxq->delta >= HINIC_RX_BUFFER_WRITE)
hinic_rx_fill_buffers(rxq);
u64_stats_update_begin(&rxq->rxq_stats.syncp);
rxq->rxq_stats.packets += nr_pkts;
rxq->rxq_stats.bytes += rx_bytes;
rxq->rxq_stats.dropped += dropped;
u64_stats_update_end(&rxq->rxq_stats.syncp);
return pkts;
}
static int rx_alloc_cqe(struct hinic_rxq *rxq)
{
struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev);
struct pci_dev *pdev = nic_dev->pdev;
struct hinic_rx_info *rx_info;
struct hinic_rq_cqe *cqe_va;
dma_addr_t cqe_pa;
u32 cqe_mem_size;
int idx;
cqe_mem_size = sizeof(*rx_info->cqe) * rxq->q_depth;
rxq->cqe_start_vaddr = dma_alloc_coherent(&pdev->dev, cqe_mem_size,
&rxq->cqe_start_paddr,
GFP_KERNEL);
if (!rxq->cqe_start_vaddr) {
nicif_err(nic_dev, drv, rxq->netdev, "Failed to allocate cqe dma\n");
return -ENOMEM;
}
cqe_va = (struct hinic_rq_cqe *)rxq->cqe_start_vaddr;
cqe_pa = rxq->cqe_start_paddr;
for (idx = 0; idx < rxq->q_depth; idx++) {
rx_info = &rxq->rx_info[idx];
rx_info->cqe = cqe_va;
rx_info->cqe_dma = cqe_pa;
cqe_va++;
cqe_pa += sizeof(*rx_info->cqe);
}
hinic_rq_cqe_addr_set(nic_dev->hwdev, rxq->q_id, rxq->cqe_start_paddr);
return 0;
}
static void rx_free_cqe(struct hinic_rxq *rxq)
{
struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev);
struct pci_dev *pdev = nic_dev->pdev;
u32 cqe_mem_size;
cqe_mem_size = sizeof(struct hinic_rq_cqe) * rxq->q_depth;
dma_free_coherent(&pdev->dev, cqe_mem_size,
rxq->cqe_start_vaddr, rxq->cqe_start_paddr);
}
static int hinic_setup_rx_resources(struct hinic_rxq *rxq,
struct net_device *netdev,
struct irq_info *entry)
{
struct hinic_nic_dev *nic_dev = netdev_priv(rxq->netdev);
u64 rx_info_sz;
int err, pkts;
rxq->irq_id = entry->irq_id;
rxq->msix_entry_idx = entry->msix_entry_idx;
rxq->next_to_alloc = 0;
rxq->next_to_update = 0;
rxq->delta = rxq->q_depth;
rxq->q_mask = rxq->q_depth - 1;
rxq->cons_idx = 0;
rx_info_sz = rxq->q_depth * sizeof(*rxq->rx_info);
if (!rx_info_sz) {
nicif_err(nic_dev, drv, netdev, "Cannot allocate zero size rx info\n");
return -EINVAL;
}
rxq->rx_info = kzalloc(rx_info_sz, GFP_KERNEL);
if (!rxq->rx_info)
return -ENOMEM;
err = rx_alloc_cqe(rxq);
if (err) {
nicif_err(nic_dev, drv, netdev, "Failed to allocate Rx cqe\n");
goto rx_cqe_err;
}
pkts = hinic_rx_fill_wqe(rxq);
if (pkts != rxq->q_depth) {
nicif_err(nic_dev, drv, netdev, "Failed to fill rx wqe\n");
err = -ENOMEM;
goto rx_pkts_err;
}
pkts = hinic_rx_fill_buffers(rxq);
if (!pkts) {
nicif_err(nic_dev, drv, netdev, "Failed to allocate Rx buffer\n");
err = -ENOMEM;
goto rx_pkts_err;
}
return 0;
rx_pkts_err:
rx_free_cqe(rxq);
rx_cqe_err:
kfree(rxq->rx_info);
return err;
}
static void hinic_free_rx_resources(struct hinic_rxq *rxq)
{
hinic_rx_free_buffers(rxq);
rx_free_cqe(rxq);
kfree(rxq->rx_info);
}
int hinic_setup_all_rx_resources(struct net_device *netdev,
struct irq_info *msix_entries)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u16 i, q_id;
int err;
for (q_id = 0; q_id < nic_dev->num_qps; q_id++) {
err = hinic_setup_rx_resources(&nic_dev->rxqs[q_id],
nic_dev->netdev,
&msix_entries[q_id]);
if (err) {
nicif_err(nic_dev, drv, netdev, "Failed to set up rxq resource\n");
goto init_rxq_err;
}
}
return 0;
init_rxq_err:
for (i = 0; i < q_id; i++)
hinic_free_rx_resources(&nic_dev->rxqs[i]);
return err;
}
void hinic_free_all_rx_resources(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u16 q_id;
for (q_id = 0; q_id < nic_dev->num_qps; q_id++)
hinic_free_rx_resources(&nic_dev->rxqs[q_id]);
}
int hinic_alloc_rxqs(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
struct pci_dev *pdev = nic_dev->pdev;
struct hinic_rxq *rxq;
u16 num_rxqs = nic_dev->max_qps;
u16 q_id;
u64 rxq_size;
rxq_size = num_rxqs * sizeof(*nic_dev->rxqs);
if (!rxq_size) {
nic_err(&pdev->dev, "Cannot allocate zero size rxqs\n");
return -EINVAL;
}
nic_dev->rxqs = kzalloc(rxq_size, GFP_KERNEL);
if (!nic_dev->rxqs)
return -ENOMEM;
for (q_id = 0; q_id < num_rxqs; q_id++) {
rxq = &nic_dev->rxqs[q_id];
rxq->netdev = netdev;
rxq->dev = &pdev->dev;
rxq->q_id = q_id;
rxq->buf_len = nic_dev->rx_buff_len;
rxq->rx_buff_shift = ilog2(nic_dev->rx_buff_len);
rxq->dma_rx_buff_size = RX_BUFF_NUM_PER_PAGE *
nic_dev->rx_buff_len;
rxq->q_depth = nic_dev->rq_depth;
rxq->q_mask = nic_dev->rq_depth - 1;
rxq_stats_init(rxq);
}
return 0;
}
void hinic_free_rxqs(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
hinic_clear_rss_config_user(nic_dev);
kfree(nic_dev->rxqs);
}
void hinic_init_rss_parameters(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
nic_dev->rss_hash_engine = HINIC_RSS_HASH_ENGINE_TYPE_XOR;
nic_dev->rss_type.tcp_ipv6_ext = 1;
nic_dev->rss_type.ipv6_ext = 1;
nic_dev->rss_type.tcp_ipv6 = 1;
nic_dev->rss_type.ipv6 = 1;
nic_dev->rss_type.tcp_ipv4 = 1;
nic_dev->rss_type.ipv4 = 1;
nic_dev->rss_type.udp_ipv6 = 1;
nic_dev->rss_type.udp_ipv4 = 1;
}
void hinic_set_default_rss_indir(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (!nic_dev->rss_indir_user)
return;
nicif_info(nic_dev, drv, netdev,
"Discard user configured Rx flow hash indirection\n");
kfree(nic_dev->rss_indir_user);
nic_dev->rss_indir_user = NULL;
}
static void hinic_maybe_reconfig_rss_indir(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int i;
if (!nic_dev->rss_indir_user)
return;
if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags))
goto discard_user_rss_indir;
for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) {
if (nic_dev->rss_indir_user[i] >= nic_dev->num_qps)
goto discard_user_rss_indir;
}
return;
discard_user_rss_indir:
hinic_set_default_rss_indir(netdev);
}
static void hinic_clear_rss_config_user(struct hinic_nic_dev *nic_dev)
{
kfree(nic_dev->rss_hkey_user);
nic_dev->rss_hkey_user_be = NULL;
nic_dev->rss_hkey_user = NULL;
kfree(nic_dev->rss_indir_user);
nic_dev->rss_indir_user = NULL;
}
static void hinic_fillout_indir_tbl(struct hinic_nic_dev *nic_dev,
u8 num_tcs, u32 *indir)
{
u16 num_rss, tc_group_size;
int i;
if (num_tcs)
tc_group_size = HINIC_RSS_INDIR_SIZE / num_tcs;
else
tc_group_size = HINIC_RSS_INDIR_SIZE;
num_rss = nic_dev->num_rss;
for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++)
indir[i] = (i / tc_group_size) * num_rss + i % num_rss;
}
static void hinic_rss_deinit(struct hinic_nic_dev *nic_dev)
{
u8 prio_tc[HINIC_DCB_UP_MAX] = {0};
hinic_rss_cfg(nic_dev->hwdev, 0, nic_dev->rss_tmpl_idx, 0, prio_tc);
}
int hinic_set_hw_rss_parameters(struct net_device *netdev, u8 rss_en, u8 num_tc,
u8 *prio_tc)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u8 tmpl_idx = 0xFF;
u8 default_rss_key[HINIC_RSS_KEY_SIZE] = {
0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa};
u32 *indir_tbl;
u8 *hkey;
int err;
tmpl_idx = nic_dev->rss_tmpl_idx;
/* RSS key */
if (nic_dev->rss_hkey_user)
hkey = nic_dev->rss_hkey_user;
else
hkey = default_rss_key;
err = hinic_rss_set_template_tbl(nic_dev->hwdev, tmpl_idx, hkey);
if (err)
return err;
hinic_maybe_reconfig_rss_indir(netdev);
indir_tbl = kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, GFP_KERNEL);
if (!indir_tbl) {
nicif_err(nic_dev, drv, netdev, "Failed to allocate set hw rss indir_tbl\n");
return -ENOMEM;
}
if (nic_dev->rss_indir_user)
memcpy(indir_tbl, nic_dev->rss_indir_user,
sizeof(u32) * HINIC_RSS_INDIR_SIZE);
else
hinic_fillout_indir_tbl(nic_dev, num_tc, indir_tbl);
err = hinic_rss_set_indir_tbl(nic_dev->hwdev, tmpl_idx, indir_tbl);
if (err)
goto out;
err = hinic_set_rss_type(nic_dev->hwdev, tmpl_idx, nic_dev->rss_type);
if (err)
goto out;
err = hinic_rss_set_hash_engine(nic_dev->hwdev, tmpl_idx,
nic_dev->rss_hash_engine);
if (err)
goto out;
err = hinic_rss_cfg(nic_dev->hwdev, rss_en, tmpl_idx, num_tc, prio_tc);
if (err)
goto out;
kfree(indir_tbl);
return 0;
out:
kfree(indir_tbl);
return err;
}
static int hinic_rss_init(struct hinic_nic_dev *nic_dev)
{
struct net_device *netdev = nic_dev->netdev;
u32 *indir_tbl;
u8 cos, num_tc = 0;
u8 prio_tc[HINIC_DCB_UP_MAX] = {0};
int err;
if (test_bit(HINIC_DCB_ENABLE, &nic_dev->flags)) {
num_tc = nic_dev->max_cos;
for (cos = 0; cos < HINIC_DCB_COS_MAX; cos++) {
if (cos < HINIC_DCB_COS_MAX - nic_dev->max_cos)
prio_tc[cos] = nic_dev->max_cos - 1;
else
prio_tc[cos] = (HINIC_DCB_COS_MAX - 1) - cos;
}
} else {
num_tc = 0;
}
indir_tbl = kzalloc(sizeof(u32) * HINIC_RSS_INDIR_SIZE, GFP_KERNEL);
if (!indir_tbl)
return -ENOMEM;
if (nic_dev->rss_indir_user)
memcpy(indir_tbl, nic_dev->rss_indir_user,
sizeof(u32) * HINIC_RSS_INDIR_SIZE);
else
hinic_fillout_indir_tbl(nic_dev, num_tc, indir_tbl);
err = hinic_set_hw_rss_parameters(netdev, 1, num_tc, prio_tc);
if (err) {
kfree(indir_tbl);
return err;
}
kfree(indir_tbl);
return 0;
}
int hinic_update_hw_tc_map(struct net_device *netdev, u8 num_tc, u8 *prio_tc)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
u8 tmpl_idx = nic_dev->rss_tmpl_idx;
/* RSS must be enable when dcb is enabled */
return hinic_rss_cfg(nic_dev->hwdev, 1, tmpl_idx, num_tc, prio_tc);
}
int hinic_rx_configure(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
int err;
if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags)) {
err = hinic_rss_init(nic_dev);
if (err) {
nicif_err(nic_dev, drv, netdev, "Failed to init rss\n");
return -EFAULT;
}
}
err = hinic_dcb_set_rq_iq_mapping(nic_dev->hwdev,
hinic_func_max_qnum(nic_dev->hwdev),
NULL);
if (err) {
nicif_err(nic_dev, drv, netdev, "Failed to set rq_iq mapping\n");
goto set_rq_cos_mapping_err;
}
return 0;
set_rq_cos_mapping_err:
if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags))
hinic_rss_deinit(nic_dev);
return err;
}
void hinic_rx_remove_configure(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (test_bit(HINIC_RSS_ENABLE, &nic_dev->flags))
hinic_rss_deinit(nic_dev);
}