// 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/types.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/errno.h>
#include <linux/sizes.h>
#include <linux/atomic.h>
#include <linux/skbuff.h>
#include <linux/io.h>
#include <asm /barrier.h>
#include <asm /byteorder.h>
#include "hinic_common.h"
#include "hinic_hw_if.h"
#include "hinic_hw_wqe.h"
#include "hinic_hw_wq.h"
#include "hinic_hw_qp_ctxt.h"
#include "hinic_hw_qp.h"
#include "hinic_hw_io.h"
#define SQ_DB_OFF SZ_2K
/* The number of cache line to prefetch Until threshold state */
#define WQ_PREFETCH_MAX 2
/* The number of cache line to prefetch After threshold state */
#define WQ_PREFETCH_MIN 1
/* Threshold state */
#define WQ_PREFETCH_THRESHOLD 256
/* sizes of the SQ/RQ ctxt */
#define Q_CTXT_SIZE 48
#define CTXT_RSVD 240
#define SQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \
(((max_rqs) + (max_sqs)) * CTXT_RSVD + (q_id) * Q_CTXT_SIZE)
#define RQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \
(((max_rqs) + (max_sqs)) * CTXT_RSVD + \
(max_sqs + (q_id)) * Q_CTXT_SIZE)
#define SIZE_16BYTES(size) (ALIGN(size, 16 ) >> 4 )
#define SIZE_8BYTES(size) (ALIGN(size, 8 ) >> 3 )
#define SECT_SIZE_FROM_8BYTES(size) ((size) << 3 )
#define SQ_DB_PI_HI_SHIFT 8
#define SQ_DB_PI_HI(prod_idx) ((prod_idx) >> SQ_DB_PI_HI_SHIFT)
#define SQ_DB_PI_LOW_MASK 0 xFF
#define SQ_DB_PI_LOW(prod_idx) ((prod_idx) & SQ_DB_PI_LOW_MASK)
#define SQ_DB_ADDR(sq, pi) ((u64 *)((sq)->db_base) + SQ_DB_PI_LOW(pi))
#define SQ_MASKED_IDX(sq, idx) ((idx) & (sq)->wq->mask)
#define RQ_MASKED_IDX(rq, idx) ((idx) & (rq)->wq->mask)
enum sq_wqe_type {
SQ_NORMAL_WQE = 0 ,
};
enum rq_completion_fmt {
RQ_COMPLETE_SGE = 1
};
void hinic_qp_prepare_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr,
enum hinic_qp_ctxt_type ctxt_type,
u16 num_queues, u16 max_queues)
{
u16 max_sqs = max_queues;
u16 max_rqs = max_queues;
qp_ctxt_hdr->num_queues = num_queues;
qp_ctxt_hdr->queue_type = ctxt_type;
if (ctxt_type == HINIC_QP_CTXT_TYPE_SQ)
qp_ctxt_hdr->addr_offset = SQ_CTXT_OFFSET(max_sqs, max_rqs, 0 );
else
qp_ctxt_hdr->addr_offset = RQ_CTXT_OFFSET(max_sqs, max_rqs, 0 );
qp_ctxt_hdr->addr_offset = SIZE_16BYTES(qp_ctxt_hdr->addr_offset);
hinic_cpu_to_be32(qp_ctxt_hdr, sizeof (*qp_ctxt_hdr));
}
void hinic_sq_prepare_ctxt(struct hinic_sq_ctxt *sq_ctxt,
struct hinic_sq *sq, u16 global_qid)
{
u32 wq_page_pfn_hi, wq_page_pfn_lo, wq_block_pfn_hi, wq_block_pfn_lo;
u64 wq_page_addr, wq_page_pfn, wq_block_pfn;
u16 pi_start, ci_start;
struct hinic_wq *wq;
wq = sq->wq;
ci_start = atomic_read(&wq->cons_idx);
pi_start = atomic_read(&wq->prod_idx);
/* Read the first page paddr from the WQ page paddr ptrs */
wq_page_addr = be64_to_cpu(*wq->block_vaddr);
wq_page_pfn = HINIC_WQ_PAGE_PFN(wq_page_addr);
wq_page_pfn_hi = upper_32_bits(wq_page_pfn);
wq_page_pfn_lo = lower_32_bits(wq_page_pfn);
/* If only one page, use 0-level CLA */
if (wq->num_q_pages == 1 )
wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq_page_addr);
else
wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr);
wq_block_pfn_hi = upper_32_bits(wq_block_pfn);
wq_block_pfn_lo = lower_32_bits(wq_block_pfn);
sq_ctxt->ceq_attr = HINIC_SQ_CTXT_CEQ_ATTR_SET(global_qid,
GLOBAL_SQ_ID) |
HINIC_SQ_CTXT_CEQ_ATTR_SET(0 , EN);
sq_ctxt->ci_wrapped = HINIC_SQ_CTXT_CI_SET(ci_start, IDX) |
HINIC_SQ_CTXT_CI_SET(1 , WRAPPED);
sq_ctxt->wq_hi_pfn_pi =
HINIC_SQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) |
HINIC_SQ_CTXT_WQ_PAGE_SET(pi_start, PI);
sq_ctxt->wq_lo_pfn = wq_page_pfn_lo;
sq_ctxt->pref_cache =
HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) |
HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) |
HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD);
sq_ctxt->pref_wrapped = 1 ;
sq_ctxt->pref_wq_hi_pfn_ci =
HINIC_SQ_CTXT_PREF_SET(ci_start, CI) |
HINIC_SQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_HI_PFN);
sq_ctxt->pref_wq_lo_pfn = wq_page_pfn_lo;
sq_ctxt->wq_block_hi_pfn =
HINIC_SQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, HI_PFN);
sq_ctxt->wq_block_lo_pfn = wq_block_pfn_lo;
hinic_cpu_to_be32(sq_ctxt, sizeof (*sq_ctxt));
}
void hinic_rq_prepare_ctxt(struct hinic_rq_ctxt *rq_ctxt,
struct hinic_rq *rq, u16 global_qid)
{
u32 wq_page_pfn_hi, wq_page_pfn_lo, wq_block_pfn_hi, wq_block_pfn_lo;
u64 wq_page_addr, wq_page_pfn, wq_block_pfn;
u16 pi_start, ci_start;
struct hinic_wq *wq;
wq = rq->wq;
ci_start = atomic_read(&wq->cons_idx);
pi_start = atomic_read(&wq->prod_idx);
/* Read the first page paddr from the WQ page paddr ptrs */
wq_page_addr = be64_to_cpu(*wq->block_vaddr);
wq_page_pfn = HINIC_WQ_PAGE_PFN(wq_page_addr);
wq_page_pfn_hi = upper_32_bits(wq_page_pfn);
wq_page_pfn_lo = lower_32_bits(wq_page_pfn);
wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr);
wq_block_pfn_hi = upper_32_bits(wq_block_pfn);
wq_block_pfn_lo = lower_32_bits(wq_block_pfn);
rq_ctxt->ceq_attr = HINIC_RQ_CTXT_CEQ_ATTR_SET(0 , EN) |
HINIC_RQ_CTXT_CEQ_ATTR_SET(1 , WRAPPED);
rq_ctxt->pi_intr_attr = HINIC_RQ_CTXT_PI_SET(pi_start, IDX) |
HINIC_RQ_CTXT_PI_SET(rq->msix_entry, INTR);
rq_ctxt->wq_hi_pfn_ci = HINIC_RQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi,
HI_PFN) |
HINIC_RQ_CTXT_WQ_PAGE_SET(ci_start, CI);
rq_ctxt->wq_lo_pfn = wq_page_pfn_lo;
rq_ctxt->pref_cache =
HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) |
HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) |
HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD);
rq_ctxt->pref_wrapped = 1 ;
rq_ctxt->pref_wq_hi_pfn_ci =
HINIC_RQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_HI_PFN) |
HINIC_RQ_CTXT_PREF_SET(ci_start, CI);
rq_ctxt->pref_wq_lo_pfn = wq_page_pfn_lo;
rq_ctxt->pi_paddr_hi = upper_32_bits(rq->pi_dma_addr);
rq_ctxt->pi_paddr_lo = lower_32_bits(rq->pi_dma_addr);
rq_ctxt->wq_block_hi_pfn =
HINIC_RQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, HI_PFN);
rq_ctxt->wq_block_lo_pfn = wq_block_pfn_lo;
hinic_cpu_to_be32(rq_ctxt, sizeof (*rq_ctxt));
}
/**
* alloc_sq_skb_arr - allocate sq array for saved skb
* @ sq : HW Send Queue
*
* Return 0 - Success , negative - Failure
**/
static int alloc_sq_skb_arr(struct hinic_sq *sq)
{
struct hinic_wq *wq = sq->wq;
size_t skb_arr_size;
skb_arr_size = wq->q_depth * sizeof (*sq->saved_skb);
sq->saved_skb = vzalloc(skb_arr_size);
if (!sq->saved_skb)
return -ENOMEM;
return 0 ;
}
/**
* free_sq_skb_arr - free sq array for saved skb
* @ sq : HW Send Queue
**/
static void free_sq_skb_arr(struct hinic_sq *sq)
{
vfree(sq->saved_skb);
}
/**
* alloc_rq_skb_arr - allocate rq array for saved skb
* @ rq : HW Receive Queue
*
* Return 0 - Success , negative - Failure
**/
static int alloc_rq_skb_arr(struct hinic_rq *rq)
{
struct hinic_wq *wq = rq->wq;
size_t skb_arr_size;
skb_arr_size = wq->q_depth * sizeof (*rq->saved_skb);
rq->saved_skb = vzalloc(skb_arr_size);
if (!rq->saved_skb)
return -ENOMEM;
return 0 ;
}
/**
* free_rq_skb_arr - free rq array for saved skb
* @ rq : HW Receive Queue
**/
static void free_rq_skb_arr(struct hinic_rq *rq)
{
vfree(rq->saved_skb);
}
/**
* hinic_init_sq - Initialize HW Send Queue
* @ sq : HW Send Queue
* @ hwif : HW Interface for accessing HW
* @ wq : Work Queue for the data of the SQ
* @ entry : msix entry for sq
* @ ci_addr : address for reading the current HW consumer index
* @ ci_dma_addr : dma address for reading the current HW consumer index
* @ db_base : doorbell base address
*
* Return 0 - Success , negative - Failure
**/
int hinic_init_sq(struct hinic_sq *sq, struct hinic_hwif *hwif,
struct hinic_wq *wq, struct msix_entry *entry,
void *ci_addr, dma_addr_t ci_dma_addr,
void __iomem *db_base)
{
sq->hwif = hwif;
sq->wq = wq;
sq->irq = entry->vector;
sq->msix_entry = entry->entry;
sq->hw_ci_addr = ci_addr;
sq->hw_ci_dma_addr = ci_dma_addr;
sq->db_base = db_base + SQ_DB_OFF;
return alloc_sq_skb_arr(sq);
}
/**
* hinic_clean_sq - Clean HW Send Queue ' s Resources
* @ sq : Send Queue
**/
void hinic_clean_sq(struct hinic_sq *sq)
{
free_sq_skb_arr(sq);
}
/**
* alloc_rq_cqe - allocate rq completion queue elements
* @ rq : HW Receive Queue
*
* Return 0 - Success , negative - Failure
**/
static int alloc_rq_cqe(struct hinic_rq *rq)
{
struct hinic_hwif *hwif = rq->hwif;
struct pci_dev *pdev = hwif->pdev;
size_t cqe_dma_size, cqe_size;
struct hinic_wq *wq = rq->wq;
int j, i;
cqe_size = wq->q_depth * sizeof (*rq->cqe);
rq->cqe = vzalloc(cqe_size);
if (!rq->cqe)
return -ENOMEM;
cqe_dma_size = wq->q_depth * sizeof (*rq->cqe_dma);
rq->cqe_dma = vzalloc(cqe_dma_size);
if (!rq->cqe_dma)
goto err_cqe_dma_arr_alloc;
for (i = 0 ; i < wq->q_depth; i++) {
rq->cqe[i] = dma_alloc_coherent(&pdev->dev,
sizeof (*rq->cqe[i]),
&rq->cqe_dma[i], GFP_KERNEL);
if (!rq->cqe[i])
goto err_cqe_alloc;
}
return 0 ;
err_cqe_alloc:
for (j = 0 ; j < i; j++)
dma_free_coherent(&pdev->dev, sizeof (*rq->cqe[j]), rq->cqe[j],
rq->cqe_dma[j]);
vfree(rq->cqe_dma);
err_cqe_dma_arr_alloc:
vfree(rq->cqe);
return -ENOMEM;
}
/**
* free_rq_cqe - free rq completion queue elements
* @ rq : HW Receive Queue
**/
static void free_rq_cqe(struct hinic_rq *rq)
{
struct hinic_hwif *hwif = rq->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_wq *wq = rq->wq;
int i;
for (i = 0 ; i < wq->q_depth; i++)
dma_free_coherent(&pdev->dev, sizeof (*rq->cqe[i]), rq->cqe[i],
rq->cqe_dma[i]);
vfree(rq->cqe_dma);
vfree(rq->cqe);
}
/**
* hinic_init_rq - Initialize HW Receive Queue
* @ rq : HW Receive Queue
* @ hwif : HW Interface for accessing HW
* @ wq : Work Queue for the data of the RQ
* @ entry : msix entry for rq
*
* Return 0 - Success , negative - Failure
**/
int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif,
struct hinic_wq *wq, struct msix_entry *entry)
{
struct pci_dev *pdev = hwif->pdev;
size_t pi_size;
int err;
rq->hwif = hwif;
rq->wq = wq;
rq->irq = entry->vector;
rq->msix_entry = entry->entry;
rq->buf_sz = HINIC_RX_BUF_SZ;
err = alloc_rq_skb_arr(rq);
if (err) {
dev_err(&pdev->dev, "Failed to allocate rq priv data\n" );
return err;
}
err = alloc_rq_cqe(rq);
if (err) {
dev_err(&pdev->dev, "Failed to allocate rq cqe\n" );
goto err_alloc_rq_cqe;
}
/* HW requirements: Must be at least 32 bit */
pi_size = ALIGN(sizeof (*rq->pi_virt_addr), sizeof (u32));
rq->pi_virt_addr = dma_alloc_coherent(&pdev->dev, pi_size,
&rq->pi_dma_addr, GFP_KERNEL);
if (!rq->pi_virt_addr) {
err = -ENOMEM;
goto err_pi_virt;
}
return 0 ;
err_pi_virt:
free_rq_cqe(rq);
err_alloc_rq_cqe:
free_rq_skb_arr(rq);
return err;
}
/**
* hinic_clean_rq - Clean HW Receive Queue ' s Resources
* @ rq : HW Receive Queue
**/
void hinic_clean_rq(struct hinic_rq *rq)
{
struct hinic_hwif *hwif = rq->hwif;
struct pci_dev *pdev = hwif->pdev;
size_t pi_size;
pi_size = ALIGN(sizeof (*rq->pi_virt_addr), sizeof (u32));
dma_free_coherent(&pdev->dev, pi_size, rq->pi_virt_addr,
rq->pi_dma_addr);
free_rq_cqe(rq);
free_rq_skb_arr(rq);
}
/**
* hinic_get_sq_free_wqebbs - return number of free wqebbs for use
* @ sq : send queue
*
* Return number of free wqebbs
**/
int hinic_get_sq_free_wqebbs(struct hinic_sq *sq)
{
struct hinic_wq *wq = sq->wq;
return atomic_read(&wq->delta) - 1 ;
}
/**
* hinic_get_rq_free_wqebbs - return number of free wqebbs for use
* @ rq : recv queue
*
* Return number of free wqebbs
**/
int hinic_get_rq_free_wqebbs(struct hinic_rq *rq)
{
struct hinic_wq *wq = rq->wq;
return atomic_read(&wq->delta) - 1 ;
}
static void sq_prepare_ctrl(struct hinic_sq_ctrl *ctrl, int nr_descs)
{
u32 ctrl_size, task_size, bufdesc_size;
ctrl_size = SIZE_8BYTES(sizeof (struct hinic_sq_ctrl));
task_size = SIZE_8BYTES(sizeof (struct hinic_sq_task));
bufdesc_size = nr_descs * sizeof (struct hinic_sq_bufdesc);
bufdesc_size = SIZE_8BYTES(bufdesc_size);
ctrl->ctrl_info = HINIC_SQ_CTRL_SET(bufdesc_size, BUFDESC_SECT_LEN) |
HINIC_SQ_CTRL_SET(task_size, TASKSECT_LEN) |
HINIC_SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) |
HINIC_SQ_CTRL_SET(ctrl_size, LEN);
ctrl->queue_info = HINIC_SQ_CTRL_SET(HINIC_MSS_DEFAULT,
QUEUE_INFO_MSS) |
HINIC_SQ_CTRL_SET(1 , QUEUE_INFO_UC);
}
static void sq_prepare_task(struct hinic_sq_task *task)
{
task->pkt_info0 = 0 ;
task->pkt_info1 = 0 ;
task->pkt_info2 = 0 ;
task->ufo_v6_identify = 0 ;
task->pkt_info4 = HINIC_SQ_TASK_INFO4_SET(HINIC_L2TYPE_ETH, L2TYPE);
task->zero_pad = 0 ;
}
void hinic_task_set_l2hdr(struct hinic_sq_task *task, u32 len)
{
task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(len, L2HDR_LEN);
}
void hinic_task_set_outter_l3(struct hinic_sq_task *task,
enum hinic_l3_offload_type l3_type,
u32 network_len)
{
task->pkt_info2 |= HINIC_SQ_TASK_INFO2_SET(l3_type, OUTER_L3TYPE) |
HINIC_SQ_TASK_INFO2_SET(network_len, OUTER_L3LEN);
}
void hinic_task_set_inner_l3(struct hinic_sq_task *task,
enum hinic_l3_offload_type l3_type,
u32 network_len)
{
task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(l3_type, INNER_L3TYPE);
task->pkt_info1 |= HINIC_SQ_TASK_INFO1_SET(network_len, INNER_L3LEN);
}
void hinic_task_set_tunnel_l4(struct hinic_sq_task *task,
enum hinic_l4_tunnel_type l4_type,
u32 tunnel_len)
{
task->pkt_info2 |= HINIC_SQ_TASK_INFO2_SET(l4_type, TUNNEL_L4TYPE) |
HINIC_SQ_TASK_INFO2_SET(tunnel_len, TUNNEL_L4LEN);
}
void hinic_set_cs_inner_l4(struct hinic_sq_task *task, u32 *queue_info,
enum hinic_l4_offload_type l4_offload,
u32 l4_len, u32 offset)
{
u32 tcp_udp_cs = 0 , sctp = 0 ;
u32 mss = HINIC_MSS_DEFAULT;
if (l4_offload == TCP_OFFLOAD_ENABLE ||
l4_offload == UDP_OFFLOAD_ENABLE)
tcp_udp_cs = 1 ;
else if (l4_offload == SCTP_OFFLOAD_ENABLE)
sctp = 1 ;
task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(l4_offload, L4_OFFLOAD);
task->pkt_info1 |= HINIC_SQ_TASK_INFO1_SET(l4_len, INNER_L4LEN);
*queue_info |= HINIC_SQ_CTRL_SET(offset, QUEUE_INFO_PLDOFF) |
HINIC_SQ_CTRL_SET(tcp_udp_cs, QUEUE_INFO_TCPUDP_CS) |
HINIC_SQ_CTRL_SET(sctp, QUEUE_INFO_SCTP);
*queue_info = HINIC_SQ_CTRL_CLEAR(*queue_info, QUEUE_INFO_MSS);
*queue_info |= HINIC_SQ_CTRL_SET(mss, QUEUE_INFO_MSS);
}
void hinic_set_tso_inner_l4(struct hinic_sq_task *task, u32 *queue_info,
enum hinic_l4_offload_type l4_offload,
u32 l4_len, u32 offset, u32 ip_ident, u32 mss)
{
u32 tso = 0 , ufo = 0 ;
if (l4_offload == TCP_OFFLOAD_ENABLE)
tso = 1 ;
else if (l4_offload == UDP_OFFLOAD_ENABLE)
ufo = 1 ;
task->ufo_v6_identify = ip_ident;
task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(l4_offload, L4_OFFLOAD);
task->pkt_info0 |= HINIC_SQ_TASK_INFO0_SET(tso || ufo, TSO_FLAG);
task->pkt_info1 |= HINIC_SQ_TASK_INFO1_SET(l4_len, INNER_L4LEN);
*queue_info |= HINIC_SQ_CTRL_SET(offset, QUEUE_INFO_PLDOFF) |
HINIC_SQ_CTRL_SET(tso, QUEUE_INFO_TSO) |
HINIC_SQ_CTRL_SET(ufo, QUEUE_INFO_UFO) |
HINIC_SQ_CTRL_SET(!!l4_offload, QUEUE_INFO_TCPUDP_CS);
/* set MSS value */
*queue_info = HINIC_SQ_CTRL_CLEAR(*queue_info, QUEUE_INFO_MSS);
*queue_info |= HINIC_SQ_CTRL_SET(mss, QUEUE_INFO_MSS);
}
/**
* hinic_sq_prepare_wqe - prepare wqe before insert to the queue
* @ sq : send queue
* @ sq_wqe : wqe to prepare
* @ sges : sges for use by the wqe for send for buf addresses
* @ nr_sges : number of sges
**/
void hinic_sq_prepare_wqe(struct hinic_sq *sq, struct hinic_sq_wqe *sq_wqe,
struct hinic_sge *sges, int nr_sges)
{
int i;
sq_prepare_ctrl(&sq_wqe->ctrl, nr_sges);
sq_prepare_task(&sq_wqe->task);
for (i = 0 ; i < nr_sges; i++)
sq_wqe->buf_descs[i].sge = sges[i];
}
/**
* sq_prepare_db - prepare doorbell to write
* @ sq : send queue
* @ prod_idx : pi value for the doorbell
* @ cos : cos of the doorbell
*
* Return db value
**/
static u32 sq_prepare_db(struct hinic_sq *sq, u16 prod_idx, unsigned int cos)
{
struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq);
u8 hi_prod_idx = SQ_DB_PI_HI(SQ_MASKED_IDX(sq, prod_idx));
/* Data should be written to HW in Big Endian Format */
return cpu_to_be32(HINIC_SQ_DB_INFO_SET(hi_prod_idx, PI_HI) |
HINIC_SQ_DB_INFO_SET(HINIC_DB_SQ_TYPE, TYPE) |
HINIC_SQ_DB_INFO_SET(HINIC_DATA_PATH, PATH) |
HINIC_SQ_DB_INFO_SET(cos, COS) |
HINIC_SQ_DB_INFO_SET(qp->q_id, QID));
}
/**
* hinic_sq_write_db - write doorbell
* @ sq : send queue
* @ prod_idx : pi value for the doorbell
* @ wqe_size : wqe size
* @ cos : cos of the wqe
**/
void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size,
unsigned int cos)
{
struct hinic_wq *wq = sq->wq;
/* increment prod_idx to the next */
prod_idx += ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
prod_idx = SQ_MASKED_IDX(sq, prod_idx);
wmb(); /* Write all before the doorbell */
writel(sq_prepare_db(sq, prod_idx, cos), SQ_DB_ADDR(sq, prod_idx));
}
/**
* hinic_sq_get_wqe - get wqe ptr in the current pi and update the pi
* @ sq : sq to get wqe from
* @ wqe_size : wqe size
* @ prod_idx : returned pi
*
* Return wqe pointer
**/
struct hinic_sq_wqe *hinic_sq_get_wqe(struct hinic_sq *sq,
unsigned int wqe_size, u16 *prod_idx)
{
struct hinic_hw_wqe *hw_wqe = hinic_get_wqe(sq->wq, wqe_size,
prod_idx);
if (IS_ERR(hw_wqe))
return NULL;
return &hw_wqe->sq_wqe;
}
/**
* hinic_sq_return_wqe - return the wqe to the sq
* @ sq : send queue
* @ wqe_size : the size of the wqe
**/
void hinic_sq_return_wqe(struct hinic_sq *sq, unsigned int wqe_size)
{
hinic_return_wqe(sq->wq, wqe_size);
}
/**
* hinic_sq_write_wqe - write the wqe to the sq
* @ sq : send queue
* @ prod_idx : pi of the wqe
* @ sq_wqe : the wqe to write
* @ skb : skb to save
* @ wqe_size : the size of the wqe
**/
void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx,
struct hinic_sq_wqe *sq_wqe,
struct sk_buff *skb, unsigned int wqe_size)
{
struct hinic_hw_wqe *hw_wqe = (struct hinic_hw_wqe *)sq_wqe;
sq->saved_skb[prod_idx] = skb;
/* The data in the HW should be in Big Endian Format */
hinic_cpu_to_be32(sq_wqe, wqe_size);
hinic_write_wqe(sq->wq, hw_wqe, wqe_size);
}
/**
* hinic_sq_read_wqebb - read wqe ptr in the current ci and update the ci , the
* wqe only have one wqebb
* @ sq : send queue
* @ skb : return skb that was saved
* @ wqe_size : the wqe size ptr
* @ cons_idx : consumer index of the wqe
*
* Return wqe in ci position
**/
struct hinic_sq_wqe *hinic_sq_read_wqebb(struct hinic_sq *sq,
struct sk_buff **skb,
unsigned int *wqe_size, u16 *cons_idx)
{
struct hinic_hw_wqe *hw_wqe;
struct hinic_sq_wqe *sq_wqe;
struct hinic_sq_ctrl *ctrl;
unsigned int buf_sect_len;
u32 ctrl_info;
/* read the ctrl section for getting wqe size */
hw_wqe = hinic_read_wqe(sq->wq, sizeof (*ctrl), cons_idx);
if (IS_ERR(hw_wqe))
return NULL;
*skb = sq->saved_skb[*cons_idx];
sq_wqe = &hw_wqe->sq_wqe;
ctrl = &sq_wqe->ctrl;
ctrl_info = be32_to_cpu(ctrl->ctrl_info);
buf_sect_len = HINIC_SQ_CTRL_GET(ctrl_info, BUFDESC_SECT_LEN);
*wqe_size = sizeof (*ctrl) + sizeof (sq_wqe->task);
*wqe_size += SECT_SIZE_FROM_8BYTES(buf_sect_len);
*wqe_size = ALIGN(*wqe_size, sq->wq->wqebb_size);
return &hw_wqe->sq_wqe;
}
/**
* hinic_sq_read_wqe - read wqe ptr in the current ci and update the ci
* @ sq : send queue
* @ skb : return skb that was saved
* @ wqe_size : the size of the wqe
* @ cons_idx : consumer index of the wqe
*
* Return wqe in ci position
**/
struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq,
struct sk_buff **skb,
unsigned int wqe_size, u16 *cons_idx)
{
struct hinic_hw_wqe *hw_wqe;
hw_wqe = hinic_read_wqe(sq->wq, wqe_size, cons_idx);
*skb = sq->saved_skb[*cons_idx];
return &hw_wqe->sq_wqe;
}
/**
* hinic_sq_put_wqe - release the ci for new wqes
* @ sq : send queue
* @ wqe_size : the size of the wqe
**/
void hinic_sq_put_wqe(struct hinic_sq *sq, unsigned int wqe_size)
{
hinic_put_wqe(sq->wq, wqe_size);
}
/**
* hinic_sq_get_sges - get sges from the wqe
* @ sq_wqe : wqe to get the sges from its buffer addresses
* @ sges : returned sges
* @ nr_sges : number sges to return
**/
void hinic_sq_get_sges(struct hinic_sq_wqe *sq_wqe, struct hinic_sge *sges,
int nr_sges)
{
int i;
for (i = 0 ; i < nr_sges && i < HINIC_MAX_SQ_BUFDESCS; i++) {
sges[i] = sq_wqe->buf_descs[i].sge;
hinic_be32_to_cpu(&sges[i], sizeof (sges[i]));
}
}
/**
* hinic_rq_get_wqe - get wqe ptr in the current pi and update the pi
* @ rq : rq to get wqe from
* @ wqe_size : wqe size
* @ prod_idx : returned pi
*
* Return wqe pointer
**/
struct hinic_rq_wqe *hinic_rq_get_wqe(struct hinic_rq *rq,
unsigned int wqe_size, u16 *prod_idx)
{
struct hinic_hw_wqe *hw_wqe = hinic_get_wqe(rq->wq, wqe_size,
prod_idx);
if (IS_ERR(hw_wqe))
return NULL;
return &hw_wqe->rq_wqe;
}
/**
* hinic_rq_write_wqe - write the wqe to the rq
* @ rq : recv queue
* @ prod_idx : pi of the wqe
* @ rq_wqe : the wqe to write
* @ skb : skb to save
**/
void hinic_rq_write_wqe(struct hinic_rq *rq, u16 prod_idx,
struct hinic_rq_wqe *rq_wqe, struct sk_buff *skb)
{
struct hinic_hw_wqe *hw_wqe = (struct hinic_hw_wqe *)rq_wqe;
rq->saved_skb[prod_idx] = skb;
/* The data in the HW should be in Big Endian Format */
hinic_cpu_to_be32(rq_wqe, sizeof (*rq_wqe));
hinic_write_wqe(rq->wq, hw_wqe, sizeof (*rq_wqe));
}
/**
* hinic_rq_read_wqe - read wqe ptr in the current ci and update the ci
* @ rq : recv queue
* @ wqe_size : the size of the wqe
* @ skb : return saved skb
* @ cons_idx : consumer index of the wqe
*
* Return wqe in ci position
**/
struct hinic_rq_wqe *hinic_rq_read_wqe(struct hinic_rq *rq,
unsigned int wqe_size,
struct sk_buff **skb, u16 *cons_idx)
{
struct hinic_hw_wqe *hw_wqe;
struct hinic_rq_cqe *cqe;
int rx_done;
u32 status;
hw_wqe = hinic_read_wqe(rq->wq, wqe_size, cons_idx);
if (IS_ERR(hw_wqe))
return NULL;
cqe = rq->cqe[*cons_idx];
status = be32_to_cpu(cqe->status);
rx_done = HINIC_RQ_CQE_STATUS_GET(status, RXDONE);
if (!rx_done)
return NULL;
*skb = rq->saved_skb[*cons_idx];
return &hw_wqe->rq_wqe;
}
/**
* hinic_rq_read_next_wqe - increment ci and read the wqe in ci position
* @ rq : recv queue
* @ wqe_size : the size of the wqe
* @ skb : return saved skb
* @ cons_idx : consumer index in the wq
*
* Return wqe in incremented ci position
**/
struct hinic_rq_wqe *hinic_rq_read_next_wqe(struct hinic_rq *rq,
unsigned int wqe_size,
struct sk_buff **skb,
u16 *cons_idx)
{
struct hinic_wq *wq = rq->wq;
struct hinic_hw_wqe *hw_wqe;
unsigned int num_wqebbs;
wqe_size = ALIGN(wqe_size, wq->wqebb_size);
num_wqebbs = wqe_size / wq->wqebb_size;
*cons_idx = RQ_MASKED_IDX(rq, *cons_idx + num_wqebbs);
*skb = rq->saved_skb[*cons_idx];
hw_wqe = hinic_read_wqe_direct(wq, *cons_idx);
return &hw_wqe->rq_wqe;
}
/**
* hinic_rq_put_wqe - release the ci for new wqes
* @ rq : recv queue
* @ cons_idx : consumer index of the wqe
* @ wqe_size : the size of the wqe
**/
void hinic_rq_put_wqe(struct hinic_rq *rq, u16 cons_idx,
unsigned int wqe_size)
{
struct hinic_rq_cqe *cqe = rq->cqe[cons_idx];
u32 status = be32_to_cpu(cqe->status);
status = HINIC_RQ_CQE_STATUS_CLEAR(status, RXDONE);
/* Rx WQE size is 1 WQEBB, no wq shadow*/
cqe->status = cpu_to_be32(status);
wmb(); /* clear done flag */
hinic_put_wqe(rq->wq, wqe_size);
}
/**
* hinic_rq_get_sge - get sge from the wqe
* @ rq : recv queue
* @ rq_wqe : wqe to get the sge from its buf address
* @ cons_idx : consumer index
* @ sge : returned sge
**/
void hinic_rq_get_sge(struct hinic_rq *rq, struct hinic_rq_wqe *rq_wqe,
u16 cons_idx, struct hinic_sge *sge)
{
struct hinic_rq_cqe *cqe = rq->cqe[cons_idx];
u32 len = be32_to_cpu(cqe->len);
sge->hi_addr = be32_to_cpu(rq_wqe->buf_desc.hi_addr);
sge->lo_addr = be32_to_cpu(rq_wqe->buf_desc.lo_addr);
sge->len = HINIC_RQ_CQE_SGE_GET(len, LEN);
}
/**
* hinic_rq_prepare_wqe - prepare wqe before insert to the queue
* @ rq : recv queue
* @ prod_idx : pi value
* @ rq_wqe : the wqe
* @ sge : sge for use by the wqe for recv buf address
**/
void hinic_rq_prepare_wqe(struct hinic_rq *rq, u16 prod_idx,
struct hinic_rq_wqe *rq_wqe, struct hinic_sge *sge)
{
struct hinic_rq_cqe_sect *cqe_sect = &rq_wqe->cqe_sect;
struct hinic_rq_bufdesc *buf_desc = &rq_wqe->buf_desc;
struct hinic_rq_cqe *cqe = rq->cqe[prod_idx];
struct hinic_rq_ctrl *ctrl = &rq_wqe->ctrl;
dma_addr_t cqe_dma = rq->cqe_dma[prod_idx];
ctrl->ctrl_info =
HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof (*ctrl)), LEN) |
HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof (*cqe_sect)),
COMPLETE_LEN) |
HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof (*buf_desc)),
BUFDESC_SECT_LEN) |
HINIC_RQ_CTRL_SET(RQ_COMPLETE_SGE, COMPLETE_FORMAT);
hinic_set_sge(&cqe_sect->sge, cqe_dma, sizeof (*cqe));
buf_desc->hi_addr = sge->hi_addr;
buf_desc->lo_addr = sge->lo_addr;
}
/**
* hinic_rq_update - update pi of the rq
* @ rq : recv queue
* @ prod_idx : pi value
**/
void hinic_rq_update(struct hinic_rq *rq, u16 prod_idx)
{
*rq->pi_virt_addr = cpu_to_be16(RQ_MASKED_IDX(rq, prod_idx + 1 ));
}
Messung V0.5 in Prozent C=94 H=92 G=92
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland