mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-22 18:44:44 +08:00
dd5f03beb4
This patch add the support for Ethernet L2 attributes in the verbs/cm/cma structures. When dealing with L2 Ethernet, we should use smac, dmac, vlan ID and priority in a similar manner that the IB L2 (and the L4 PKEY) attributes are used. Thus, those attributes were added to the following structures: * ib_ah_attr - added dmac * ib_qp_attr - added smac and vlan_id, (sl remains vlan priority) * ib_wc - added smac, vlan_id * ib_sa_path_rec - added smac, dmac, vlan_id * cm_av - added smac and vlan_id For the path record structure, extra care was taken to avoid the new fields when packing it into wire format, so we don't break the IB CM and SA wire protocol. On the active side, the CM fills. its internal structures from the path provided by the ULP. We add there taking the ETH L2 attributes and placing them into the CM Address Handle (struct cm_av). On the passive side, the CM fills its internal structures from the WC associated with the REQ message. We add there taking the ETH L2 attributes from the WC. When the HW driver provides the required ETH L2 attributes in the WC, they set the IB_WC_WITH_SMAC and IB_WC_WITH_VLAN flags. The IB core code checks for the presence of these flags, and in their absence does address resolution from the ib_init_ah_from_wc() helper function. ib_modify_qp_is_ok is also updated to consider the link layer. Some parameters are mandatory for Ethernet link layer, while they are irrelevant for IB. Vendor drivers are modified to support the new function signature. Signed-off-by: Matan Barak <matanb@mellanox.com> Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: Roland Dreier <roland@purestorage.com>
2258 lines
63 KiB
C
2258 lines
63 KiB
C
/*
|
|
* IBM eServer eHCA Infiniband device driver for Linux on POWER
|
|
*
|
|
* QP functions
|
|
*
|
|
* Authors: Joachim Fenkes <fenkes@de.ibm.com>
|
|
* Stefan Roscher <stefan.roscher@de.ibm.com>
|
|
* Waleri Fomin <fomin@de.ibm.com>
|
|
* Hoang-Nam Nguyen <hnguyen@de.ibm.com>
|
|
* Reinhard Ernst <rernst@de.ibm.com>
|
|
* Heiko J Schick <schickhj@de.ibm.com>
|
|
*
|
|
* Copyright (c) 2005 IBM Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This source code is distributed under a dual license of GPL v2.0 and OpenIB
|
|
* BSD.
|
|
*
|
|
* OpenIB BSD License
|
|
*
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include "ehca_classes.h"
|
|
#include "ehca_tools.h"
|
|
#include "ehca_qes.h"
|
|
#include "ehca_iverbs.h"
|
|
#include "hcp_if.h"
|
|
#include "hipz_fns.h"
|
|
|
|
static struct kmem_cache *qp_cache;
|
|
|
|
/*
|
|
* attributes not supported by query qp
|
|
*/
|
|
#define QP_ATTR_QUERY_NOT_SUPPORTED (IB_QP_ACCESS_FLAGS | \
|
|
IB_QP_EN_SQD_ASYNC_NOTIFY)
|
|
|
|
/*
|
|
* ehca (internal) qp state values
|
|
*/
|
|
enum ehca_qp_state {
|
|
EHCA_QPS_RESET = 1,
|
|
EHCA_QPS_INIT = 2,
|
|
EHCA_QPS_RTR = 3,
|
|
EHCA_QPS_RTS = 5,
|
|
EHCA_QPS_SQD = 6,
|
|
EHCA_QPS_SQE = 8,
|
|
EHCA_QPS_ERR = 128
|
|
};
|
|
|
|
/*
|
|
* qp state transitions as defined by IB Arch Rel 1.1 page 431
|
|
*/
|
|
enum ib_qp_statetrans {
|
|
IB_QPST_ANY2RESET,
|
|
IB_QPST_ANY2ERR,
|
|
IB_QPST_RESET2INIT,
|
|
IB_QPST_INIT2RTR,
|
|
IB_QPST_INIT2INIT,
|
|
IB_QPST_RTR2RTS,
|
|
IB_QPST_RTS2SQD,
|
|
IB_QPST_RTS2RTS,
|
|
IB_QPST_SQD2RTS,
|
|
IB_QPST_SQE2RTS,
|
|
IB_QPST_SQD2SQD,
|
|
IB_QPST_MAX /* nr of transitions, this must be last!!! */
|
|
};
|
|
|
|
/*
|
|
* ib2ehca_qp_state maps IB to ehca qp_state
|
|
* returns ehca qp state corresponding to given ib qp state
|
|
*/
|
|
static inline enum ehca_qp_state ib2ehca_qp_state(enum ib_qp_state ib_qp_state)
|
|
{
|
|
switch (ib_qp_state) {
|
|
case IB_QPS_RESET:
|
|
return EHCA_QPS_RESET;
|
|
case IB_QPS_INIT:
|
|
return EHCA_QPS_INIT;
|
|
case IB_QPS_RTR:
|
|
return EHCA_QPS_RTR;
|
|
case IB_QPS_RTS:
|
|
return EHCA_QPS_RTS;
|
|
case IB_QPS_SQD:
|
|
return EHCA_QPS_SQD;
|
|
case IB_QPS_SQE:
|
|
return EHCA_QPS_SQE;
|
|
case IB_QPS_ERR:
|
|
return EHCA_QPS_ERR;
|
|
default:
|
|
ehca_gen_err("invalid ib_qp_state=%x", ib_qp_state);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ehca2ib_qp_state maps ehca to IB qp_state
|
|
* returns ib qp state corresponding to given ehca qp state
|
|
*/
|
|
static inline enum ib_qp_state ehca2ib_qp_state(enum ehca_qp_state
|
|
ehca_qp_state)
|
|
{
|
|
switch (ehca_qp_state) {
|
|
case EHCA_QPS_RESET:
|
|
return IB_QPS_RESET;
|
|
case EHCA_QPS_INIT:
|
|
return IB_QPS_INIT;
|
|
case EHCA_QPS_RTR:
|
|
return IB_QPS_RTR;
|
|
case EHCA_QPS_RTS:
|
|
return IB_QPS_RTS;
|
|
case EHCA_QPS_SQD:
|
|
return IB_QPS_SQD;
|
|
case EHCA_QPS_SQE:
|
|
return IB_QPS_SQE;
|
|
case EHCA_QPS_ERR:
|
|
return IB_QPS_ERR;
|
|
default:
|
|
ehca_gen_err("invalid ehca_qp_state=%x", ehca_qp_state);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ehca_qp_type used as index for req_attr and opt_attr of
|
|
* struct ehca_modqp_statetrans
|
|
*/
|
|
enum ehca_qp_type {
|
|
QPT_RC = 0,
|
|
QPT_UC = 1,
|
|
QPT_UD = 2,
|
|
QPT_SQP = 3,
|
|
QPT_MAX
|
|
};
|
|
|
|
/*
|
|
* ib2ehcaqptype maps Ib to ehca qp_type
|
|
* returns ehca qp type corresponding to ib qp type
|
|
*/
|
|
static inline enum ehca_qp_type ib2ehcaqptype(enum ib_qp_type ibqptype)
|
|
{
|
|
switch (ibqptype) {
|
|
case IB_QPT_SMI:
|
|
case IB_QPT_GSI:
|
|
return QPT_SQP;
|
|
case IB_QPT_RC:
|
|
return QPT_RC;
|
|
case IB_QPT_UC:
|
|
return QPT_UC;
|
|
case IB_QPT_UD:
|
|
return QPT_UD;
|
|
default:
|
|
ehca_gen_err("Invalid ibqptype=%x", ibqptype);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static inline enum ib_qp_statetrans get_modqp_statetrans(int ib_fromstate,
|
|
int ib_tostate)
|
|
{
|
|
int index = -EINVAL;
|
|
switch (ib_tostate) {
|
|
case IB_QPS_RESET:
|
|
index = IB_QPST_ANY2RESET;
|
|
break;
|
|
case IB_QPS_INIT:
|
|
switch (ib_fromstate) {
|
|
case IB_QPS_RESET:
|
|
index = IB_QPST_RESET2INIT;
|
|
break;
|
|
case IB_QPS_INIT:
|
|
index = IB_QPST_INIT2INIT;
|
|
break;
|
|
}
|
|
break;
|
|
case IB_QPS_RTR:
|
|
if (ib_fromstate == IB_QPS_INIT)
|
|
index = IB_QPST_INIT2RTR;
|
|
break;
|
|
case IB_QPS_RTS:
|
|
switch (ib_fromstate) {
|
|
case IB_QPS_RTR:
|
|
index = IB_QPST_RTR2RTS;
|
|
break;
|
|
case IB_QPS_RTS:
|
|
index = IB_QPST_RTS2RTS;
|
|
break;
|
|
case IB_QPS_SQD:
|
|
index = IB_QPST_SQD2RTS;
|
|
break;
|
|
case IB_QPS_SQE:
|
|
index = IB_QPST_SQE2RTS;
|
|
break;
|
|
}
|
|
break;
|
|
case IB_QPS_SQD:
|
|
if (ib_fromstate == IB_QPS_RTS)
|
|
index = IB_QPST_RTS2SQD;
|
|
break;
|
|
case IB_QPS_SQE:
|
|
break;
|
|
case IB_QPS_ERR:
|
|
index = IB_QPST_ANY2ERR;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
/*
|
|
* ibqptype2servicetype returns hcp service type corresponding to given
|
|
* ib qp type used by create_qp()
|
|
*/
|
|
static inline int ibqptype2servicetype(enum ib_qp_type ibqptype)
|
|
{
|
|
switch (ibqptype) {
|
|
case IB_QPT_SMI:
|
|
case IB_QPT_GSI:
|
|
return ST_UD;
|
|
case IB_QPT_RC:
|
|
return ST_RC;
|
|
case IB_QPT_UC:
|
|
return ST_UC;
|
|
case IB_QPT_UD:
|
|
return ST_UD;
|
|
case IB_QPT_RAW_IPV6:
|
|
return -EINVAL;
|
|
case IB_QPT_RAW_ETHERTYPE:
|
|
return -EINVAL;
|
|
default:
|
|
ehca_gen_err("Invalid ibqptype=%x", ibqptype);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* init userspace queue info from ipz_queue data
|
|
*/
|
|
static inline void queue2resp(struct ipzu_queue_resp *resp,
|
|
struct ipz_queue *queue)
|
|
{
|
|
resp->qe_size = queue->qe_size;
|
|
resp->act_nr_of_sg = queue->act_nr_of_sg;
|
|
resp->queue_length = queue->queue_length;
|
|
resp->pagesize = queue->pagesize;
|
|
resp->toggle_state = queue->toggle_state;
|
|
resp->offset = queue->offset;
|
|
}
|
|
|
|
/*
|
|
* init_qp_queue initializes/constructs r/squeue and registers queue pages.
|
|
*/
|
|
static inline int init_qp_queue(struct ehca_shca *shca,
|
|
struct ehca_pd *pd,
|
|
struct ehca_qp *my_qp,
|
|
struct ipz_queue *queue,
|
|
int q_type,
|
|
u64 expected_hret,
|
|
struct ehca_alloc_queue_parms *parms,
|
|
int wqe_size)
|
|
{
|
|
int ret, cnt, ipz_rc, nr_q_pages;
|
|
void *vpage;
|
|
u64 rpage, h_ret;
|
|
struct ib_device *ib_dev = &shca->ib_device;
|
|
struct ipz_adapter_handle ipz_hca_handle = shca->ipz_hca_handle;
|
|
|
|
if (!parms->queue_size)
|
|
return 0;
|
|
|
|
if (parms->is_small) {
|
|
nr_q_pages = 1;
|
|
ipz_rc = ipz_queue_ctor(pd, queue, nr_q_pages,
|
|
128 << parms->page_size,
|
|
wqe_size, parms->act_nr_sges, 1);
|
|
} else {
|
|
nr_q_pages = parms->queue_size;
|
|
ipz_rc = ipz_queue_ctor(pd, queue, nr_q_pages,
|
|
EHCA_PAGESIZE, wqe_size,
|
|
parms->act_nr_sges, 0);
|
|
}
|
|
|
|
if (!ipz_rc) {
|
|
ehca_err(ib_dev, "Cannot allocate page for queue. ipz_rc=%i",
|
|
ipz_rc);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* register queue pages */
|
|
for (cnt = 0; cnt < nr_q_pages; cnt++) {
|
|
vpage = ipz_qpageit_get_inc(queue);
|
|
if (!vpage) {
|
|
ehca_err(ib_dev, "ipz_qpageit_get_inc() "
|
|
"failed p_vpage= %p", vpage);
|
|
ret = -EINVAL;
|
|
goto init_qp_queue1;
|
|
}
|
|
rpage = __pa(vpage);
|
|
|
|
h_ret = hipz_h_register_rpage_qp(ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
NULL, 0, q_type,
|
|
rpage, parms->is_small ? 0 : 1,
|
|
my_qp->galpas.kernel);
|
|
if (cnt == (nr_q_pages - 1)) { /* last page! */
|
|
if (h_ret != expected_hret) {
|
|
ehca_err(ib_dev, "hipz_qp_register_rpage() "
|
|
"h_ret=%lli", h_ret);
|
|
ret = ehca2ib_return_code(h_ret);
|
|
goto init_qp_queue1;
|
|
}
|
|
vpage = ipz_qpageit_get_inc(&my_qp->ipz_rqueue);
|
|
if (vpage) {
|
|
ehca_err(ib_dev, "ipz_qpageit_get_inc() "
|
|
"should not succeed vpage=%p", vpage);
|
|
ret = -EINVAL;
|
|
goto init_qp_queue1;
|
|
}
|
|
} else {
|
|
if (h_ret != H_PAGE_REGISTERED) {
|
|
ehca_err(ib_dev, "hipz_qp_register_rpage() "
|
|
"h_ret=%lli", h_ret);
|
|
ret = ehca2ib_return_code(h_ret);
|
|
goto init_qp_queue1;
|
|
}
|
|
}
|
|
}
|
|
|
|
ipz_qeit_reset(queue);
|
|
|
|
return 0;
|
|
|
|
init_qp_queue1:
|
|
ipz_queue_dtor(pd, queue);
|
|
return ret;
|
|
}
|
|
|
|
static inline int ehca_calc_wqe_size(int act_nr_sge, int is_llqp)
|
|
{
|
|
if (is_llqp)
|
|
return 128 << act_nr_sge;
|
|
else
|
|
return offsetof(struct ehca_wqe,
|
|
u.nud.sg_list[act_nr_sge]);
|
|
}
|
|
|
|
static void ehca_determine_small_queue(struct ehca_alloc_queue_parms *queue,
|
|
int req_nr_sge, int is_llqp)
|
|
{
|
|
u32 wqe_size, q_size;
|
|
int act_nr_sge = req_nr_sge;
|
|
|
|
if (!is_llqp)
|
|
/* round up #SGEs so WQE size is a power of 2 */
|
|
for (act_nr_sge = 4; act_nr_sge <= 252;
|
|
act_nr_sge = 4 + 2 * act_nr_sge)
|
|
if (act_nr_sge >= req_nr_sge)
|
|
break;
|
|
|
|
wqe_size = ehca_calc_wqe_size(act_nr_sge, is_llqp);
|
|
q_size = wqe_size * (queue->max_wr + 1);
|
|
|
|
if (q_size <= 512)
|
|
queue->page_size = 2;
|
|
else if (q_size <= 1024)
|
|
queue->page_size = 3;
|
|
else
|
|
queue->page_size = 0;
|
|
|
|
queue->is_small = (queue->page_size != 0);
|
|
}
|
|
|
|
/* needs to be called with cq->spinlock held */
|
|
void ehca_add_to_err_list(struct ehca_qp *qp, int on_sq)
|
|
{
|
|
struct list_head *list, *node;
|
|
|
|
/* TODO: support low latency QPs */
|
|
if (qp->ext_type == EQPT_LLQP)
|
|
return;
|
|
|
|
if (on_sq) {
|
|
list = &qp->send_cq->sqp_err_list;
|
|
node = &qp->sq_err_node;
|
|
} else {
|
|
list = &qp->recv_cq->rqp_err_list;
|
|
node = &qp->rq_err_node;
|
|
}
|
|
|
|
if (list_empty(node))
|
|
list_add_tail(node, list);
|
|
|
|
return;
|
|
}
|
|
|
|
static void del_from_err_list(struct ehca_cq *cq, struct list_head *node)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&cq->spinlock, flags);
|
|
|
|
if (!list_empty(node))
|
|
list_del_init(node);
|
|
|
|
spin_unlock_irqrestore(&cq->spinlock, flags);
|
|
}
|
|
|
|
static void reset_queue_map(struct ehca_queue_map *qmap)
|
|
{
|
|
int i;
|
|
|
|
qmap->tail = qmap->entries - 1;
|
|
qmap->left_to_poll = 0;
|
|
qmap->next_wqe_idx = 0;
|
|
for (i = 0; i < qmap->entries; i++) {
|
|
qmap->map[i].reported = 1;
|
|
qmap->map[i].cqe_req = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create an ib_qp struct that is either a QP or an SRQ, depending on
|
|
* the value of the is_srq parameter. If init_attr and srq_init_attr share
|
|
* fields, the field out of init_attr is used.
|
|
*/
|
|
static struct ehca_qp *internal_create_qp(
|
|
struct ib_pd *pd,
|
|
struct ib_qp_init_attr *init_attr,
|
|
struct ib_srq_init_attr *srq_init_attr,
|
|
struct ib_udata *udata, int is_srq)
|
|
{
|
|
struct ehca_qp *my_qp, *my_srq = NULL;
|
|
struct ehca_pd *my_pd = container_of(pd, struct ehca_pd, ib_pd);
|
|
struct ehca_shca *shca = container_of(pd->device, struct ehca_shca,
|
|
ib_device);
|
|
struct ib_ucontext *context = NULL;
|
|
u64 h_ret;
|
|
int is_llqp = 0, has_srq = 0, is_user = 0;
|
|
int qp_type, max_send_sge, max_recv_sge, ret;
|
|
|
|
/* h_call's out parameters */
|
|
struct ehca_alloc_qp_parms parms;
|
|
u32 swqe_size = 0, rwqe_size = 0, ib_qp_num;
|
|
unsigned long flags;
|
|
|
|
if (!atomic_add_unless(&shca->num_qps, 1, shca->max_num_qps)) {
|
|
ehca_err(pd->device, "Unable to create QP, max number of %i "
|
|
"QPs reached.", shca->max_num_qps);
|
|
ehca_err(pd->device, "To increase the maximum number of QPs "
|
|
"use the number_of_qps module parameter.\n");
|
|
return ERR_PTR(-ENOSPC);
|
|
}
|
|
|
|
if (init_attr->create_flags) {
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
memset(&parms, 0, sizeof(parms));
|
|
qp_type = init_attr->qp_type;
|
|
|
|
if (init_attr->sq_sig_type != IB_SIGNAL_REQ_WR &&
|
|
init_attr->sq_sig_type != IB_SIGNAL_ALL_WR) {
|
|
ehca_err(pd->device, "init_attr->sg_sig_type=%x not allowed",
|
|
init_attr->sq_sig_type);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
/* save LLQP info */
|
|
if (qp_type & 0x80) {
|
|
is_llqp = 1;
|
|
parms.ext_type = EQPT_LLQP;
|
|
parms.ll_comp_flags = qp_type & LLQP_COMP_MASK;
|
|
}
|
|
qp_type &= 0x1F;
|
|
init_attr->qp_type &= 0x1F;
|
|
|
|
/* handle SRQ base QPs */
|
|
if (init_attr->srq) {
|
|
my_srq = container_of(init_attr->srq, struct ehca_qp, ib_srq);
|
|
|
|
if (qp_type == IB_QPT_UC) {
|
|
ehca_err(pd->device, "UC with SRQ not supported");
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
has_srq = 1;
|
|
parms.ext_type = EQPT_SRQBASE;
|
|
parms.srq_qpn = my_srq->real_qp_num;
|
|
}
|
|
|
|
if (is_llqp && has_srq) {
|
|
ehca_err(pd->device, "LLQPs can't have an SRQ");
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
/* handle SRQs */
|
|
if (is_srq) {
|
|
parms.ext_type = EQPT_SRQ;
|
|
parms.srq_limit = srq_init_attr->attr.srq_limit;
|
|
if (init_attr->cap.max_recv_sge > 3) {
|
|
ehca_err(pd->device, "no more than three SGEs "
|
|
"supported for SRQ pd=%p max_sge=%x",
|
|
pd, init_attr->cap.max_recv_sge);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
}
|
|
|
|
/* check QP type */
|
|
if (qp_type != IB_QPT_UD &&
|
|
qp_type != IB_QPT_UC &&
|
|
qp_type != IB_QPT_RC &&
|
|
qp_type != IB_QPT_SMI &&
|
|
qp_type != IB_QPT_GSI) {
|
|
ehca_err(pd->device, "wrong QP Type=%x", qp_type);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
if (is_llqp) {
|
|
switch (qp_type) {
|
|
case IB_QPT_RC:
|
|
if ((init_attr->cap.max_send_wr > 255) ||
|
|
(init_attr->cap.max_recv_wr > 255)) {
|
|
ehca_err(pd->device,
|
|
"Invalid Number of max_sq_wr=%x "
|
|
"or max_rq_wr=%x for RC LLQP",
|
|
init_attr->cap.max_send_wr,
|
|
init_attr->cap.max_recv_wr);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
break;
|
|
case IB_QPT_UD:
|
|
if (!EHCA_BMASK_GET(HCA_CAP_UD_LL_QP, shca->hca_cap)) {
|
|
ehca_err(pd->device, "UD LLQP not supported "
|
|
"by this adapter");
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-ENOSYS);
|
|
}
|
|
if (!(init_attr->cap.max_send_sge <= 5
|
|
&& init_attr->cap.max_send_sge >= 1
|
|
&& init_attr->cap.max_recv_sge <= 5
|
|
&& init_attr->cap.max_recv_sge >= 1)) {
|
|
ehca_err(pd->device,
|
|
"Invalid Number of max_send_sge=%x "
|
|
"or max_recv_sge=%x for UD LLQP",
|
|
init_attr->cap.max_send_sge,
|
|
init_attr->cap.max_recv_sge);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
} else if (init_attr->cap.max_send_wr > 255) {
|
|
ehca_err(pd->device,
|
|
"Invalid Number of "
|
|
"max_send_wr=%x for UD QP_TYPE=%x",
|
|
init_attr->cap.max_send_wr, qp_type);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
break;
|
|
default:
|
|
ehca_err(pd->device, "unsupported LL QP Type=%x",
|
|
qp_type);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
} else {
|
|
int max_sge = (qp_type == IB_QPT_UD || qp_type == IB_QPT_SMI
|
|
|| qp_type == IB_QPT_GSI) ? 250 : 252;
|
|
|
|
if (init_attr->cap.max_send_sge > max_sge
|
|
|| init_attr->cap.max_recv_sge > max_sge) {
|
|
ehca_err(pd->device, "Invalid number of SGEs requested "
|
|
"send_sge=%x recv_sge=%x max_sge=%x",
|
|
init_attr->cap.max_send_sge,
|
|
init_attr->cap.max_recv_sge, max_sge);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
}
|
|
|
|
my_qp = kmem_cache_zalloc(qp_cache, GFP_KERNEL);
|
|
if (!my_qp) {
|
|
ehca_err(pd->device, "pd=%p not enough memory to alloc qp", pd);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
if (pd->uobject && udata) {
|
|
is_user = 1;
|
|
context = pd->uobject->context;
|
|
}
|
|
|
|
atomic_set(&my_qp->nr_events, 0);
|
|
init_waitqueue_head(&my_qp->wait_completion);
|
|
spin_lock_init(&my_qp->spinlock_s);
|
|
spin_lock_init(&my_qp->spinlock_r);
|
|
my_qp->qp_type = qp_type;
|
|
my_qp->ext_type = parms.ext_type;
|
|
my_qp->state = IB_QPS_RESET;
|
|
|
|
if (init_attr->recv_cq)
|
|
my_qp->recv_cq =
|
|
container_of(init_attr->recv_cq, struct ehca_cq, ib_cq);
|
|
if (init_attr->send_cq)
|
|
my_qp->send_cq =
|
|
container_of(init_attr->send_cq, struct ehca_cq, ib_cq);
|
|
|
|
idr_preload(GFP_KERNEL);
|
|
write_lock_irqsave(&ehca_qp_idr_lock, flags);
|
|
|
|
ret = idr_alloc(&ehca_qp_idr, my_qp, 0, 0x2000000, GFP_NOWAIT);
|
|
if (ret >= 0)
|
|
my_qp->token = ret;
|
|
|
|
write_unlock_irqrestore(&ehca_qp_idr_lock, flags);
|
|
idr_preload_end();
|
|
if (ret < 0) {
|
|
if (ret == -ENOSPC) {
|
|
ret = -EINVAL;
|
|
ehca_err(pd->device, "Invalid number of qp");
|
|
} else {
|
|
ret = -ENOMEM;
|
|
ehca_err(pd->device, "Can't allocate new idr entry.");
|
|
}
|
|
goto create_qp_exit0;
|
|
}
|
|
|
|
if (has_srq)
|
|
parms.srq_token = my_qp->token;
|
|
|
|
parms.servicetype = ibqptype2servicetype(qp_type);
|
|
if (parms.servicetype < 0) {
|
|
ret = -EINVAL;
|
|
ehca_err(pd->device, "Invalid qp_type=%x", qp_type);
|
|
goto create_qp_exit1;
|
|
}
|
|
|
|
/* Always signal by WQE so we can hide circ. WQEs */
|
|
parms.sigtype = HCALL_SIGT_BY_WQE;
|
|
|
|
/* UD_AV CIRCUMVENTION */
|
|
max_send_sge = init_attr->cap.max_send_sge;
|
|
max_recv_sge = init_attr->cap.max_recv_sge;
|
|
if (parms.servicetype == ST_UD && !is_llqp) {
|
|
max_send_sge += 2;
|
|
max_recv_sge += 2;
|
|
}
|
|
|
|
parms.token = my_qp->token;
|
|
parms.eq_handle = shca->eq.ipz_eq_handle;
|
|
parms.pd = my_pd->fw_pd;
|
|
if (my_qp->send_cq)
|
|
parms.send_cq_handle = my_qp->send_cq->ipz_cq_handle;
|
|
if (my_qp->recv_cq)
|
|
parms.recv_cq_handle = my_qp->recv_cq->ipz_cq_handle;
|
|
|
|
parms.squeue.max_wr = init_attr->cap.max_send_wr;
|
|
parms.rqueue.max_wr = init_attr->cap.max_recv_wr;
|
|
parms.squeue.max_sge = max_send_sge;
|
|
parms.rqueue.max_sge = max_recv_sge;
|
|
|
|
/* RC QPs need one more SWQE for unsolicited ack circumvention */
|
|
if (qp_type == IB_QPT_RC)
|
|
parms.squeue.max_wr++;
|
|
|
|
if (EHCA_BMASK_GET(HCA_CAP_MINI_QP, shca->hca_cap)) {
|
|
if (HAS_SQ(my_qp))
|
|
ehca_determine_small_queue(
|
|
&parms.squeue, max_send_sge, is_llqp);
|
|
if (HAS_RQ(my_qp))
|
|
ehca_determine_small_queue(
|
|
&parms.rqueue, max_recv_sge, is_llqp);
|
|
parms.qp_storage =
|
|
(parms.squeue.is_small || parms.rqueue.is_small);
|
|
}
|
|
|
|
h_ret = hipz_h_alloc_resource_qp(shca->ipz_hca_handle, &parms, is_user);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_err(pd->device, "h_alloc_resource_qp() failed h_ret=%lli",
|
|
h_ret);
|
|
ret = ehca2ib_return_code(h_ret);
|
|
goto create_qp_exit1;
|
|
}
|
|
|
|
ib_qp_num = my_qp->real_qp_num = parms.real_qp_num;
|
|
my_qp->ipz_qp_handle = parms.qp_handle;
|
|
my_qp->galpas = parms.galpas;
|
|
|
|
swqe_size = ehca_calc_wqe_size(parms.squeue.act_nr_sges, is_llqp);
|
|
rwqe_size = ehca_calc_wqe_size(parms.rqueue.act_nr_sges, is_llqp);
|
|
|
|
switch (qp_type) {
|
|
case IB_QPT_RC:
|
|
if (is_llqp) {
|
|
parms.squeue.act_nr_sges = 1;
|
|
parms.rqueue.act_nr_sges = 1;
|
|
}
|
|
/* hide the extra WQE */
|
|
parms.squeue.act_nr_wqes--;
|
|
break;
|
|
case IB_QPT_UD:
|
|
case IB_QPT_GSI:
|
|
case IB_QPT_SMI:
|
|
/* UD circumvention */
|
|
if (is_llqp) {
|
|
parms.squeue.act_nr_sges = 1;
|
|
parms.rqueue.act_nr_sges = 1;
|
|
} else {
|
|
parms.squeue.act_nr_sges -= 2;
|
|
parms.rqueue.act_nr_sges -= 2;
|
|
}
|
|
|
|
if (IB_QPT_GSI == qp_type || IB_QPT_SMI == qp_type) {
|
|
parms.squeue.act_nr_wqes = init_attr->cap.max_send_wr;
|
|
parms.rqueue.act_nr_wqes = init_attr->cap.max_recv_wr;
|
|
parms.squeue.act_nr_sges = init_attr->cap.max_send_sge;
|
|
parms.rqueue.act_nr_sges = init_attr->cap.max_recv_sge;
|
|
ib_qp_num = (qp_type == IB_QPT_SMI) ? 0 : 1;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* initialize r/squeue and register queue pages */
|
|
if (HAS_SQ(my_qp)) {
|
|
ret = init_qp_queue(
|
|
shca, my_pd, my_qp, &my_qp->ipz_squeue, 0,
|
|
HAS_RQ(my_qp) ? H_PAGE_REGISTERED : H_SUCCESS,
|
|
&parms.squeue, swqe_size);
|
|
if (ret) {
|
|
ehca_err(pd->device, "Couldn't initialize squeue "
|
|
"and pages ret=%i", ret);
|
|
goto create_qp_exit2;
|
|
}
|
|
|
|
if (!is_user) {
|
|
my_qp->sq_map.entries = my_qp->ipz_squeue.queue_length /
|
|
my_qp->ipz_squeue.qe_size;
|
|
my_qp->sq_map.map = vmalloc(my_qp->sq_map.entries *
|
|
sizeof(struct ehca_qmap_entry));
|
|
if (!my_qp->sq_map.map) {
|
|
ehca_err(pd->device, "Couldn't allocate squeue "
|
|
"map ret=%i", ret);
|
|
goto create_qp_exit3;
|
|
}
|
|
INIT_LIST_HEAD(&my_qp->sq_err_node);
|
|
/* to avoid the generation of bogus flush CQEs */
|
|
reset_queue_map(&my_qp->sq_map);
|
|
}
|
|
}
|
|
|
|
if (HAS_RQ(my_qp)) {
|
|
ret = init_qp_queue(
|
|
shca, my_pd, my_qp, &my_qp->ipz_rqueue, 1,
|
|
H_SUCCESS, &parms.rqueue, rwqe_size);
|
|
if (ret) {
|
|
ehca_err(pd->device, "Couldn't initialize rqueue "
|
|
"and pages ret=%i", ret);
|
|
goto create_qp_exit4;
|
|
}
|
|
if (!is_user) {
|
|
my_qp->rq_map.entries = my_qp->ipz_rqueue.queue_length /
|
|
my_qp->ipz_rqueue.qe_size;
|
|
my_qp->rq_map.map = vmalloc(my_qp->rq_map.entries *
|
|
sizeof(struct ehca_qmap_entry));
|
|
if (!my_qp->rq_map.map) {
|
|
ehca_err(pd->device, "Couldn't allocate squeue "
|
|
"map ret=%i", ret);
|
|
goto create_qp_exit5;
|
|
}
|
|
INIT_LIST_HEAD(&my_qp->rq_err_node);
|
|
/* to avoid the generation of bogus flush CQEs */
|
|
reset_queue_map(&my_qp->rq_map);
|
|
}
|
|
} else if (init_attr->srq && !is_user) {
|
|
/* this is a base QP, use the queue map of the SRQ */
|
|
my_qp->rq_map = my_srq->rq_map;
|
|
INIT_LIST_HEAD(&my_qp->rq_err_node);
|
|
|
|
my_qp->ipz_rqueue = my_srq->ipz_rqueue;
|
|
}
|
|
|
|
if (is_srq) {
|
|
my_qp->ib_srq.pd = &my_pd->ib_pd;
|
|
my_qp->ib_srq.device = my_pd->ib_pd.device;
|
|
|
|
my_qp->ib_srq.srq_context = init_attr->qp_context;
|
|
my_qp->ib_srq.event_handler = init_attr->event_handler;
|
|
} else {
|
|
my_qp->ib_qp.qp_num = ib_qp_num;
|
|
my_qp->ib_qp.pd = &my_pd->ib_pd;
|
|
my_qp->ib_qp.device = my_pd->ib_pd.device;
|
|
|
|
my_qp->ib_qp.recv_cq = init_attr->recv_cq;
|
|
my_qp->ib_qp.send_cq = init_attr->send_cq;
|
|
|
|
my_qp->ib_qp.qp_type = qp_type;
|
|
my_qp->ib_qp.srq = init_attr->srq;
|
|
|
|
my_qp->ib_qp.qp_context = init_attr->qp_context;
|
|
my_qp->ib_qp.event_handler = init_attr->event_handler;
|
|
}
|
|
|
|
init_attr->cap.max_inline_data = 0; /* not supported yet */
|
|
init_attr->cap.max_recv_sge = parms.rqueue.act_nr_sges;
|
|
init_attr->cap.max_recv_wr = parms.rqueue.act_nr_wqes;
|
|
init_attr->cap.max_send_sge = parms.squeue.act_nr_sges;
|
|
init_attr->cap.max_send_wr = parms.squeue.act_nr_wqes;
|
|
my_qp->init_attr = *init_attr;
|
|
|
|
if (qp_type == IB_QPT_SMI || qp_type == IB_QPT_GSI) {
|
|
shca->sport[init_attr->port_num - 1].ibqp_sqp[qp_type] =
|
|
&my_qp->ib_qp;
|
|
if (ehca_nr_ports < 0) {
|
|
/* alloc array to cache subsequent modify qp parms
|
|
* for autodetect mode
|
|
*/
|
|
my_qp->mod_qp_parm =
|
|
kzalloc(EHCA_MOD_QP_PARM_MAX *
|
|
sizeof(*my_qp->mod_qp_parm),
|
|
GFP_KERNEL);
|
|
if (!my_qp->mod_qp_parm) {
|
|
ehca_err(pd->device,
|
|
"Could not alloc mod_qp_parm");
|
|
goto create_qp_exit5;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* NOTE: define_apq0() not supported yet */
|
|
if (qp_type == IB_QPT_GSI) {
|
|
h_ret = ehca_define_sqp(shca, my_qp, init_attr);
|
|
if (h_ret != H_SUCCESS) {
|
|
kfree(my_qp->mod_qp_parm);
|
|
my_qp->mod_qp_parm = NULL;
|
|
/* the QP pointer is no longer valid */
|
|
shca->sport[init_attr->port_num - 1].ibqp_sqp[qp_type] =
|
|
NULL;
|
|
ret = ehca2ib_return_code(h_ret);
|
|
goto create_qp_exit6;
|
|
}
|
|
}
|
|
|
|
if (my_qp->send_cq) {
|
|
ret = ehca_cq_assign_qp(my_qp->send_cq, my_qp);
|
|
if (ret) {
|
|
ehca_err(pd->device,
|
|
"Couldn't assign qp to send_cq ret=%i", ret);
|
|
goto create_qp_exit7;
|
|
}
|
|
}
|
|
|
|
/* copy queues, galpa data to user space */
|
|
if (context && udata) {
|
|
struct ehca_create_qp_resp resp;
|
|
memset(&resp, 0, sizeof(resp));
|
|
|
|
resp.qp_num = my_qp->real_qp_num;
|
|
resp.token = my_qp->token;
|
|
resp.qp_type = my_qp->qp_type;
|
|
resp.ext_type = my_qp->ext_type;
|
|
resp.qkey = my_qp->qkey;
|
|
resp.real_qp_num = my_qp->real_qp_num;
|
|
|
|
if (HAS_SQ(my_qp))
|
|
queue2resp(&resp.ipz_squeue, &my_qp->ipz_squeue);
|
|
if (HAS_RQ(my_qp))
|
|
queue2resp(&resp.ipz_rqueue, &my_qp->ipz_rqueue);
|
|
resp.fw_handle_ofs = (u32)
|
|
(my_qp->galpas.user.fw_handle & (PAGE_SIZE - 1));
|
|
|
|
if (ib_copy_to_udata(udata, &resp, sizeof resp)) {
|
|
ehca_err(pd->device, "Copy to udata failed");
|
|
ret = -EINVAL;
|
|
goto create_qp_exit8;
|
|
}
|
|
}
|
|
|
|
return my_qp;
|
|
|
|
create_qp_exit8:
|
|
ehca_cq_unassign_qp(my_qp->send_cq, my_qp->real_qp_num);
|
|
|
|
create_qp_exit7:
|
|
kfree(my_qp->mod_qp_parm);
|
|
|
|
create_qp_exit6:
|
|
if (HAS_RQ(my_qp) && !is_user)
|
|
vfree(my_qp->rq_map.map);
|
|
|
|
create_qp_exit5:
|
|
if (HAS_RQ(my_qp))
|
|
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
|
|
|
|
create_qp_exit4:
|
|
if (HAS_SQ(my_qp) && !is_user)
|
|
vfree(my_qp->sq_map.map);
|
|
|
|
create_qp_exit3:
|
|
if (HAS_SQ(my_qp))
|
|
ipz_queue_dtor(my_pd, &my_qp->ipz_squeue);
|
|
|
|
create_qp_exit2:
|
|
hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp);
|
|
|
|
create_qp_exit1:
|
|
write_lock_irqsave(&ehca_qp_idr_lock, flags);
|
|
idr_remove(&ehca_qp_idr, my_qp->token);
|
|
write_unlock_irqrestore(&ehca_qp_idr_lock, flags);
|
|
|
|
create_qp_exit0:
|
|
kmem_cache_free(qp_cache, my_qp);
|
|
atomic_dec(&shca->num_qps);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
struct ib_qp *ehca_create_qp(struct ib_pd *pd,
|
|
struct ib_qp_init_attr *qp_init_attr,
|
|
struct ib_udata *udata)
|
|
{
|
|
struct ehca_qp *ret;
|
|
|
|
ret = internal_create_qp(pd, qp_init_attr, NULL, udata, 0);
|
|
return IS_ERR(ret) ? (struct ib_qp *)ret : &ret->ib_qp;
|
|
}
|
|
|
|
static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
|
|
struct ib_uobject *uobject);
|
|
|
|
struct ib_srq *ehca_create_srq(struct ib_pd *pd,
|
|
struct ib_srq_init_attr *srq_init_attr,
|
|
struct ib_udata *udata)
|
|
{
|
|
struct ib_qp_init_attr qp_init_attr;
|
|
struct ehca_qp *my_qp;
|
|
struct ib_srq *ret;
|
|
struct ehca_shca *shca = container_of(pd->device, struct ehca_shca,
|
|
ib_device);
|
|
struct hcp_modify_qp_control_block *mqpcb;
|
|
u64 hret, update_mask;
|
|
|
|
if (srq_init_attr->srq_type != IB_SRQT_BASIC)
|
|
return ERR_PTR(-ENOSYS);
|
|
|
|
/* For common attributes, internal_create_qp() takes its info
|
|
* out of qp_init_attr, so copy all common attrs there.
|
|
*/
|
|
memset(&qp_init_attr, 0, sizeof(qp_init_attr));
|
|
qp_init_attr.event_handler = srq_init_attr->event_handler;
|
|
qp_init_attr.qp_context = srq_init_attr->srq_context;
|
|
qp_init_attr.sq_sig_type = IB_SIGNAL_ALL_WR;
|
|
qp_init_attr.qp_type = IB_QPT_RC;
|
|
qp_init_attr.cap.max_recv_wr = srq_init_attr->attr.max_wr;
|
|
qp_init_attr.cap.max_recv_sge = srq_init_attr->attr.max_sge;
|
|
|
|
my_qp = internal_create_qp(pd, &qp_init_attr, srq_init_attr, udata, 1);
|
|
if (IS_ERR(my_qp))
|
|
return (struct ib_srq *)my_qp;
|
|
|
|
/* copy back return values */
|
|
srq_init_attr->attr.max_wr = qp_init_attr.cap.max_recv_wr;
|
|
srq_init_attr->attr.max_sge = 3;
|
|
|
|
/* drive SRQ into RTR state */
|
|
mqpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
|
|
if (!mqpcb) {
|
|
ehca_err(pd->device, "Could not get zeroed page for mqpcb "
|
|
"ehca_qp=%p qp_num=%x ", my_qp, my_qp->real_qp_num);
|
|
ret = ERR_PTR(-ENOMEM);
|
|
goto create_srq1;
|
|
}
|
|
|
|
mqpcb->qp_state = EHCA_QPS_INIT;
|
|
mqpcb->prim_phys_port = 1;
|
|
update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_STATE, 1);
|
|
hret = hipz_h_modify_qp(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
update_mask,
|
|
mqpcb, my_qp->galpas.kernel);
|
|
if (hret != H_SUCCESS) {
|
|
ehca_err(pd->device, "Could not modify SRQ to INIT "
|
|
"ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, my_qp->real_qp_num, hret);
|
|
goto create_srq2;
|
|
}
|
|
|
|
mqpcb->qp_enable = 1;
|
|
update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_ENABLE, 1);
|
|
hret = hipz_h_modify_qp(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
update_mask,
|
|
mqpcb, my_qp->galpas.kernel);
|
|
if (hret != H_SUCCESS) {
|
|
ehca_err(pd->device, "Could not enable SRQ "
|
|
"ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, my_qp->real_qp_num, hret);
|
|
goto create_srq2;
|
|
}
|
|
|
|
mqpcb->qp_state = EHCA_QPS_RTR;
|
|
update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_STATE, 1);
|
|
hret = hipz_h_modify_qp(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
update_mask,
|
|
mqpcb, my_qp->galpas.kernel);
|
|
if (hret != H_SUCCESS) {
|
|
ehca_err(pd->device, "Could not modify SRQ to RTR "
|
|
"ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, my_qp->real_qp_num, hret);
|
|
goto create_srq2;
|
|
}
|
|
|
|
ehca_free_fw_ctrlblock(mqpcb);
|
|
|
|
return &my_qp->ib_srq;
|
|
|
|
create_srq2:
|
|
ret = ERR_PTR(ehca2ib_return_code(hret));
|
|
ehca_free_fw_ctrlblock(mqpcb);
|
|
|
|
create_srq1:
|
|
internal_destroy_qp(pd->device, my_qp, my_qp->ib_srq.uobject);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* prepare_sqe_rts called by internal_modify_qp() at trans sqe -> rts
|
|
* set purge bit of bad wqe and subsequent wqes to avoid reentering sqe
|
|
* returns total number of bad wqes in bad_wqe_cnt
|
|
*/
|
|
static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca,
|
|
int *bad_wqe_cnt)
|
|
{
|
|
u64 h_ret;
|
|
struct ipz_queue *squeue;
|
|
void *bad_send_wqe_p, *bad_send_wqe_v;
|
|
u64 q_ofs;
|
|
struct ehca_wqe *wqe;
|
|
int qp_num = my_qp->ib_qp.qp_num;
|
|
|
|
/* get send wqe pointer */
|
|
h_ret = hipz_h_disable_and_get_wqe(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle, &my_qp->pf,
|
|
&bad_send_wqe_p, NULL, 2);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_err(&shca->ib_device, "hipz_h_disable_and_get_wqe() failed"
|
|
" ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, qp_num, h_ret);
|
|
return ehca2ib_return_code(h_ret);
|
|
}
|
|
bad_send_wqe_p = (void *)((u64)bad_send_wqe_p & (~(1L << 63)));
|
|
ehca_dbg(&shca->ib_device, "qp_num=%x bad_send_wqe_p=%p",
|
|
qp_num, bad_send_wqe_p);
|
|
/* convert wqe pointer to vadr */
|
|
bad_send_wqe_v = __va((u64)bad_send_wqe_p);
|
|
if (ehca_debug_level >= 2)
|
|
ehca_dmp(bad_send_wqe_v, 32, "qp_num=%x bad_wqe", qp_num);
|
|
squeue = &my_qp->ipz_squeue;
|
|
if (ipz_queue_abs_to_offset(squeue, (u64)bad_send_wqe_p, &q_ofs)) {
|
|
ehca_err(&shca->ib_device, "failed to get wqe offset qp_num=%x"
|
|
" bad_send_wqe_p=%p", qp_num, bad_send_wqe_p);
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* loop sets wqe's purge bit */
|
|
wqe = (struct ehca_wqe *)ipz_qeit_calc(squeue, q_ofs);
|
|
*bad_wqe_cnt = 0;
|
|
while (wqe->optype != 0xff && wqe->wqef != 0xff) {
|
|
if (ehca_debug_level >= 2)
|
|
ehca_dmp(wqe, 32, "qp_num=%x wqe", qp_num);
|
|
wqe->nr_of_data_seg = 0; /* suppress data access */
|
|
wqe->wqef = WQEF_PURGE; /* WQE to be purged */
|
|
q_ofs = ipz_queue_advance_offset(squeue, q_ofs);
|
|
wqe = (struct ehca_wqe *)ipz_qeit_calc(squeue, q_ofs);
|
|
*bad_wqe_cnt = (*bad_wqe_cnt)+1;
|
|
}
|
|
/*
|
|
* bad wqe will be reprocessed and ignored when pol_cq() is called,
|
|
* i.e. nr of wqes with flush error status is one less
|
|
*/
|
|
ehca_dbg(&shca->ib_device, "qp_num=%x flusherr_wqe_cnt=%x",
|
|
qp_num, (*bad_wqe_cnt)-1);
|
|
wqe->wqef = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int calc_left_cqes(u64 wqe_p, struct ipz_queue *ipz_queue,
|
|
struct ehca_queue_map *qmap)
|
|
{
|
|
void *wqe_v;
|
|
u64 q_ofs;
|
|
u32 wqe_idx;
|
|
unsigned int tail_idx;
|
|
|
|
/* convert real to abs address */
|
|
wqe_p = wqe_p & (~(1UL << 63));
|
|
|
|
wqe_v = __va(wqe_p);
|
|
|
|
if (ipz_queue_abs_to_offset(ipz_queue, wqe_p, &q_ofs)) {
|
|
ehca_gen_err("Invalid offset for calculating left cqes "
|
|
"wqe_p=%#llx wqe_v=%p\n", wqe_p, wqe_v);
|
|
return -EFAULT;
|
|
}
|
|
|
|
tail_idx = next_index(qmap->tail, qmap->entries);
|
|
wqe_idx = q_ofs / ipz_queue->qe_size;
|
|
|
|
/* check all processed wqes, whether a cqe is requested or not */
|
|
while (tail_idx != wqe_idx) {
|
|
if (qmap->map[tail_idx].cqe_req)
|
|
qmap->left_to_poll++;
|
|
tail_idx = next_index(tail_idx, qmap->entries);
|
|
}
|
|
/* save index in queue, where we have to start flushing */
|
|
qmap->next_wqe_idx = wqe_idx;
|
|
return 0;
|
|
}
|
|
|
|
static int check_for_left_cqes(struct ehca_qp *my_qp, struct ehca_shca *shca)
|
|
{
|
|
u64 h_ret;
|
|
void *send_wqe_p, *recv_wqe_p;
|
|
int ret;
|
|
unsigned long flags;
|
|
int qp_num = my_qp->ib_qp.qp_num;
|
|
|
|
/* this hcall is not supported on base QPs */
|
|
if (my_qp->ext_type != EQPT_SRQBASE) {
|
|
/* get send and receive wqe pointer */
|
|
h_ret = hipz_h_disable_and_get_wqe(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle, &my_qp->pf,
|
|
&send_wqe_p, &recv_wqe_p, 4);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_err(&shca->ib_device, "disable_and_get_wqe() "
|
|
"failed ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, qp_num, h_ret);
|
|
return ehca2ib_return_code(h_ret);
|
|
}
|
|
|
|
/*
|
|
* acquire lock to ensure that nobody is polling the cq which
|
|
* could mean that the qmap->tail pointer is in an
|
|
* inconsistent state.
|
|
*/
|
|
spin_lock_irqsave(&my_qp->send_cq->spinlock, flags);
|
|
ret = calc_left_cqes((u64)send_wqe_p, &my_qp->ipz_squeue,
|
|
&my_qp->sq_map);
|
|
spin_unlock_irqrestore(&my_qp->send_cq->spinlock, flags);
|
|
if (ret)
|
|
return ret;
|
|
|
|
|
|
spin_lock_irqsave(&my_qp->recv_cq->spinlock, flags);
|
|
ret = calc_left_cqes((u64)recv_wqe_p, &my_qp->ipz_rqueue,
|
|
&my_qp->rq_map);
|
|
spin_unlock_irqrestore(&my_qp->recv_cq->spinlock, flags);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
spin_lock_irqsave(&my_qp->send_cq->spinlock, flags);
|
|
my_qp->sq_map.left_to_poll = 0;
|
|
my_qp->sq_map.next_wqe_idx = next_index(my_qp->sq_map.tail,
|
|
my_qp->sq_map.entries);
|
|
spin_unlock_irqrestore(&my_qp->send_cq->spinlock, flags);
|
|
|
|
spin_lock_irqsave(&my_qp->recv_cq->spinlock, flags);
|
|
my_qp->rq_map.left_to_poll = 0;
|
|
my_qp->rq_map.next_wqe_idx = next_index(my_qp->rq_map.tail,
|
|
my_qp->rq_map.entries);
|
|
spin_unlock_irqrestore(&my_qp->recv_cq->spinlock, flags);
|
|
}
|
|
|
|
/* this assures flush cqes being generated only for pending wqes */
|
|
if ((my_qp->sq_map.left_to_poll == 0) &&
|
|
(my_qp->rq_map.left_to_poll == 0)) {
|
|
spin_lock_irqsave(&my_qp->send_cq->spinlock, flags);
|
|
ehca_add_to_err_list(my_qp, 1);
|
|
spin_unlock_irqrestore(&my_qp->send_cq->spinlock, flags);
|
|
|
|
if (HAS_RQ(my_qp)) {
|
|
spin_lock_irqsave(&my_qp->recv_cq->spinlock, flags);
|
|
ehca_add_to_err_list(my_qp, 0);
|
|
spin_unlock_irqrestore(&my_qp->recv_cq->spinlock,
|
|
flags);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* internal_modify_qp with circumvention to handle aqp0 properly
|
|
* smi_reset2init indicates if this is an internal reset-to-init-call for
|
|
* smi. This flag must always be zero if called from ehca_modify_qp()!
|
|
* This internal func was intorduced to avoid recursion of ehca_modify_qp()!
|
|
*/
|
|
static int internal_modify_qp(struct ib_qp *ibqp,
|
|
struct ib_qp_attr *attr,
|
|
int attr_mask, int smi_reset2init)
|
|
{
|
|
enum ib_qp_state qp_cur_state, qp_new_state;
|
|
int cnt, qp_attr_idx, ret = 0;
|
|
enum ib_qp_statetrans statetrans;
|
|
struct hcp_modify_qp_control_block *mqpcb;
|
|
struct ehca_qp *my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
|
|
struct ehca_shca *shca =
|
|
container_of(ibqp->pd->device, struct ehca_shca, ib_device);
|
|
u64 update_mask;
|
|
u64 h_ret;
|
|
int bad_wqe_cnt = 0;
|
|
int is_user = 0;
|
|
int squeue_locked = 0;
|
|
unsigned long flags = 0;
|
|
|
|
/* do query_qp to obtain current attr values */
|
|
mqpcb = ehca_alloc_fw_ctrlblock(GFP_ATOMIC);
|
|
if (!mqpcb) {
|
|
ehca_err(ibqp->device, "Could not get zeroed page for mqpcb "
|
|
"ehca_qp=%p qp_num=%x ", my_qp, ibqp->qp_num);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
h_ret = hipz_h_query_qp(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
mqpcb, my_qp->galpas.kernel);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_err(ibqp->device, "hipz_h_query_qp() failed "
|
|
"ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, ibqp->qp_num, h_ret);
|
|
ret = ehca2ib_return_code(h_ret);
|
|
goto modify_qp_exit1;
|
|
}
|
|
if (ibqp->uobject)
|
|
is_user = 1;
|
|
|
|
qp_cur_state = ehca2ib_qp_state(mqpcb->qp_state);
|
|
|
|
if (qp_cur_state == -EINVAL) { /* invalid qp state */
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid current ehca_qp_state=%x "
|
|
"ehca_qp=%p qp_num=%x",
|
|
mqpcb->qp_state, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit1;
|
|
}
|
|
/*
|
|
* circumvention to set aqp0 initial state to init
|
|
* as expected by IB spec
|
|
*/
|
|
if (smi_reset2init == 0 &&
|
|
ibqp->qp_type == IB_QPT_SMI &&
|
|
qp_cur_state == IB_QPS_RESET &&
|
|
(attr_mask & IB_QP_STATE) &&
|
|
attr->qp_state == IB_QPS_INIT) { /* RESET -> INIT */
|
|
struct ib_qp_attr smiqp_attr = {
|
|
.qp_state = IB_QPS_INIT,
|
|
.port_num = my_qp->init_attr.port_num,
|
|
.pkey_index = 0,
|
|
.qkey = 0
|
|
};
|
|
int smiqp_attr_mask = IB_QP_STATE | IB_QP_PORT |
|
|
IB_QP_PKEY_INDEX | IB_QP_QKEY;
|
|
int smirc = internal_modify_qp(
|
|
ibqp, &smiqp_attr, smiqp_attr_mask, 1);
|
|
if (smirc) {
|
|
ehca_err(ibqp->device, "SMI RESET -> INIT failed. "
|
|
"ehca_modify_qp() rc=%i", smirc);
|
|
ret = H_PARAMETER;
|
|
goto modify_qp_exit1;
|
|
}
|
|
qp_cur_state = IB_QPS_INIT;
|
|
ehca_dbg(ibqp->device, "SMI RESET -> INIT succeeded");
|
|
}
|
|
/* is transmitted current state equal to "real" current state */
|
|
if ((attr_mask & IB_QP_CUR_STATE) &&
|
|
qp_cur_state != attr->cur_qp_state) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device,
|
|
"Invalid IB_QP_CUR_STATE attr->curr_qp_state=%x <>"
|
|
" actual cur_qp_state=%x. ehca_qp=%p qp_num=%x",
|
|
attr->cur_qp_state, qp_cur_state, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit1;
|
|
}
|
|
|
|
ehca_dbg(ibqp->device, "ehca_qp=%p qp_num=%x current qp_state=%x "
|
|
"new qp_state=%x attribute_mask=%x",
|
|
my_qp, ibqp->qp_num, qp_cur_state, attr->qp_state, attr_mask);
|
|
|
|
qp_new_state = attr_mask & IB_QP_STATE ? attr->qp_state : qp_cur_state;
|
|
if (!smi_reset2init &&
|
|
!ib_modify_qp_is_ok(qp_cur_state, qp_new_state, ibqp->qp_type,
|
|
attr_mask, IB_LINK_LAYER_UNSPECIFIED)) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device,
|
|
"Invalid qp transition new_state=%x cur_state=%x "
|
|
"ehca_qp=%p qp_num=%x attr_mask=%x", qp_new_state,
|
|
qp_cur_state, my_qp, ibqp->qp_num, attr_mask);
|
|
goto modify_qp_exit1;
|
|
}
|
|
|
|
mqpcb->qp_state = ib2ehca_qp_state(qp_new_state);
|
|
if (mqpcb->qp_state)
|
|
update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_STATE, 1);
|
|
else {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid new qp state=%x "
|
|
"ehca_qp=%p qp_num=%x",
|
|
qp_new_state, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit1;
|
|
}
|
|
|
|
/* retrieve state transition struct to get req and opt attrs */
|
|
statetrans = get_modqp_statetrans(qp_cur_state, qp_new_state);
|
|
if (statetrans < 0) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "<INVALID STATE CHANGE> qp_cur_state=%x "
|
|
"new_qp_state=%x State_xsition=%x ehca_qp=%p "
|
|
"qp_num=%x", qp_cur_state, qp_new_state,
|
|
statetrans, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit1;
|
|
}
|
|
|
|
qp_attr_idx = ib2ehcaqptype(ibqp->qp_type);
|
|
|
|
if (qp_attr_idx < 0) {
|
|
ret = qp_attr_idx;
|
|
ehca_err(ibqp->device,
|
|
"Invalid QP type=%x ehca_qp=%p qp_num=%x",
|
|
ibqp->qp_type, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit1;
|
|
}
|
|
|
|
ehca_dbg(ibqp->device,
|
|
"ehca_qp=%p qp_num=%x <VALID STATE CHANGE> qp_state_xsit=%x",
|
|
my_qp, ibqp->qp_num, statetrans);
|
|
|
|
/* eHCA2 rev2 and higher require the SEND_GRH_FLAG to be set
|
|
* in non-LL UD QPs.
|
|
*/
|
|
if ((my_qp->qp_type == IB_QPT_UD) &&
|
|
(my_qp->ext_type != EQPT_LLQP) &&
|
|
(statetrans == IB_QPST_INIT2RTR) &&
|
|
(shca->hw_level >= 0x22)) {
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SEND_GRH_FLAG, 1);
|
|
mqpcb->send_grh_flag = 1;
|
|
}
|
|
|
|
/* sqe -> rts: set purge bit of bad wqe before actual trans */
|
|
if ((my_qp->qp_type == IB_QPT_UD ||
|
|
my_qp->qp_type == IB_QPT_GSI ||
|
|
my_qp->qp_type == IB_QPT_SMI) &&
|
|
statetrans == IB_QPST_SQE2RTS) {
|
|
/* mark next free wqe if kernel */
|
|
if (!ibqp->uobject) {
|
|
struct ehca_wqe *wqe;
|
|
/* lock send queue */
|
|
spin_lock_irqsave(&my_qp->spinlock_s, flags);
|
|
squeue_locked = 1;
|
|
/* mark next free wqe */
|
|
wqe = (struct ehca_wqe *)
|
|
ipz_qeit_get(&my_qp->ipz_squeue);
|
|
wqe->optype = wqe->wqef = 0xff;
|
|
ehca_dbg(ibqp->device, "qp_num=%x next_free_wqe=%p",
|
|
ibqp->qp_num, wqe);
|
|
}
|
|
ret = prepare_sqe_rts(my_qp, shca, &bad_wqe_cnt);
|
|
if (ret) {
|
|
ehca_err(ibqp->device, "prepare_sqe_rts() failed "
|
|
"ehca_qp=%p qp_num=%x ret=%i",
|
|
my_qp, ibqp->qp_num, ret);
|
|
goto modify_qp_exit2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* enable RDMA_Atomic_Control if reset->init und reliable con
|
|
* this is necessary since gen2 does not provide that flag,
|
|
* but pHyp requires it
|
|
*/
|
|
if (statetrans == IB_QPST_RESET2INIT &&
|
|
(ibqp->qp_type == IB_QPT_RC || ibqp->qp_type == IB_QPT_UC)) {
|
|
mqpcb->rdma_atomic_ctrl = 3;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RDMA_ATOMIC_CTRL, 1);
|
|
}
|
|
/* circ. pHyp requires #RDMA/Atomic Resp Res for UC INIT -> RTR */
|
|
if (statetrans == IB_QPST_INIT2RTR &&
|
|
(ibqp->qp_type == IB_QPT_UC) &&
|
|
!(attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)) {
|
|
mqpcb->rdma_nr_atomic_resp_res = 1; /* default to 1 */
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES, 1);
|
|
}
|
|
|
|
if (attr_mask & IB_QP_PKEY_INDEX) {
|
|
if (attr->pkey_index >= 16) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid pkey_index=%x. "
|
|
"ehca_qp=%p qp_num=%x max_pkey_index=f",
|
|
attr->pkey_index, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit2;
|
|
}
|
|
mqpcb->prim_p_key_idx = attr->pkey_index;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_P_KEY_IDX, 1);
|
|
}
|
|
if (attr_mask & IB_QP_PORT) {
|
|
struct ehca_sport *sport;
|
|
struct ehca_qp *aqp1;
|
|
if (attr->port_num < 1 || attr->port_num > shca->num_ports) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid port=%x. "
|
|
"ehca_qp=%p qp_num=%x num_ports=%x",
|
|
attr->port_num, my_qp, ibqp->qp_num,
|
|
shca->num_ports);
|
|
goto modify_qp_exit2;
|
|
}
|
|
sport = &shca->sport[attr->port_num - 1];
|
|
if (!sport->ibqp_sqp[IB_QPT_GSI]) {
|
|
/* should not occur */
|
|
ret = -EFAULT;
|
|
ehca_err(ibqp->device, "AQP1 was not created for "
|
|
"port=%x", attr->port_num);
|
|
goto modify_qp_exit2;
|
|
}
|
|
aqp1 = container_of(sport->ibqp_sqp[IB_QPT_GSI],
|
|
struct ehca_qp, ib_qp);
|
|
if (ibqp->qp_type != IB_QPT_GSI &&
|
|
ibqp->qp_type != IB_QPT_SMI &&
|
|
aqp1->mod_qp_parm) {
|
|
/*
|
|
* firmware will reject this modify_qp() because
|
|
* port is not activated/initialized fully
|
|
*/
|
|
ret = -EFAULT;
|
|
ehca_warn(ibqp->device, "Couldn't modify qp port=%x: "
|
|
"either port is being activated (try again) "
|
|
"or cabling issue", attr->port_num);
|
|
goto modify_qp_exit2;
|
|
}
|
|
mqpcb->prim_phys_port = attr->port_num;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_PHYS_PORT, 1);
|
|
}
|
|
if (attr_mask & IB_QP_QKEY) {
|
|
mqpcb->qkey = attr->qkey;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_QKEY, 1);
|
|
}
|
|
if (attr_mask & IB_QP_AV) {
|
|
mqpcb->dlid = attr->ah_attr.dlid;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DLID, 1);
|
|
mqpcb->source_path_bits = attr->ah_attr.src_path_bits;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SOURCE_PATH_BITS, 1);
|
|
mqpcb->service_level = attr->ah_attr.sl;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SERVICE_LEVEL, 1);
|
|
|
|
if (ehca_calc_ipd(shca, mqpcb->prim_phys_port,
|
|
attr->ah_attr.static_rate,
|
|
&mqpcb->max_static_rate)) {
|
|
ret = -EINVAL;
|
|
goto modify_qp_exit2;
|
|
}
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_MAX_STATIC_RATE, 1);
|
|
|
|
/*
|
|
* Always supply the GRH flag, even if it's zero, to give the
|
|
* hypervisor a clear "yes" or "no" instead of a "perhaps"
|
|
*/
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SEND_GRH_FLAG, 1);
|
|
|
|
/*
|
|
* only if GRH is TRUE we might consider SOURCE_GID_IDX
|
|
* and DEST_GID otherwise phype will return H_ATTR_PARM!!!
|
|
*/
|
|
if (attr->ah_attr.ah_flags == IB_AH_GRH) {
|
|
mqpcb->send_grh_flag = 1;
|
|
|
|
mqpcb->source_gid_idx = attr->ah_attr.grh.sgid_index;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_SOURCE_GID_IDX, 1);
|
|
|
|
for (cnt = 0; cnt < 16; cnt++)
|
|
mqpcb->dest_gid.byte[cnt] =
|
|
attr->ah_attr.grh.dgid.raw[cnt];
|
|
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DEST_GID, 1);
|
|
mqpcb->flow_label = attr->ah_attr.grh.flow_label;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_FLOW_LABEL, 1);
|
|
mqpcb->hop_limit = attr->ah_attr.grh.hop_limit;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_HOP_LIMIT, 1);
|
|
mqpcb->traffic_class = attr->ah_attr.grh.traffic_class;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_TRAFFIC_CLASS, 1);
|
|
}
|
|
}
|
|
|
|
if (attr_mask & IB_QP_PATH_MTU) {
|
|
/* store ld(MTU) */
|
|
my_qp->mtu_shift = attr->path_mtu + 7;
|
|
mqpcb->path_mtu = attr->path_mtu;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PATH_MTU, 1);
|
|
}
|
|
if (attr_mask & IB_QP_TIMEOUT) {
|
|
mqpcb->timeout = attr->timeout;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_TIMEOUT, 1);
|
|
}
|
|
if (attr_mask & IB_QP_RETRY_CNT) {
|
|
mqpcb->retry_count = attr->retry_cnt;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RETRY_COUNT, 1);
|
|
}
|
|
if (attr_mask & IB_QP_RNR_RETRY) {
|
|
mqpcb->rnr_retry_count = attr->rnr_retry;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RNR_RETRY_COUNT, 1);
|
|
}
|
|
if (attr_mask & IB_QP_RQ_PSN) {
|
|
mqpcb->receive_psn = attr->rq_psn;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RECEIVE_PSN, 1);
|
|
}
|
|
if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
|
|
mqpcb->rdma_nr_atomic_resp_res = attr->max_dest_rd_atomic < 3 ?
|
|
attr->max_dest_rd_atomic : 2;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES, 1);
|
|
}
|
|
if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
|
|
mqpcb->rdma_atomic_outst_dest_qp = attr->max_rd_atomic < 3 ?
|
|
attr->max_rd_atomic : 2;
|
|
update_mask |=
|
|
EHCA_BMASK_SET
|
|
(MQPCB_MASK_RDMA_ATOMIC_OUTST_DEST_QP, 1);
|
|
}
|
|
if (attr_mask & IB_QP_ALT_PATH) {
|
|
if (attr->alt_port_num < 1
|
|
|| attr->alt_port_num > shca->num_ports) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid alt_port=%x. "
|
|
"ehca_qp=%p qp_num=%x num_ports=%x",
|
|
attr->alt_port_num, my_qp, ibqp->qp_num,
|
|
shca->num_ports);
|
|
goto modify_qp_exit2;
|
|
}
|
|
mqpcb->alt_phys_port = attr->alt_port_num;
|
|
|
|
if (attr->alt_pkey_index >= 16) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid alt_pkey_index=%x. "
|
|
"ehca_qp=%p qp_num=%x max_pkey_index=f",
|
|
attr->pkey_index, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit2;
|
|
}
|
|
mqpcb->alt_p_key_idx = attr->alt_pkey_index;
|
|
|
|
mqpcb->timeout_al = attr->alt_timeout;
|
|
mqpcb->dlid_al = attr->alt_ah_attr.dlid;
|
|
mqpcb->source_path_bits_al = attr->alt_ah_attr.src_path_bits;
|
|
mqpcb->service_level_al = attr->alt_ah_attr.sl;
|
|
|
|
if (ehca_calc_ipd(shca, mqpcb->alt_phys_port,
|
|
attr->alt_ah_attr.static_rate,
|
|
&mqpcb->max_static_rate_al)) {
|
|
ret = -EINVAL;
|
|
goto modify_qp_exit2;
|
|
}
|
|
|
|
/* OpenIB doesn't support alternate retry counts - copy them */
|
|
mqpcb->retry_count_al = mqpcb->retry_count;
|
|
mqpcb->rnr_retry_count_al = mqpcb->rnr_retry_count;
|
|
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_ALT_PHYS_PORT, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_ALT_P_KEY_IDX, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_TIMEOUT_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_DLID_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_SOURCE_PATH_BITS_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_SERVICE_LEVEL_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_MAX_STATIC_RATE_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_RETRY_COUNT_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_RNR_RETRY_COUNT_AL, 1);
|
|
|
|
/*
|
|
* Always supply the GRH flag, even if it's zero, to give the
|
|
* hypervisor a clear "yes" or "no" instead of a "perhaps"
|
|
*/
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SEND_GRH_FLAG_AL, 1);
|
|
|
|
/*
|
|
* only if GRH is TRUE we might consider SOURCE_GID_IDX
|
|
* and DEST_GID otherwise phype will return H_ATTR_PARM!!!
|
|
*/
|
|
if (attr->alt_ah_attr.ah_flags == IB_AH_GRH) {
|
|
mqpcb->send_grh_flag_al = 1;
|
|
|
|
for (cnt = 0; cnt < 16; cnt++)
|
|
mqpcb->dest_gid_al.byte[cnt] =
|
|
attr->alt_ah_attr.grh.dgid.raw[cnt];
|
|
mqpcb->source_gid_idx_al =
|
|
attr->alt_ah_attr.grh.sgid_index;
|
|
mqpcb->flow_label_al = attr->alt_ah_attr.grh.flow_label;
|
|
mqpcb->hop_limit_al = attr->alt_ah_attr.grh.hop_limit;
|
|
mqpcb->traffic_class_al =
|
|
attr->alt_ah_attr.grh.traffic_class;
|
|
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_SOURCE_GID_IDX_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_DEST_GID_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_FLOW_LABEL_AL, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_HOP_LIMIT_AL, 1) |
|
|
EHCA_BMASK_SET(MQPCB_MASK_TRAFFIC_CLASS_AL, 1);
|
|
}
|
|
}
|
|
|
|
if (attr_mask & IB_QP_MIN_RNR_TIMER) {
|
|
mqpcb->min_rnr_nak_timer_field = attr->min_rnr_timer;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_MIN_RNR_NAK_TIMER_FIELD, 1);
|
|
}
|
|
|
|
if (attr_mask & IB_QP_SQ_PSN) {
|
|
mqpcb->send_psn = attr->sq_psn;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SEND_PSN, 1);
|
|
}
|
|
|
|
if (attr_mask & IB_QP_DEST_QPN) {
|
|
mqpcb->dest_qp_nr = attr->dest_qp_num;
|
|
update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DEST_QP_NR, 1);
|
|
}
|
|
|
|
if (attr_mask & IB_QP_PATH_MIG_STATE) {
|
|
if (attr->path_mig_state != IB_MIG_REARM
|
|
&& attr->path_mig_state != IB_MIG_MIGRATED) {
|
|
ret = -EINVAL;
|
|
ehca_err(ibqp->device, "Invalid mig_state=%x",
|
|
attr->path_mig_state);
|
|
goto modify_qp_exit2;
|
|
}
|
|
mqpcb->path_migration_state = attr->path_mig_state + 1;
|
|
if (attr->path_mig_state == IB_MIG_REARM)
|
|
my_qp->mig_armed = 1;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_PATH_MIGRATION_STATE, 1);
|
|
}
|
|
|
|
if (attr_mask & IB_QP_CAP) {
|
|
mqpcb->max_nr_outst_send_wr = attr->cap.max_send_wr+1;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_MAX_NR_OUTST_SEND_WR, 1);
|
|
mqpcb->max_nr_outst_recv_wr = attr->cap.max_recv_wr+1;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_MAX_NR_OUTST_RECV_WR, 1);
|
|
/* no support for max_send/recv_sge yet */
|
|
}
|
|
|
|
if (ehca_debug_level >= 2)
|
|
ehca_dmp(mqpcb, 4*70, "qp_num=%x", ibqp->qp_num);
|
|
|
|
h_ret = hipz_h_modify_qp(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
update_mask,
|
|
mqpcb, my_qp->galpas.kernel);
|
|
|
|
if (h_ret != H_SUCCESS) {
|
|
ret = ehca2ib_return_code(h_ret);
|
|
ehca_err(ibqp->device, "hipz_h_modify_qp() failed h_ret=%lli "
|
|
"ehca_qp=%p qp_num=%x", h_ret, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit2;
|
|
}
|
|
|
|
if ((my_qp->qp_type == IB_QPT_UD ||
|
|
my_qp->qp_type == IB_QPT_GSI ||
|
|
my_qp->qp_type == IB_QPT_SMI) &&
|
|
statetrans == IB_QPST_SQE2RTS) {
|
|
/* doorbell to reprocessing wqes */
|
|
iosync(); /* serialize GAL register access */
|
|
hipz_update_sqa(my_qp, bad_wqe_cnt-1);
|
|
ehca_gen_dbg("doorbell for %x wqes", bad_wqe_cnt);
|
|
}
|
|
|
|
if (statetrans == IB_QPST_RESET2INIT ||
|
|
statetrans == IB_QPST_INIT2INIT) {
|
|
mqpcb->qp_enable = 1;
|
|
mqpcb->qp_state = EHCA_QPS_INIT;
|
|
update_mask = 0;
|
|
update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_ENABLE, 1);
|
|
|
|
h_ret = hipz_h_modify_qp(shca->ipz_hca_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
update_mask,
|
|
mqpcb,
|
|
my_qp->galpas.kernel);
|
|
|
|
if (h_ret != H_SUCCESS) {
|
|
ret = ehca2ib_return_code(h_ret);
|
|
ehca_err(ibqp->device, "ENABLE in context of "
|
|
"RESET_2_INIT failed! Maybe you didn't get "
|
|
"a LID h_ret=%lli ehca_qp=%p qp_num=%x",
|
|
h_ret, my_qp, ibqp->qp_num);
|
|
goto modify_qp_exit2;
|
|
}
|
|
}
|
|
if ((qp_new_state == IB_QPS_ERR) && (qp_cur_state != IB_QPS_ERR)
|
|
&& !is_user) {
|
|
ret = check_for_left_cqes(my_qp, shca);
|
|
if (ret)
|
|
goto modify_qp_exit2;
|
|
}
|
|
|
|
if (statetrans == IB_QPST_ANY2RESET) {
|
|
ipz_qeit_reset(&my_qp->ipz_rqueue);
|
|
ipz_qeit_reset(&my_qp->ipz_squeue);
|
|
|
|
if (qp_cur_state == IB_QPS_ERR && !is_user) {
|
|
del_from_err_list(my_qp->send_cq, &my_qp->sq_err_node);
|
|
|
|
if (HAS_RQ(my_qp))
|
|
del_from_err_list(my_qp->recv_cq,
|
|
&my_qp->rq_err_node);
|
|
}
|
|
if (!is_user)
|
|
reset_queue_map(&my_qp->sq_map);
|
|
|
|
if (HAS_RQ(my_qp) && !is_user)
|
|
reset_queue_map(&my_qp->rq_map);
|
|
}
|
|
|
|
if (attr_mask & IB_QP_QKEY)
|
|
my_qp->qkey = attr->qkey;
|
|
|
|
modify_qp_exit2:
|
|
if (squeue_locked) { /* this means: sqe -> rts */
|
|
spin_unlock_irqrestore(&my_qp->spinlock_s, flags);
|
|
my_qp->sqerr_purgeflag = 1;
|
|
}
|
|
|
|
modify_qp_exit1:
|
|
ehca_free_fw_ctrlblock(mqpcb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ehca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask,
|
|
struct ib_udata *udata)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct ehca_shca *shca = container_of(ibqp->device, struct ehca_shca,
|
|
ib_device);
|
|
struct ehca_qp *my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
|
|
|
|
/* The if-block below caches qp_attr to be modified for GSI and SMI
|
|
* qps during the initialization by ib_mad. When the respective port
|
|
* is activated, ie we got an event PORT_ACTIVE, we'll replay the
|
|
* cached modify calls sequence, see ehca_recover_sqs() below.
|
|
* Why that is required:
|
|
* 1) If one port is connected, older code requires that port one
|
|
* to be connected and module option nr_ports=1 to be given by
|
|
* user, which is very inconvenient for end user.
|
|
* 2) Firmware accepts modify_qp() only if respective port has become
|
|
* active. Older code had a wait loop of 30sec create_qp()/
|
|
* define_aqp1(), which is not appropriate in practice. This
|
|
* code now removes that wait loop, see define_aqp1(), and always
|
|
* reports all ports to ib_mad resp. users. Only activated ports
|
|
* will then usable for the users.
|
|
*/
|
|
if (ibqp->qp_type == IB_QPT_GSI || ibqp->qp_type == IB_QPT_SMI) {
|
|
int port = my_qp->init_attr.port_num;
|
|
struct ehca_sport *sport = &shca->sport[port - 1];
|
|
unsigned long flags;
|
|
spin_lock_irqsave(&sport->mod_sqp_lock, flags);
|
|
/* cache qp_attr only during init */
|
|
if (my_qp->mod_qp_parm) {
|
|
struct ehca_mod_qp_parm *p;
|
|
if (my_qp->mod_qp_parm_idx >= EHCA_MOD_QP_PARM_MAX) {
|
|
ehca_err(&shca->ib_device,
|
|
"mod_qp_parm overflow state=%x port=%x"
|
|
" type=%x", attr->qp_state,
|
|
my_qp->init_attr.port_num,
|
|
ibqp->qp_type);
|
|
spin_unlock_irqrestore(&sport->mod_sqp_lock,
|
|
flags);
|
|
return -EINVAL;
|
|
}
|
|
p = &my_qp->mod_qp_parm[my_qp->mod_qp_parm_idx];
|
|
p->mask = attr_mask;
|
|
p->attr = *attr;
|
|
my_qp->mod_qp_parm_idx++;
|
|
ehca_dbg(&shca->ib_device,
|
|
"Saved qp_attr for state=%x port=%x type=%x",
|
|
attr->qp_state, my_qp->init_attr.port_num,
|
|
ibqp->qp_type);
|
|
spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
|
|
goto out;
|
|
}
|
|
spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
|
|
}
|
|
|
|
ret = internal_modify_qp(ibqp, attr, attr_mask, 0);
|
|
|
|
out:
|
|
if ((ret == 0) && (attr_mask & IB_QP_STATE))
|
|
my_qp->state = attr->qp_state;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ehca_recover_sqp(struct ib_qp *sqp)
|
|
{
|
|
struct ehca_qp *my_sqp = container_of(sqp, struct ehca_qp, ib_qp);
|
|
int port = my_sqp->init_attr.port_num;
|
|
struct ib_qp_attr attr;
|
|
struct ehca_mod_qp_parm *qp_parm;
|
|
int i, qp_parm_idx, ret;
|
|
unsigned long flags, wr_cnt;
|
|
|
|
if (!my_sqp->mod_qp_parm)
|
|
return;
|
|
ehca_dbg(sqp->device, "SQP port=%x qp_num=%x", port, sqp->qp_num);
|
|
|
|
qp_parm = my_sqp->mod_qp_parm;
|
|
qp_parm_idx = my_sqp->mod_qp_parm_idx;
|
|
for (i = 0; i < qp_parm_idx; i++) {
|
|
attr = qp_parm[i].attr;
|
|
ret = internal_modify_qp(sqp, &attr, qp_parm[i].mask, 0);
|
|
if (ret) {
|
|
ehca_err(sqp->device, "Could not modify SQP port=%x "
|
|
"qp_num=%x ret=%x", port, sqp->qp_num, ret);
|
|
goto free_qp_parm;
|
|
}
|
|
ehca_dbg(sqp->device, "SQP port=%x qp_num=%x in state=%x",
|
|
port, sqp->qp_num, attr.qp_state);
|
|
}
|
|
|
|
/* re-trigger posted recv wrs */
|
|
wr_cnt = my_sqp->ipz_rqueue.current_q_offset /
|
|
my_sqp->ipz_rqueue.qe_size;
|
|
if (wr_cnt) {
|
|
spin_lock_irqsave(&my_sqp->spinlock_r, flags);
|
|
hipz_update_rqa(my_sqp, wr_cnt);
|
|
spin_unlock_irqrestore(&my_sqp->spinlock_r, flags);
|
|
ehca_dbg(sqp->device, "doorbell port=%x qp_num=%x wr_cnt=%lx",
|
|
port, sqp->qp_num, wr_cnt);
|
|
}
|
|
|
|
free_qp_parm:
|
|
kfree(qp_parm);
|
|
/* this prevents subsequent calls to modify_qp() to cache qp_attr */
|
|
my_sqp->mod_qp_parm = NULL;
|
|
}
|
|
|
|
int ehca_query_qp(struct ib_qp *qp,
|
|
struct ib_qp_attr *qp_attr,
|
|
int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr)
|
|
{
|
|
struct ehca_qp *my_qp = container_of(qp, struct ehca_qp, ib_qp);
|
|
struct ehca_shca *shca = container_of(qp->device, struct ehca_shca,
|
|
ib_device);
|
|
struct ipz_adapter_handle adapter_handle = shca->ipz_hca_handle;
|
|
struct hcp_modify_qp_control_block *qpcb;
|
|
int cnt, ret = 0;
|
|
u64 h_ret;
|
|
|
|
if (qp_attr_mask & QP_ATTR_QUERY_NOT_SUPPORTED) {
|
|
ehca_err(qp->device, "Invalid attribute mask "
|
|
"ehca_qp=%p qp_num=%x qp_attr_mask=%x ",
|
|
my_qp, qp->qp_num, qp_attr_mask);
|
|
return -EINVAL;
|
|
}
|
|
|
|
qpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
|
|
if (!qpcb) {
|
|
ehca_err(qp->device, "Out of memory for qpcb "
|
|
"ehca_qp=%p qp_num=%x", my_qp, qp->qp_num);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
h_ret = hipz_h_query_qp(adapter_handle,
|
|
my_qp->ipz_qp_handle,
|
|
&my_qp->pf,
|
|
qpcb, my_qp->galpas.kernel);
|
|
|
|
if (h_ret != H_SUCCESS) {
|
|
ret = ehca2ib_return_code(h_ret);
|
|
ehca_err(qp->device, "hipz_h_query_qp() failed "
|
|
"ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, qp->qp_num, h_ret);
|
|
goto query_qp_exit1;
|
|
}
|
|
|
|
qp_attr->cur_qp_state = ehca2ib_qp_state(qpcb->qp_state);
|
|
qp_attr->qp_state = qp_attr->cur_qp_state;
|
|
|
|
if (qp_attr->cur_qp_state == -EINVAL) {
|
|
ret = -EINVAL;
|
|
ehca_err(qp->device, "Got invalid ehca_qp_state=%x "
|
|
"ehca_qp=%p qp_num=%x",
|
|
qpcb->qp_state, my_qp, qp->qp_num);
|
|
goto query_qp_exit1;
|
|
}
|
|
|
|
if (qp_attr->qp_state == IB_QPS_SQD)
|
|
qp_attr->sq_draining = 1;
|
|
|
|
qp_attr->qkey = qpcb->qkey;
|
|
qp_attr->path_mtu = qpcb->path_mtu;
|
|
qp_attr->path_mig_state = qpcb->path_migration_state - 1;
|
|
qp_attr->rq_psn = qpcb->receive_psn;
|
|
qp_attr->sq_psn = qpcb->send_psn;
|
|
qp_attr->min_rnr_timer = qpcb->min_rnr_nak_timer_field;
|
|
qp_attr->cap.max_send_wr = qpcb->max_nr_outst_send_wr-1;
|
|
qp_attr->cap.max_recv_wr = qpcb->max_nr_outst_recv_wr-1;
|
|
/* UD_AV CIRCUMVENTION */
|
|
if (my_qp->qp_type == IB_QPT_UD) {
|
|
qp_attr->cap.max_send_sge =
|
|
qpcb->actual_nr_sges_in_sq_wqe - 2;
|
|
qp_attr->cap.max_recv_sge =
|
|
qpcb->actual_nr_sges_in_rq_wqe - 2;
|
|
} else {
|
|
qp_attr->cap.max_send_sge =
|
|
qpcb->actual_nr_sges_in_sq_wqe;
|
|
qp_attr->cap.max_recv_sge =
|
|
qpcb->actual_nr_sges_in_rq_wqe;
|
|
}
|
|
|
|
qp_attr->cap.max_inline_data = my_qp->sq_max_inline_data_size;
|
|
qp_attr->dest_qp_num = qpcb->dest_qp_nr;
|
|
|
|
qp_attr->pkey_index = qpcb->prim_p_key_idx;
|
|
qp_attr->port_num = qpcb->prim_phys_port;
|
|
qp_attr->timeout = qpcb->timeout;
|
|
qp_attr->retry_cnt = qpcb->retry_count;
|
|
qp_attr->rnr_retry = qpcb->rnr_retry_count;
|
|
|
|
qp_attr->alt_pkey_index = qpcb->alt_p_key_idx;
|
|
qp_attr->alt_port_num = qpcb->alt_phys_port;
|
|
qp_attr->alt_timeout = qpcb->timeout_al;
|
|
|
|
qp_attr->max_dest_rd_atomic = qpcb->rdma_nr_atomic_resp_res;
|
|
qp_attr->max_rd_atomic = qpcb->rdma_atomic_outst_dest_qp;
|
|
|
|
/* primary av */
|
|
qp_attr->ah_attr.sl = qpcb->service_level;
|
|
|
|
if (qpcb->send_grh_flag) {
|
|
qp_attr->ah_attr.ah_flags = IB_AH_GRH;
|
|
}
|
|
|
|
qp_attr->ah_attr.static_rate = qpcb->max_static_rate;
|
|
qp_attr->ah_attr.dlid = qpcb->dlid;
|
|
qp_attr->ah_attr.src_path_bits = qpcb->source_path_bits;
|
|
qp_attr->ah_attr.port_num = qp_attr->port_num;
|
|
|
|
/* primary GRH */
|
|
qp_attr->ah_attr.grh.traffic_class = qpcb->traffic_class;
|
|
qp_attr->ah_attr.grh.hop_limit = qpcb->hop_limit;
|
|
qp_attr->ah_attr.grh.sgid_index = qpcb->source_gid_idx;
|
|
qp_attr->ah_attr.grh.flow_label = qpcb->flow_label;
|
|
|
|
for (cnt = 0; cnt < 16; cnt++)
|
|
qp_attr->ah_attr.grh.dgid.raw[cnt] =
|
|
qpcb->dest_gid.byte[cnt];
|
|
|
|
/* alternate AV */
|
|
qp_attr->alt_ah_attr.sl = qpcb->service_level_al;
|
|
if (qpcb->send_grh_flag_al) {
|
|
qp_attr->alt_ah_attr.ah_flags = IB_AH_GRH;
|
|
}
|
|
|
|
qp_attr->alt_ah_attr.static_rate = qpcb->max_static_rate_al;
|
|
qp_attr->alt_ah_attr.dlid = qpcb->dlid_al;
|
|
qp_attr->alt_ah_attr.src_path_bits = qpcb->source_path_bits_al;
|
|
|
|
/* alternate GRH */
|
|
qp_attr->alt_ah_attr.grh.traffic_class = qpcb->traffic_class_al;
|
|
qp_attr->alt_ah_attr.grh.hop_limit = qpcb->hop_limit_al;
|
|
qp_attr->alt_ah_attr.grh.sgid_index = qpcb->source_gid_idx_al;
|
|
qp_attr->alt_ah_attr.grh.flow_label = qpcb->flow_label_al;
|
|
|
|
for (cnt = 0; cnt < 16; cnt++)
|
|
qp_attr->alt_ah_attr.grh.dgid.raw[cnt] =
|
|
qpcb->dest_gid_al.byte[cnt];
|
|
|
|
/* return init attributes given in ehca_create_qp */
|
|
if (qp_init_attr)
|
|
*qp_init_attr = my_qp->init_attr;
|
|
|
|
if (ehca_debug_level >= 2)
|
|
ehca_dmp(qpcb, 4*70, "qp_num=%x", qp->qp_num);
|
|
|
|
query_qp_exit1:
|
|
ehca_free_fw_ctrlblock(qpcb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ehca_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr,
|
|
enum ib_srq_attr_mask attr_mask, struct ib_udata *udata)
|
|
{
|
|
struct ehca_qp *my_qp =
|
|
container_of(ibsrq, struct ehca_qp, ib_srq);
|
|
struct ehca_shca *shca =
|
|
container_of(ibsrq->pd->device, struct ehca_shca, ib_device);
|
|
struct hcp_modify_qp_control_block *mqpcb;
|
|
u64 update_mask;
|
|
u64 h_ret;
|
|
int ret = 0;
|
|
|
|
mqpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
|
|
if (!mqpcb) {
|
|
ehca_err(ibsrq->device, "Could not get zeroed page for mqpcb "
|
|
"ehca_qp=%p qp_num=%x ", my_qp, my_qp->real_qp_num);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
update_mask = 0;
|
|
if (attr_mask & IB_SRQ_LIMIT) {
|
|
attr_mask &= ~IB_SRQ_LIMIT;
|
|
update_mask |=
|
|
EHCA_BMASK_SET(MQPCB_MASK_CURR_SRQ_LIMIT, 1)
|
|
| EHCA_BMASK_SET(MQPCB_MASK_QP_AFF_ASYN_EV_LOG_REG, 1);
|
|
mqpcb->curr_srq_limit = attr->srq_limit;
|
|
mqpcb->qp_aff_asyn_ev_log_reg =
|
|
EHCA_BMASK_SET(QPX_AAELOG_RESET_SRQ_LIMIT, 1);
|
|
}
|
|
|
|
/* by now, all bits in attr_mask should have been cleared */
|
|
if (attr_mask) {
|
|
ehca_err(ibsrq->device, "invalid attribute mask bits set "
|
|
"attr_mask=%x", attr_mask);
|
|
ret = -EINVAL;
|
|
goto modify_srq_exit0;
|
|
}
|
|
|
|
if (ehca_debug_level >= 2)
|
|
ehca_dmp(mqpcb, 4*70, "qp_num=%x", my_qp->real_qp_num);
|
|
|
|
h_ret = hipz_h_modify_qp(shca->ipz_hca_handle, my_qp->ipz_qp_handle,
|
|
NULL, update_mask, mqpcb,
|
|
my_qp->galpas.kernel);
|
|
|
|
if (h_ret != H_SUCCESS) {
|
|
ret = ehca2ib_return_code(h_ret);
|
|
ehca_err(ibsrq->device, "hipz_h_modify_qp() failed h_ret=%lli "
|
|
"ehca_qp=%p qp_num=%x",
|
|
h_ret, my_qp, my_qp->real_qp_num);
|
|
}
|
|
|
|
modify_srq_exit0:
|
|
ehca_free_fw_ctrlblock(mqpcb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ehca_query_srq(struct ib_srq *srq, struct ib_srq_attr *srq_attr)
|
|
{
|
|
struct ehca_qp *my_qp = container_of(srq, struct ehca_qp, ib_srq);
|
|
struct ehca_shca *shca = container_of(srq->device, struct ehca_shca,
|
|
ib_device);
|
|
struct ipz_adapter_handle adapter_handle = shca->ipz_hca_handle;
|
|
struct hcp_modify_qp_control_block *qpcb;
|
|
int ret = 0;
|
|
u64 h_ret;
|
|
|
|
qpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL);
|
|
if (!qpcb) {
|
|
ehca_err(srq->device, "Out of memory for qpcb "
|
|
"ehca_qp=%p qp_num=%x", my_qp, my_qp->real_qp_num);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
h_ret = hipz_h_query_qp(adapter_handle, my_qp->ipz_qp_handle,
|
|
NULL, qpcb, my_qp->galpas.kernel);
|
|
|
|
if (h_ret != H_SUCCESS) {
|
|
ret = ehca2ib_return_code(h_ret);
|
|
ehca_err(srq->device, "hipz_h_query_qp() failed "
|
|
"ehca_qp=%p qp_num=%x h_ret=%lli",
|
|
my_qp, my_qp->real_qp_num, h_ret);
|
|
goto query_srq_exit1;
|
|
}
|
|
|
|
srq_attr->max_wr = qpcb->max_nr_outst_recv_wr - 1;
|
|
srq_attr->max_sge = 3;
|
|
srq_attr->srq_limit = qpcb->curr_srq_limit;
|
|
|
|
if (ehca_debug_level >= 2)
|
|
ehca_dmp(qpcb, 4*70, "qp_num=%x", my_qp->real_qp_num);
|
|
|
|
query_srq_exit1:
|
|
ehca_free_fw_ctrlblock(qpcb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp,
|
|
struct ib_uobject *uobject)
|
|
{
|
|
struct ehca_shca *shca = container_of(dev, struct ehca_shca, ib_device);
|
|
struct ehca_pd *my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd,
|
|
ib_pd);
|
|
struct ehca_sport *sport = &shca->sport[my_qp->init_attr.port_num - 1];
|
|
u32 qp_num = my_qp->real_qp_num;
|
|
int ret;
|
|
u64 h_ret;
|
|
u8 port_num;
|
|
int is_user = 0;
|
|
enum ib_qp_type qp_type;
|
|
unsigned long flags;
|
|
|
|
if (uobject) {
|
|
is_user = 1;
|
|
if (my_qp->mm_count_galpa ||
|
|
my_qp->mm_count_rqueue || my_qp->mm_count_squeue) {
|
|
ehca_err(dev, "Resources still referenced in "
|
|
"user space qp_num=%x", qp_num);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (my_qp->send_cq) {
|
|
ret = ehca_cq_unassign_qp(my_qp->send_cq, qp_num);
|
|
if (ret) {
|
|
ehca_err(dev, "Couldn't unassign qp from "
|
|
"send_cq ret=%i qp_num=%x cq_num=%x", ret,
|
|
qp_num, my_qp->send_cq->cq_number);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
write_lock_irqsave(&ehca_qp_idr_lock, flags);
|
|
idr_remove(&ehca_qp_idr, my_qp->token);
|
|
write_unlock_irqrestore(&ehca_qp_idr_lock, flags);
|
|
|
|
/*
|
|
* SRQs will never get into an error list and do not have a recv_cq,
|
|
* so we need to skip them here.
|
|
*/
|
|
if (HAS_RQ(my_qp) && !IS_SRQ(my_qp) && !is_user)
|
|
del_from_err_list(my_qp->recv_cq, &my_qp->rq_err_node);
|
|
|
|
if (HAS_SQ(my_qp) && !is_user)
|
|
del_from_err_list(my_qp->send_cq, &my_qp->sq_err_node);
|
|
|
|
/* now wait until all pending events have completed */
|
|
wait_event(my_qp->wait_completion, !atomic_read(&my_qp->nr_events));
|
|
|
|
h_ret = hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp);
|
|
if (h_ret != H_SUCCESS) {
|
|
ehca_err(dev, "hipz_h_destroy_qp() failed h_ret=%lli "
|
|
"ehca_qp=%p qp_num=%x", h_ret, my_qp, qp_num);
|
|
return ehca2ib_return_code(h_ret);
|
|
}
|
|
|
|
port_num = my_qp->init_attr.port_num;
|
|
qp_type = my_qp->init_attr.qp_type;
|
|
|
|
if (qp_type == IB_QPT_SMI || qp_type == IB_QPT_GSI) {
|
|
spin_lock_irqsave(&sport->mod_sqp_lock, flags);
|
|
kfree(my_qp->mod_qp_parm);
|
|
my_qp->mod_qp_parm = NULL;
|
|
shca->sport[port_num - 1].ibqp_sqp[qp_type] = NULL;
|
|
spin_unlock_irqrestore(&sport->mod_sqp_lock, flags);
|
|
}
|
|
|
|
/* no support for IB_QPT_SMI yet */
|
|
if (qp_type == IB_QPT_GSI) {
|
|
struct ib_event event;
|
|
ehca_info(dev, "device %s: port %x is inactive.",
|
|
shca->ib_device.name, port_num);
|
|
event.device = &shca->ib_device;
|
|
event.event = IB_EVENT_PORT_ERR;
|
|
event.element.port_num = port_num;
|
|
shca->sport[port_num - 1].port_state = IB_PORT_DOWN;
|
|
ib_dispatch_event(&event);
|
|
}
|
|
|
|
if (HAS_RQ(my_qp)) {
|
|
ipz_queue_dtor(my_pd, &my_qp->ipz_rqueue);
|
|
if (!is_user)
|
|
vfree(my_qp->rq_map.map);
|
|
}
|
|
if (HAS_SQ(my_qp)) {
|
|
ipz_queue_dtor(my_pd, &my_qp->ipz_squeue);
|
|
if (!is_user)
|
|
vfree(my_qp->sq_map.map);
|
|
}
|
|
kmem_cache_free(qp_cache, my_qp);
|
|
atomic_dec(&shca->num_qps);
|
|
return 0;
|
|
}
|
|
|
|
int ehca_destroy_qp(struct ib_qp *qp)
|
|
{
|
|
return internal_destroy_qp(qp->device,
|
|
container_of(qp, struct ehca_qp, ib_qp),
|
|
qp->uobject);
|
|
}
|
|
|
|
int ehca_destroy_srq(struct ib_srq *srq)
|
|
{
|
|
return internal_destroy_qp(srq->device,
|
|
container_of(srq, struct ehca_qp, ib_srq),
|
|
srq->uobject);
|
|
}
|
|
|
|
int ehca_init_qp_cache(void)
|
|
{
|
|
qp_cache = kmem_cache_create("ehca_cache_qp",
|
|
sizeof(struct ehca_qp), 0,
|
|
SLAB_HWCACHE_ALIGN,
|
|
NULL);
|
|
if (!qp_cache)
|
|
return -ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
void ehca_cleanup_qp_cache(void)
|
|
{
|
|
if (qp_cache)
|
|
kmem_cache_destroy(qp_cache);
|
|
}
|