From patchwork Tue Oct 5 06:54:07 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bernard Metzler X-Patchwork-Id: 66759 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 5662EB70A9 for ; Tue, 5 Oct 2010 17:54:35 +1100 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757668Ab0JEGyS (ORCPT ); Tue, 5 Oct 2010 02:54:18 -0400 Received: from mtagate2.de.ibm.com ([195.212.17.162]:42727 "EHLO mtagate2.de.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757658Ab0JEGyO (ORCPT ); Tue, 5 Oct 2010 02:54:14 -0400 Received: from d12nrmr1607.megacenter.de.ibm.com (d12nrmr1607.megacenter.de.ibm.com [9.149.167.49]) by mtagate2.de.ibm.com (8.13.1/8.13.1) with ESMTP id o956s8TC010763; Tue, 5 Oct 2010 06:54:08 GMT Received: from d12av02.megacenter.de.ibm.com (d12av02.megacenter.de.ibm.com [9.149.165.228]) by d12nrmr1607.megacenter.de.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id o956s8773948716; Tue, 5 Oct 2010 08:54:08 +0200 Received: from d12av02.megacenter.de.ibm.com (loopback [127.0.0.1]) by d12av02.megacenter.de.ibm.com (8.12.11.20060308/8.13.3) with ESMTP id o956s8hi028396; Tue, 5 Oct 2010 08:54:08 +0200 Received: from inn.zurich.ibm.com (inn.zurich.ibm.com [9.4.4.229]) by d12av02.megacenter.de.ibm.com (8.12.11.20060308/8.12.11) with ESMTP id o956s79Y028379; Tue, 5 Oct 2010 08:54:08 +0200 Received: from localhost.localdomain (achilles.zurich.ibm.com [9.4.243.2]) by inn.zurich.ibm.com (AIX5.3/8.13.4/8.13.4) with ESMTP id o956s7f0679952; Tue, 5 Oct 2010 08:54:07 +0200 From: Bernard Metzler To: netdev@vger.kernel.org Cc: linux-rdma@vger.kernel.org, Bernard Metzler Subject: [PATCH] SIW: User interface Date: Tue, 5 Oct 2010 08:54:07 +0200 Message-Id: <1286261647-5139-1-git-send-email-bmt@zurich.ibm.com> X-Mailer: git-send-email 1.5.4.3 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org --- drivers/infiniband/hw/siw/siw_ae.c | 96 ++ drivers/infiniband/hw/siw/siw_user.h | 66 ++ drivers/infiniband/hw/siw/siw_verbs.c | 1564 +++++++++++++++++++++++++++++++++ drivers/infiniband/hw/siw/siw_verbs.h | 96 ++ 4 files changed, 1822 insertions(+), 0 deletions(-) create mode 100644 drivers/infiniband/hw/siw/siw_ae.c create mode 100644 drivers/infiniband/hw/siw/siw_user.h create mode 100644 drivers/infiniband/hw/siw/siw_verbs.c create mode 100644 drivers/infiniband/hw/siw/siw_verbs.h diff --git a/drivers/infiniband/hw/siw/siw_ae.c b/drivers/infiniband/hw/siw/siw_ae.c new file mode 100644 index 0000000..7e9ab3f --- /dev/null +++ b/drivers/infiniband/hw/siw/siw_ae.c @@ -0,0 +1,96 @@ +/* + * Software iWARP device driver for Linux + * + * Authors: Bernard Metzler + * + * Copyright (c) 2008-2010, IBM 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 + * 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. + * + * - Neither the name of IBM nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "siw.h" +#include "siw_obj.h" +#include "siw_cm.h" + +/* + * siw_async_ev() + * + * Report Asynchonous event to user. + */ +void siw_async_ev(struct siw_qp *qp, struct siw_cq *cq, + enum ib_event_type etype) +{ + static struct ib_event event; + + dprint(DBG_EH, "(QP%d): AE type %d\n", QP_ID(qp), etype); + + event.event = etype; + event.device = qp->ofa_qp.device; + if (cq) + event.element.cq = &cq->ofa_cq; + else + event.element.qp = &qp->ofa_qp; + + if (!(qp->attrs.flags & SIW_QP_IN_DESTROY) && + qp->ofa_qp.event_handler) { + dprint(DBG_EH, "(QP%d): Call AEH\n", QP_ID(qp)); + (*qp->ofa_qp.event_handler)(&event, qp->ofa_qp.qp_context); + } +} + +void siw_async_srq_ev(struct siw_srq *srq, enum ib_event_type etype) +{ + static struct ib_event event; + + dprint(DBG_EH, "(SRQ%p): AE type %d\n", srq, etype); + + event.event = etype; + event.device = srq->ofa_srq.device; + event.element.srq = &srq->ofa_srq; + + if (srq->ofa_srq.event_handler) + (*srq->ofa_srq.event_handler)(&event, srq->ofa_srq.srq_context); +} diff --git a/drivers/infiniband/hw/siw/siw_user.h b/drivers/infiniband/hw/siw/siw_user.h new file mode 100644 index 0000000..ce7857d --- /dev/null +++ b/drivers/infiniband/hw/siw/siw_user.h @@ -0,0 +1,66 @@ +/* + * Software iWARP device driver for Linux + * + * Authors: Bernard Metzler + * + * Copyright (c) 2008-2010, IBM 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 + * 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. + * + * - Neither the name of IBM nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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. + */ + +#ifndef _SIW_USER_H +#define _SIW_USER_H + +/* + * user commands/command responses must correlate with the siw_abi + * in user land. + */ + +struct siw_uresp_create_cq { + __u32 cq_id; +}; + +struct siw_uresp_create_qp { + __u32 qp_id; + __u32 sq_size; + __u32 rq_size; +}; + +struct siw_uresp_reg_mr { + __u32 stag; +}; + +struct siw_ureq_reg_mr { + __u8 stag_key; + __u8 reserved[3]; +}; + +#endif diff --git a/drivers/infiniband/hw/siw/siw_verbs.c b/drivers/infiniband/hw/siw/siw_verbs.c new file mode 100644 index 0000000..8633b0f --- /dev/null +++ b/drivers/infiniband/hw/siw/siw_verbs.c @@ -0,0 +1,1564 @@ +/* + * Software iWARP device driver for Linux + * + * Authors: Bernard Metzler + * + * Copyright (c) 2008-2010, IBM 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 + * 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. + * + * - Neither the name of IBM nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "siw.h" +#include "siw_obj.h" +#include "siw_cm.h" + +static int ib_qp_state_to_siw_qp_state[IB_QPS_ERR+1] = { + [IB_QPS_RESET] = SIW_QP_STATE_IDLE, + [IB_QPS_INIT] = SIW_QP_STATE_IDLE, + [IB_QPS_RTR] = SIW_QP_STATE_RTR, + [IB_QPS_RTS] = SIW_QP_STATE_RTS, + [IB_QPS_SQD] = SIW_QP_STATE_CLOSING, + [IB_QPS_SQE] = SIW_QP_STATE_TERMINATE, + [IB_QPS_ERR] = SIW_QP_STATE_ERROR +}; + +static inline struct siw_mr *siw_mr_ofa2siw(struct ib_mr *ofa_mr) +{ + return container_of(ofa_mr, struct siw_mr, ofa_mr); +} + +static inline struct siw_pd *siw_pd_ofa2siw(struct ib_pd *ofa_pd) +{ + return container_of(ofa_pd, struct siw_pd, ofa_pd); +} + +static inline struct siw_ucontext *siw_ctx_ofa2siw( + struct ib_ucontext *ofa_ctx) +{ + return container_of(ofa_ctx, struct siw_ucontext, ib_ucontext); +} + +static inline struct siw_qp *siw_qp_ofa2siw(struct ib_qp *ofa_qp) +{ + return container_of(ofa_qp, struct siw_qp, ofa_qp); +} + +static inline struct siw_cq *siw_cq_ofa2siw(struct ib_cq *ofa_cq) +{ + return container_of(ofa_cq, struct siw_cq, ofa_cq); +} + +static inline struct siw_srq *siw_srq_ofa2siw(struct ib_srq *ofa_srq) +{ + return container_of(ofa_srq, struct siw_srq, ofa_srq); +} + +struct ib_ucontext *siw_alloc_ucontext(struct ib_device *ofa_dev, + struct ib_udata *udata) +{ + struct siw_ucontext *ctx; + + dprint(DBG_CM, "(device=%s)\n", ofa_dev->name); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + dprint(DBG_ON, " kzalloc\n"); + return ERR_PTR(-ENOMEM); + } + return &ctx->ib_ucontext; +} + +int siw_dealloc_ucontext(struct ib_ucontext *ctx) +{ + struct siw_ucontext *ucontext; + + ucontext = siw_ctx_ofa2siw(ctx); + + kfree(ucontext); + + return 0; +} + +int siw_query_device(struct ib_device *ofa_dev, struct ib_device_attr *attr) +{ + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + + memset(attr, 0, sizeof *attr); + + attr->max_mr_size = dev->attrs.max_mr_size; + attr->vendor_id = dev->attrs.vendor_id; + attr->vendor_part_id = dev->attrs.vendor_part_id; + attr->max_qp = dev->attrs.max_qp; + attr->max_qp_wr = dev->attrs.max_qp_wr; + + /* + * RDMA Read parameters: + * Max. ORD (Outbound Read queue Depth), a.k.a. max_initiator_depth + * Max. IRD (Inbound Read queue Depth), a.k.a. max_responder_resources + */ + attr->max_qp_rd_atom = dev->attrs.max_ord; + attr->max_qp_init_rd_atom = dev->attrs.max_ird; + attr->max_res_rd_atom = dev->attrs.max_qp * dev->attrs.max_ird; + attr->device_cap_flags = dev->attrs.cap_flags; + attr->max_sge = dev->attrs.max_sge; + attr->max_sge_rd = dev->attrs.max_sge_rd; + attr->max_cq = dev->attrs.max_cq; + attr->max_cqe = dev->attrs.max_cqe; + attr->max_mr = dev->attrs.max_mr; + attr->max_pd = dev->attrs.max_pd; + attr->max_mw = dev->attrs.max_mw; + attr->max_fmr = dev->attrs.max_fmr; + attr->max_srq = dev->attrs.max_srq; + attr->max_srq_wr = dev->attrs.max_srq_wr; + attr->max_srq_sge = dev->attrs.max_srq_sge; + + memcpy(&attr->sys_image_guid, dev->l2dev->dev_addr, 6); + + /* + * TODO: understand what of the following should + * get useful information + * + * attr->fw_ver; + * attr->max_ah + * attr->max_map_per_fmr + * attr->max_ee + * attr->max_rdd + * attr->max_ee_rd_atom; + * attr->max_ee_init_rd_atom; + * attr->max_raw_ipv6_qp + * attr->max_raw_ethy_qp + * attr->max_mcast_grp + * attr->max_mcast_qp_attach + * attr->max_total_mcast_qp_attach + * attr->max_pkeys + * attr->atomic_cap; + * attr->page_size_cap; + * attr->hw_ver; + * attr->local_ca_ack_delay; + */ + return 0; +} + +/* + * Approximate translation of real MTU for IB. + * + * TODO: is that needed for RNIC's? We may have a medium + * which reports MTU of 64kb and have to degrade to 4k?? + */ +static inline enum ib_mtu siw_mtu_net2ofa(unsigned short mtu) +{ + if (mtu >= 4096) + return IB_MTU_4096; + if (mtu >= 2048) + return IB_MTU_2048; + if (mtu >= 1024) + return IB_MTU_1024; + if (mtu >= 512) + return IB_MTU_512; + if (mtu >= 256) + return IB_MTU_256; + return -1; +} + +int siw_query_port(struct ib_device *ofa_dev, u8 port, + struct ib_port_attr *attr) +{ + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + + memset(attr, 0, sizeof *attr); + /* + * TODO: fully understand what to do here + */ + attr->state = IB_PORT_ACTIVE; /* ?? */ + attr->max_mtu = siw_mtu_net2ofa(dev->l2dev->mtu); + attr->active_mtu = attr->max_mtu; + attr->gid_tbl_len = 1; + attr->port_cap_flags = IB_PORT_CM_SUP; /* ?? */ + attr->port_cap_flags |= IB_PORT_DEVICE_MGMT_SUP; + attr->max_msg_sz = -1; + attr->pkey_tbl_len = 1; + attr->active_width = 2; + attr->active_speed = 2; + /* + * All zero + * + * attr->lid = 0; + * attr->bad_pkey_cntr = 0; + * attr->qkey_viol_cntr = 0; + * attr->sm_lid = 0; + * attr->lmc = 0; + * attr->max_vl_num = 0; + * attr->sm_sl = 0; + * attr->subnet_timeout = 0; + * attr->init_type_repy = 0; + * attr->phys_state = 0; + */ + return 0; +} + +int siw_query_pkey(struct ib_device *ofa_dev, u8 port, u16 idx, u16 *pkey) +{ + *pkey = 0; + return 0; +} + +int siw_query_gid(struct ib_device *ofa_dev, u8 port, int idx, + union ib_gid *gid) +{ + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + + /* subnet_prefix == interface_id == 0; */ + memset(gid, 0, sizeof *gid); + memcpy(&gid->raw[0], dev->l2dev->dev_addr, 6); + + return 0; +} + +struct ib_pd *siw_alloc_pd(struct ib_device *ofa_dev, + struct ib_ucontext *context, struct ib_udata *udata) +{ + struct siw_pd *pd = NULL; + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + int rv; + + if (atomic_inc_return(&dev->num_pd) > SIW_MAX_PD) { + dprint(DBG_ON, ": Out of PD's\n"); + rv = -ENOMEM; + goto err_out; + } + pd = kmalloc(sizeof *pd, GFP_KERNEL); + if (!pd) { + dprint(DBG_ON, ": malloc\n"); + rv = -ENOMEM; + goto err_out; + } + rv = siw_pd_add(dev, pd); + if (rv) { + dprint(DBG_ON, ": siw_pd_add\n"); + rv = -ENOMEM; + goto err_out; + } + if (context) { + if (ib_copy_to_udata(udata, &pd->hdr.id, sizeof pd->hdr.id)) { + rv = -EFAULT; + goto err_out_idr; + } + } + return &pd->ofa_pd; + +err_out_idr: + siw_remove_obj(&dev->idr_lock, &dev->pd_idr, &pd->hdr); +err_out: + kfree(pd); + atomic_dec(&dev->num_pd); + + return ERR_PTR(rv); +} + +int siw_dealloc_pd(struct ib_pd *ofa_pd) +{ + struct siw_pd *pd = siw_pd_ofa2siw(ofa_pd); + struct siw_dev *dev = siw_dev_ofa2siw(ofa_pd->device); + + siw_remove_obj(&dev->idr_lock, &dev->pd_idr, &pd->hdr); + siw_pd_put(pd); + + atomic_dec(&dev->num_pd); + return 0; +} + +struct ib_ah *siw_create_ah(struct ib_pd *pd, struct ib_ah_attr *attr) +{ + return ERR_PTR(-ENOSYS); +} + +int siw_destroy_ah(struct ib_ah *ah) +{ + return -ENOSYS; +} + + +void siw_qp_get_ref(struct ib_qp *ofa_qp) +{ + struct siw_qp *qp = siw_qp_ofa2siw(ofa_qp); + + dprint(DBG_OBJ|DBG_CM, "(QP%d): Get Reference\n", QP_ID(qp)); + siw_qp_get(qp); +} + + +void siw_qp_put_ref(struct ib_qp *ofa_qp) +{ + struct siw_qp *qp = siw_qp_ofa2siw(ofa_qp); + + dprint(DBG_OBJ|DBG_CM, "(QP%d): Put Reference\n", QP_ID(qp)); + siw_qp_put(qp); +} + +int siw_no_mad(struct ib_device *ofa_dev, int flags, u8 port, + struct ib_wc *wc, struct ib_grh *grh, + struct ib_mad *in_mad, struct ib_mad *out_mad) +{ + return -ENOSYS; +} + + +/* + * siw_create_qp() + * + * Create QP of requested size on given device. + * + * @ofa_pd: OFA PD contained in siw PD + * @attrs: Initial QP attributes. + * @udata: used to provide QP ID, SQ and RQ size back to user. + */ + +struct ib_qp *siw_create_qp(struct ib_pd *ofa_pd, struct ib_qp_init_attr *attrs, + struct ib_udata *udata) +{ + struct siw_qp *qp = NULL; + struct siw_pd *pd = siw_pd_ofa2siw(ofa_pd); + struct ib_device *ofa_dev = ofa_pd->device; + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + struct siw_cq *scq = NULL, *rcq = NULL; + struct siw_iwarp_tx *c_tx; + struct siw_iwarp_rx *c_rx; + struct siw_uresp_create_qp uresp; + + int rv = 0; + + dprint(DBG_OBJ|DBG_CM, ": new QP on device %s\n", + ofa_dev->name); + + if (atomic_inc_return(&dev->num_qp) > SIW_MAX_QP) { + dprint(DBG_ON, ": Out of QP's\n"); + rv = -ENOMEM; + goto err_out; + } + if (attrs->qp_type != IB_QPT_RC) { + dprint(DBG_ON, ": Only RC QP's supported\n"); + rv = -EINVAL; + goto err_out; + } + if ((attrs->cap.max_send_wr > SIW_MAX_QP_WR) || + (attrs->cap.max_recv_wr > SIW_MAX_QP_WR) || + (attrs->cap.max_send_sge > SIW_MAX_SGE) || + (attrs->cap.max_recv_sge > SIW_MAX_SGE)) { + dprint(DBG_ON, ": QP Size!\n"); + rv = -EINVAL; + goto err_out; + } + /* + * NOTE: we allow for zero element SQ and RQ WQE's SGL's + * but not for a QP unable to hold any WQE (SQ + RQ) + */ + if (attrs->cap.max_send_wr + attrs->cap.max_recv_wr == 0) { + rv = -EINVAL; + goto err_out; + } + + scq = siw_cq_id2obj(dev, ((struct siw_cq *)attrs->send_cq)->hdr.id); + rcq = siw_cq_id2obj(dev, ((struct siw_cq *)attrs->recv_cq)->hdr.id); + + if (!scq || !rcq) { + dprint(DBG_OBJ, ": Fail: SCQ: 0x%p, RCQ: 0x%p\n", + scq, rcq); + rv = -EINVAL; + goto err_out; + } + qp = kzalloc(sizeof(*qp), GFP_KERNEL); + if (!qp) { + dprint(DBG_ON, ": kzalloc\n"); + rv = -ENOMEM; + goto err_out; + } + + rv = siw_qp_add(dev, qp); + if (rv) + goto err_out; + + INIT_LIST_HEAD(&qp->wqe_freelist); + INIT_LIST_HEAD(&qp->sq); + INIT_LIST_HEAD(&qp->rq); + INIT_LIST_HEAD(&qp->orq); + INIT_LIST_HEAD(&qp->irq); + + init_rwsem(&qp->state_lock); + spin_lock_init(&qp->freelist_lock); + spin_lock_init(&qp->sq_lock); + spin_lock_init(&qp->rq_lock); + spin_lock_init(&qp->orq_lock); + + init_waitqueue_head(&qp->tx_ctx.waitq); + + qp->pd = pd; + qp->scq = scq; + qp->rcq = rcq; + + if (attrs->srq) { + /* + * SRQ support. + * Verbs 6.3.7: ignore RQ size, if SRQ present + * Verbs 6.3.5: do not check PD of SRQ against PD of QP + */ + qp->srq = siw_srq_ofa2siw(attrs->srq); + qp->attrs.rq_size = 0; + atomic_set(&qp->rq_space, 0); + dprint(DBG_OBJ, " QP(%d): SRQ(%p) attached\n", + QP_ID(qp), qp->srq); + } else { + qp->srq = NULL; + qp->attrs.rq_size = attrs->cap.max_recv_wr; + atomic_set(&qp->rq_space, qp->attrs.rq_size); + } + qp->attrs.sq_size = attrs->cap.max_send_wr; + atomic_set(&qp->sq_space, qp->attrs.sq_size); + qp->attrs.sq_max_sges = attrs->cap.max_send_sge; + /* + * ofed has no max_send_sge_rdmawrite + */ + qp->attrs.sq_max_sges_rdmaw = attrs->cap.max_send_sge; + qp->attrs.rq_max_sges = attrs->cap.max_recv_sge; + /* + * while not part of attrs we init ord/ird here + */ + qp->attrs.ord = dev->attrs.max_ord; + qp->attrs.ird = dev->attrs.max_ird; + + qp->attrs.state = SIW_QP_STATE_IDLE; + + if (udata) { + uresp.sq_size = qp->attrs.sq_size; + uresp.rq_size = qp->attrs.rq_size; + uresp.qp_id = QP_ID(qp); + + rv = ib_copy_to_udata(udata, &uresp, sizeof uresp); + if (rv) + goto err_out_idr; + } + c_tx = &qp->tx_ctx; + c_rx = &qp->rx_ctx; + + c_tx->crc_enabled = c_rx->crc_enabled = CONFIG_RDMA_SIW_CRC_ENFORCED; + + if (c_tx->crc_enabled) { + c_tx->mpa_crc_hd.tfm = + crypto_alloc_hash("crc32c", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(c_tx->mpa_crc_hd.tfm)) { + rv = -PTR_ERR(c_tx->mpa_crc_hd.tfm); + dprint(DBG_ON, "(QP%d): Failed loading crc32c" + " with error %d. ", QP_ID(qp), rv); + goto err_out_idr; + } + } + if (c_rx->crc_enabled) { + c_rx->mpa_crc_hd.tfm = + crypto_alloc_hash("crc32c", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(c_rx->mpa_crc_hd.tfm)) { + rv = -PTR_ERR(c_rx->mpa_crc_hd.tfm); + crypto_free_hash(c_tx->mpa_crc_hd.tfm); + goto err_out_idr; + } + } + atomic_set(&qp->tx_ctx.in_use, 0); + + qp->ofa_qp.qp_num = QP_ID(qp); + + siw_pd_get(pd); + + return &qp->ofa_qp; + +err_out_idr: + siw_remove_obj(&dev->idr_lock, &dev->qp_idr, &qp->hdr); +err_out: + if (scq) + siw_cq_put(scq); + if (rcq) + siw_cq_put(rcq); + + kfree(qp); + atomic_dec(&dev->num_qp); + + return ERR_PTR(rv); +} + +/* + * Minimum siw_query_qp() verb interface to allow for qperf application + * to run on siw. + * + * TODO: all. + */ +int siw_query_qp(struct ib_qp *qp, struct ib_qp_attr *qp_attr, + int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr) +{ + qp_attr->cap.max_inline_data = SIW_MAX_INLINE; + qp_init_attr->cap.max_inline_data = 0; + + return 0; +} + +int siw_ofed_modify_qp(struct ib_qp *ofa_qp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata) +{ + struct siw_qp_attrs new_attrs; + enum siw_qp_attr_mask siw_attr_mask = 0; + struct siw_qp *qp = siw_qp_ofa2siw(ofa_qp); + int rv; + + if (!attr_mask) { + dprint(DBG_CM, "(QP%d): attr_mask==0 ignored\n", QP_ID(qp)); + return 0; + } + siw_dprint_qp_attr_mask(attr_mask); + + memset(&new_attrs, 0, sizeof new_attrs); + + if (attr_mask & IB_QP_ACCESS_FLAGS) { + + siw_attr_mask |= SIW_QP_ATTR_ACCESS_FLAGS; + + if (attr->qp_access_flags & IB_ACCESS_REMOTE_READ) + new_attrs.flags |= SIW_RDMA_READ_ENABLED; + if (attr->qp_access_flags & IB_ACCESS_REMOTE_WRITE) + new_attrs.flags |= SIW_RDMA_WRITE_ENABLED; + if (attr->qp_access_flags & IB_ACCESS_MW_BIND) + new_attrs.flags |= SIW_RDMA_BIND_ENABLED; + } + if (attr_mask & IB_QP_STATE) { + dprint(DBG_CM, "(QP%d): Desired IB QP state: %s\n", + QP_ID(qp), ib_qp_state_to_string[attr->qp_state]); + + new_attrs.state = ib_qp_state_to_siw_qp_state[attr->qp_state]; + + if (new_attrs.state > SIW_QP_STATE_RTS) + qp->tx_ctx.tx_suspend = 1; + + /* TODO: SIW_QP_STATE_UNDEF is currently not possible ... */ + if (new_attrs.state == SIW_QP_STATE_UNDEF) + return -EINVAL; + + siw_attr_mask |= SIW_QP_ATTR_STATE; + } + if (!attr_mask) + return 0; + + down_write(&qp->state_lock); + + rv = siw_qp_modify(qp, &new_attrs, siw_attr_mask); + + up_write(&qp->state_lock); + return rv; +} + +int siw_destroy_qp(struct ib_qp *ofa_qp) +{ + struct ib_device *ofa_dev = ofa_qp->device; + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + struct siw_qp *qp = siw_qp_ofa2siw(ofa_qp); + struct siw_cep *cep; + struct siw_qp_attrs qp_attrs; + + dprint(DBG_CM, "(QP%d): SIW QP state=%d, cep=0x%p\n", + QP_ID(qp), qp->attrs.state, qp->cep); + + /* + * Mark QP as in process of destruction to prevent from eventual async + * callbacks to OFA core + */ + qp->attrs.flags |= SIW_QP_IN_DESTROY; + qp->rx_ctx.rx_suspend = 1; + + down_write(&qp->state_lock); + + qp_attrs.state = SIW_QP_STATE_ERROR; + (void)siw_qp_modify(qp, &qp_attrs, SIW_QP_ATTR_STATE); + + up_write(&qp->state_lock); + + cep = qp->cep; + if (cep) { + /* + * Wait if CM work is scheduled. calling siw_qp_modify() + * already dropped the network connection. + */ + dprint(DBG_CM, " (QP%d) (CEP 0x%p): %s (%d)\n", + QP_ID(qp), cep, atomic_read(&cep->ref.refcount) > 1 ? + "Wait for CM" : "CM done", + atomic_read(&cep->ref.refcount)); + + wait_event(cep->waitq, atomic_read(&cep->ref.refcount) == 1); + dprint(DBG_CM, "(QP%d): CM done 2\n", QP_ID(qp)); + qp->cep = 0; + siw_cep_put(cep); + } + + if (qp->rx_ctx.crc_enabled) + crypto_free_hash(qp->rx_ctx.mpa_crc_hd.tfm); + if (qp->tx_ctx.crc_enabled) + crypto_free_hash(qp->tx_ctx.mpa_crc_hd.tfm); + + siw_remove_obj(&dev->idr_lock, &dev->qp_idr, &qp->hdr); + + /* Drop references */ + siw_cq_put(qp->scq); + siw_cq_put(qp->rcq); + siw_pd_put(qp->pd); + qp->scq = qp->rcq = NULL; + + siw_qp_freeq_flush(qp); + + siw_qp_put(qp); + + atomic_dec(&dev->num_qp); + return 0; +} + +/* + * siw_copy_sgl() + * + * Copy SGL from user (OFA) representation to local + * representation. + * Memory lookup and base+bounds checks must + * be deferred until wqe gets executed + */ +static int siw_copy_sgl(struct ib_sge *ofa_sge, struct siw_sge *si_sge, + int num_sge) +{ + int bytes = 0; + + while (num_sge--) { + si_sge->addr = ofa_sge->addr; + si_sge->len = ofa_sge->length; + si_sge->lkey = ofa_sge->lkey; + /* + * defer memory lookup to WQE processing + */ + si_sge->mem.obj = NULL; + + bytes += si_sge->len; + si_sge++; ofa_sge++; + } + return bytes; +} + +/* + * siw_copy_inline_sgl() + * + * Prepare sgl of inlined data for sending. + * User provided sgl with unregistered user buffers. The function checks + * if the given buffer addresses and len's are within process context + * bounds and copies data into one kernel buffer. This implies dual copy + * operation in the tx path since TCP will make another copy for + * retransmission. There is room for efficiency improvement. + */ +static int siw_copy_inline_sgl(struct ib_sge *ofa_sge, struct siw_sge *si_sge, + int num_sge) +{ + char *kbuf; + int i, bytes = 0; + + if (unlikely(num_sge == 0)) + return 0; + + for (i = 0; i < num_sge; i++) { + struct ib_sge *sge = &ofa_sge[i]; + + if (unlikely(!access_ok(VERIFY_READ, sge->addr, sge->length))) + return -EFAULT; + + bytes += sge->length; + + if (bytes > SIW_MAX_INLINE) + return -EINVAL; + } + if (unlikely(!bytes)) + return 0; + + kbuf = kmalloc(bytes, GFP_KERNEL); + if (unlikely(!kbuf)) { + dprint(DBG_ON, " kmalloc\n"); + return -ENOMEM; + } + si_sge->mem.buf = kbuf; + + while (num_sge--) { + if (__copy_from_user(kbuf, + (void *)(unsigned long)ofa_sge->addr, + ofa_sge->length)) { + kfree(si_sge->mem.buf); + return -EFAULT; + } + kbuf += ofa_sge->length; + ofa_sge++; + } + si_sge->len = bytes; + si_sge->lkey = 0; + si_sge->addr = 0; /* don't need the user addr */ + return bytes; +} + + +/* + * siw_post_send() + * + * Post a list of S-WR's to a SQ. + * + * @ofa_qp: OFA QP contained in siw QP + * @wr: Null terminated list of user WR's + * @bad_wr: Points to failing WR in case of synchronous failure. + */ +int siw_post_send(struct ib_qp *ofa_qp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr) +{ + struct siw_wqe *wqe = NULL; + struct siw_qp *qp = siw_qp_ofa2siw(ofa_qp); + + unsigned long flags; + int rv = 0; + + dprint(DBG_WR|DBG_TX, "(QP%d): state=%d\n", + QP_ID(qp), qp->attrs.state); + + /* + * Acquire QP state lock for reading. The idea is that a + * user cannot move the QP out of RTS during TX/RX processing. + */ + down_read(&qp->state_lock); + + if (qp->attrs.state != SIW_QP_STATE_RTS) { + dprint(DBG_WR|DBG_ON, "(QP%d): state=%d\n", + QP_ID(qp), qp->attrs.state); + up_read(&qp->state_lock); + *bad_wr = wr; + return -ENOTCONN; + } + dprint(DBG_WR|DBG_TX, "(QP%d): sq_space(#1)=%d\n", + QP_ID(qp), atomic_read(&qp->sq_space)); + + while (wr) { + if (!atomic_read(&qp->sq_space)) { + dprint(DBG_ON, " sq_space\n"); + wqe = NULL; + rv = -ENOMEM; + break; + } + wqe = siw_wqe_get(qp, wr->opcode); + if (!wqe) { + dprint(DBG_ON, " siw_wqe_get\n"); + rv = -ENOMEM; + break; + } + if (wr->num_sge > qp->attrs.sq_max_sges) { + /* + * NOTE: we allow for zero length wr's here. + */ + dprint(DBG_WR, "(QP%d): Num SGE: %d\n", + QP_ID(qp), wr->num_sge); + rv = -EINVAL; + break; + } + wr_type(wqe) = wr->opcode; + wr_flags(wqe) = wr->send_flags; + wr_id(wqe) = wr->wr_id; + + if (SIW_INLINED_DATA(wqe)) + dprint(DBG_WR, "(QP%d): INLINE DATA\n", QP_ID(qp)); + + switch (wr->opcode) { + + case IB_WR_SEND: + if (!SIW_INLINED_DATA(wqe)) { + rv = siw_copy_sgl(wr->sg_list, wqe->wr.send.sge, + wr->num_sge); + wqe->wr.send.num_sge = wr->num_sge; + } else { + rv = siw_copy_inline_sgl(wr->sg_list, + wqe->wr.send.sge, + wr->num_sge); + wqe->wr.send.num_sge = 1; + } + if (rv <= 0) { + rv = -EINVAL; + break; + } + wqe->bytes = rv; + break; + + case IB_WR_RDMA_READ: + /* + * OFED WR restricts RREAD sink to SGL containing + * 1 SGE only. we could relax to SGL with multiple + * elements referring the SAME ltag or even sending + * a private per-rreq tag referring to a checked + * local sgl with MULTIPLE ltag's. would be easy + * to do... + */ + if (wr->num_sge != 1) { + rv = -EINVAL; + break; + } + rv = siw_copy_sgl(wr->sg_list, wqe->wr.rread.sge, 1); + /* + * NOTE: zero length RREAD is allowed! + */ + wqe->wr.rread.raddr = wr->wr.rdma.remote_addr; + wqe->wr.rread.rtag = wr->wr.rdma.rkey; + wqe->wr.rread.num_sge = 1; + wqe->bytes = rv; + break; + + case IB_WR_RDMA_WRITE: + if (!SIW_INLINED_DATA(wqe)) { + rv = siw_copy_sgl(wr->sg_list, wqe->wr.send.sge, + wr->num_sge); + wqe->wr.write.num_sge = wr->num_sge; + } else { + rv = siw_copy_inline_sgl(wr->sg_list, + wqe->wr.send.sge, + wr->num_sge); + wqe->wr.write.num_sge = min(1, wr->num_sge); + } + /* + * NOTE: zero length WRITE is allowed! + */ + if (rv < 0) { + rv = -EINVAL; + break; + } + wqe->wr.write.raddr = wr->wr.rdma.remote_addr; + wqe->wr.write.rtag = wr->wr.rdma.rkey; + wqe->bytes = rv; + break; + + default: + dprint(DBG_WR|DBG_TX, + "(QP%d): Opcode %d not yet implemented\n", + QP_ID(qp), wr->opcode); + rv = -EINVAL; + break; + } + dprint(DBG_WR|DBG_TX, "(QP%d): opcode %d, bytes %d, " + "flags 0x%x\n", + QP_ID(qp), wr_type(wqe), wqe->bytes, + wr_flags(wqe)); + if (rv < 0) + break; + + wqe->wr_status = SR_WR_QUEUED; + + lock_sq_rxsave(qp, flags); + list_add_tail(&wqe->list, &qp->sq); + atomic_dec(&qp->sq_space); + unlock_sq_rxsave(qp, flags); + + wr = wr->next; + } + /* + * Send directly if SQ processing is not in progress. + * Eventual immediate errors (rv < 0) do not affect the involved + * RI resources (Verbs, 8.3.1) and thus do not prevent from SQ + * processing, if new work is already pending. But rv must be passed + * to caller. + */ + lock_sq_rxsave(qp, flags); + + if (tx_wqe(qp) == NULL) { + struct siw_wqe *next = siw_next_tx_wqe(qp); + if (next != NULL) { + if (wr_type(next) != SIW_WR_RDMA_READ_REQ || + !ORD_SUSPEND_SQ(qp)) { + tx_wqe(qp) = next; + if (wr_type(next) != SIW_WR_RDMA_READ_REQ) + list_del_init(&next->list); + else + siw_rreq_queue(next, qp); + + unlock_sq_rxsave(qp, flags); + + dprint(DBG_WR|DBG_TX, + "(QP%d): Direct sending...\n", + QP_ID(qp)); + + if (siw_qp_sq_process(qp, 1) != 0 && + !(qp->tx_ctx.tx_suspend)) + siw_qp_cm_drop(qp, 0); + } else + unlock_sq_rxsave(qp, flags); + } else + unlock_sq_rxsave(qp, flags); + } else + unlock_sq_rxsave(qp, flags); + + up_read(&qp->state_lock); + + dprint(DBG_WR|DBG_TX, "(QP%d): sq_space(#2)=%d\n", QP_ID(qp), + atomic_read(&qp->sq_space)); + if (rv >= 0) + return 0; + /* + * Immediate error + */ + dprint(DBG_WR|DBG_ON, "(QP%d): error=%d\n", QP_ID(qp), rv); + + if (wqe != NULL) + siw_wqe_put(wqe); + *bad_wr = wr; + return rv; +} + +/* + * siw_post_receive() + * + * Post a list of R-WR's to a RQ. + * + * @ofa_qp: OFA QP contained in siw QP + * @wr: Null terminated list of user WR's + * @bad_wr: Points to failing WR in case of synchronous failure. + */ +int siw_post_receive(struct ib_qp *ofa_qp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct siw_wqe *wqe = NULL; + struct siw_qp *qp = siw_qp_ofa2siw(ofa_qp); + unsigned long flags; + int rv = 0; + + dprint(DBG_WR|DBG_TX, "(QP%d): state=%d\n", QP_ID(qp), + qp->attrs.state); + + if (qp->srq) + return -EOPNOTSUPP; /* what else from errno.h? */ + /* + * Acquire a QP state lock for reading. The idea is that a + * user cannot move the QP out of RTS during TX/RX processing. + */ + down_read(&qp->state_lock); + + if (qp->attrs.state > SIW_QP_STATE_RTS) { + up_read(&qp->state_lock); + dprint(DBG_ON, " (QP%d): state=%d\n", QP_ID(qp), + qp->attrs.state); + return -EINVAL; + } + while (wr) { + /* + * NOTE: siw_wqe_get() calls kzalloc(), which may sleep. + */ + if (!atomic_read(&qp->rq_space) || + !(wqe = siw_wqe_get(qp, SIW_WR_RECEIVE))) { + dprint(DBG_ON, " siw_wqe_get? (%d)\n", + atomic_read(&qp->rq_space)); + rv = -ENOMEM; + break; + } + if (wr->num_sge > qp->attrs.rq_max_sges) { + dprint(DBG_WR|DBG_ON, "(QP%d): Num SGE: %d\n", + QP_ID(qp), wr->num_sge); + rv = -EINVAL; + break; + } + wr_type(wqe) = SIW_WR_RECEIVE; + wr_id(wqe) = wr->wr_id; + + rv = siw_copy_sgl(wr->sg_list, wqe->wr.recv.sge, wr->num_sge); + if (rv < 0) { + /* + * XXX tentatively allow zero length receive + */ + rv = -EINVAL; + break; + } + wqe->wr.recv.num_sge = wr->num_sge; + wqe->bytes = rv; + + lock_rq_rxsave(qp, flags); + + list_add_tail(&wqe->list, &qp->rq); + wqe->wr_status = SR_WR_QUEUED; + atomic_dec(&qp->rq_space); + + unlock_rq_rxsave(qp, flags); + + wr = wr->next; + } + if (rv <= 0) { + dprint(DBG_WR|DBG_ON, "(QP%d): error=%d\n", QP_ID(qp), rv); + if (wqe != NULL) + siw_wqe_put(wqe); + *bad_wr = wr; + } + dprint(DBG_WR|DBG_RX, "(QP%d): rq_space=%d\n", QP_ID(qp), + atomic_read(&qp->rq_space)); + + up_read(&qp->state_lock); + + return rv > 0 ? 0 : rv; +} + +int siw_destroy_cq(struct ib_cq *ofa_cq) +{ + struct siw_cq *cq = siw_cq_ofa2siw(ofa_cq); + struct ib_device *ofa_dev = ofa_cq->device; + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + + siw_cq_flush(cq); + + siw_remove_obj(&dev->idr_lock, &dev->cq_idr, &cq->hdr); + siw_cq_put(cq); + atomic_dec(&dev->num_cq); + return 0; +} + +/* + * siw_create_cq() + * + * Create CQ of requested size on given device. + * + * @ofa_dev: OFA device contained in siw device + * @size: maximum number of CQE's allowed. + * @ib_context: user context. + * @udata: used to provide CQ ID back to user. + */ + +struct ib_cq *siw_create_cq(struct ib_device *ofa_dev, int size, + int vec /* unused */, + struct ib_ucontext *ib_context, + struct ib_udata *udata) +{ + struct siw_cq *cq = NULL; + struct siw_dev *dev = siw_dev_ofa2siw(ofa_dev); + struct siw_uresp_create_cq uresp; + int rv; + + if (atomic_inc_return(&dev->num_cq) > SIW_MAX_CQ) { + dprint(DBG_ON, ": Out of CQ's\n"); + rv = -ENOMEM; + goto err_out; + } + if (size < 1 || size > SIW_MAX_CQE) { + dprint(DBG_ON, ": CQE: %d\n", size); + rv = -EINVAL; + goto err_out; + } + cq = kmalloc(sizeof *cq, GFP_KERNEL); + if (!cq) { + dprint(DBG_ON, ": kmalloc\n"); + rv = -ENOMEM; + goto err_out; + } + cq->ofa_cq.cqe = size - 1; + + rv = siw_cq_add(dev, cq); + if (rv) + goto err_out_idr; + + INIT_LIST_HEAD(&cq->queue); + spin_lock_init(&cq->lock); + atomic_set(&cq->qlen, 0); + + if (ib_context) { + uresp.cq_id = OBJ_ID(cq); + + rv = ib_copy_to_udata(udata, &uresp, sizeof uresp); + if (rv) + goto err_out_idr; + } + return &cq->ofa_cq; + +err_out_idr: + siw_remove_obj(&dev->idr_lock, &dev->cq_idr, &cq->hdr); +err_out: + dprint(DBG_OBJ, ": CQ creation failed\n"); + + kfree(cq); + atomic_dec(&dev->num_cq); + + return ERR_PTR(rv); +} + +/* + * siw_poll_cq() + * + * Reap CQ entries if available and copy work completion status into + * array of WC's provided by caller. Returns number of reaped CQE's. + * + * @ofa_cq: OFA CQ contained in siw CQ. + * @num_cqe: Maximum number of CQE's to reap. + * @wc: Array of work completions to be filled by siw. + */ +int siw_poll_cq(struct ib_cq *ofa_cq, int num_cqe, struct ib_wc *wc) +{ + struct siw_cq *cq = siw_cq_ofa2siw(ofa_cq); + int i; + + for (i = 0; i < num_cqe; i++) { + if (!(siw_reap_cqe(cq, wc))) + break; + wc++; + } + dprint(DBG_WR, " CQ%d: reap %d comletions (%d left)\n", + OBJ_ID(cq), i, atomic_read(&cq->qlen)); + + return i; +} + +/* + * siw_req_notify_cq() + * + * Request notification for new CQE's added to that CQ. + * Defined flags: + * o SIW_CQ_NOTIFY_SOLICITED lets siw trigger a notification + * event if a WQE with notification flag set enters the CQ + * o SIW_CQ_NOTIFY_NEXT_COMP lets siw trigger a notification + * event if a WQE enters the CQ. + * o IB_CQ_REPORT_MISSED_EVENTS: return value will provide the + * number of not reaped CQE's regardless of its notification + * type and current or new CQ notification settings. + * + * @ofa_cq: OFA CQ contained in siw CQ. + * @flags: Requested notification flags. + */ +int siw_req_notify_cq(struct ib_cq *ofa_cq, enum ib_cq_notify_flags flags) +{ + struct siw_cq *cq = siw_cq_ofa2siw(ofa_cq); + + dprint(DBG_EH, "(CQ%d:) flags: 0x%8x\n", OBJ_ID(cq), flags); + + if ((flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED) + cq->notify = SIW_CQ_NOTIFY_SOLICITED; + else + cq->notify = SIW_CQ_NOTIFY_ALL; + + if (flags & IB_CQ_REPORT_MISSED_EVENTS) + return atomic_read(&cq->qlen); + + return 0; +} + +/* + * siw_dereg_mr() + * + * Release Memory Region. + * + * TODO: Update function if Memory Windows are supported by siw: + * Is OFED core checking for MW dependencies for current + * MR before calling MR deregistration?. + * + * @ofa_mr: OFA MR contained in siw MR. + */ +int siw_dereg_mr(struct ib_mr *ofa_mr) +{ + struct siw_mr *mr; + struct siw_dev *dev = siw_dev_ofa2siw(ofa_mr->device); + + mr = siw_mr_ofa2siw(ofa_mr); + + dprint(DBG_OBJ|DBG_MM, "(MEM%d): Release UMem %p, #ref's: %d\n", + mr->mem.hdr.id, mr->umem, + atomic_read(&mr->mem.hdr.ref.refcount)); + + mr->mem.stag_state = STAG_INVALID; + + siw_pd_put(mr->pd); + siw_remove_obj(&dev->idr_lock, &dev->mem_idr, &mr->mem.hdr); + siw_mem_put(&mr->mem); + + atomic_dec(&dev->num_mem); + return 0; +} + +/* + * siw_reg_user_mr() + * + * Register Memory Region. + * + * @ofa_pd: OFA PD contained in siw PD. + * @start: starting address of MR (virtual address) + * @len: len of MR + * @rnic_va: not used by siw + * @rights: MR access rights + * @udata: user buffer to communicate STag and Key. + */ +struct ib_mr *siw_reg_user_mr(struct ib_pd *ofa_pd, u64 start, u64 len, + u64 rnic_va, int rights, struct ib_udata *udata) +{ + struct siw_mr *mr = NULL; + struct siw_pd *pd = siw_pd_ofa2siw(ofa_pd); + struct ib_umem *umem = NULL; + struct siw_ureq_reg_mr ureq; + struct siw_uresp_reg_mr uresp; + struct siw_dev *dev = pd->hdr.dev; + int rv; + + dprint(DBG_MM|DBG_OBJ, " start: 0x%016llx, " + "va: 0x%016llx, len: %llu, ctx: %p\n", + (unsigned long long)start, + (unsigned long long)rnic_va, + (unsigned long long)len, + ofa_pd->uobject->context); + + if (atomic_inc_return(&dev->num_mem) > SIW_MAX_MR) { + dprint(DBG_ON, ": Out of MRs: %d\n", + atomic_read(&dev->num_mem)); + rv = -ENOMEM; + goto err_out; + } + if (!len) { + rv = -EINVAL; + goto err_out; + } + + umem = ib_umem_get(ofa_pd->uobject->context, start, len, rights, 0); + if (IS_ERR(umem)) { + dprint(DBG_MM, " ib_umem_get:%ld LOCKED:%lu, LIMIT:%lu\n", + PTR_ERR(umem), current->mm->locked_vm, + current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> + PAGE_SHIFT); + rv = -PTR_ERR(umem); + umem = NULL; + goto err_out; + } + mr = kmalloc(sizeof *mr, GFP_KERNEL); + if (!mr) { + dprint(DBG_ON, ": malloc\n"); + rv = -ENOMEM; + goto err_out; + } + mr->mem.stag_state = STAG_INVALID; + + if (siw_mem_add(dev, &mr->mem) < 0) { + dprint(DBG_ON, ": siw_mem_add\n"); + rv = -ENOMEM; + goto err_out; + } + dprint(DBG_OBJ|DBG_MM, "(MEM%d): New Object, UMEM %p\n", + mr->mem.hdr.id, umem); + + mr->ofa_mr.lkey = mr->ofa_mr.rkey = mr->mem.hdr.id << 8; + + mr->mem.va = start; + mr->mem.len = len; + mr->mem.fbo = 0 ; + mr->mem.mr = NULL; + mr->mem.perms = SR_MEM_LREAD | /* not selectable in OFA */ + (rights & IB_ACCESS_REMOTE_READ ? SR_MEM_RREAD : 0) | + (rights & IB_ACCESS_LOCAL_WRITE ? SR_MEM_LWRITE : 0) | + (rights & IB_ACCESS_REMOTE_WRITE ? SR_MEM_RWRITE : 0); + + mr->umem = umem; + + if (udata) { + rv = ib_copy_from_udata(&ureq, udata, sizeof ureq); + if (rv) + goto err_out_idr; + + mr->ofa_mr.lkey |= ureq.stag_key; + mr->ofa_mr.rkey |= ureq.stag_key; /* XXX ??? */ + uresp.stag = mr->ofa_mr.lkey; + + rv = ib_copy_to_udata(udata, &uresp, sizeof uresp); + if (rv) + goto err_out_idr; + } + mr->pd = pd; + siw_pd_get(pd); + + mr->mem.stag_state = STAG_VALID; + + return &mr->ofa_mr; + +err_out_idr: + siw_remove_obj(&dev->idr_lock, &dev->mem_idr, &mr->mem.hdr); +err_out: + if (umem) + ib_umem_release(umem); + + kfree(mr); + + atomic_dec(&dev->num_mem); + + return ERR_PTR(rv); +} + +/* + * siw_create_srq() + * + * Create Shared Receive Queue of attributes @init_attrs + * within protection domain given by @ofa_pd. + * + * @ofa_pd: OFA PD contained in siw PD. + * @init_attrs: SRQ init attributes. + * @udata: not used by siw. + */ +struct ib_srq *siw_create_srq(struct ib_pd *ofa_pd, + struct ib_srq_init_attr *init_attrs, + struct ib_udata *udata) +{ + struct siw_srq *srq = NULL; + struct ib_srq_attr *attrs = &init_attrs->attr; + struct siw_pd *pd = siw_pd_ofa2siw(ofa_pd); + struct siw_dev *dev = pd->hdr.dev; + int rv; + + if (atomic_inc_return(&dev->num_srq) > SIW_MAX_SRQ) { + dprint(DBG_ON, " Out of SRQ's\n"); + rv = -ENOMEM; + goto err_out; + } + if (attrs->max_wr > SIW_MAX_SRQ_WR || attrs->max_sge > SIW_MAX_SGE || + attrs->srq_limit > attrs->max_wr) { + rv = -EINVAL; + goto err_out; + } + + srq = kmalloc(sizeof *srq, GFP_KERNEL); + if (!srq) { + dprint(DBG_ON, " malloc\n"); + rv = -ENOMEM; + goto err_out; + } + INIT_LIST_HEAD(&srq->rq); + srq->max_sge = attrs->max_sge; + atomic_set(&srq->space, attrs->max_wr); + srq->limit = attrs->srq_limit; + if (srq->limit) + srq->armed = 1; + + srq->pd = pd; + siw_pd_get(pd); + + spin_lock_init(&srq->lock); + + return &srq->ofa_srq; + +err_out: + kfree(srq); + atomic_dec(&dev->num_srq); + + return ERR_PTR(rv); +} + +/* + * siw_modify_srq() + * + * Modify SRQ. The caller may resize SRQ and/or set/reset notification + * limit and (re)arm IB_EVENT_SRQ_LIMIT_REACHED notification. + * + * NOTE: it is unclear if OFA allows for changing the MAX_SGE + * parameter. siw_modify_srq() does not check the attrs->max_sge param. + */ +int siw_modify_srq(struct ib_srq *ofa_srq, struct ib_srq_attr *attrs, + enum ib_srq_attr_mask attr_mask, struct ib_udata *udata) +{ + struct siw_srq *srq = siw_srq_ofa2siw(ofa_srq); + unsigned long flags; + int rv = 0; + + lock_srq_rxsave(srq, flags); + + if (attr_mask & IB_SRQ_MAX_WR) { + /* resize request */ + if (attrs->max_wr > SIW_MAX_SRQ_WR) { + rv = -EINVAL; + goto out; + } + if (attrs->max_wr < srq->max_wr) { /* shrink */ + if (attrs->max_wr < + srq->max_wr - atomic_read(&srq->space)) { + rv = -EBUSY; + goto out; + } + atomic_sub(srq->max_wr - attrs->max_wr, &srq->space); + } else /* grow */ + atomic_add(attrs->max_wr - srq->max_wr, &srq->space); + srq->max_wr = attrs->max_wr; + } + if (attr_mask & IB_SRQ_LIMIT) { + if (attrs->srq_limit) { + if (attrs->srq_limit > srq->max_wr) { + rv = -EINVAL; + /* FIXME: restore old space & max_wr?? */ + goto out; + } + srq->armed = 1; + } else + srq->armed = 0; + + srq->limit = attrs->srq_limit; + } +out: + unlock_srq_rxsave(srq, flags); + return rv; +} + +/* + * siw_query_srq() + * + * Query SRQ attributes. + */ +int siw_query_srq(struct ib_srq *ofa_srq, struct ib_srq_attr *attrs) +{ + struct siw_srq *srq = siw_srq_ofa2siw(ofa_srq); + unsigned long flags; + + lock_srq_rxsave(srq, flags); + + attrs->max_wr = srq->max_wr; + attrs->max_sge = srq->max_sge; + attrs->srq_limit = srq->limit; + + unlock_srq_rxsave(srq, flags); + + return 0; +} + +/* + * siw_destroy_srq() + * + * Destroy SRQ. + * SRQ WQE's are silently destroyed, since not belonging to any QP. + * Furthermore, it is assumed that the SRQ is not referenced by any + * QP anymore - the code trusts the OFA environment to keep track + * of QP references. + */ +int siw_destroy_srq(struct ib_srq *ofa_srq) +{ + struct list_head *listp, *tmp; + struct siw_srq *srq = siw_srq_ofa2siw(ofa_srq); + struct siw_dev *dev = srq->pd->hdr.dev; + unsigned long flags; + + lock_srq_rxsave(srq, flags); /* probably not necessary */ + list_for_each_safe(listp, tmp, &srq->rq) { + list_del(listp); + siw_wqe_put(list_entry(listp, struct siw_wqe, list)); + } + unlock_srq_rxsave(srq, flags); + + siw_pd_put(srq->pd); + kfree(srq); + atomic_dec(&dev->num_srq); + + return 0; +} + +/* + * siw_post_srq_recv() + * + * Post a list of receive queue elements to SRQ. + * NOTE: The function does not check or lock a certain SRQ state + * during the post operation. The code simply trusts the + * OFA environment. + * + * @ofa_srq: OFA SRQ contained in siw SRQ + * @wr: List of R-WR's + * @bad_wr: Updated to failing WR if posting fails. + */ +int siw_post_srq_recv(struct ib_srq *ofa_srq, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct siw_srq *srq = siw_srq_ofa2siw(ofa_srq); + struct siw_wqe *wqe = NULL; + unsigned long flags; + int rv = 0; + + while (wr) { + if (!atomic_read(&srq->space) || + !(wqe = siw_srq_wqe_get(srq))) { + dprint(DBG_ON, " siw_srq_wqe_get\n"); + rv = -ENOMEM; + break; + } + if (!wr->num_sge || wr->num_sge > srq->max_sge) { + dprint(DBG_WR|DBG_ON, + "(SRQ%p): Num SGE: %d\n", srq, wr->num_sge); + rv = -EINVAL; + break; + } + wr_type(wqe) = SIW_WR_RECEIVE; + wr_id(wqe) = wr->wr_id; + wqe->wr_status = SR_WR_QUEUED; + + rv = siw_copy_sgl(wr->sg_list, wqe->wr.recv.sge, wr->num_sge); + if (rv == 0) { + /* + * do not allow zero length receive + * XXX correct? + */ + rv = -EINVAL; + break; + } + wqe->wr.recv.num_sge = wr->num_sge; + wqe->bytes = rv; + + lock_srq_rxsave(srq, flags); + + list_add_tail(&wqe->list, &srq->rq); + atomic_dec(&srq->space); + + unlock_srq_rxsave(srq, flags); + + wr = wr->next; + } + if (rv <= 0) { + dprint(DBG_WR|DBG_ON, "(SRQ %p): error=%d\n", + srq, rv); + + if (wqe != NULL) + siw_wqe_put(wqe); + *bad_wr = wr; + } + dprint(DBG_WR|DBG_RX, "(SRQ%p): space=%d\n", + srq, atomic_read(&srq->space)); + + return rv > 0 ? 0 : rv; +} + + +struct ib_mr *siw_get_dma_mr(struct ib_pd *pd, int rights) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +int siw_mmap(struct ib_ucontext *ctx, struct vm_area_struct *vma) +{ + return -ENOSYS; +} diff --git a/drivers/infiniband/hw/siw/siw_verbs.h b/drivers/infiniband/hw/siw/siw_verbs.h new file mode 100644 index 0000000..53eac4f --- /dev/null +++ b/drivers/infiniband/hw/siw/siw_verbs.h @@ -0,0 +1,96 @@ +/* + * Software iWARP device driver for Linux + * + * Authors: Bernard Metzler + * + * Copyright (c) 2008-2010, IBM 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 + * 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. + * + * - Neither the name of IBM nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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. + */ + +#ifndef _SIW_VERBS_H +#define _SIW_VERBS_H + +#include + +#include +#include +#include +#include + +#include "siw.h" +#include "siw_cm.h" + +extern int siw_query_device(struct ib_device *, struct ib_device_attr *); + +extern struct ib_ucontext *siw_alloc_ucontext(struct ib_device *, + struct ib_udata *); +extern int siw_dealloc_ucontext(struct ib_ucontext *); +extern int siw_query_port(struct ib_device *, u8, struct ib_port_attr *); +extern int siw_query_pkey(struct ib_device *, u8, u16, u16 *); +extern int siw_query_gid(struct ib_device *, u8, int, union ib_gid *); + +extern struct ib_pd *siw_alloc_pd(struct ib_device *, struct ib_ucontext *, + struct ib_udata *); +extern int siw_dealloc_pd(struct ib_pd *); +extern struct ib_ah *siw_create_ah(struct ib_pd *, struct ib_ah_attr *); +extern int siw_destroy_ah(struct ib_ah *); +extern struct ib_qp *siw_create_qp(struct ib_pd *, struct ib_qp_init_attr *, + struct ib_udata *); +extern int siw_query_qp(struct ib_qp *, struct ib_qp_attr *, int, + struct ib_qp_init_attr *); +extern int siw_ofed_modify_qp(struct ib_qp *, struct ib_qp_attr *, int, + struct ib_udata *); +extern int siw_destroy_qp(struct ib_qp *); +extern int siw_post_send(struct ib_qp *, struct ib_send_wr *, + struct ib_send_wr **); +extern int siw_post_receive(struct ib_qp *, struct ib_recv_wr *, + struct ib_recv_wr **); +extern struct ib_cq *siw_create_cq(struct ib_device *, int, int, + struct ib_ucontext *, struct ib_udata *); +extern int siw_destroy_cq(struct ib_cq *); +extern int siw_poll_cq(struct ib_cq *, int num_entries, struct ib_wc *); +extern int siw_req_notify_cq(struct ib_cq *, enum ib_cq_notify_flags); +extern struct ib_mr *siw_reg_user_mr(struct ib_pd *, u64, u64, u64, int, + struct ib_udata *); +extern struct ib_mr *siw_get_dma_mr(struct ib_pd *, int); +extern int siw_dereg_mr(struct ib_mr *); +extern struct ib_srq *siw_create_srq(struct ib_pd *, struct ib_srq_init_attr *, + struct ib_udata *); +extern int siw_modify_srq(struct ib_srq *, struct ib_srq_attr *, + enum ib_srq_attr_mask, struct ib_udata *); +extern int siw_query_srq(struct ib_srq *, struct ib_srq_attr *); +extern int siw_destroy_srq(struct ib_srq *); +extern int siw_post_srq_recv(struct ib_srq *, struct ib_recv_wr *, + struct ib_recv_wr **); +extern int siw_mmap(struct ib_ucontext *, struct vm_area_struct *); + +#endif