/* QLogic qedr NIC Driver
* Copyright (c) 2015-2016 QLogic Corporation
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and /or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <linux/dma-mapping.h>
#include <linux/crc32.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/udp.h>
#include <linux/iommu.h>
#include <rdma/ib_verbs.h>
#include <rdma/ib_user_verbs.h>
#include <rdma/iw_cm.h>
#include <rdma/ib_umem.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>
#include <rdma/uverbs_ioctl.h>
#include <linux/qed/common_hsi.h>
#include "qedr_hsi_rdma.h"
#include <linux/qed/qed_if.h>
#include "qedr.h"
#include "verbs.h"
#include <rdma/qedr-abi.h>
#include "qedr_roce_cm.h"
#include "qedr_iw_cm.h"
#define QEDR_SRQ_WQE_ELEM_SIZE sizeof (union rdma_srq_elm)
#define RDMA_MAX_SGE_PER_SRQ (4)
#define RDMA_MAX_SRQ_WQE_SIZE (RDMA_MAX_SGE_PER_SRQ + 1)
#define DB_ADDR_SHIFT(addr) ((addr) << DB_PWM_ADDR_OFFSET_SHIFT)
enum {
QEDR_USER_MMAP_IO_WC = 0,
QEDR_USER_MMAP_PHYS_PAGE,
};
static inline int qedr_ib_copy_to_udata(struct ib_udata *udata, void *src,
size_t len)
{
size_t min_len = min_t(size_t, len, udata->outlen);
return ib_copy_to_udata(udata, src, min_len);
}
int qedr_query_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey)
{
if (index >= QEDR_ROCE_PKEY_TABLE_LEN)
return -EINVAL;
*pkey = QEDR_ROCE_PKEY_DEFAULT;
return 0;
}
int qedr_iw_query_gid(struct ib_device *ibdev, u32 port,
int index, union ib_gid *sgid)
{
struct qedr_dev *dev = get_qedr_dev(ibdev);
memset(sgid->raw, 0, sizeof (sgid->raw));
ether_addr_copy(sgid->raw, dev->ndev->dev_addr);
DP_DEBUG(dev, QEDR_MSG_INIT, "QUERY sgid[%d]=%llx:%llx\n" , index,
sgid->global.interface_id, sgid->global.subnet_prefix);
return 0;
}
int qedr_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr)
{
struct qedr_dev *dev = get_qedr_dev(ibsrq->device);
struct qedr_device_attr *qattr = &dev->attr;
struct qedr_srq *srq = get_qedr_srq(ibsrq);
srq_attr->srq_limit = srq->srq_limit;
srq_attr->max_wr = qattr->max_srq_wr;
srq_attr->max_sge = qattr->max_sge;
return 0;
}
int qedr_query_device(struct ib_device *ibdev,
struct ib_device_attr *attr, struct ib_udata *udata)
{
struct qedr_dev *dev = get_qedr_dev(ibdev);
struct qedr_device_attr *qattr = &dev->attr;
if (!dev->rdma_ctx) {
DP_ERR(dev,
"qedr_query_device called with invalid params rdma_ctx=%p\n" ,
dev->rdma_ctx);
return -EINVAL;
}
memset(attr, 0, sizeof (*attr));
attr->fw_ver = qattr->fw_ver;
attr->sys_image_guid = qattr->sys_image_guid;
attr->max_mr_size = qattr->max_mr_size;
attr->page_size_cap = qattr->page_size_caps;
attr->vendor_id = qattr->vendor_id;
attr->vendor_part_id = qattr->vendor_part_id;
attr->hw_ver = qattr->hw_ver;
attr->max_qp = qattr->max_qp;
attr->max_qp_wr = max_t(u32, qattr->max_sqe, qattr->max_rqe);
attr->device_cap_flags = IB_DEVICE_CURR_QP_STATE_MOD |
IB_DEVICE_RC_RNR_NAK_GEN |
IB_DEVICE_MEM_MGT_EXTENSIONS;
attr->kernel_cap_flags = IBK_LOCAL_DMA_LKEY;
if (!rdma_protocol_iwarp(&dev->ibdev, 1))
attr->device_cap_flags |= IB_DEVICE_XRC;
attr->max_send_sge = qattr->max_sge;
attr->max_recv_sge = qattr->max_sge;
attr->max_sge_rd = qattr->max_sge;
attr->max_cq = qattr->max_cq;
attr->max_cqe = qattr->max_cqe;
attr->max_mr = qattr->max_mr;
attr->max_mw = qattr->max_mw;
attr->max_pd = qattr->max_pd;
attr->atomic_cap = dev->atomic_cap;
attr->max_qp_init_rd_atom =
1 << (fls(qattr->max_qp_req_rd_atomic_resc) - 1);
attr->max_qp_rd_atom =
min(1 << (fls(qattr->max_qp_resp_rd_atomic_resc) - 1),
attr->max_qp_init_rd_atom);
attr->max_srq = qattr->max_srq;
attr->max_srq_sge = qattr->max_srq_sge;
attr->max_srq_wr = qattr->max_srq_wr;
attr->local_ca_ack_delay = qattr->dev_ack_delay;
attr->max_fast_reg_page_list_len = qattr->max_mr / 8;
attr->max_pkeys = qattr->max_pkey;
attr->max_ah = qattr->max_ah;
return 0;
}
static inline void get_link_speed_and_width(int speed, u16 *ib_speed,
u8 *ib_width)
{
switch (speed) {
case 1000:
*ib_speed = IB_SPEED_SDR;
*ib_width = IB_WIDTH_1X;
break ;
case 10000:
*ib_speed = IB_SPEED_QDR;
*ib_width = IB_WIDTH_1X;
break ;
case 20000:
*ib_speed = IB_SPEED_DDR;
*ib_width = IB_WIDTH_4X;
break ;
case 25000:
*ib_speed = IB_SPEED_EDR;
*ib_width = IB_WIDTH_1X;
break ;
case 40000:
*ib_speed = IB_SPEED_QDR;
*ib_width = IB_WIDTH_4X;
break ;
case 50000:
*ib_speed = IB_SPEED_HDR;
*ib_width = IB_WIDTH_1X;
break ;
case 100000:
*ib_speed = IB_SPEED_EDR;
*ib_width = IB_WIDTH_4X;
break ;
default :
/* Unsupported */
*ib_speed = IB_SPEED_SDR;
*ib_width = IB_WIDTH_1X;
}
}
int qedr_query_port(struct ib_device *ibdev, u32 port,
struct ib_port_attr *attr)
{
struct qedr_dev *dev;
struct qed_rdma_port *rdma_port;
dev = get_qedr_dev(ibdev);
if (!dev->rdma_ctx) {
DP_ERR(dev, "rdma_ctx is NULL\n" );
return -EINVAL;
}
rdma_port = dev->ops->rdma_query_port(dev->rdma_ctx);
/* *attr being zeroed by the caller, avoid zeroing it here */
if (rdma_port->port_state == QED_RDMA_PORT_UP) {
attr->state = IB_PORT_ACTIVE;
attr->phys_state = IB_PORT_PHYS_STATE_LINK_UP;
} else {
attr->state = IB_PORT_DOWN;
attr->phys_state = IB_PORT_PHYS_STATE_DISABLED;
}
attr->max_mtu = IB_MTU_4096;
attr->lid = 0;
attr->lmc = 0;
attr->sm_lid = 0;
attr->sm_sl = 0;
attr->ip_gids = true ;
if (rdma_protocol_iwarp(&dev->ibdev, 1)) {
attr->active_mtu = iboe_get_mtu(dev->iwarp_max_mtu);
attr->gid_tbl_len = 1;
} else {
attr->active_mtu = iboe_get_mtu(dev->ndev->mtu);
attr->gid_tbl_len = QEDR_MAX_SGID;
attr->pkey_tbl_len = QEDR_ROCE_PKEY_TABLE_LEN;
}
attr->bad_pkey_cntr = rdma_port->pkey_bad_counter;
attr->qkey_viol_cntr = 0;
get_link_speed_and_width(rdma_port->link_speed,
&attr->active_speed, &attr->active_width);
attr->max_msg_sz = rdma_port->max_msg_size;
attr->max_vl_num = 4;
return 0;
}
int qedr_alloc_ucontext(struct ib_ucontext *uctx, struct ib_udata *udata)
{
struct ib_device *ibdev = uctx->device;
int rc;
struct qedr_ucontext *ctx = get_qedr_ucontext(uctx);
struct qedr_alloc_ucontext_resp uresp = {};
struct qedr_alloc_ucontext_req ureq = {};
struct qedr_dev *dev = get_qedr_dev(ibdev);
struct qed_rdma_add_user_out_params oparams;
struct qedr_user_mmap_entry *entry;
if (!udata)
return -EFAULT;
if (udata->inlen) {
rc = ib_copy_from_udata(&ureq, udata,
min(sizeof (ureq), udata->inlen));
if (rc) {
DP_ERR(dev, "Problem copying data from user space\n" );
return -EFAULT;
}
ctx->edpm_mode = !!(ureq.context_flags &
QEDR_ALLOC_UCTX_EDPM_MODE);
ctx->db_rec = !!(ureq.context_flags & QEDR_ALLOC_UCTX_DB_REC);
}
rc = dev->ops->rdma_add_user(dev->rdma_ctx, &oparams);
if (rc) {
DP_ERR(dev,
"failed to allocate a DPI for a new RoCE application, rc=%d. To overcome this consider to increase the number of DPIs, increase the doorbell BAR size or just close unnecessary RoCE applications. In order to increase the number of DPIs consult the qedr readme\n" ,
rc);
return rc;
}
ctx->dpi = oparams.dpi;
ctx->dpi_addr = oparams.dpi_addr;
ctx->dpi_phys_addr = oparams.dpi_phys_addr;
ctx->dpi_size = oparams.dpi_size;
entry = kzalloc(sizeof (*entry), GFP_KERNEL);
if (!entry) {
rc = -ENOMEM;
goto err;
}
entry->io_address = ctx->dpi_phys_addr;
entry->length = ctx->dpi_size;
entry->mmap_flag = QEDR_USER_MMAP_IO_WC;
entry->dpi = ctx->dpi;
entry->dev = dev;
rc = rdma_user_mmap_entry_insert(uctx, &entry->rdma_entry,
ctx->dpi_size);
if (rc) {
kfree(entry);
goto err;
}
ctx->db_mmap_entry = &entry->rdma_entry;
if (!dev->user_dpm_enabled)
uresp.dpm_flags = 0;
else if (rdma_protocol_iwarp(&dev->ibdev, 1))
uresp.dpm_flags = QEDR_DPM_TYPE_IWARP_LEGACY;
else
uresp.dpm_flags = QEDR_DPM_TYPE_ROCE_ENHANCED |
QEDR_DPM_TYPE_ROCE_LEGACY |
QEDR_DPM_TYPE_ROCE_EDPM_MODE;
if (ureq.context_flags & QEDR_SUPPORT_DPM_SIZES) {
uresp.dpm_flags |= QEDR_DPM_SIZES_SET;
uresp.ldpm_limit_size = QEDR_LDPM_MAX_SIZE;
uresp.edpm_trans_size = QEDR_EDPM_TRANS_SIZE;
uresp.edpm_limit_size = QEDR_EDPM_MAX_SIZE;
}
uresp.wids_enabled = 1;
uresp.wid_count = oparams.wid_count;
uresp.db_pa = rdma_user_mmap_get_offset(ctx->db_mmap_entry);
uresp.db_size = ctx->dpi_size;
uresp.max_send_wr = dev->attr.max_sqe;
uresp.max_recv_wr = dev->attr.max_rqe;
uresp.max_srq_wr = dev->attr.max_srq_wr;
uresp.sges_per_send_wr = QEDR_MAX_SQE_ELEMENTS_PER_SQE;
uresp.sges_per_recv_wr = QEDR_MAX_RQE_ELEMENTS_PER_RQE;
uresp.sges_per_srq_wr = dev->attr.max_srq_sge;
uresp.max_cqes = QEDR_MAX_CQES;
rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof (uresp));
if (rc)
goto err;
ctx->dev = dev;
DP_DEBUG(dev, QEDR_MSG_INIT, "Allocating user context %p\n" ,
&ctx->ibucontext);
return 0;
err:
if (!ctx->db_mmap_entry)
dev->ops->rdma_remove_user(dev->rdma_ctx, ctx->dpi);
else
rdma_user_mmap_entry_remove(ctx->db_mmap_entry);
return rc;
}
void qedr_dealloc_ucontext(struct ib_ucontext *ibctx)
{
struct qedr_ucontext *uctx = get_qedr_ucontext(ibctx);
DP_DEBUG(uctx->dev, QEDR_MSG_INIT, "Deallocating user context %p\n" ,
uctx);
rdma_user_mmap_entry_remove(uctx->db_mmap_entry);
}
void qedr_mmap_free(struct rdma_user_mmap_entry *rdma_entry)
{
struct qedr_user_mmap_entry *entry = get_qedr_mmap_entry(rdma_entry);
struct qedr_dev *dev = entry->dev;
if (entry->mmap_flag == QEDR_USER_MMAP_PHYS_PAGE)
free_page((unsigned long )entry->address);
else if (entry->mmap_flag == QEDR_USER_MMAP_IO_WC)
dev->ops->rdma_remove_user(dev->rdma_ctx, entry->dpi);
kfree(entry);
}
int qedr_mmap(struct ib_ucontext *ucontext, struct vm_area_struct *vma)
{
struct ib_device *dev = ucontext->device;
size_t length = vma->vm_end - vma->vm_start;
struct rdma_user_mmap_entry *rdma_entry;
struct qedr_user_mmap_entry *entry;
int rc = 0;
u64 pfn;
ibdev_dbg(dev,
"start %#lx, end %#lx, length = %#zx, pgoff = %#lx\n" ,
vma->vm_start, vma->vm_end, length, vma->vm_pgoff);
rdma_entry = rdma_user_mmap_entry_get(ucontext, vma);
if (!rdma_entry) {
ibdev_dbg(dev, "pgoff[%#lx] does not have valid entry\n" ,
vma->vm_pgoff);
return -EINVAL;
}
entry = get_qedr_mmap_entry(rdma_entry);
ibdev_dbg(dev,
"Mapping address[%#llx], length[%#zx], mmap_flag[%d]\n" ,
entry->io_address, length, entry->mmap_flag);
switch (entry->mmap_flag) {
case QEDR_USER_MMAP_IO_WC:
pfn = entry->io_address >> PAGE_SHIFT;
rc = rdma_user_mmap_io(ucontext, vma, pfn, length,
pgprot_writecombine(vma->vm_page_prot),
rdma_entry);
break ;
case QEDR_USER_MMAP_PHYS_PAGE:
rc = vm_insert_page(vma, vma->vm_start,
virt_to_page(entry->address));
break ;
default :
rc = -EINVAL;
}
if (rc)
ibdev_dbg(dev,
"Couldn't mmap address[%#llx] length[%#zx] mmap_flag[%d] err[%d]\n" ,
entry->io_address, length, entry->mmap_flag, rc);
rdma_user_mmap_entry_put(rdma_entry);
return rc;
}
int qedr_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
{
struct ib_device *ibdev = ibpd->device;
struct qedr_dev *dev = get_qedr_dev(ibdev);
struct qedr_pd *pd = get_qedr_pd(ibpd);
u16 pd_id;
int rc;
DP_DEBUG(dev, QEDR_MSG_INIT, "Function called from: %s\n" ,
udata ? "User Lib" : "Kernel" );
if (!dev->rdma_ctx) {
DP_ERR(dev, "invalid RDMA context\n" );
return -EINVAL;
}
rc = dev->ops->rdma_alloc_pd(dev->rdma_ctx, &pd_id);
if (rc)
return rc;
pd->pd_id = pd_id;
if (udata) {
struct qedr_alloc_pd_uresp uresp = {
.pd_id = pd_id,
};
struct qedr_ucontext *context = rdma_udata_to_drv_context(
udata, struct qedr_ucontext, ibucontext);
rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof (uresp));
if (rc) {
DP_ERR(dev, "copy error pd_id=0x%x.\n" , pd_id);
dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd_id);
return rc;
}
pd->uctx = context;
pd->uctx->pd = pd;
}
return 0;
}
int qedr_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata)
{
struct qedr_dev *dev = get_qedr_dev(ibpd->device);
struct qedr_pd *pd = get_qedr_pd(ibpd);
DP_DEBUG(dev, QEDR_MSG_INIT, "Deallocating PD %d\n" , pd->pd_id);
dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd->pd_id);
return 0;
}
int qedr_alloc_xrcd(struct ib_xrcd *ibxrcd, struct ib_udata *udata)
{
struct qedr_dev *dev = get_qedr_dev(ibxrcd->device);
struct qedr_xrcd *xrcd = get_qedr_xrcd(ibxrcd);
return dev->ops->rdma_alloc_xrcd(dev->rdma_ctx, &xrcd->xrcd_id);
}
int qedr_dealloc_xrcd(struct ib_xrcd *ibxrcd, struct ib_udata *udata)
{
struct qedr_dev *dev = get_qedr_dev(ibxrcd->device);
u16 xrcd_id = get_qedr_xrcd(ibxrcd)->xrcd_id;
dev->ops->rdma_dealloc_xrcd(dev->rdma_ctx, xrcd_id);
return 0;
}
static void qedr_free_pbl(struct qedr_dev *dev,
struct qedr_pbl_info *pbl_info, struct qedr_pbl *pbl)
{
struct pci_dev *pdev = dev->pdev;
int i;
for (i = 0; i < pbl_info->num_pbls; i++) {
if (!pbl[i].va)
continue ;
dma_free_coherent(&pdev->dev, pbl_info->pbl_size,
pbl[i].va, pbl[i].pa);
}
kfree(pbl);
}
#define MIN_FW_PBL_PAGE_SIZE (4 * 1024)
#define MAX_FW_PBL_PAGE_SIZE (64 * 1024)
#define NUM_PBES_ON_PAGE(_page_size) (_page_size / sizeof (u64))
#define MAX_PBES_ON_PAGE NUM_PBES_ON_PAGE(MAX_FW_PBL_PAGE_SIZE)
#define MAX_PBES_TWO_LAYER (MAX_PBES_ON_PAGE * MAX_PBES_ON_PAGE)
static struct qedr_pbl *qedr_alloc_pbl_tbl(struct qedr_dev *dev,
struct qedr_pbl_info *pbl_info,
gfp_t flags)
{
struct pci_dev *pdev = dev->pdev;
struct qedr_pbl *pbl_table;
dma_addr_t *pbl_main_tbl;
dma_addr_t pa;
void *va;
int i;
pbl_table = kcalloc(pbl_info->num_pbls, sizeof (*pbl_table), flags);
if (!pbl_table)
return ERR_PTR(-ENOMEM);
for (i = 0; i < pbl_info->num_pbls; i++) {
va = dma_alloc_coherent(&pdev->dev, pbl_info->pbl_size, &pa,
flags);
if (!va)
goto err;
pbl_table[i].va = va;
pbl_table[i].pa = pa;
}
/* Two-Layer PBLs, if we have more than one pbl we need to initialize
* the first one with physical pointers to all of the rest
*/
pbl_main_tbl = (dma_addr_t *)pbl_table[0].va;
for (i = 0; i < pbl_info->num_pbls - 1; i++)
pbl_main_tbl[i] = pbl_table[i + 1].pa;
return pbl_table;
err:
for (i--; i >= 0; i--)
dma_free_coherent(&pdev->dev, pbl_info->pbl_size,
pbl_table[i].va, pbl_table[i].pa);
qedr_free_pbl(dev, pbl_info, pbl_table);
return ERR_PTR(-ENOMEM);
}
static int qedr_prepare_pbl_tbl(struct qedr_dev *dev,
struct qedr_pbl_info *pbl_info,
u32 num_pbes, int two_layer_capable)
{
u32 pbl_capacity;
u32 pbl_size;
u32 num_pbls;
if ((num_pbes > MAX_PBES_ON_PAGE) && two_layer_capable) {
if (num_pbes > MAX_PBES_TWO_LAYER) {
DP_ERR(dev, "prepare pbl table: too many pages %d\n" ,
num_pbes);
return -EINVAL;
}
/* calculate required pbl page size */
pbl_size = MIN_FW_PBL_PAGE_SIZE;
pbl_capacity = NUM_PBES_ON_PAGE(pbl_size) *
NUM_PBES_ON_PAGE(pbl_size);
while (pbl_capacity < num_pbes) {
pbl_size *= 2;
pbl_capacity = pbl_size / sizeof (u64);
pbl_capacity = pbl_capacity * pbl_capacity;
}
num_pbls = DIV_ROUND_UP(num_pbes, NUM_PBES_ON_PAGE(pbl_size));
num_pbls++; /* One for the layer0 ( points to the pbls) */
pbl_info->two_layered = true ;
} else {
/* One layered PBL */
num_pbls = 1;
pbl_size = max_t(u32, MIN_FW_PBL_PAGE_SIZE,
roundup_pow_of_two((num_pbes * sizeof (u64))));
pbl_info->two_layered = false ;
}
pbl_info->num_pbls = num_pbls;
pbl_info->pbl_size = pbl_size;
pbl_info->num_pbes = num_pbes;
DP_DEBUG(dev, QEDR_MSG_MR,
"prepare pbl table: num_pbes=%d, num_pbls=%d, pbl_size=%d\n" ,
pbl_info->num_pbes, pbl_info->num_pbls, pbl_info->pbl_size);
return 0;
}
static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem,
struct qedr_pbl *pbl,
struct qedr_pbl_info *pbl_info, u32 pg_shift)
{
int pbe_cnt, total_num_pbes = 0;
struct qedr_pbl *pbl_tbl;
struct ib_block_iter biter;
struct regpair *pbe;
if (!pbl_info->num_pbes)
return ;
/* If we have a two layered pbl, the first pbl points to the rest
* of the pbls and the first entry lays on the second pbl in the table
*/
if (pbl_info->two_layered)
pbl_tbl = &pbl[1];
else
pbl_tbl = pbl;
pbe = (struct regpair *)pbl_tbl->va;
if (!pbe) {
DP_ERR(dev, "cannot populate PBL due to a NULL PBE\n" );
return ;
}
pbe_cnt = 0;
rdma_umem_for_each_dma_block (umem, &biter, BIT(pg_shift)) {
u64 pg_addr = rdma_block_iter_dma_address(&biter);
pbe->lo = cpu_to_le32(pg_addr);
pbe->hi = cpu_to_le32(upper_32_bits(pg_addr));
pbe_cnt++;
total_num_pbes++;
pbe++;
if (total_num_pbes == pbl_info->num_pbes)
return ;
/* If the given pbl is full storing the pbes, move to next pbl.
*/
if (pbe_cnt == (pbl_info->pbl_size / sizeof (u64))) {
pbl_tbl++;
pbe = (struct regpair *)pbl_tbl->va;
pbe_cnt = 0;
}
}
}
static int qedr_db_recovery_add(struct qedr_dev *dev,
void __iomem *db_addr,
void *db_data,
enum qed_db_rec_width db_width,
enum qed_db_rec_space db_space)
{
if (!db_data) {
DP_DEBUG(dev, QEDR_MSG_INIT, "avoiding db rec since old lib\n" );
return 0;
}
return dev->ops->common->db_recovery_add(dev->cdev, db_addr, db_data,
db_width, db_space);
}
static void qedr_db_recovery_del(struct qedr_dev *dev,
void __iomem *db_addr,
void *db_data)
{
if (!db_data) {
DP_DEBUG(dev, QEDR_MSG_INIT, "avoiding db rec since old lib\n" );
return ;
}
/* Ignore return code as there is not much we can do about it. Error
* log will be printed inside.
*/
dev->ops->common->db_recovery_del(dev->cdev, db_addr, db_data);
}
static int qedr_copy_cq_uresp(struct qedr_dev *dev,
struct qedr_cq *cq, struct ib_udata *udata,
u32 db_offset)
{
struct qedr_create_cq_uresp uresp;
int rc;
memset(&uresp, 0, sizeof (uresp));
uresp.db_offset = db_offset;
uresp.icid = cq->icid;
if (cq->q.db_mmap_entry)
uresp.db_rec_addr =
rdma_user_mmap_get_offset(cq->q.db_mmap_entry);
rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof (uresp));
if (rc)
DP_ERR(dev, "copy error cqid=0x%x.\n" , cq->icid);
return rc;
}
static void consume_cqe(struct qedr_cq *cq)
{
if (cq->latest_cqe == cq->toggle_cqe)
cq->pbl_toggle ^= RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK;
cq->latest_cqe = qed_chain_consume(&cq->pbl);
}
static inline int qedr_align_cq_entries(int entries)
{
u64 size, aligned_size;
/* We allocate an extra entry that we don't report to the FW. */
size = (entries + 1) * QEDR_CQE_SIZE;
aligned_size = ALIGN(size, PAGE_SIZE);
return aligned_size / QEDR_CQE_SIZE;
}
static int qedr_init_user_db_rec(struct ib_udata *udata,
struct qedr_dev *dev, struct qedr_userq *q,
bool requires_db_rec)
{
struct qedr_ucontext *uctx =
rdma_udata_to_drv_context(udata, struct qedr_ucontext,
ibucontext);
struct qedr_user_mmap_entry *entry;
int rc;
/* Aborting for non doorbell userqueue (SRQ) or non-supporting lib */
if (requires_db_rec == 0 || !uctx->db_rec)
return 0;
/* Allocate a page for doorbell recovery, add to mmap */
q->db_rec_data = (void *)get_zeroed_page(GFP_USER);
if (!q->db_rec_data) {
DP_ERR(dev, "get_zeroed_page failed\n" );
return -ENOMEM;
}
entry = kzalloc(sizeof (*entry), GFP_KERNEL);
if (!entry)
goto err_free_db_data;
entry->address = q->db_rec_data;
entry->length = PAGE_SIZE;
entry->mmap_flag = QEDR_USER_MMAP_PHYS_PAGE;
rc = rdma_user_mmap_entry_insert(&uctx->ibucontext,
&entry->rdma_entry,
PAGE_SIZE);
if (rc)
goto err_free_entry;
q->db_mmap_entry = &entry->rdma_entry;
return 0;
err_free_entry:
kfree(entry);
err_free_db_data:
free_page((unsigned long )q->db_rec_data);
q->db_rec_data = NULL;
return -ENOMEM;
}
static inline int qedr_init_user_queue(struct ib_udata *udata,
struct qedr_dev *dev,
struct qedr_userq *q, u64 buf_addr,
size_t buf_len, bool requires_db_rec,
int access,
int alloc_and_init)
{
u32 fw_pages;
int rc;
q->buf_addr = buf_addr;
q->buf_len = buf_len;
q->umem = ib_umem_get(&dev->ibdev, q->buf_addr, q->buf_len, access);
if (IS_ERR(q->umem)) {
DP_ERR(dev, "create user queue: failed ib_umem_get, got %ld\n" ,
PTR_ERR(q->umem));
return PTR_ERR(q->umem);
}
fw_pages = ib_umem_num_dma_blocks(q->umem, 1 << FW_PAGE_SHIFT);
rc = qedr_prepare_pbl_tbl(dev, &q->pbl_info, fw_pages, 0);
if (rc)
goto err0;
if (alloc_and_init) {
q->pbl_tbl = qedr_alloc_pbl_tbl(dev, &q->pbl_info, GFP_KERNEL);
if (IS_ERR(q->pbl_tbl)) {
rc = PTR_ERR(q->pbl_tbl);
goto err0;
}
qedr_populate_pbls(dev, q->umem, q->pbl_tbl, &q->pbl_info,
FW_PAGE_SHIFT);
} else {
q->pbl_tbl = kzalloc(sizeof (*q->pbl_tbl), GFP_KERNEL);
if (!q->pbl_tbl) {
rc = -ENOMEM;
goto err0;
}
}
/* mmap the user address used to store doorbell data for recovery */
return qedr_init_user_db_rec(udata, dev, q, requires_db_rec);
err0:
ib_umem_release(q->umem);
q->umem = NULL;
return rc;
}
static inline void qedr_init_cq_params(struct qedr_cq *cq,
struct qedr_ucontext *ctx,
struct qedr_dev *dev, int vector,
int chain_entries, int page_cnt,
u64 pbl_ptr,
struct qed_rdma_create_cq_in_params
*params)
{
memset(params, 0, sizeof (*params));
params->cq_handle_hi = upper_32_bits((uintptr_t)cq);
params->cq_handle_lo = lower_32_bits((uintptr_t)cq);
params->cnq_id = vector;
params->cq_size = chain_entries - 1;
params->dpi = (ctx) ? ctx->dpi : dev->dpi;
params->pbl_num_pages = page_cnt;
params->pbl_ptr = pbl_ptr;
params->pbl_two_level = 0;
}
static void doorbell_cq(struct qedr_cq *cq, u32 cons, u8 flags)
{
cq->db.data.agg_flags = flags;
cq->db.data.value = cpu_to_le32(cons);
writeq(cq->db.raw, cq->db_addr);
}
int qedr_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
{
struct qedr_cq *cq = get_qedr_cq(ibcq);
unsigned long sflags;
struct qedr_dev *dev;
dev = get_qedr_dev(ibcq->device);
if (cq->destroyed) {
DP_ERR(dev,
"warning: arm was invoked after destroy for cq %p (icid=%d)\n" ,
cq, cq->icid);
return -EINVAL;
}
if (cq->cq_type == QEDR_CQ_TYPE_GSI)
return 0;
spin_lock_irqsave(&cq->cq_lock, sflags);
cq->arm_flags = 0;
if (flags & IB_CQ_SOLICITED)
cq->arm_flags |= DQ_UCM_ROCE_CQ_ARM_SE_CF_CMD;
if (flags & IB_CQ_NEXT_COMP)
cq->arm_flags |= DQ_UCM_ROCE_CQ_ARM_CF_CMD;
doorbell_cq(cq, cq->cq_cons - 1, cq->arm_flags);
spin_unlock_irqrestore(&cq->cq_lock, sflags);
return 0;
}
int qedr_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
struct uverbs_attr_bundle *attrs)
{
struct ib_udata *udata = &attrs->driver_udata;
struct ib_device *ibdev = ibcq->device;
struct qedr_ucontext *ctx = rdma_udata_to_drv_context(
udata, struct qedr_ucontext, ibucontext);
struct qed_rdma_destroy_cq_out_params destroy_oparams;
struct qed_rdma_destroy_cq_in_params destroy_iparams;
struct qed_chain_init_params chain_params = {
.mode = QED_CHAIN_MODE_PBL,
.intended_use = QED_CHAIN_USE_TO_CONSUME,
.cnt_type = QED_CHAIN_CNT_TYPE_U32,
.elem_size = sizeof (union rdma_cqe),
};
struct qedr_dev *dev = get_qedr_dev(ibdev);
struct qed_rdma_create_cq_in_params params;
struct qedr_create_cq_ureq ureq = {};
int vector = attr->comp_vector;
int entries = attr->cqe;
struct qedr_cq *cq = get_qedr_cq(ibcq);
int chain_entries;
u32 db_offset;
int page_cnt;
u64 pbl_ptr;
u16 icid;
int rc;
DP_DEBUG(dev, QEDR_MSG_INIT,
"create_cq: called from %s. entries=%d, vector=%d\n" ,
udata ? "User Lib" : "Kernel" , entries, vector);
if (attr->flags)
return -EOPNOTSUPP;
if (entries > QEDR_MAX_CQES) {
DP_ERR(dev,
"create cq: the number of entries %d is too high. Must be equal or below %d.\n" ,
entries, QEDR_MAX_CQES);
return -EINVAL;
}
chain_entries = qedr_align_cq_entries(entries);
chain_entries = min_t(int , chain_entries, QEDR_MAX_CQES);
chain_params.num_elems = chain_entries;
/* calc db offset. user will add DPI base, kernel will add db addr */
db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT);
if (udata) {
if (ib_copy_from_udata(&ureq, udata, min(sizeof (ureq),
udata->inlen))) {
DP_ERR(dev,
"create cq: problem copying data from user space\n" );
goto err0;
}
if (!ureq.len) {
DP_ERR(dev,
"create cq: cannot create a cq with 0 entries\n" );
goto err0;
}
cq->cq_type = QEDR_CQ_TYPE_USER;
rc = qedr_init_user_queue(udata, dev, &cq->q, ureq.addr,
ureq.len, true , IB_ACCESS_LOCAL_WRITE,
1);
if (rc)
goto err0;
pbl_ptr = cq->q.pbl_tbl->pa;
page_cnt = cq->q.pbl_info.num_pbes;
cq->ibcq.cqe = chain_entries;
cq->q.db_addr = ctx->dpi_addr + db_offset;
} else {
cq->cq_type = QEDR_CQ_TYPE_KERNEL;
rc = dev->ops->common->chain_alloc(dev->cdev, &cq->pbl,
&chain_params);
if (rc)
goto err0;
page_cnt = qed_chain_get_page_cnt(&cq->pbl);
pbl_ptr = qed_chain_get_pbl_phys(&cq->pbl);
cq->ibcq.cqe = cq->pbl.capacity;
}
qedr_init_cq_params(cq, ctx, dev, vector, chain_entries, page_cnt,
pbl_ptr, ¶ms);
rc = dev->ops->rdma_create_cq(dev->rdma_ctx, ¶ms, &icid);
if (rc)
goto err1;
cq->icid = icid;
cq->sig = QEDR_CQ_MAGIC_NUMBER;
spin_lock_init(&cq->cq_lock);
if (udata) {
rc = qedr_copy_cq_uresp(dev, cq, udata, db_offset);
if (rc)
goto err2;
rc = qedr_db_recovery_add(dev, cq->q.db_addr,
&cq->q.db_rec_data->db_data,
DB_REC_WIDTH_64B,
DB_REC_USER);
if (rc)
goto err2;
} else {
/* Generate doorbell address. */
cq->db.data.icid = cq->icid;
cq->db_addr = dev->db_addr + db_offset;
cq->db.data.params = DB_AGG_CMD_MAX <<
RDMA_PWM_VAL32_DATA_AGG_CMD_SHIFT;
/* point to the very last element, passing it we will toggle */
cq->toggle_cqe = qed_chain_get_last_elem(&cq->pbl);
cq->pbl_toggle = RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK;
cq->latest_cqe = NULL;
consume_cqe(cq);
cq->cq_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
rc = qedr_db_recovery_add(dev, cq->db_addr, &cq->db.data,
DB_REC_WIDTH_64B, DB_REC_KERNEL);
if (rc)
goto err2;
}
DP_DEBUG(dev, QEDR_MSG_CQ,
"create cq: icid=0x%0x, addr=%p, size(entries)=0x%0x\n" ,
cq->icid, cq, params.cq_size);
return 0;
err2:
destroy_iparams.icid = cq->icid;
dev->ops->rdma_destroy_cq(dev->rdma_ctx, &destroy_iparams,
&destroy_oparams);
err1:
if (udata) {
qedr_free_pbl(dev, &cq->q.pbl_info, cq->q.pbl_tbl);
ib_umem_release(cq->q.umem);
if (cq->q.db_mmap_entry)
rdma_user_mmap_entry_remove(cq->q.db_mmap_entry);
} else {
dev->ops->common->chain_free(dev->cdev, &cq->pbl);
}
err0:
return -EINVAL;
}
#define QEDR_DESTROY_CQ_MAX_ITERATIONS (10)
#define QEDR_DESTROY_CQ_ITER_DURATION (10)
int qedr_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata)
{
struct qedr_dev *dev = get_qedr_dev(ibcq->device);
struct qed_rdma_destroy_cq_out_params oparams;
struct qed_rdma_destroy_cq_in_params iparams;
struct qedr_cq *cq = get_qedr_cq(ibcq);
int iter;
DP_DEBUG(dev, QEDR_MSG_CQ, "destroy cq %p (icid=%d)\n" , cq, cq->icid);
cq->destroyed = 1;
/* GSIs CQs are handled by driver, so they don't exist in the FW */
if (cq->cq_type == QEDR_CQ_TYPE_GSI) {
qedr_db_recovery_del(dev, cq->db_addr, &cq->db.data);
return 0;
}
iparams.icid = cq->icid;
dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
dev->ops->common->chain_free(dev->cdev, &cq->pbl);
if (udata) {
qedr_free_pbl(dev, &cq->q.pbl_info, cq->q.pbl_tbl);
ib_umem_release(cq->q.umem);
if (cq->q.db_rec_data) {
qedr_db_recovery_del(dev, cq->q.db_addr,
&cq->q.db_rec_data->db_data);
rdma_user_mmap_entry_remove(cq->q.db_mmap_entry);
}
} else {
qedr_db_recovery_del(dev, cq->db_addr, &cq->db.data);
}
/* We don't want the IRQ handler to handle a non-existing CQ so we
* wait until all CNQ interrupts, if any, are received. This will always
* happen and will always happen very fast. If not, then a serious error
* has occured. That is why we can use a long delay.
* We spin for a short time so we don’t lose time on context switching
* in case all the completions are handled in that span. Otherwise
* we sleep for a while and check again. Since the CNQ may be
* associated with (only) the current CPU we use msleep to allow the
* current CPU to be freed.
* The CNQ notification is increased in qedr_irq_handler().
*/
iter = QEDR_DESTROY_CQ_MAX_ITERATIONS;
while (oparams.num_cq_notif != READ_ONCE(cq->cnq_notif) && iter) {
udelay(QEDR_DESTROY_CQ_ITER_DURATION);
iter--;
}
iter = QEDR_DESTROY_CQ_MAX_ITERATIONS;
while (oparams.num_cq_notif != READ_ONCE(cq->cnq_notif) && iter) {
msleep(QEDR_DESTROY_CQ_ITER_DURATION);
iter--;
}
/* Note that we don't need to have explicit code to wait for the
* completion of the event handler because it is invoked from the EQ.
* Since the destroy CQ ramrod has also been received on the EQ we can
* be certain that there's no event handler in process.
*/
return 0;
}
static inline int get_gid_info_from_table(struct ib_qp *ibqp,
struct ib_qp_attr *attr,
int attr_mask,
struct qed_rdma_modify_qp_in_params
*qp_params)
{
const struct ib_gid_attr *gid_attr;
enum rdma_network_type nw_type;
const struct ib_global_route *grh = rdma_ah_read_grh(&attr->ah_attr);
u32 ipv4_addr;
int ret;
int i;
gid_attr = grh->sgid_attr;
ret = rdma_read_gid_l2_fields(gid_attr, &qp_params->vlan_id, NULL);
if (ret)
return ret;
nw_type = rdma_gid_attr_network_type(gid_attr);
switch (nw_type) {
case RDMA_NETWORK_IPV6:
memcpy(&qp_params->sgid.bytes[0], &gid_attr->gid.raw[0],
sizeof (qp_params->sgid));
memcpy(&qp_params->dgid.bytes[0],
&grh->dgid,
sizeof (qp_params->dgid));
qp_params->roce_mode = ROCE_V2_IPV6;
SET_FIELD(qp_params->modify_flags,
QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
break ;
case RDMA_NETWORK_ROCE_V1:
memcpy(&qp_params->sgid.bytes[0], &gid_attr->gid.raw[0],
sizeof (qp_params->sgid));
memcpy(&qp_params->dgid.bytes[0],
&grh->dgid,
sizeof (qp_params->dgid));
qp_params->roce_mode = ROCE_V1;
break ;
case RDMA_NETWORK_IPV4:
memset(&qp_params->sgid, 0, sizeof (qp_params->sgid));
memset(&qp_params->dgid, 0, sizeof (qp_params->dgid));
ipv4_addr = qedr_get_ipv4_from_gid(gid_attr->gid.raw);
qp_params->sgid.ipv4_addr = ipv4_addr;
ipv4_addr =
qedr_get_ipv4_from_gid(grh->dgid.raw);
qp_params->dgid.ipv4_addr = ipv4_addr;
SET_FIELD(qp_params->modify_flags,
QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
qp_params->roce_mode = ROCE_V2_IPV4;
break ;
default :
return -EINVAL;
}
for (i = 0; i < 4; i++) {
qp_params->sgid.dwords[i] = ntohl(qp_params->sgid.dwords[i]);
qp_params->dgid.dwords[i] = ntohl(qp_params->dgid.dwords[i]);
}
if (qp_params->vlan_id >= VLAN_CFI_MASK)
qp_params->vlan_id = 0;
return 0;
}
static int qedr_check_qp_attrs(struct ib_pd *ibpd, struct qedr_dev *dev,
struct ib_qp_init_attr *attrs,
struct ib_udata *udata)
{
struct qedr_device_attr *qattr = &dev->attr;
/* QP0... attrs->qp_type == IB_QPT_GSI */
if (attrs->qp_type != IB_QPT_RC &&
attrs->qp_type != IB_QPT_GSI &&
attrs->qp_type != IB_QPT_XRC_INI &&
attrs->qp_type != IB_QPT_XRC_TGT) {
DP_DEBUG(dev, QEDR_MSG_QP,
"create qp: unsupported qp type=0x%x requested\n" ,
attrs->qp_type);
return -EOPNOTSUPP;
}
if (attrs->cap.max_send_wr > qattr->max_sqe) {
DP_ERR(dev,
"create qp: cannot create a SQ with %d elements (max_send_wr=0x%x)\n" ,
attrs->cap.max_send_wr, qattr->max_sqe);
return -EINVAL;
}
if (attrs->cap.max_inline_data > qattr->max_inline) {
DP_ERR(dev,
"create qp: unsupported inline data size=0x%x requested (max_inline=0x%x)\n" ,
attrs->cap.max_inline_data, qattr->max_inline);
return -EINVAL;
}
if (attrs->cap.max_send_sge > qattr->max_sge) {
DP_ERR(dev,
"create qp: unsupported send_sge=0x%x requested (max_send_sge=0x%x)\n" ,
attrs->cap.max_send_sge, qattr->max_sge);
return -EINVAL;
}
if (attrs->cap.max_recv_sge > qattr->max_sge) {
DP_ERR(dev,
"create qp: unsupported recv_sge=0x%x requested (max_recv_sge=0x%x)\n" ,
attrs->cap.max_recv_sge, qattr->max_sge);
return -EINVAL;
}
/* verify consumer QPs are not trying to use GSI QP's CQ.
* TGT QP isn't associated with RQ/SQ
*/
if ((attrs->qp_type != IB_QPT_GSI) && (dev->gsi_qp_created) &&
(attrs->qp_type != IB_QPT_XRC_TGT) &&
(attrs->qp_type != IB_QPT_XRC_INI)) {
struct qedr_cq *send_cq = get_qedr_cq(attrs->send_cq);
struct qedr_cq *recv_cq = get_qedr_cq(attrs->recv_cq);
if ((send_cq->cq_type == QEDR_CQ_TYPE_GSI) ||
(recv_cq->cq_type == QEDR_CQ_TYPE_GSI)) {
DP_ERR(dev,
"create qp: consumer QP cannot use GSI CQs.\n" );
return -EINVAL;
}
}
return 0;
}
static int qedr_copy_srq_uresp(struct qedr_dev *dev,
struct qedr_srq *srq, struct ib_udata *udata)
{
struct qedr_create_srq_uresp uresp = {};
int rc;
uresp.srq_id = srq->srq_id;
rc = ib_copy_to_udata(udata, &uresp, sizeof (uresp));
if (rc)
DP_ERR(dev, "create srq: problem copying data to user space\n" );
return rc;
}
static void qedr_copy_rq_uresp(struct qedr_dev *dev,
struct qedr_create_qp_uresp *uresp,
struct qedr_qp *qp)
{
/* iWARP requires two doorbells per RQ. */
if (rdma_protocol_iwarp(&dev->ibdev, 1)) {
uresp->rq_db_offset =
DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_IWARP_RQ_PROD);
uresp->rq_db2_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_FLAGS);
} else {
uresp->rq_db_offset =
DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD);
}
uresp->rq_icid = qp->icid;
if (qp->urq.db_mmap_entry)
uresp->rq_db_rec_addr =
rdma_user_mmap_get_offset(qp->urq.db_mmap_entry);
}
static void qedr_copy_sq_uresp(struct qedr_dev *dev,
struct qedr_create_qp_uresp *uresp,
struct qedr_qp *qp)
{
uresp->sq_db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
/* iWARP uses the same cid for rq and sq */
if (rdma_protocol_iwarp(&dev->ibdev, 1))
uresp->sq_icid = qp->icid;
else
uresp->sq_icid = qp->icid + 1;
if (qp->usq.db_mmap_entry)
uresp->sq_db_rec_addr =
rdma_user_mmap_get_offset(qp->usq.db_mmap_entry);
}
static int qedr_copy_qp_uresp(struct qedr_dev *dev,
struct qedr_qp *qp, struct ib_udata *udata,
struct qedr_create_qp_uresp *uresp)
{
int rc;
memset(uresp, 0, sizeof (*uresp));
if (qedr_qp_has_sq(qp))
qedr_copy_sq_uresp(dev, uresp, qp);
if (qedr_qp_has_rq(qp))
qedr_copy_rq_uresp(dev, uresp, qp);
uresp->atomic_supported = dev->atomic_cap != IB_ATOMIC_NONE;
uresp->qp_id = qp->qp_id;
rc = qedr_ib_copy_to_udata(udata, uresp, sizeof (*uresp));
if (rc)
DP_ERR(dev,
"create qp: failed a copy to user space with qp icid=0x%x.\n" ,
qp->icid);
return rc;
}
static void qedr_reset_qp_hwq_info(struct qedr_qp_hwq_info *qph)
{
qed_chain_reset(&qph->pbl);
qph->prod = 0;
qph->cons = 0;
qph->wqe_cons = 0;
qph->db_data.data.value = cpu_to_le16(0);
}
static void qedr_set_common_qp_params(struct qedr_dev *dev,
struct qedr_qp *qp,
struct qedr_pd *pd,
struct ib_qp_init_attr *attrs)
{
spin_lock_init(&qp->q_lock);
if (rdma_protocol_iwarp(&dev->ibdev, 1)) {
kref_init(&qp->refcnt);
init_completion(&qp->iwarp_cm_comp);
init_completion(&qp->qp_rel_comp);
}
qp->pd = pd;
qp->qp_type = attrs->qp_type;
qp->max_inline_data = attrs->cap.max_inline_data;
qp->state = QED_ROCE_QP_STATE_RESET;
qp->prev_wqe_size = 0;
qp->signaled = attrs->sq_sig_type == IB_SIGNAL_ALL_WR;
qp->dev = dev;
if (qedr_qp_has_sq(qp)) {
qedr_reset_qp_hwq_info(&qp->sq);
qp->sq.max_sges = attrs->cap.max_send_sge;
qp->sq_cq = get_qedr_cq(attrs->send_cq);
DP_DEBUG(dev, QEDR_MSG_QP,
"SQ params:\tsq_max_sges = %d, sq_cq_id = %d\n" ,
qp->sq.max_sges, qp->sq_cq->icid);
}
if (attrs->srq)
qp->srq = get_qedr_srq(attrs->srq);
if (qedr_qp_has_rq(qp)) {
qedr_reset_qp_hwq_info(&qp->rq);
qp->rq_cq = get_qedr_cq(attrs->recv_cq);
qp->rq.max_sges = attrs->cap.max_recv_sge;
DP_DEBUG(dev, QEDR_MSG_QP,
"RQ params:\trq_max_sges = %d, rq_cq_id = %d\n" ,
qp->rq.max_sges, qp->rq_cq->icid);
}
DP_DEBUG(dev, QEDR_MSG_QP,
"QP params:\tpd = %d, qp_type = %d, max_inline_data = %d, state = %d, signaled = %d, use_srq=%d\n" ,
pd->pd_id, qp->qp_type, qp->max_inline_data,
qp->state, qp->signaled, (attrs->srq) ? 1 : 0);
DP_DEBUG(dev, QEDR_MSG_QP,
"SQ params:\tsq_max_sges = %d, sq_cq_id = %d\n" ,
qp->sq.max_sges, qp->sq_cq->icid);
}
static int qedr_set_roce_db_info(struct qedr_dev *dev, struct qedr_qp *qp)
{
int rc = 0;
if (qedr_qp_has_sq(qp)) {
qp->sq.db = dev->db_addr +
DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
qp->sq.db_data.data.icid = qp->icid + 1;
rc = qedr_db_recovery_add(dev, qp->sq.db, &qp->sq.db_data,
DB_REC_WIDTH_32B, DB_REC_KERNEL);
if (rc)
return rc;
}
if (qedr_qp_has_rq(qp)) {
qp->rq.db = dev->db_addr +
DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD);
qp->rq.db_data.data.icid = qp->icid;
rc = qedr_db_recovery_add(dev, qp->rq.db, &qp->rq.db_data,
DB_REC_WIDTH_32B, DB_REC_KERNEL);
if (rc && qedr_qp_has_sq(qp))
qedr_db_recovery_del(dev, qp->sq.db, &qp->sq.db_data);
}
return rc;
}
static int qedr_check_srq_params(struct qedr_dev *dev,
struct ib_srq_init_attr *attrs,
struct ib_udata *udata)
{
struct qedr_device_attr *qattr = &dev->attr;
if (attrs->attr.max_wr > qattr->max_srq_wr) {
DP_ERR(dev,
"create srq: unsupported srq_wr=0x%x requested (max_srq_wr=0x%x)\n" ,
attrs->attr.max_wr, qattr->max_srq_wr);
return -EINVAL;
}
if (attrs->attr.max_sge > qattr->max_sge) {
DP_ERR(dev,
"create srq: unsupported sge=0x%x requested (max_srq_sge=0x%x)\n" ,
attrs->attr.max_sge, qattr->max_sge);
}
if (!udata && attrs->srq_type == IB_SRQT_XRC) {
DP_ERR(dev, "XRC SRQs are not supported in kernel-space\n" );
return -EINVAL;
}
return 0;
}
static void qedr_free_srq_user_params(struct qedr_srq *srq)
{
qedr_free_pbl(srq->dev, &srq->usrq.pbl_info, srq->usrq.pbl_tbl);
ib_umem_release(srq->usrq.umem);
ib_umem_release(srq->prod_umem);
}
static void qedr_free_srq_kernel_params(struct qedr_srq *srq)
{
struct qedr_srq_hwq_info *hw_srq = &srq->hw_srq;
struct qedr_dev *dev = srq->dev;
dev->ops->common->chain_free(dev->cdev, &hw_srq->pbl);
dma_free_coherent(&dev->pdev->dev, sizeof (struct rdma_srq_producers),
hw_srq->virt_prod_pair_addr,
hw_srq->phy_prod_pair_addr);
}
static int qedr_init_srq_user_params(struct ib_udata *udata,
struct qedr_srq *srq,
struct qedr_create_srq_ureq *ureq,
int access)
{
struct scatterlist *sg;
int rc;
rc = qedr_init_user_queue(udata, srq->dev, &srq->usrq, ureq->srq_addr,
ureq->srq_len, false , access, 1);
if (rc)
return rc;
srq->prod_umem = ib_umem_get(srq->ibsrq.device, ureq->prod_pair_addr,
sizeof (struct rdma_srq_producers), access);
if (IS_ERR(srq->prod_umem)) {
qedr_free_pbl(srq->dev, &srq->usrq.pbl_info, srq->usrq.pbl_tbl);
ib_umem_release(srq->usrq.umem);
DP_ERR(srq->dev,
"create srq: failed ib_umem_get for producer, got %ld\n" ,
PTR_ERR(srq->prod_umem));
return PTR_ERR(srq->prod_umem);
}
sg = srq->prod_umem->sgt_append.sgt.sgl;
srq->hw_srq.phy_prod_pair_addr = sg_dma_address(sg);
return 0;
}
static int qedr_alloc_srq_kernel_params(struct qedr_srq *srq,
struct qedr_dev *dev,
struct ib_srq_init_attr *init_attr)
{
struct qedr_srq_hwq_info *hw_srq = &srq->hw_srq;
struct qed_chain_init_params params = {
.mode = QED_CHAIN_MODE_PBL,
.intended_use = QED_CHAIN_USE_TO_CONSUME_PRODUCE,
.cnt_type = QED_CHAIN_CNT_TYPE_U32,
.elem_size = QEDR_SRQ_WQE_ELEM_SIZE,
};
dma_addr_t phy_prod_pair_addr;
u32 num_elems;
void *va;
int rc;
va = dma_alloc_coherent(&dev->pdev->dev,
sizeof (struct rdma_srq_producers),
&phy_prod_pair_addr, GFP_KERNEL);
if (!va) {
DP_ERR(dev,
"create srq: failed to allocate dma memory for producer\n" );
return -ENOMEM;
}
hw_srq->phy_prod_pair_addr = phy_prod_pair_addr;
hw_srq->virt_prod_pair_addr = va;
num_elems = init_attr->attr.max_wr * RDMA_MAX_SRQ_WQE_SIZE;
params.num_elems = num_elems;
rc = dev->ops->common->chain_alloc(dev->cdev, &hw_srq->pbl, ¶ms);
if (rc)
goto err0;
hw_srq->num_elems = num_elems;
return 0;
err0:
dma_free_coherent(&dev->pdev->dev, sizeof (struct rdma_srq_producers),
va, phy_prod_pair_addr);
return rc;
}
int qedr_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init_attr,
struct ib_udata *udata)
{
struct qed_rdma_destroy_srq_in_params destroy_in_params;
struct qed_rdma_create_srq_in_params in_params = {};
struct qedr_dev *dev = get_qedr_dev(ibsrq->device);
struct qed_rdma_create_srq_out_params out_params;
struct qedr_pd *pd = get_qedr_pd(ibsrq->pd);
struct qedr_create_srq_ureq ureq = {};
u64 pbl_base_addr, phy_prod_pair_addr;
struct qedr_srq_hwq_info *hw_srq;
u32 page_cnt, page_size;
struct qedr_srq *srq = get_qedr_srq(ibsrq);
int rc = 0;
DP_DEBUG(dev, QEDR_MSG_QP,
"create SRQ called from %s (pd %p)\n" ,
(udata) ? "User lib" : "kernel" , pd);
if (init_attr->srq_type != IB_SRQT_BASIC &&
init_attr->srq_type != IB_SRQT_XRC)
return -EOPNOTSUPP;
rc = qedr_check_srq_params(dev, init_attr, udata);
if (rc)
return -EINVAL;
srq->dev = dev;
srq->is_xrc = (init_attr->srq_type == IB_SRQT_XRC);
hw_srq = &srq->hw_srq;
spin_lock_init(&srq->lock);
hw_srq->max_wr = init_attr->attr.max_wr;
hw_srq->max_sges = init_attr->attr.max_sge;
if (udata) {
if (ib_copy_from_udata(&ureq, udata, min(sizeof (ureq),
udata->inlen))) {
DP_ERR(dev,
"create srq: problem copying data from user space\n" );
goto err0;
}
rc = qedr_init_srq_user_params(udata, srq, &ureq, 0);
if (rc)
goto err0;
page_cnt = srq->usrq.pbl_info.num_pbes;
pbl_base_addr = srq->usrq.pbl_tbl->pa;
phy_prod_pair_addr = hw_srq->phy_prod_pair_addr;
page_size = PAGE_SIZE;
} else {
struct qed_chain *pbl;
rc = qedr_alloc_srq_kernel_params(srq, dev, init_attr);
if (rc)
goto err0;
pbl = &hw_srq->pbl;
page_cnt = qed_chain_get_page_cnt(pbl);
pbl_base_addr = qed_chain_get_pbl_phys(pbl);
phy_prod_pair_addr = hw_srq->phy_prod_pair_addr;
page_size = QED_CHAIN_PAGE_SIZE;
}
in_params.pd_id = pd->pd_id;
in_params.pbl_base_addr = pbl_base_addr;
in_params.prod_pair_addr = phy_prod_pair_addr;
in_params.num_pages = page_cnt;
in_params.page_size = page_size;
if (srq->is_xrc) {
struct qedr_xrcd *xrcd = get_qedr_xrcd(init_attr->ext.xrc.xrcd);
struct qedr_cq *cq = get_qedr_cq(init_attr->ext.cq);
in_params.is_xrc = 1;
in_params.xrcd_id = xrcd->xrcd_id;
in_params.cq_cid = cq->icid;
}
rc = dev->ops->rdma_create_srq(dev->rdma_ctx, &in_params, &out_params);
if (rc)
goto err1;
srq->srq_id = out_params.srq_id;
if (udata) {
rc = qedr_copy_srq_uresp(dev, srq, udata);
if (rc)
goto err2;
}
rc = xa_insert_irq(&dev->srqs, srq->srq_id, srq, GFP_KERNEL);
if (rc)
goto err2;
DP_DEBUG(dev, QEDR_MSG_SRQ,
"create srq: created srq with srq_id=0x%0x\n" , srq->srq_id);
return 0;
err2:
destroy_in_params.srq_id = srq->srq_id;
dev->ops->rdma_destroy_srq(dev->rdma_ctx, &destroy_in_params);
err1:
if (udata)
qedr_free_srq_user_params(srq);
else
qedr_free_srq_kernel_params(srq);
err0:
return -EFAULT;
}
int qedr_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata)
{
struct qed_rdma_destroy_srq_in_params in_params = {};
struct qedr_dev *dev = get_qedr_dev(ibsrq->device);
struct qedr_srq *srq = get_qedr_srq(ibsrq);
xa_erase_irq(&dev->srqs, srq->srq_id);
in_params.srq_id = srq->srq_id;
in_params.is_xrc = srq->is_xrc;
dev->ops->rdma_destroy_srq(dev->rdma_ctx, &in_params);
if (ibsrq->uobject)
qedr_free_srq_user_params(srq);
else
qedr_free_srq_kernel_params(srq);
DP_DEBUG(dev, QEDR_MSG_SRQ,
"destroy srq: destroyed srq with srq_id=0x%0x\n" ,
srq->srq_id);
return 0;
}
int qedr_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
enum ib_srq_attr_mask attr_mask, struct ib_udata *udata)
{
struct qed_rdma_modify_srq_in_params in_params = {};
struct qedr_dev *dev = get_qedr_dev(ibsrq->device);
struct qedr_srq *srq = get_qedr_srq(ibsrq);
int rc;
if (attr_mask & IB_SRQ_MAX_WR) {
DP_ERR(dev,
"modify srq: invalid attribute mask=0x%x specified for %p\n" ,
attr_mask, srq);
return -EINVAL;
}
if (attr_mask & IB_SRQ_LIMIT) {
if (attr->srq_limit >= srq->hw_srq.max_wr) {
DP_ERR(dev,
"modify srq: invalid srq_limit=0x%x (max_srq_limit=0x%x)\n" ,
attr->srq_limit, srq->hw_srq.max_wr);
return -EINVAL;
}
in_params.srq_id = srq->srq_id;
in_params.wqe_limit = attr->srq_limit;
rc = dev->ops->rdma_modify_srq(dev->rdma_ctx, &in_params);
if (rc)
return rc;
}
srq->srq_limit = attr->srq_limit;
DP_DEBUG(dev, QEDR_MSG_SRQ,
"modify srq: modified srq with srq_id=0x%0x\n" , srq->srq_id);
return 0;
}
static enum qed_rdma_qp_type qedr_ib_to_qed_qp_type(enum ib_qp_type ib_qp_type)
{
switch (ib_qp_type) {
case IB_QPT_RC:
return QED_RDMA_QP_TYPE_RC;
case IB_QPT_XRC_INI:
return QED_RDMA_QP_TYPE_XRC_INI;
case IB_QPT_XRC_TGT:
return QED_RDMA_QP_TYPE_XRC_TGT;
default :
return QED_RDMA_QP_TYPE_INVAL;
}
}
static inline void
qedr_init_common_qp_in_params(struct qedr_dev *dev,
struct qedr_pd *pd,
struct qedr_qp *qp,
struct ib_qp_init_attr *attrs,
bool fmr_and_reserved_lkey,
struct qed_rdma_create_qp_in_params *params)
{
/* QP handle to be written in an async event */
params->qp_handle_async_lo = lower_32_bits((uintptr_t) qp);
params->qp_handle_async_hi = upper_32_bits((uintptr_t) qp);
params->signal_all = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR);
params->fmr_and_reserved_lkey = fmr_and_reserved_lkey;
params->qp_type = qedr_ib_to_qed_qp_type(attrs->qp_type);
params->stats_queue = 0;
if (pd) {
params->pd = pd->pd_id;
params->dpi = pd->uctx ? pd->uctx->dpi : dev->dpi;
}
if (qedr_qp_has_sq(qp))
params->sq_cq_id = get_qedr_cq(attrs->send_cq)->icid;
if (qedr_qp_has_rq(qp))
params->rq_cq_id = get_qedr_cq(attrs->recv_cq)->icid;
if (qedr_qp_has_srq(qp)) {
params->rq_cq_id = get_qedr_cq(attrs->recv_cq)->icid;
params->srq_id = qp->srq->srq_id;
params->use_srq = true ;
} else {
params->srq_id = 0;
params->use_srq = false ;
}
}
static inline void qedr_qp_user_print(struct qedr_dev *dev, struct qedr_qp *qp)
{
DP_DEBUG(dev, QEDR_MSG_QP, "create qp: successfully created user QP. "
"qp=%p. "
"sq_addr=0x%llx, "
"sq_len=%zd, "
"rq_addr=0x%llx, "
"rq_len=%zd"
"\n" ,
qp,
qedr_qp_has_sq(qp) ? qp->usq.buf_addr : 0x0,
qedr_qp_has_sq(qp) ? qp->usq.buf_len : 0,
qedr_qp_has_rq(qp) ? qp->urq.buf_addr : 0x0,
qedr_qp_has_sq(qp) ? qp->urq.buf_len : 0);
}
static inline void
qedr_iwarp_populate_user_qp(struct qedr_dev *dev,
struct qedr_qp *qp,
struct qed_rdma_create_qp_out_params *out_params)
{
qp->usq.pbl_tbl->va = out_params->sq_pbl_virt;
qp->usq.pbl_tbl->pa = out_params->sq_pbl_phys;
qedr_populate_pbls(dev, qp->usq.umem, qp->usq.pbl_tbl,
&qp->usq.pbl_info, FW_PAGE_SHIFT);
if (!qp->srq) {
qp->urq.pbl_tbl->va = out_params->rq_pbl_virt;
qp->urq.pbl_tbl->pa = out_params->rq_pbl_phys;
}
qedr_populate_pbls(dev, qp->urq.umem, qp->urq.pbl_tbl,
&qp->urq.pbl_info, FW_PAGE_SHIFT);
}
static void qedr_cleanup_user(struct qedr_dev *dev,
struct qedr_ucontext *ctx,
struct qedr_qp *qp)
{
if (qedr_qp_has_sq(qp)) {
ib_umem_release(qp->usq.umem);
qp->usq.umem = NULL;
}
if (qedr_qp_has_rq(qp)) {
ib_umem_release(qp->urq.umem);
qp->urq.umem = NULL;
}
if (rdma_protocol_roce(&dev->ibdev, 1)) {
qedr_free_pbl(dev, &qp->usq.pbl_info, qp->usq.pbl_tbl);
qedr_free_pbl(dev, &qp->urq.pbl_info, qp->urq.pbl_tbl);
} else {
kfree(qp->usq.pbl_tbl);
kfree(qp->urq.pbl_tbl);
}
if (qp->usq.db_rec_data) {
qedr_db_recovery_del(dev, qp->usq.db_addr,
&qp->usq.db_rec_data->db_data);
rdma_user_mmap_entry_remove(qp->usq.db_mmap_entry);
}
if (qp->urq.db_rec_data) {
qedr_db_recovery_del(dev, qp->urq.db_addr,
&qp->urq.db_rec_data->db_data);
rdma_user_mmap_entry_remove(qp->urq.db_mmap_entry);
}
if (rdma_protocol_iwarp(&dev->ibdev, 1))
qedr_db_recovery_del(dev, qp->urq.db_rec_db2_addr,
&qp->urq.db_rec_db2_data);
}
static int qedr_create_user_qp(struct qedr_dev *dev,
struct qedr_qp *qp,
struct ib_pd *ibpd,
struct ib_udata *udata,
struct ib_qp_init_attr *attrs)
{
struct qed_rdma_create_qp_in_params in_params;
struct qed_rdma_create_qp_out_params out_params;
struct qedr_create_qp_uresp uresp = {};
struct qedr_create_qp_ureq ureq = {};
int alloc_and_init = rdma_protocol_roce(&dev->ibdev, 1);
struct qedr_ucontext *ctx = NULL;
struct qedr_pd *pd = NULL;
int rc = 0;
qp->create_type = QEDR_QP_CREATE_USER;
if (ibpd) {
pd = get_qedr_pd(ibpd);
ctx = pd->uctx;
}
if (udata) {
rc = ib_copy_from_udata(&ureq, udata, min(sizeof (ureq),
udata->inlen));
if (rc) {
DP_ERR(dev, "Problem copying data from user space\n" );
return rc;
}
}
if (qedr_qp_has_sq(qp)) {
/* SQ - read access only (0) */
rc = qedr_init_user_queue(udata, dev, &qp->usq, ureq.sq_addr,
ureq.sq_len, true , 0, alloc_and_init);
if (rc)
return rc;
}
if (qedr_qp_has_rq(qp)) {
/* RQ - read access only (0) */
rc = qedr_init_user_queue(udata, dev, &qp->urq, ureq.rq_addr,
ureq.rq_len, true , 0, alloc_and_init);
if (rc) {
ib_umem_release(qp->usq.umem);
qp->usq.umem = NULL;
if (rdma_protocol_roce(&dev->ibdev, 1)) {
qedr_free_pbl(dev, &qp->usq.pbl_info,
qp->usq.pbl_tbl);
} else {
kfree(qp->usq.pbl_tbl);
}
return rc;
}
}
memset(&in_params, 0, sizeof (in_params));
qedr_init_common_qp_in_params(dev, pd, qp, attrs, false , &in_params);
in_params.qp_handle_lo = ureq.qp_handle_lo;
in_params.qp_handle_hi = ureq.qp_handle_hi;
if (qp->qp_type == IB_QPT_XRC_TGT) {
struct qedr_xrcd *xrcd = get_qedr_xrcd(attrs->xrcd);
in_params.xrcd_id = xrcd->xrcd_id;
in_params.qp_handle_lo = qp->qp_id;
in_params.use_srq = 1;
}
if (qedr_qp_has_sq(qp)) {
in_params.sq_num_pages = qp->usq.pbl_info.num_pbes;
in_params.sq_pbl_ptr = qp->usq.pbl_tbl->pa;
}
if (qedr_qp_has_rq(qp)) {
in_params.rq_num_pages = qp->urq.pbl_info.num_pbes;
in_params.rq_pbl_ptr = qp->urq.pbl_tbl->pa;
}
if (ctx)
SET_FIELD(in_params.flags, QED_ROCE_EDPM_MODE, ctx->edpm_mode);
qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx,
&in_params, &out_params);
if (!qp->qed_qp) {
rc = -ENOMEM;
goto err1;
}
if (rdma_protocol_iwarp(&dev->ibdev, 1))
qedr_iwarp_populate_user_qp(dev, qp, &out_params);
qp->qp_id = out_params.qp_id;
qp->icid = out_params.icid;
if (udata) {
rc = qedr_copy_qp_uresp(dev, qp, udata, &uresp);
if (rc)
goto err;
}
/* db offset was calculated in copy_qp_uresp, now set in the user q */
if (qedr_qp_has_sq(qp)) {
qp->usq.db_addr = ctx->dpi_addr + uresp.sq_db_offset;
qp->sq.max_wr = attrs->cap.max_send_wr;
rc = qedr_db_recovery_add(dev, qp->usq.db_addr,
&qp->usq.db_rec_data->db_data,
DB_REC_WIDTH_32B,
DB_REC_USER);
if (rc)
goto err;
}
if (qedr_qp_has_rq(qp)) {
qp->urq.db_addr = ctx->dpi_addr + uresp.rq_db_offset;
qp->rq.max_wr = attrs->cap.max_recv_wr;
rc = qedr_db_recovery_add(dev, qp->urq.db_addr,
&qp->urq.db_rec_data->db_data,
DB_REC_WIDTH_32B,
DB_REC_USER);
if (rc)
goto err;
}
if (rdma_protocol_iwarp(&dev->ibdev, 1)) {
qp->urq.db_rec_db2_addr = ctx->dpi_addr + uresp.rq_db2_offset;
/* calculate the db_rec_db2 data since it is constant so no
* need to reflect from user
*/
qp->urq.db_rec_db2_data.data.icid = cpu_to_le16(qp->icid);
qp->urq.db_rec_db2_data.data.value =
cpu_to_le16(DQ_TCM_IWARP_POST_RQ_CF_CMD);
rc = qedr_db_recovery_add(dev, qp->urq.db_rec_db2_addr,
&qp->urq.db_rec_db2_data,
DB_REC_WIDTH_32B,
DB_REC_USER);
if (rc)
goto err;
}
qedr_qp_user_print(dev, qp);
return rc;
err:
rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
if (rc)
DP_ERR(dev, "create qp: fatal fault. rc=%d" , rc);
err1:
qedr_cleanup_user(dev, ctx, qp);
return rc;
}
static int qedr_set_iwarp_db_info(struct qedr_dev *dev, struct qedr_qp *qp)
{
int rc;
qp->sq.db = dev->db_addr +
DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
qp->sq.db_data.data.icid = qp->icid;
rc = qedr_db_recovery_add(dev, qp->sq.db,
&qp->sq.db_data,
DB_REC_WIDTH_32B,
DB_REC_KERNEL);
if (rc)
return rc;
qp->rq.db = dev->db_addr +
DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_IWARP_RQ_PROD);
qp->rq.db_data.data.icid = qp->icid;
qp->rq.iwarp_db2 = dev->db_addr +
DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_FLAGS);
qp->rq.iwarp_db2_data.data.icid = qp->icid;
qp->rq.iwarp_db2_data.data.value = DQ_TCM_IWARP_POST_RQ_CF_CMD;
rc = qedr_db_recovery_add(dev, qp->rq.db,
&qp->rq.db_data,
DB_REC_WIDTH_32B,
DB_REC_KERNEL);
if (rc)
return rc;
rc = qedr_db_recovery_add(dev, qp->rq.iwarp_db2,
&qp->rq.iwarp_db2_data,
DB_REC_WIDTH_32B,
DB_REC_KERNEL);
return rc;
}
static int
qedr_roce_create_kernel_qp(struct qedr_dev *dev,
struct qedr_qp *qp,
struct qed_rdma_create_qp_in_params *in_params,
u32 n_sq_elems, u32 n_rq_elems)
{
struct qed_rdma_create_qp_out_params out_params;
struct qed_chain_init_params params = {
.mode = QED_CHAIN_MODE_PBL,
.cnt_type = QED_CHAIN_CNT_TYPE_U32,
};
int rc;
params.intended_use = QED_CHAIN_USE_TO_PRODUCE;
params.num_elems = n_sq_elems;
params.elem_size = QEDR_SQE_ELEMENT_SIZE;
rc = dev->ops->common->chain_alloc(dev->cdev, &qp->sq.pbl, ¶ms);
if (rc)
return rc;
in_params->sq_num_pages = qed_chain_get_page_cnt(&qp->sq.pbl);
in_params->sq_pbl_ptr = qed_chain_get_pbl_phys(&qp->sq.pbl);
params.intended_use = QED_CHAIN_USE_TO_CONSUME_PRODUCE;
params.num_elems = n_rq_elems;
params.elem_size = QEDR_RQE_ELEMENT_SIZE;
rc = dev->ops->common->chain_alloc(dev->cdev, &qp->rq.pbl, ¶ms);
if (rc)
return rc;
in_params->rq_num_pages = qed_chain_get_page_cnt(&qp->rq.pbl);
in_params->rq_pbl_ptr = qed_chain_get_pbl_phys(&qp->rq.pbl);
qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx,
in_params, &out_params);
if (!qp->qed_qp)
return -EINVAL;
qp->qp_id = out_params.qp_id;
qp->icid = out_params.icid;
return qedr_set_roce_db_info(dev, qp);
}
static int
qedr_iwarp_create_kernel_qp(struct qedr_dev *dev,
struct qedr_qp *qp,
struct qed_rdma_create_qp_in_params *in_params,
u32 n_sq_elems, u32 n_rq_elems)
{
struct qed_rdma_create_qp_out_params out_params;
struct qed_chain_init_params params = {
.mode = QED_CHAIN_MODE_PBL,
.cnt_type = QED_CHAIN_CNT_TYPE_U32,
};
int rc;
in_params->sq_num_pages = QED_CHAIN_PAGE_CNT(n_sq_elems,
QEDR_SQE_ELEMENT_SIZE,
QED_CHAIN_PAGE_SIZE,
QED_CHAIN_MODE_PBL);
in_params->rq_num_pages = QED_CHAIN_PAGE_CNT(n_rq_elems,
QEDR_RQE_ELEMENT_SIZE,
QED_CHAIN_PAGE_SIZE,
QED_CHAIN_MODE_PBL);
qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx,
in_params, &out_params);
if (!qp->qed_qp)
return -EINVAL;
/* Now we allocate the chain */
params.intended_use = QED_CHAIN_USE_TO_PRODUCE;
params.num_elems = n_sq_elems;
params.elem_size = QEDR_SQE_ELEMENT_SIZE;
params.ext_pbl_virt = out_params.sq_pbl_virt;
params.ext_pbl_phys = out_params.sq_pbl_phys;
rc = dev->ops->common->chain_alloc(dev->cdev, &qp->sq.pbl, ¶ms);
if (rc)
goto err;
params.intended_use = QED_CHAIN_USE_TO_CONSUME_PRODUCE;
params.num_elems = n_rq_elems;
params.elem_size = QEDR_RQE_ELEMENT_SIZE;
params.ext_pbl_virt = out_params.rq_pbl_virt;
params.ext_pbl_phys = out_params.rq_pbl_phys;
rc = dev->ops->common->chain_alloc(dev->cdev, &qp->rq.pbl, ¶ms);
if (rc)
goto err;
qp->qp_id = out_params.qp_id;
qp->icid = out_params.icid;
return qedr_set_iwarp_db_info(dev, qp);
err:
dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
return rc;
}
static void qedr_cleanup_kernel(struct qedr_dev *dev, struct qedr_qp *qp)
{
dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl);
kfree(qp->wqe_wr_id);
dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl);
kfree(qp->rqe_wr_id);
/* GSI qp is not registered to db mechanism so no need to delete */
if (qp->qp_type == IB_QPT_GSI)
return ;
qedr_db_recovery_del(dev, qp->sq.db, &qp->sq.db_data);
if (!qp->srq) {
qedr_db_recovery_del(dev, qp->rq.db, &qp->rq.db_data);
if (rdma_protocol_iwarp(&dev->ibdev, 1))
qedr_db_recovery_del(dev, qp->rq.iwarp_db2,
&qp->rq.iwarp_db2_data);
}
}
static int qedr_create_kernel_qp(struct qedr_dev *dev,
struct qedr_qp *qp,
struct ib_pd *ibpd,
struct ib_qp_init_attr *attrs)
{
struct qed_rdma_create_qp_in_params in_params;
struct qedr_pd *pd = get_qedr_pd(ibpd);
int rc = -EINVAL;
u32 n_rq_elems;
u32 n_sq_elems;
u32 n_sq_entries;
memset(&in_params, 0, sizeof (in_params));
qp->create_type = QEDR_QP_CREATE_KERNEL;
/* A single work request may take up to QEDR_MAX_SQ_WQE_SIZE elements in
* the ring. The ring should allow at least a single WR, even if the
* user requested none, due to allocation issues.
* We should add an extra WR since the prod and cons indices of
* wqe_wr_id are managed in such a way that the WQ is considered full
* when (prod+1)%max_wr==cons. We currently don't do that because we
* double the number of entries due an iSER issue that pushes far more
* WRs than indicated. If we decline its ib_post_send() then we get
* error prints in the dmesg we'd like to avoid.
*/
qp->sq.max_wr = min_t(u32, attrs->cap.max_send_wr * dev->wq_multiplier,
dev->attr.max_sqe);
qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof (*qp->wqe_wr_id),
GFP_KERNEL);
if (!qp->wqe_wr_id) {
DP_ERR(dev, "create qp: failed SQ shadow memory allocation\n" );
return -ENOMEM;
}
/* QP handle to be written in CQE */
in_params.qp_handle_lo = lower_32_bits((uintptr_t) qp);
in_params.qp_handle_hi = upper_32_bits((uintptr_t) qp);
/* A single work request may take up to QEDR_MAX_RQ_WQE_SIZE elements in
* the ring. There ring should allow at least a single WR, even if the
* user requested none, due to allocation issues.
*/
qp->rq.max_wr = (u16) max_t(u32, attrs->cap.max_recv_wr, 1);
/* Allocate driver internal RQ array */
qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof (*qp->rqe_wr_id),
GFP_KERNEL);
if (!qp->rqe_wr_id) {
DP_ERR(dev,
"create qp: failed RQ shadow memory allocation\n" );
kfree(qp->wqe_wr_id);
return -ENOMEM;
}
qedr_init_common_qp_in_params(dev, pd, qp, attrs, true , &in_params);
n_sq_entries = attrs->cap.max_send_wr;
n_sq_entries = min_t(u32, n_sq_entries, dev->attr.max_sqe);
n_sq_entries = max_t(u32, n_sq_entries, 1);
n_sq_elems = n_sq_entries * QEDR_MAX_SQE_ELEMENTS_PER_SQE;
n_rq_elems = qp->rq.max_wr * QEDR_MAX_RQE_ELEMENTS_PER_RQE;
if (rdma_protocol_iwarp(&dev->ibdev, 1))
rc = qedr_iwarp_create_kernel_qp(dev, qp, &in_params,
n_sq_elems, n_rq_elems);
else
rc = qedr_roce_create_kernel_qp(dev, qp, &in_params,
n_sq_elems, n_rq_elems);
if (rc)
qedr_cleanup_kernel(dev, qp);
return rc;
}
static int qedr_free_qp_resources(struct qedr_dev *dev, struct qedr_qp *qp,
struct ib_udata *udata)
{
struct qedr_ucontext *ctx =
rdma_udata_to_drv_context(udata, struct qedr_ucontext,
ibucontext);
int rc;
if (qp->qp_type != IB_QPT_GSI) {
rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
if (rc)
return rc;
}
if (qp->create_type == QEDR_QP_CREATE_USER)
qedr_cleanup_user(dev, ctx, qp);
else
qedr_cleanup_kernel(dev, qp);
return 0;
}
int qedr_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs,
struct ib_udata *udata)
{
struct qedr_xrcd *xrcd = NULL;
struct ib_pd *ibpd = ibqp->pd;
struct qedr_pd *pd = get_qedr_pd(ibpd);
struct qedr_dev *dev = get_qedr_dev(ibqp->device);
struct qedr_qp *qp = get_qedr_qp(ibqp);
int rc = 0;
if (attrs->create_flags)
return -EOPNOTSUPP;
if (attrs->qp_type == IB_QPT_XRC_TGT)
xrcd = get_qedr_xrcd(attrs->xrcd);
else
pd = get_qedr_pd(ibpd);
DP_DEBUG(dev, QEDR_MSG_QP, "create qp: called from %s, pd=%p\n" ,
udata ? "user library" : "kernel" , pd);
rc = qedr_check_qp_attrs(ibpd, dev, attrs, udata);
if (rc)
return rc;
DP_DEBUG(dev, QEDR_MSG_QP,
"create qp: called from %s, event_handler=%p, eepd=%p sq_cq=%p, sq_icid=%d, rq_cq=%p, rq_icid=%d\n" ,
udata ? "user library" : "kernel" , attrs->event_handler, pd,
get_qedr_cq(attrs->send_cq),
get_qedr_cq(attrs->send_cq)->icid,
get_qedr_cq(attrs->recv_cq),
attrs->recv_cq ? get_qedr_cq(attrs->recv_cq)->icid : 0);
qedr_set_common_qp_params(dev, qp, pd, attrs);
if (attrs->qp_type == IB_QPT_GSI)
return qedr_create_gsi_qp(dev, attrs, qp);
if (udata || xrcd)
rc = qedr_create_user_qp(dev, qp, ibpd, udata, attrs);
else
rc = qedr_create_kernel_qp(dev, qp, ibpd, attrs);
if (rc)
return rc;
qp->ibqp.qp_num = qp->qp_id;
if (rdma_protocol_iwarp(&dev->ibdev, 1)) {
rc = xa_insert(&dev->qps, qp->qp_id, qp, GFP_KERNEL);
if (rc)
goto out_free_qp_resources;
}
return 0;
out_free_qp_resources:
qedr_free_qp_resources(dev, qp, udata);
return -EFAULT;
}
static enum ib_qp_state qedr_get_ibqp_state(enum qed_roce_qp_state qp_state)
{
switch (qp_state) {
case QED_ROCE_QP_STATE_RESET:
return IB_QPS_RESET;
case QED_ROCE_QP_STATE_INIT:
return IB_QPS_INIT;
case QED_ROCE_QP_STATE_RTR:
return IB_QPS_RTR;
case QED_ROCE_QP_STATE_RTS:
return IB_QPS_RTS;
case QED_ROCE_QP_STATE_SQD:
return IB_QPS_SQD;
case QED_ROCE_QP_STATE_ERR:
return IB_QPS_ERR;
case QED_ROCE_QP_STATE_SQE:
return IB_QPS_SQE;
}
return IB_QPS_ERR;
}
static enum qed_roce_qp_state qedr_get_state_from_ibqp(
enum ib_qp_state qp_state)
{
switch (qp_state) {
case IB_QPS_RESET:
return QED_ROCE_QP_STATE_RESET;
case IB_QPS_INIT:
return QED_ROCE_QP_STATE_INIT;
case IB_QPS_RTR:
return QED_ROCE_QP_STATE_RTR;
case IB_QPS_RTS:
return QED_ROCE_QP_STATE_RTS;
case IB_QPS_SQD:
return QED_ROCE_QP_STATE_SQD;
case IB_QPS_ERR:
return QED_ROCE_QP_STATE_ERR;
default :
return QED_ROCE_QP_STATE_ERR;
}
}
static int qedr_update_qp_state(struct qedr_dev *dev,
struct qedr_qp *qp,
enum qed_roce_qp_state cur_state,
enum qed_roce_qp_state new_state)
{
int status = 0;
if (new_state == cur_state)
return 0;
switch (cur_state) {
case QED_ROCE_QP_STATE_RESET:
switch (new_state) {
case QED_ROCE_QP_STATE_INIT:
break ;
default :
status = -EINVAL;
break ;
}
break ;
case QED_ROCE_QP_STATE_INIT:
switch (new_state) {
case QED_ROCE_QP_STATE_RTR:
/* Update doorbell (in case post_recv was
* done before move to RTR)
*/
if (rdma_protocol_roce(&dev->ibdev, 1)) {
writel(qp->rq.db_data.raw, qp->rq.db);
}
break ;
case QED_ROCE_QP_STATE_ERR:
break ;
default :
/* Invalid state change. */
status = -EINVAL;
break ;
}
break ;
case QED_ROCE_QP_STATE_RTR:
/* RTR->XXX */
switch (new_state) {
case QED_ROCE_QP_STATE_RTS:
break ;
case QED_ROCE_QP_STATE_ERR:
break ;
default :
/* Invalid state change. */
status = -EINVAL;
break ;
}
break ;
case QED_ROCE_QP_STATE_RTS:
/* RTS->XXX */
switch (new_state) {
case QED_ROCE_QP_STATE_SQD:
break ;
case QED_ROCE_QP_STATE_ERR:
break ;
default :
/* Invalid state change. */
status = -EINVAL;
break ;
}
break ;
case QED_ROCE_QP_STATE_SQD:
/* SQD->XXX */
switch (new_state) {
case QED_ROCE_QP_STATE_RTS:
case QED_ROCE_QP_STATE_ERR:
break ;
default :
/* Invalid state change. */
status = -EINVAL;
break ;
}
break ;
case QED_ROCE_QP_STATE_ERR:
/* ERR->XXX */
switch (new_state) {
case QED_ROCE_QP_STATE_RESET:
if ((qp->rq.prod != qp->rq.cons) ||
(qp->sq.prod != qp->sq.cons)) {
DP_NOTICE(dev,
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=99 H=92 G=95
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland