693 lines
17 KiB
C
693 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2021 - 2023, Shanghai Yunsilicon Technology Co., Ltd.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kref.h>
|
|
#include <rdma/ib_umem.h>
|
|
#include "xsc_ib.h"
|
|
#include "user.h"
|
|
#include "common/xsc_hsi.h"
|
|
#include <linux/dma-direct.h>
|
|
|
|
enum {
|
|
CQ_OK = 0,
|
|
CQ_EMPTY = -1,
|
|
CQ_POLL_ERR = -2
|
|
};
|
|
|
|
enum {
|
|
XSC_CQE_APP_TAG_MATCHING = 1,
|
|
};
|
|
|
|
enum {
|
|
XSC_CQE_APP_OP_TM_CONSUMED = 0x1,
|
|
XSC_CQE_APP_OP_TM_EXPECTED = 0x2,
|
|
XSC_CQE_APP_OP_TM_UNEXPECTED = 0x3,
|
|
XSC_CQE_APP_OP_TM_NO_TAG = 0x4,
|
|
XSC_CQE_APP_OP_TM_APPEND = 0x5,
|
|
XSC_CQE_APP_OP_TM_REMOVE = 0x6,
|
|
XSC_CQE_APP_OP_TM_NOOP = 0x7,
|
|
XSC_CQE_APP_OP_TM_CONSUMED_SW_RDNV = 0x9,
|
|
XSC_CQE_APP_OP_TM_CONSUMED_MSG = 0xA,
|
|
XSC_CQE_APP_OP_TM_CONSUMED_MSG_SW_RDNV = 0xB,
|
|
XSC_CQE_APP_OP_TM_MSG_COMPLETION_CANCELED = 0xC,
|
|
};
|
|
|
|
static const u32 xsc_msg_opcode[][2][2] = {
|
|
[XSC_MSG_OPCODE_SEND][XSC_REQ][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_REQ_SEND,
|
|
[XSC_MSG_OPCODE_SEND][XSC_REQ][XSC_WITH_IMMDT] = XSC_OPCODE_RDMA_REQ_SEND_IMMDT,
|
|
[XSC_MSG_OPCODE_SEND][XSC_RSP][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_RSP_RECV,
|
|
[XSC_MSG_OPCODE_SEND][XSC_RSP][XSC_WITH_IMMDT] = XSC_OPCODE_RDMA_RSP_RECV_IMMDT,
|
|
[XSC_MSG_OPCODE_RDMA_WRITE][XSC_REQ][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_REQ_WRITE,
|
|
[XSC_MSG_OPCODE_RDMA_WRITE][XSC_REQ][XSC_WITH_IMMDT] = XSC_OPCODE_RDMA_REQ_WRITE_IMMDT,
|
|
[XSC_MSG_OPCODE_RDMA_WRITE][XSC_RSP][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_CQE_ERROR,
|
|
[XSC_MSG_OPCODE_RDMA_WRITE][XSC_RSP][XSC_WITH_IMMDT] = XSC_OPCODE_RDMA_RSP_WRITE_IMMDT,
|
|
[XSC_MSG_OPCODE_RDMA_READ][XSC_REQ][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_REQ_READ,
|
|
[XSC_MSG_OPCODE_RDMA_READ][XSC_REQ][XSC_WITH_IMMDT] = XSC_OPCODE_RDMA_CQE_ERROR,
|
|
[XSC_MSG_OPCODE_RDMA_READ][XSC_RSP][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_CQE_ERROR,
|
|
[XSC_MSG_OPCODE_RDMA_READ][XSC_RSP][XSC_WITH_IMMDT] = XSC_OPCODE_RDMA_CQE_ERROR,
|
|
[XSC_MSG_OPCODE_MAD][XSC_REQ][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_MAD_REQ_SEND,
|
|
[XSC_MSG_OPCODE_MAD][XSC_RSP][XSC_WITHOUT_IMMDT] = XSC_OPCODE_RDMA_MAD_RSP_RECV,
|
|
};
|
|
|
|
static const u32 xsc_cqe_opcode[] = {
|
|
[XSC_OPCODE_RDMA_REQ_SEND] = IB_WC_SEND,
|
|
[XSC_OPCODE_RDMA_REQ_SEND_IMMDT] = IB_WC_SEND,
|
|
[XSC_OPCODE_RDMA_RSP_RECV] = IB_WC_RECV,
|
|
[XSC_OPCODE_RDMA_RSP_RECV_IMMDT] = IB_WC_RECV,
|
|
[XSC_OPCODE_RDMA_REQ_WRITE] = IB_WC_RDMA_WRITE,
|
|
[XSC_OPCODE_RDMA_REQ_WRITE_IMMDT] = IB_WC_RDMA_WRITE,
|
|
[XSC_OPCODE_RDMA_RSP_WRITE_IMMDT] = IB_WC_RECV_RDMA_WITH_IMM,
|
|
[XSC_OPCODE_RDMA_REQ_READ] = IB_WC_RDMA_READ,
|
|
[XSC_OPCODE_RDMA_MAD_REQ_SEND] = IB_WC_SEND,
|
|
[XSC_OPCODE_RDMA_MAD_RSP_RECV] = IB_WC_RECV,
|
|
};
|
|
|
|
int xsc_stall_num_loop = 60;
|
|
int xsc_stall_cq_poll_min = 60;
|
|
int xsc_stall_cq_poll_max = 100000;
|
|
int xsc_stall_cq_inc_step = 100;
|
|
int xsc_stall_cq_dec_step = 10;
|
|
|
|
static inline u8 xsc_get_cqe_opcode(struct xsc_cqe *cqe)
|
|
{
|
|
if (cqe->is_error)
|
|
return cqe->type ? XSC_OPCODE_RDMA_RSP_ERROR : XSC_OPCODE_RDMA_REQ_ERROR;
|
|
if (cqe->msg_opcode > XSC_MSG_OPCODE_MAD)
|
|
return XSC_OPCODE_RDMA_CQE_ERROR;
|
|
return xsc_msg_opcode[cqe->msg_opcode][cqe->type][cqe->with_immdt];
|
|
}
|
|
|
|
static void xsc_ib_cq_comp(struct xsc_core_cq *cq)
|
|
{
|
|
struct ib_cq *ibcq = &to_xibcq(cq)->ibcq;
|
|
|
|
ibcq->comp_handler(ibcq, ibcq->cq_context);
|
|
}
|
|
|
|
static void xsc_ib_cq_event(struct xsc_core_cq *xcq, enum xsc_event type)
|
|
{
|
|
struct xsc_ib_cq *cq = container_of(xcq, struct xsc_ib_cq, xcq);
|
|
struct xsc_ib_dev *dev = to_mdev(cq->ibcq.device);
|
|
struct ib_cq *ibcq = &cq->ibcq;
|
|
struct ib_event event;
|
|
|
|
if (type != XSC_EVENT_TYPE_CQ_ERROR) {
|
|
xsc_ib_err(dev, "Unexpected event type %d on CQ %06x\n",
|
|
type, xcq->cqn);
|
|
return;
|
|
}
|
|
|
|
if (ibcq->event_handler) {
|
|
event.device = &dev->ib_dev;
|
|
event.event = IB_EVENT_CQ_ERR;
|
|
event.element.cq = ibcq;
|
|
ibcq->event_handler(&event, ibcq->cq_context);
|
|
}
|
|
}
|
|
|
|
static void *get_cqe_from_buf(struct xsc_ib_cq_buf *buf, int n, int size)
|
|
{
|
|
return xsc_buf_offset(&buf->buf, n * size);
|
|
}
|
|
|
|
static void *get_cqe(struct xsc_ib_cq *cq, int n)
|
|
{
|
|
return get_cqe_from_buf(&cq->buf, n, cq->xcq.cqe_sz);
|
|
}
|
|
|
|
static void *get_sw_cqe(struct xsc_ib_cq *cq, int n)
|
|
{
|
|
struct xsc_cqe64 *cqe64;
|
|
|
|
cqe64 = (struct xsc_cqe64 *)get_cqe(cq, n & cq->ibcq.cqe);
|
|
|
|
return ((cqe64->owner & XSC_CQE_OWNER_MASK) ^
|
|
!!(n & (cq->ibcq.cqe + 1))) ? NULL : cqe64;
|
|
}
|
|
|
|
static inline void handle_good_req(struct ib_wc *wc,
|
|
struct xsc_cqe *cqe,
|
|
u8 opcode)
|
|
{
|
|
wc->opcode = xsc_cqe_opcode[opcode];
|
|
if (opcode == XSC_OPCODE_RDMA_REQ_READ)
|
|
wc->byte_len = RD_LE_32(cqe->msg_len);
|
|
wc->status = IB_WC_SUCCESS;
|
|
}
|
|
|
|
static void handle_responder(struct ib_wc *wc, struct xsc_cqe *cqe,
|
|
struct xsc_ib_qp *qp, u8 opcode)
|
|
{
|
|
struct xsc_ib_wq *wq = &qp->rq;
|
|
u16 idx;
|
|
|
|
wc->byte_len = RD_LE_32(cqe->msg_len);
|
|
wc->opcode = xsc_cqe_opcode[opcode];
|
|
wc->status = IB_WC_SUCCESS;
|
|
|
|
idx = wq->tail & (wq->wqe_cnt - 1);
|
|
wc->wr_id = wq->wrid[idx];
|
|
++wq->tail;
|
|
}
|
|
|
|
struct ib_mad_list_head {
|
|
struct list_head list;
|
|
struct ib_cqe cqe;
|
|
struct ib_mad_queue *mad_queue;
|
|
};
|
|
|
|
struct ib_mad_private_header {
|
|
struct ib_mad_list_head mad_list;
|
|
struct ib_mad_recv_wc recv_wc;
|
|
struct ib_wc wc;
|
|
u64 mapping;
|
|
} __packed;
|
|
|
|
struct ib_mad_private {
|
|
struct ib_mad_private_header header;
|
|
size_t mad_size;
|
|
struct ib_grh grh;
|
|
u8 mad[];
|
|
} __packed;
|
|
|
|
static void *get_wqe(struct xsc_ib_qp *qp, int offset)
|
|
{
|
|
return xsc_buf_offset(&qp->buf, offset);
|
|
}
|
|
|
|
static void *get_recv_wqe(struct xsc_ib_qp *qp, int n)
|
|
{
|
|
return get_wqe(qp, qp->rq.offset + (n << qp->rq.wqe_shift));
|
|
}
|
|
|
|
static void *get_seg_wqe(void *first, int n)
|
|
{
|
|
return first + (n << XSC_BASE_WQE_SHIFT);
|
|
}
|
|
|
|
static void xsc_handle_rdma_mad_resp_recv(struct xsc_ib_cq *cq,
|
|
struct xsc_ib_qp **cur_qp,
|
|
struct ib_wc *wc,
|
|
struct xsc_cqe *cqe,
|
|
u8 opcode)
|
|
{
|
|
struct xsc_ib_dev *dev = to_mdev(cq->ibcq.device);
|
|
void *recv;
|
|
struct xsc_wqe_data_seg *data_seg;
|
|
struct iphdr *ip4h = NULL;
|
|
struct udphdr *udph;
|
|
struct ib_unpacked_eth *eth;
|
|
struct ib_unpacked_vlan *vlan;
|
|
struct ib_grh *grh;
|
|
struct ib_mad *mad;
|
|
struct rxe_bth *bth;
|
|
struct rxe_deth *deth;
|
|
unsigned int pading_sz = 0;
|
|
struct xsc_ib_wq *wq;
|
|
int idx;
|
|
|
|
wq = &(*cur_qp)->rq;
|
|
idx = wq->tail & (wq->wqe_cnt - 1);
|
|
|
|
handle_responder(wc, cqe, *cur_qp, opcode);
|
|
|
|
data_seg = get_seg_wqe(get_recv_wqe(*cur_qp, idx), 0);
|
|
recv = phys_to_virt(dma_to_phys(dev->ib_dev.dma_device, data_seg->va));
|
|
|
|
eth = (struct ib_unpacked_eth *)recv;
|
|
grh = (struct ib_grh *)recv;
|
|
if (eth->type == htons(ETH_P_8021Q)) {
|
|
vlan = (struct ib_unpacked_vlan *)(eth + 1);
|
|
ip4h = (struct iphdr *)(vlan + 1);
|
|
|
|
wc->vlan_id = ntohs(vlan->tag) & 0x0fff;
|
|
wc->sl = (ntohs(vlan->tag) >> 13) & 0x7;
|
|
wc->wc_flags |= IB_WC_WITH_VLAN;
|
|
} else {
|
|
ip4h = (struct iphdr *)(eth + 1);
|
|
}
|
|
udph = (struct udphdr *)(ip4h + 1);
|
|
bth = (struct rxe_bth *)(udph + 1);
|
|
deth = (struct rxe_deth *)(bth + 1);
|
|
mad = (struct ib_mad *)(deth + 1);
|
|
|
|
memcpy(grh + 1, mad, sizeof(*mad));
|
|
pading_sz = sizeof(*grh) - sizeof(*ip4h);
|
|
memmove((u8 *)(grh + 1) - sizeof(*ip4h), ip4h, sizeof(*ip4h));
|
|
memset(grh, 0, pading_sz);
|
|
|
|
wc->wc_flags |= IB_WC_GRH;
|
|
|
|
xsc_ib_dbg(dev, "recv cqe idx:%u, len:%u\n", wq->tail, wc->byte_len);
|
|
wc->status = IB_WC_SUCCESS;
|
|
}
|
|
|
|
static int xsc_poll_one(struct xsc_ib_cq *cq,
|
|
struct xsc_ib_qp **cur_qp,
|
|
struct ib_wc *wc)
|
|
{
|
|
struct xsc_ib_dev *dev = to_mdev(cq->ibcq.device);
|
|
struct xsc_core_qp *xqp;
|
|
struct xsc_ib_wq *wq;
|
|
u8 opcode;
|
|
u32 qpn;
|
|
int idx;
|
|
struct xsc_cqe *cqe;
|
|
u32 *p = NULL;
|
|
|
|
cqe = get_sw_cqe(cq, cq->xcq.cons_index);
|
|
if (!cqe)
|
|
return -EAGAIN;
|
|
|
|
++cq->xcq.cons_index;
|
|
|
|
/* Make sure we read CQ entry contents after we've checked the
|
|
* ownership bit.
|
|
*/
|
|
rmb();
|
|
|
|
p = (u32 *)cqe;
|
|
|
|
qpn = cqe->qp_id;
|
|
qpn = le32_to_cpu(qpn);
|
|
if (!*cur_qp || (qpn != (*cur_qp)->ibqp.qp_num)) {
|
|
/* We do not have to take the QP table lock here,
|
|
* because CQs will be locked while QPs are removed
|
|
* from the table.
|
|
*/
|
|
xqp = __xsc_qp_lookup(dev->xdev, qpn);
|
|
if (unlikely(!xqp)) {
|
|
xsc_ib_warn(dev, "CQE@CQ %d for unknown QPN %d\n",
|
|
cq->xcq.cqn, qpn);
|
|
return -EINVAL;
|
|
}
|
|
|
|
*cur_qp = to_xibqp(xqp);
|
|
}
|
|
|
|
memset(wc, 0, sizeof(*wc));
|
|
wc->qp = &(*cur_qp)->ibqp;
|
|
opcode = xsc_get_cqe_opcode(cqe);
|
|
switch (opcode) {
|
|
case XSC_OPCODE_RDMA_REQ_SEND:
|
|
case XSC_OPCODE_RDMA_REQ_WRITE:
|
|
case XSC_OPCODE_RDMA_REQ_READ:
|
|
case XSC_OPCODE_RDMA_MAD_REQ_SEND:
|
|
wq = &(*cur_qp)->sq;
|
|
idx = cqe->wqe_id >> (wq->wqe_shift - XSC_BASE_WQE_SHIFT);
|
|
idx &= (wq->wqe_cnt - 1);
|
|
handle_good_req(wc, cqe, opcode);
|
|
wc->wr_id = wq->wrid[idx];
|
|
wq->tail = wq->wqe_head[idx] + 1;
|
|
xsc_ib_dbg(dev, "wqeid:%u, wq tail:%u qpn:%u\n", idx, wq->tail, qpn);
|
|
wc->status = IB_WC_SUCCESS;
|
|
break;
|
|
case XSC_OPCODE_RDMA_RSP_RECV:
|
|
wq = &(*cur_qp)->rq;
|
|
handle_responder(wc, cqe, *cur_qp, opcode);
|
|
xsc_ib_dbg(dev, "recv cqe idx:%u, len:%u, qpn:%u\n", wq->tail, wc->byte_len, qpn);
|
|
wc->status = IB_WC_SUCCESS;
|
|
break;
|
|
|
|
case XSC_OPCODE_RDMA_MAD_RSP_RECV:
|
|
xsc_ib_dbg(dev, "recv MAD, qpn:%u\n", qpn);
|
|
xsc_handle_rdma_mad_resp_recv(cq, cur_qp, wc, cqe, opcode);
|
|
break;
|
|
|
|
default:
|
|
xsc_ib_err(dev, "completion error\n%08x %08x %08x %08x %08x %08x\n",
|
|
p[0], p[1], p[2], p[3], p[5], p[6]);
|
|
wc->status = IB_WC_GENERAL_ERR;
|
|
wc->wr_id = 0;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xsc_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
|
|
{
|
|
struct xsc_ib_cq *cq = to_xcq(ibcq);
|
|
struct xsc_core_cq *xcq = &cq->xcq;
|
|
struct xsc_ib_qp *cur_qp = NULL;
|
|
int npolled = 0;
|
|
int err = 0;
|
|
unsigned long flags;
|
|
u32 next_cid;
|
|
|
|
spin_lock_irqsave(&cq->lock, flags);
|
|
next_cid = xcq->cons_index;
|
|
|
|
for (npolled = 0; npolled < num_entries; npolled++) {
|
|
err = xsc_poll_one(cq, &cur_qp, wc + npolled);
|
|
if (err)
|
|
break;
|
|
}
|
|
|
|
/* make sure cqe read out before update ci */
|
|
rmb();
|
|
|
|
if (next_cid != xcq->cons_index)
|
|
xsc_cq_set_ci(xcq);
|
|
|
|
spin_unlock_irqrestore(&cq->lock, flags);
|
|
|
|
return npolled;
|
|
}
|
|
|
|
int xsc_cqe_is_empty(struct xsc_ib_cq *cq)
|
|
{
|
|
struct xsc_cqe *cqe = get_sw_cqe(cq, cq->xcq.cons_index);
|
|
|
|
if (!cqe)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xsc_ib_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
|
|
{
|
|
#ifdef MSIX_SUPPORT
|
|
union xsc_cq_doorbell db;
|
|
struct xsc_ib_cq *xcq = to_xcq(ibcq);
|
|
struct xsc_core_cq *cq = &xcq->xcq;
|
|
int ret = 0;
|
|
unsigned long irq_flags;
|
|
|
|
spin_lock_irqsave(&xcq->lock, irq_flags);
|
|
db.val = 0;
|
|
db.cq_next_cid = cq->cons_index;
|
|
db.cq_id = cq->cqn;
|
|
if (flags & IB_CQ_NEXT_COMP)
|
|
db.arm = 0;
|
|
else if (flags & IB_CQ_SOLICITED)
|
|
db.arm = 1;/* arm next:0 arm solicited:1 */
|
|
|
|
if ((flags & IB_CQ_REPORT_MISSED_EVENTS) && (!xsc_cqe_is_empty(xcq))) {
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* make sure val write to memory done */
|
|
wmb();
|
|
writel(db.val, REG_ADDR(cq->dev, cq->arm_db));
|
|
out:
|
|
spin_unlock_irqrestore(&xcq->lock, irq_flags);
|
|
return ret;
|
|
#else
|
|
if ((flags & IB_CQ_REPORT_MISSED_EVENTS))
|
|
return 1;
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static int alloc_cq_buf(struct xsc_ib_dev *dev, struct xsc_ib_cq_buf *buf,
|
|
int nent, int cqe_size)
|
|
{
|
|
int err;
|
|
|
|
err = xsc_buf_alloc(dev->xdev, nent * cqe_size,
|
|
PAGE_SIZE, &buf->buf);
|
|
if (err)
|
|
return err;
|
|
|
|
buf->cqe_size = cqe_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void free_cq_buf(struct xsc_ib_dev *dev, struct xsc_ib_cq_buf *buf)
|
|
{
|
|
xsc_buf_free(dev->xdev, &buf->buf);
|
|
}
|
|
|
|
static int create_cq_user(struct xsc_ib_dev *dev, struct ib_udata *udata,
|
|
struct ib_ucontext *context, struct xsc_ib_cq *cq,
|
|
int entries, struct xsc_create_cq_mbox_in **cqb,
|
|
int *cqe_size, int *index, int *inlen)
|
|
{
|
|
struct xsc_ib_create_cq ucmd;
|
|
int page_shift;
|
|
int npages;
|
|
int ncont;
|
|
int err;
|
|
int log_cq_sz;
|
|
int hw_npages;
|
|
|
|
if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd)))
|
|
return -EFAULT;
|
|
|
|
*cqe_size = ucmd.cqe_size;
|
|
|
|
cq->buf.umem = ib_umem_get(&dev->ib_dev, ucmd.buf_addr,
|
|
entries * ucmd.cqe_size,
|
|
IB_ACCESS_LOCAL_WRITE);
|
|
if (IS_ERR(cq->buf.umem)) {
|
|
err = PTR_ERR(cq->buf.umem);
|
|
return err;
|
|
}
|
|
|
|
xsc_ib_cont_pages(cq->buf.umem, ucmd.buf_addr, &npages, &page_shift,
|
|
&ncont, NULL);
|
|
if (ncont != npages) {
|
|
xsc_ib_dbg(dev, "bad page_shift:%d, ncont:%d\n", page_shift, ncont);
|
|
/* amber doesn't support compound pages */
|
|
page_shift = PAGE_SHIFT;
|
|
ncont = npages;
|
|
xsc_ib_dbg(dev, "overwrite to page_shift:%d, ncont:%d\n", page_shift, ncont);
|
|
}
|
|
log_cq_sz = ilog2(entries);
|
|
hw_npages = DIV_ROUND_UP((1 << log_cq_sz) * sizeof(struct xsc_cqe), PAGE_SIZE_4K);
|
|
xsc_ib_dbg(dev, "addr 0x%llx, entries %d, size %u, npages %d, page_shift %d, ncont %d, hw_npages %d\n",
|
|
ucmd.buf_addr, entries, ucmd.cqe_size, npages, page_shift, ncont, hw_npages);
|
|
|
|
*inlen = sizeof(**cqb) + sizeof(*(*cqb)->pas) * hw_npages;
|
|
*cqb = xsc_vzalloc(*inlen);
|
|
if (!*cqb) {
|
|
err = -ENOMEM;
|
|
goto err_umem;
|
|
}
|
|
xsc_ib_populate_pas(dev, cq->buf.umem, page_shift, (*cqb)->pas, hw_npages, true);
|
|
(*cqb)->ctx.pa_num = cpu_to_be16(hw_npages);
|
|
|
|
return 0;
|
|
|
|
err_umem:
|
|
ib_umem_release(cq->buf.umem);
|
|
return err;
|
|
}
|
|
|
|
static void destroy_cq_user(struct xsc_ib_cq *cq, struct ib_udata *udata)
|
|
{
|
|
ib_umem_release(cq->buf.umem);
|
|
}
|
|
|
|
static int create_cq_kernel(struct xsc_ib_dev *dev, struct xsc_ib_cq *cq,
|
|
int entries, int cqe_size,
|
|
struct xsc_create_cq_mbox_in **cqb,
|
|
int *index, int *inlen)
|
|
{
|
|
int err;
|
|
int i = 0;
|
|
struct xsc_cqe *cqe = NULL;
|
|
int hw_npages;
|
|
|
|
cq->xcq.cqe_sz = cqe_size;
|
|
|
|
err = alloc_cq_buf(dev, &cq->buf, entries, cqe_size);
|
|
if (err)
|
|
return err;
|
|
|
|
for (i = 0; i < entries; i++) {
|
|
cqe = (struct xsc_cqe *)get_cqe(cq, i);
|
|
cqe->owner = 1;
|
|
}
|
|
|
|
hw_npages = DIV_ROUND_UP(entries * cqe_size, PAGE_SIZE_4K);
|
|
*inlen = sizeof(**cqb) + sizeof(*(*cqb)->pas) * hw_npages;
|
|
*cqb = xsc_vzalloc(*inlen);
|
|
if (!*cqb) {
|
|
err = -ENOMEM;
|
|
goto err_buf;
|
|
}
|
|
xsc_fill_page_array(&cq->buf.buf, (*cqb)->pas, hw_npages);
|
|
(*cqb)->ctx.pa_num = cpu_to_be16(hw_npages);
|
|
|
|
return 0;
|
|
|
|
err_buf:
|
|
free_cq_buf(dev, &cq->buf);
|
|
return err;
|
|
}
|
|
|
|
static void destroy_cq_kernel(struct xsc_ib_dev *dev, struct xsc_ib_cq *cq)
|
|
{
|
|
free_cq_buf(dev, &cq->buf);
|
|
}
|
|
|
|
xsc_ib_create_cq_def()
|
|
{
|
|
struct ib_device *ibdev = ibcq->device;
|
|
int entries = attr->cqe;
|
|
int vector = attr->comp_vector;
|
|
struct xsc_create_cq_mbox_in *cqb = NULL;
|
|
struct xsc_ib_dev *dev = to_mdev(ibdev);
|
|
struct xsc_ib_cq *cq;
|
|
int index;
|
|
int inlen;
|
|
int cqe_size;
|
|
int irqn;
|
|
int err;
|
|
unsigned int eqn;
|
|
|
|
entries = roundup_pow_of_two(entries + 1);
|
|
|
|
xsc_ib_dbg(dev, "entries:%d, vector:%d, max_cqes:%d\n", entries, vector,
|
|
dev->xdev->caps.max_cqes);
|
|
|
|
if (entries > dev->xdev->caps.max_cqes)
|
|
entries = dev->xdev->caps.max_cqes;
|
|
cq = to_xcq(ibcq);
|
|
cq->ibcq.cqe = entries - 1;
|
|
mutex_init(&cq->resize_mutex);
|
|
spin_lock_init(&cq->lock);
|
|
cq->resize_buf = NULL;
|
|
cq->resize_umem = NULL;
|
|
|
|
if (udata) {
|
|
err = create_cq_user(dev, udata, NULL, cq, entries,
|
|
&cqb, &cqe_size, &index, &inlen);
|
|
if (err)
|
|
goto err_create;
|
|
} else {
|
|
cqe_size = sizeof(struct xsc_cqe);
|
|
err = create_cq_kernel(dev, cq, entries, cqe_size, &cqb, &index, &inlen);
|
|
if (err)
|
|
goto err_create;
|
|
}
|
|
|
|
cq->cqe_size = cqe_size;
|
|
cqb->ctx.log_cq_sz = ilog2(entries);
|
|
cqb->ctx.glb_func_id = cpu_to_be16(dev->xdev->glb_func_id);
|
|
|
|
err = xsc_vector2eqn(dev->xdev, vector, &eqn, &irqn);
|
|
if (err)
|
|
goto err_cqb;
|
|
|
|
cqb->ctx.eqn = eqn;
|
|
cqb->ctx.eqn = cpu_to_be16(cqb->ctx.eqn);
|
|
|
|
err = xsc_core_create_cq(dev->xdev, &cq->xcq, cqb, inlen);
|
|
if (err)
|
|
goto err_cqb;
|
|
|
|
xsc_ib_dbg(dev, "cqn 0x%x\n", cq->xcq.cqn);
|
|
cq->xcq.irqn = irqn;
|
|
cq->xcq.comp = xsc_ib_cq_comp;
|
|
cq->xcq.event = xsc_ib_cq_event;
|
|
|
|
if (udata) {
|
|
if (ib_copy_to_udata(udata, &cq->xcq.cqn, sizeof(__u32))) {
|
|
err = -EFAULT;
|
|
goto err_cmd;
|
|
}
|
|
}
|
|
|
|
xsc_vfree(cqb);
|
|
|
|
return 0;
|
|
|
|
err_cmd:
|
|
xsc_core_destroy_cq(dev->xdev, &cq->xcq);
|
|
|
|
err_cqb:
|
|
xsc_vfree(cqb);
|
|
if (udata)
|
|
destroy_cq_user(cq, udata);
|
|
else
|
|
destroy_cq_kernel(dev, cq);
|
|
|
|
err_create:
|
|
return RET_VALUE(err);
|
|
}
|
|
|
|
xsc_ib_destroy_cq_def()
|
|
{
|
|
struct xsc_ib_dev *dev = to_mdev(cq->device);
|
|
struct xsc_ib_cq *xcq = to_xcq(cq);
|
|
|
|
xsc_core_destroy_cq(dev->xdev, &xcq->xcq);
|
|
if (udata)
|
|
destroy_cq_user(xcq, udata);
|
|
else
|
|
destroy_cq_kernel(dev, xcq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int is_equal_rsn(struct xsc_cqe64 *cqe, u32 rsn)
|
|
{
|
|
u32 qpn = le32_to_cpu(cqe->qp_id);
|
|
return rsn == qpn;
|
|
}
|
|
|
|
void __xsc_ib_cq_clean(struct xsc_ib_cq *cq, u32 rsn)
|
|
{
|
|
struct xsc_cqe64 *cqe, *dest;
|
|
u32 prod_index;
|
|
int nfreed = 0;
|
|
u8 owner_bit;
|
|
|
|
if (!cq)
|
|
return;
|
|
|
|
/* First we need to find the current producer index, so we
|
|
* know where to start cleaning from. It doesn't matter if HW
|
|
* adds new entries after this loop -- the QP we're worried
|
|
* about is already in RESET, so the new entries won't come
|
|
* from our QP and therefore don't need to be checked.
|
|
*/
|
|
for (prod_index = cq->xcq.cons_index; get_sw_cqe(cq, prod_index); prod_index++)
|
|
if (prod_index == cq->xcq.cons_index + cq->ibcq.cqe)
|
|
break;
|
|
|
|
/* Now sweep backwards through the CQ, removing CQ entries
|
|
* that match our QP by copying older entries on top of them.
|
|
*/
|
|
while ((int)(--prod_index) - (int)cq->xcq.cons_index >= 0) {
|
|
cqe = get_cqe(cq, prod_index & cq->ibcq.cqe);
|
|
if (is_equal_rsn(cqe, rsn)) {
|
|
++nfreed;
|
|
} else if (nfreed) {
|
|
dest = get_cqe(cq, (prod_index + nfreed) & cq->ibcq.cqe);
|
|
owner_bit = dest->owner & XSC_CQE_OWNER_MASK;
|
|
memcpy(dest, cqe, cq->xcq.cqe_sz);
|
|
dest->owner = owner_bit |
|
|
(dest->owner & ~XSC_CQE_OWNER_MASK);
|
|
}
|
|
}
|
|
|
|
if (nfreed) {
|
|
cq->xcq.cons_index += nfreed;
|
|
/* Make sure update of buffer contents is done before
|
|
* updating consumer index.
|
|
*/
|
|
wmb();
|
|
xsc_cq_set_ci(&cq->xcq);
|
|
}
|
|
}
|
|
|
|
void xsc_ib_cq_clean(struct xsc_ib_cq *cq, u32 qpn)
|
|
{
|
|
if (!cq)
|
|
return;
|
|
|
|
spin_lock_irq(&cq->lock);
|
|
__xsc_ib_cq_clean(cq, qpn);
|
|
spin_unlock_irq(&cq->lock);
|
|
}
|
|
|