drivers/infiniband/hw/ehca/ehca_mrmw.c | 2472 ++++++++++++++++++++++++++++++++
drivers/infiniband/hw/ehca/ehca_mrmw.h | 143 ++
2 files changed, 2615 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c
new file mode 100644
index 0000000..99160d7
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c
@@ -0,0 +1,2472 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * MR/MW functions
+ *
+ * Authors: Dietmar Decker <[email protected]>
+ * Christoph Raisch <[email protected]>
+ *
+ * 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.
+ */
+
+#undef DEB_PREFIX
+#define DEB_PREFIX "mrmw"
+
+#include <asm/current.h>
+
+#include "ehca_iverbs.h"
+#include "ehca_mrmw.h"
+#include "hcp_if.h"
+#include "hipz_hw.h"
+
+extern int ehca_use_hp_mr;
+
+static struct ehca_mr *ehca_mr_new(void)
+{
+ extern struct ehca_module ehca_module;
+ struct ehca_mr *me;
+
+ me = kmem_cache_alloc(ehca_module.cache_mr, SLAB_KERNEL);
+ if (me) {
+ memset(me, 0, sizeof(struct ehca_mr));
+ spin_lock_init(&me->mrlock);
+ EDEB_EX(7, "ehca_mr=%p sizeof(ehca_mr_t)=%x", me,
+ (u32) sizeof(struct ehca_mr));
+ } else {
+ EDEB_ERR(3, "alloc failed");
+ }
+
+ return me;
+}
+
+static void ehca_mr_delete(struct ehca_mr *me)
+{
+ extern struct ehca_module ehca_module;
+
+ kmem_cache_free(ehca_module.cache_mr, me);
+}
+
+static struct ehca_mw *ehca_mw_new(void)
+{
+ extern struct ehca_module ehca_module;
+ struct ehca_mw *me;
+
+ me = kmem_cache_alloc(ehca_module.cache_mw, SLAB_KERNEL);
+ if (me) {
+ memset(me, 0, sizeof(struct ehca_mw));
+ spin_lock_init(&me->mwlock);
+ EDEB_EX(7, "ehca_mw=%p sizeof(ehca_mw_t)=%x", me,
+ (u32) sizeof(struct ehca_mw));
+ } else {
+ EDEB_ERR(3, "alloc failed");
+ }
+
+ return me;
+}
+
+static void ehca_mw_delete(struct ehca_mw *me)
+{
+ extern struct ehca_module ehca_module;
+
+ kmem_cache_free(ehca_module.cache_mw, me);
+}
+
+/*----------------------------------------------------------------------*/
+
+struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags)
+{
+ struct ib_mr *ib_mr = NULL;
+ int ret = 0;
+ struct ehca_mr *e_maxmr = NULL;
+ struct ehca_pd *e_pd = NULL;
+ struct ehca_shca *shca = NULL;
+
+ EDEB_EN(7, "pd=%p mr_access_flags=%x", pd, mr_access_flags);
+
+ EHCA_CHECK_PD_P(pd);
+ e_pd = container_of(pd, struct ehca_pd, ib_pd);
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+ if (shca->maxmr) {
+ e_maxmr = ehca_mr_new();
+ if (!e_maxmr) {
+ EDEB_ERR(4, "out of memory");
+ ib_mr = ERR_PTR(-ENOMEM);
+ goto get_dma_mr_exit0;
+ }
+
+ ret = ehca_reg_maxmr(shca, e_maxmr, (u64*)KERNELBASE,
+ mr_access_flags, e_pd,
+ &e_maxmr->ib.ib_mr.lkey,
+ &e_maxmr->ib.ib_mr.rkey);
+ if (ret) {
+ ib_mr = ERR_PTR(ret);
+ goto get_dma_mr_exit0;
+ }
+ ib_mr = &e_maxmr->ib.ib_mr;
+ } else {
+ EDEB_ERR(4, "no internal max-MR exist!");
+ ib_mr = ERR_PTR(-EINVAL);
+ goto get_dma_mr_exit0;
+ }
+
+get_dma_mr_exit0:
+ if (IS_ERR(ib_mr))
+ EDEB_EX(4, "rc=%lx pd=%p mr_access_flags=%x ",
+ PTR_ERR(ib_mr), pd, mr_access_flags);
+ else
+ EDEB_EX(7, "ib_mr=%p lkey=%x rkey=%x",
+ ib_mr, ib_mr->lkey, ib_mr->rkey);
+ return ib_mr;
+} /* end ehca_get_dma_mr() */
+
+/*----------------------------------------------------------------------*/
+
+struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd,
+ struct ib_phys_buf *phys_buf_array,
+ int num_phys_buf,
+ int mr_access_flags,
+ u64 *iova_start)
+{
+ struct ib_mr *ib_mr = NULL;
+ int ret = 0;
+ struct ehca_mr *e_mr = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ehca_pd *e_pd = NULL;
+ u64 size = 0;
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+ u32 num_pages_mr = 0;
+ u32 num_pages_4k = 0; /* 4k portion "pages" */
+
+ EDEB_EN(7, "pd=%p phys_buf_array=%p num_phys_buf=%x "
+ "mr_access_flags=%x iova_start=%p", pd, phys_buf_array,
+ num_phys_buf, mr_access_flags, iova_start);
+
+ EHCA_CHECK_PD_P(pd);
+ if ((num_phys_buf <= 0) || ehca_adr_bad(phys_buf_array)) {
+ EDEB_ERR(4, "bad input values: num_phys_buf=%x "
+ "phys_buf_array=%p", num_phys_buf, phys_buf_array);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_phys_mr_exit0;
+ }
+ if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+ ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE))) {
+ /*
+ * Remote Write Access requires Local Write Access
+ * Remote Atomic Access requires Local Write Access
+ */
+ EDEB_ERR(4, "bad input values: mr_access_flags=%x",
+ mr_access_flags);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_phys_mr_exit0;
+ }
+
+ /* check physical buffer list and calculate size */
+ ret = ehca_mr_chk_buf_and_calc_size(phys_buf_array, num_phys_buf,
+ iova_start, &size);
+ if (ret) {
+ ib_mr = ERR_PTR(ret);
+ goto reg_phys_mr_exit0;
+ }
+ if ((size == 0) ||
+ (((u64)iova_start + size) < (u64)iova_start)) {
+ EDEB_ERR(4, "bad input values: size=%lx iova_start=%p",
+ size, iova_start);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_phys_mr_exit0;
+ }
+
+ e_pd = container_of(pd, struct ehca_pd, ib_pd);
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+ e_mr = ehca_mr_new();
+ if (!e_mr) {
+ EDEB_ERR(4, "out of memory");
+ ib_mr = ERR_PTR(-ENOMEM);
+ goto reg_phys_mr_exit0;
+ }
+
+ /* determine number of MR pages */
+ num_pages_mr = ((((u64)iova_start % PAGE_SIZE) + size +
+ PAGE_SIZE - 1) / PAGE_SIZE);
+ num_pages_4k = ((((u64)iova_start % EHCA_PAGESIZE) + size +
+ EHCA_PAGESIZE - 1) / EHCA_PAGESIZE);
+
+ /* register MR on HCA */
+ if (ehca_mr_is_maxmr(size, iova_start)) {
+ e_mr->flags |= EHCA_MR_FLAG_MAXMR;
+ ret = ehca_reg_maxmr(shca, e_mr, iova_start, mr_access_flags,
+ e_pd, &e_mr->ib.ib_mr.lkey,
+ &e_mr->ib.ib_mr.rkey);
+ if (ret) {
+ ib_mr = ERR_PTR(ret);
+ goto reg_phys_mr_exit1;
+ }
+ } else {
+ pginfo.type = EHCA_MR_PGI_PHYS;
+ pginfo.num_pages = num_pages_mr;
+ pginfo.num_4k = num_pages_4k;
+ pginfo.num_phys_buf = num_phys_buf;
+ pginfo.phys_buf_array = phys_buf_array;
+ pginfo.next_4k = (((u64)iova_start & ~PAGE_MASK) /
+ EHCA_PAGESIZE);
+
+ ret = ehca_reg_mr(shca, e_mr, iova_start, size, mr_access_flags,
+ e_pd, &pginfo, &e_mr->ib.ib_mr.lkey,
+ &e_mr->ib.ib_mr.rkey);
+ if (ret) {
+ ib_mr = ERR_PTR(ret);
+ goto reg_phys_mr_exit1;
+ }
+ }
+
+ /* successful registration of all pages */
+ ib_mr = &e_mr->ib.ib_mr;
+ goto reg_phys_mr_exit0;
+
+reg_phys_mr_exit1:
+ ehca_mr_delete(e_mr);
+reg_phys_mr_exit0:
+ if (IS_ERR(ib_mr))
+ EDEB_EX(4, "rc=%lx pd=%p phys_buf_array=%p "
+ "num_phys_buf=%x mr_access_flags=%x iova_start=%p",
+ PTR_ERR(ib_mr), pd, phys_buf_array,
+ num_phys_buf, mr_access_flags, iova_start);
+ else
+ EDEB_EX(7, "ib_mr=%p lkey=%x rkey=%x",
+ ib_mr, ib_mr->lkey, ib_mr->rkey);
+ return ib_mr;
+} /* end ehca_reg_phys_mr() */
+
+/*----------------------------------------------------------------------*/
+
+struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd,
+ struct ib_umem *region,
+ int mr_access_flags,
+ struct ib_udata *udata)
+{
+ struct ib_mr *ib_mr = NULL;
+ struct ehca_mr *e_mr = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ehca_pd *e_pd = NULL;
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+ int ret = 0;
+ u32 num_pages_mr = 0;
+ u32 num_pages_4k = 0; /* 4k portion "pages" */
+
+ EDEB_EN(7, "pd=%p region=%p mr_access_flags=%x udata=%p",
+ pd, region, mr_access_flags, udata);
+
+ EHCA_CHECK_PD_P(pd);
+ if (ehca_adr_bad(region)) {
+ EDEB_ERR(4, "bad input values: region=%p", region);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_user_mr_exit0;
+ }
+ if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+ ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE))) {
+ /*
+ * Remote Write Access requires Local Write Access
+ * Remote Atomic Access requires Local Write Access
+ */
+ EDEB_ERR(4, "bad input values: mr_access_flags=%x",
+ mr_access_flags);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_user_mr_exit0;
+ }
+ EDEB(7, "user_base=%lx virt_base=%lx length=%lx offset=%x page_size=%x "
+ "chunk_list.next=%p",
+ region->user_base, region->virt_base, region->length,
+ region->offset, region->page_size, region->chunk_list.next);
+ if (region->page_size != PAGE_SIZE) {
+ EDEB_ERR(4, "page size not supported, region->page_size=%x",
+ region->page_size);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_user_mr_exit0;
+ }
+
+ if ((region->length == 0) ||
+ ((region->virt_base + region->length) < region->virt_base)) {
+ EDEB_ERR(4, "bad input values: length=%lx virt_base=%lx",
+ region->length, region->virt_base);
+ ib_mr = ERR_PTR(-EINVAL);
+ goto reg_user_mr_exit0;
+ }
+
+ e_pd = container_of(pd, struct ehca_pd, ib_pd);
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+ e_mr = ehca_mr_new();
+ if (!e_mr) {
+ EDEB_ERR(4, "out of memory");
+ ib_mr = ERR_PTR(-ENOMEM);
+ goto reg_user_mr_exit0;
+ }
+
+ /* determine number of MR pages */
+ num_pages_mr = (((region->virt_base % PAGE_SIZE) + region->length +
+ PAGE_SIZE - 1) / PAGE_SIZE);
+ num_pages_4k = (((region->virt_base % EHCA_PAGESIZE) + region->length +
+ EHCA_PAGESIZE - 1) / EHCA_PAGESIZE);
+
+ /* register MR on HCA */
+ pginfo.type = EHCA_MR_PGI_USER;
+ pginfo.num_pages = num_pages_mr;
+ pginfo.num_4k = num_pages_4k;
+ pginfo.region = region;
+ pginfo.next_4k = region->offset / EHCA_PAGESIZE;
+ pginfo.next_chunk = list_prepare_entry(pginfo.next_chunk,
+ (®ion->chunk_list),
+ list);
+
+ ret = ehca_reg_mr(shca, e_mr, (u64*)region->virt_base,
+ region->length, mr_access_flags, e_pd, &pginfo,
+ &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey);
+ if (ret) {
+ ib_mr = ERR_PTR(ret);
+ goto reg_user_mr_exit1;
+ }
+
+ /* successful registration of all pages */
+ ib_mr = &e_mr->ib.ib_mr;
+ goto reg_user_mr_exit0;
+
+reg_user_mr_exit1:
+ ehca_mr_delete(e_mr);
+reg_user_mr_exit0:
+ if (IS_ERR(ib_mr))
+ EDEB_EX(4, "rc=%lx pd=%p region=%p mr_access_flags=%x "
+ "udata=%p",
+ PTR_ERR(ib_mr), pd, region, mr_access_flags, udata);
+ else
+ EDEB_EX(7, "ib_mr=%p lkey=%x rkey=%x",
+ ib_mr, ib_mr->lkey, ib_mr->rkey);
+ return ib_mr;
+} /* end ehca_reg_user_mr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_rereg_phys_mr(struct ib_mr *mr,
+ int mr_rereg_mask,
+ struct ib_pd *pd,
+ struct ib_phys_buf *phys_buf_array,
+ int num_phys_buf,
+ int mr_access_flags,
+ u64 *iova_start)
+{
+ int ret = 0;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mr *e_mr = NULL;
+ u64 new_size = 0;
+ u64 *new_start = NULL;
+ u32 new_acl = 0;
+ struct ehca_pd *new_pd = NULL;
+ u32 tmp_lkey = 0;
+ u32 tmp_rkey = 0;
+ unsigned long sl_flags;
+ u32 num_pages_mr = 0;
+ u32 num_pages_4k = 0; /* 4k portion "pages" */
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+
+ EDEB_EN(7, "mr=%p mr_rereg_mask=%x pd=%p phys_buf_array=%p "
+ "num_phys_buf=%x mr_access_flags=%x iova_start=%p",
+ mr, mr_rereg_mask, pd, phys_buf_array, num_phys_buf,
+ mr_access_flags, iova_start);
+
+ EHCA_CHECK_MR(mr);
+ my_pd = container_of(mr->pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ (my_pd->ownpid != cur_pid)) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+
+ if (!(mr_rereg_mask & IB_MR_REREG_TRANS)) {
+ /* TODO not supported, because PHYP rereg hCall needs pages */
+ EDEB_ERR(4, "rereg without IB_MR_REREG_TRANS not supported yet,"
+ " mr_rereg_mask=%x", mr_rereg_mask);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+
+ e_mr = container_of(mr, struct ehca_mr, ib.ib_mr);
+ if (mr_rereg_mask & IB_MR_REREG_PD) {
+ EHCA_CHECK_PD(pd);
+ }
+
+ if ((mr_rereg_mask &
+ ~(IB_MR_REREG_TRANS | IB_MR_REREG_PD | IB_MR_REREG_ACCESS)) ||
+ (mr_rereg_mask == 0)) {
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+
+ shca = container_of(mr->device, struct ehca_shca, ib_device);
+
+ /* check other parameters */
+ if (e_mr == shca->maxmr) {
+ /* should be impossible, however reject to be sure */
+ EDEB_ERR(3, "rereg internal max-MR impossible, mr=%p "
+ "shca->maxmr=%p mr->lkey=%x",
+ mr, shca->maxmr, mr->lkey);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+ if (mr_rereg_mask & IB_MR_REREG_TRANS) { /* transl., i.e. addr/size */
+ if (e_mr->flags & EHCA_MR_FLAG_FMR) {
+ EDEB_ERR(4, "not supported for FMR, mr=%p flags=%x",
+ mr, e_mr->flags);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+ if (ehca_adr_bad(phys_buf_array) || num_phys_buf <= 0) {
+ EDEB_ERR(4, "bad input values: mr_rereg_mask=%x "
+ "phys_buf_array=%p num_phys_buf=%x",
+ mr_rereg_mask, phys_buf_array, num_phys_buf);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+ }
+ if ((mr_rereg_mask & IB_MR_REREG_ACCESS) && /* change ACL */
+ (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+ ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)))) {
+ /*
+ * Remote Write Access requires Local Write Access
+ * Remote Atomic Access requires Local Write Access
+ */
+ EDEB_ERR(4, "bad input values: mr_rereg_mask=%x "
+ "mr_access_flags=%x", mr_rereg_mask, mr_access_flags);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit0;
+ }
+
+ /* set requested values dependent on rereg request */
+ spin_lock_irqsave(&e_mr->mrlock, sl_flags);
+ new_start = e_mr->start; /* new == old address */
+ new_size = e_mr->size; /* new == old length */
+ new_acl = e_mr->acl; /* new == old access control */
+ new_pd = container_of(mr->pd,struct ehca_pd,ib_pd); /*new == old PD*/
+
+ if (mr_rereg_mask & IB_MR_REREG_TRANS) {
+ new_start = iova_start; /* change address */
+ /* check physical buffer list and calculate size */
+ ret = ehca_mr_chk_buf_and_calc_size(phys_buf_array,
+ num_phys_buf, iova_start,
+ &new_size);
+ if (ret)
+ goto rereg_phys_mr_exit1;
+ if ((new_size == 0) ||
+ (((u64)iova_start + new_size) < (u64)iova_start)) {
+ EDEB_ERR(4, "bad input values: new_size=%lx "
+ "iova_start=%p", new_size, iova_start);
+ ret = -EINVAL;
+ goto rereg_phys_mr_exit1;
+ }
+ num_pages_mr = ((((u64)new_start % PAGE_SIZE) + new_size +
+ PAGE_SIZE - 1) / PAGE_SIZE);
+ num_pages_4k = ((((u64)new_start % EHCA_PAGESIZE) + new_size +
+ EHCA_PAGESIZE - 1) / EHCA_PAGESIZE);
+ pginfo.type = EHCA_MR_PGI_PHYS;
+ pginfo.num_pages = num_pages_mr;
+ pginfo.num_4k = num_pages_4k;
+ pginfo.num_phys_buf = num_phys_buf;
+ pginfo.phys_buf_array = phys_buf_array;
+ pginfo.next_4k = (((u64)iova_start & ~PAGE_MASK) /
+ EHCA_PAGESIZE);
+ }
+ if (mr_rereg_mask & IB_MR_REREG_ACCESS)
+ new_acl = mr_access_flags;
+ if (mr_rereg_mask & IB_MR_REREG_PD)
+ new_pd = container_of(pd, struct ehca_pd, ib_pd);
+
+ EDEB(7, "mr=%p new_start=%p new_size=%lx new_acl=%x new_pd=%p "
+ "num_pages_mr=%x num_pages_4k=%x", e_mr, new_start, new_size,
+ new_acl, new_pd, num_pages_mr, num_pages_4k);
+
+ ret = ehca_rereg_mr(shca, e_mr, new_start, new_size, new_acl,
+ new_pd, &pginfo, &tmp_lkey, &tmp_rkey);
+ if (ret)
+ goto rereg_phys_mr_exit1;
+
+ /* successful reregistration */
+ if (mr_rereg_mask & IB_MR_REREG_PD)
+ mr->pd = pd;
+ mr->lkey = tmp_lkey;
+ mr->rkey = tmp_rkey;
+
+rereg_phys_mr_exit1:
+ spin_unlock_irqrestore(&e_mr->mrlock, sl_flags);
+rereg_phys_mr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x mr=%p mr_rereg_mask=%x pd=%p "
+ "phys_buf_array=%p num_phys_buf=%x mr_access_flags=%x "
+ "iova_start=%p",
+ ret, mr, mr_rereg_mask, pd, phys_buf_array,
+ num_phys_buf, mr_access_flags, iova_start);
+ else
+ EDEB_EX(7, "mr=%p mr_rereg_mask=%x pd=%p phys_buf_array=%p "
+ "num_phys_buf=%x mr_access_flags=%x iova_start=%p",
+ mr, mr_rereg_mask, pd, phys_buf_array, num_phys_buf,
+ mr_access_flags, iova_start);
+
+ return ret;
+} /* end ehca_rereg_phys_mr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mr *e_mr = NULL;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+ unsigned long sl_flags;
+ struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0};
+
+ EDEB_EN(7, "mr=%p mr_attr=%p", mr, mr_attr);
+
+ EHCA_CHECK_MR(mr);
+
+ my_pd = container_of(mr->pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ (my_pd->ownpid != cur_pid)) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ ret = -EINVAL;
+ goto query_mr_exit0;
+ }
+
+ e_mr = container_of(mr, struct ehca_mr, ib.ib_mr);
+ if (ehca_adr_bad(mr_attr)) {
+ EDEB_ERR(4, "bad input values: mr_attr=%p", mr_attr);
+ ret = -EINVAL;
+ goto query_mr_exit0;
+ }
+ if ((e_mr->flags & EHCA_MR_FLAG_FMR)) {
+ EDEB_ERR(4, "not supported for FMR, mr=%p e_mr=%p "
+ "e_mr->flags=%x", mr, e_mr, e_mr->flags);
+ ret = -EINVAL;
+ goto query_mr_exit0;
+ }
+
+ shca = container_of(mr->device, struct ehca_shca, ib_device);
+ memset(mr_attr, 0, sizeof(struct ib_mr_attr));
+ spin_lock_irqsave(&e_mr->mrlock, sl_flags);
+
+ h_ret = hipz_h_query_mr(shca->ipz_hca_handle, e_mr, &hipzout);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_mr_query failed, h_ret=%lx mr=%p "
+ "hca_hndl=%lx mr_hndl=%lx lkey=%x",
+ h_ret, mr, shca->ipz_hca_handle.handle,
+ e_mr->ipz_mr_handle.handle, mr->lkey);
+ ret = ehca_mrmw_map_hrc_query_mr(h_ret);
+ goto query_mr_exit1;
+ }
+ mr_attr->pd = mr->pd;
+ mr_attr->device_virt_addr = hipzout.vaddr;
+ mr_attr->size = hipzout.len;
+ mr_attr->lkey = hipzout.lkey;
+ mr_attr->rkey = hipzout.rkey;
+ ehca_mrmw_reverse_map_acl(&hipzout.acl, &mr_attr->mr_access_flags);
+
+query_mr_exit1:
+ spin_unlock_irqrestore(&e_mr->mrlock, sl_flags);
+query_mr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x mr=%p mr_attr=%p", ret, mr, mr_attr);
+ else
+ EDEB_EX(7, "pd=%p device_virt_addr=%lx size=%lx "
+ "mr_access_flags=%x lkey=%x rkey=%x",
+ mr_attr->pd, mr_attr->device_virt_addr,
+ mr_attr->size, mr_attr->mr_access_flags,
+ mr_attr->lkey, mr_attr->rkey);
+ return ret;
+} /* end ehca_query_mr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_dereg_mr(struct ib_mr *mr)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mr *e_mr = NULL;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+
+ EDEB_EN(7, "mr=%p", mr);
+
+ EHCA_CHECK_MR(mr);
+ my_pd = container_of(mr->pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ (my_pd->ownpid != cur_pid)) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ ret = -EINVAL;
+ goto dereg_mr_exit0;
+ }
+
+ e_mr = container_of(mr, struct ehca_mr, ib.ib_mr);
+ shca = container_of(mr->device, struct ehca_shca, ib_device);
+
+ if ((e_mr->flags & EHCA_MR_FLAG_FMR)) {
+ EDEB_ERR(4, "not supported for FMR, mr=%p e_mr=%p "
+ "e_mr->flags=%x", mr, e_mr, e_mr->flags);
+ ret = -EINVAL;
+ goto dereg_mr_exit0;
+ } else if (e_mr == shca->maxmr) {
+ /* should be impossible, however reject to be sure */
+ EDEB_ERR(3, "dereg internal max-MR impossible, mr=%p "
+ "shca->maxmr=%p mr->lkey=%x",
+ mr, shca->maxmr, mr->lkey);
+ ret = -EINVAL;
+ goto dereg_mr_exit0;
+ }
+
+ /* TODO: BUSY: MR still has bound window(s) */
+ h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_mr);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_free_mr failed, h_ret=%lx shca=%p e_mr=%p"
+ " hca_hndl=%lx mr_hndl=%lx mr->lkey=%x",
+ h_ret, shca, e_mr, shca->ipz_hca_handle.handle,
+ e_mr->ipz_mr_handle.handle, mr->lkey);
+ ret = ehca_mrmw_map_hrc_free_mr(h_ret);
+ goto dereg_mr_exit0;
+ }
+
+ /* successful deregistration */
+ ehca_mr_delete(e_mr);
+
+dereg_mr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x mr=%p", ret, mr);
+ else
+ EDEB_EX(7, "");
+ return ret;
+} /* end ehca_dereg_mr() */
+
+/*----------------------------------------------------------------------*/
+
+struct ib_mw *ehca_alloc_mw(struct ib_pd *pd)
+{
+ struct ib_mw *ib_mw = NULL;
+ u64 h_ret = H_SUCCESS;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mw *e_mw = NULL;
+ struct ehca_pd *e_pd = NULL;
+ struct ehca_mw_hipzout_parms hipzout = {{0},0};
+
+ EDEB_EN(7, "pd=%p", pd);
+
+ EHCA_CHECK_PD_P(pd);
+ e_pd = container_of(pd, struct ehca_pd, ib_pd);
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+ e_mw = ehca_mw_new();
+ if (!e_mw) {
+ ib_mw = ERR_PTR(-ENOMEM);
+ goto alloc_mw_exit0;
+ }
+
+ h_ret = hipz_h_alloc_resource_mw(shca->ipz_hca_handle, e_mw,
+ e_pd->fw_pd, &hipzout);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_mw_allocate failed, h_ret=%lx shca=%p "
+ "hca_hndl=%lx mw=%p", h_ret, shca,
+ shca->ipz_hca_handle.handle, e_mw);
+ ib_mw = ERR_PTR(ehca_mrmw_map_hrc_alloc(h_ret));
+ goto alloc_mw_exit1;
+ }
+ /* successful MW allocation */
+ e_mw->ipz_mw_handle = hipzout.handle;
+ e_mw->ib_mw.rkey = hipzout.rkey;
+ ib_mw = &e_mw->ib_mw;
+ goto alloc_mw_exit0;
+
+alloc_mw_exit1:
+ ehca_mw_delete(e_mw);
+alloc_mw_exit0:
+ if (IS_ERR(ib_mw))
+ EDEB_EX(4, "rc=%lx pd=%p", PTR_ERR(ib_mw), pd);
+ else
+ EDEB_EX(7, "ib_mw=%p rkey=%x", ib_mw, ib_mw->rkey);
+ return ib_mw;
+} /* end ehca_alloc_mw() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_bind_mw(struct ib_qp *qp,
+ struct ib_mw *mw,
+ struct ib_mw_bind *mw_bind)
+{
+ int ret = 0;
+
+ /* TODO: not supported up to now */
+ EDEB_ERR(4, "bind MW currently not supported by HCAD");
+ ret = -EPERM;
+ goto bind_mw_exit0;
+
+bind_mw_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x qp=%p mw=%p mw_bind=%p",
+ ret, qp, mw, mw_bind);
+ else
+ EDEB_EX(7, "qp=%p mw=%p mw_bind=%p", qp, mw, mw_bind);
+ return ret;
+} /* end ehca_bind_mw() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_dealloc_mw(struct ib_mw *mw)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mw *e_mw = NULL;
+
+ EDEB_EN(7, "mw=%p", mw);
+
+ EHCA_CHECK_MW(mw);
+ e_mw = container_of(mw, struct ehca_mw, ib_mw);
+ shca = container_of(mw->device, struct ehca_shca, ib_device);
+
+ h_ret = hipz_h_free_resource_mw(shca->ipz_hca_handle, e_mw);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_free_mw failed, h_ret=%lx shca=%p mw=%p "
+ "rkey=%x hca_hndl=%lx mw_hndl=%lx",
+ h_ret, shca, mw, mw->rkey, shca->ipz_hca_handle.handle,
+ e_mw->ipz_mw_handle.handle);
+ ret = ehca_mrmw_map_hrc_free_mw(h_ret);
+ goto dealloc_mw_exit0;
+ }
+ /* successful deallocation */
+ ehca_mw_delete(e_mw);
+
+dealloc_mw_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x mw=%p", ret, mw);
+ else
+ EDEB_EX(7, "");
+ return ret;
+} /* end ehca_dealloc_mw() */
+
+/*----------------------------------------------------------------------*/
+
+struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd,
+ int mr_access_flags,
+ struct ib_fmr_attr *fmr_attr)
+{
+ struct ib_fmr *ib_fmr = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mr *e_fmr = NULL;
+ int ret = 0;
+ struct ehca_pd *e_pd = NULL;
+ u32 tmp_lkey = 0;
+ u32 tmp_rkey = 0;
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+
+ EDEB_EN(7, "pd=%p mr_access_flags=%x fmr_attr=%p",
+ pd, mr_access_flags, fmr_attr);
+
+ EHCA_CHECK_PD_P(pd);
+ if (ehca_adr_bad(fmr_attr)) {
+ EDEB_ERR(4, "bad input values: fmr_attr=%p", fmr_attr);
+ ib_fmr = ERR_PTR(-EINVAL);
+ goto alloc_fmr_exit0;
+ }
+
+ EDEB(7, "max_pages=%x max_maps=%x page_shift=%x",
+ fmr_attr->max_pages, fmr_attr->max_maps, fmr_attr->page_shift);
+
+ /* check other parameters */
+ if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE)) ||
+ ((mr_access_flags & IB_ACCESS_REMOTE_ATOMIC) &&
+ !(mr_access_flags & IB_ACCESS_LOCAL_WRITE))) {
+ /*
+ * Remote Write Access requires Local Write Access
+ * Remote Atomic Access requires Local Write Access
+ */
+ EDEB_ERR(4, "bad input values: mr_access_flags=%x",
+ mr_access_flags);
+ ib_fmr = ERR_PTR(-EINVAL);
+ goto alloc_fmr_exit0;
+ }
+ if (mr_access_flags & IB_ACCESS_MW_BIND) {
+ EDEB_ERR(4, "bad input values: mr_access_flags=%x",
+ mr_access_flags);
+ ib_fmr = ERR_PTR(-EINVAL);
+ goto alloc_fmr_exit0;
+ }
+ if ((fmr_attr->max_pages == 0) || (fmr_attr->max_maps == 0)) {
+ EDEB_ERR(4, "bad input values: fmr_attr->max_pages=%x "
+ "fmr_attr->max_maps=%x fmr_attr->page_shift=%x",
+ fmr_attr->max_pages, fmr_attr->max_maps,
+ fmr_attr->page_shift);
+ ib_fmr = ERR_PTR(-EINVAL);
+ goto alloc_fmr_exit0;
+ }
+ if (((1 << fmr_attr->page_shift) != EHCA_PAGESIZE) &&
+ ((1 << fmr_attr->page_shift) != PAGE_SIZE)) {
+ EDEB_ERR(4, "unsupported fmr_attr->page_shift=%x",
+ fmr_attr->page_shift);
+ ib_fmr = ERR_PTR(-EINVAL);
+ goto alloc_fmr_exit0;
+ }
+
+ e_pd = container_of(pd, struct ehca_pd, ib_pd);
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+ e_fmr = ehca_mr_new();
+ if (!e_fmr) {
+ ib_fmr = ERR_PTR(-ENOMEM);
+ goto alloc_fmr_exit0;
+ }
+ e_fmr->flags |= EHCA_MR_FLAG_FMR;
+
+ /* register MR on HCA */
+ ret = ehca_reg_mr(shca, e_fmr, NULL,
+ fmr_attr->max_pages * (1 << fmr_attr->page_shift),
+ mr_access_flags, e_pd, &pginfo,
+ &tmp_lkey, &tmp_rkey);
+ if (ret) {
+ ib_fmr = ERR_PTR(ret);
+ goto alloc_fmr_exit1;
+ }
+
+ /* successful */
+ e_fmr->fmr_page_size = 1 << fmr_attr->page_shift;
+ e_fmr->fmr_max_pages = fmr_attr->max_pages;
+ e_fmr->fmr_max_maps = fmr_attr->max_maps;
+ e_fmr->fmr_map_cnt = 0;
+ ib_fmr = &e_fmr->ib.ib_fmr;
+ goto alloc_fmr_exit0;
+
+alloc_fmr_exit1:
+ ehca_mr_delete(e_fmr);
+alloc_fmr_exit0:
+ if (IS_ERR(ib_fmr))
+ EDEB_EX(4, "rc=%lx pd=%p mr_access_flags=%x "
+ "fmr_attr=%p", PTR_ERR(ib_fmr), pd,
+ mr_access_flags, fmr_attr);
+ else
+ EDEB_EX(7, "ib_fmr=%p tmp_lkey=%x tmp_rkey=%x",
+ ib_fmr, tmp_lkey, tmp_rkey);
+ return ib_fmr;
+} /* end ehca_alloc_fmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_map_phys_fmr(struct ib_fmr *fmr,
+ u64 *page_list,
+ int list_len,
+ u64 iova)
+{
+ int ret = 0;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mr *e_fmr = NULL;
+ struct ehca_pd *e_pd = NULL;
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+ u32 tmp_lkey = 0;
+ u32 tmp_rkey = 0;
+
+ EDEB_EN(7, "fmr=%p page_list=%p list_len=%x iova=%lx",
+ fmr, page_list, list_len, iova);
+
+ EHCA_CHECK_FMR(fmr);
+ e_fmr = container_of(fmr, struct ehca_mr, ib.ib_fmr);
+ shca = container_of(fmr->device, struct ehca_shca, ib_device);
+ e_pd = container_of(fmr->pd, struct ehca_pd, ib_pd);
+
+ if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) {
+ EDEB_ERR(4, "not a FMR, e_fmr=%p e_fmr->flags=%x",
+ e_fmr, e_fmr->flags);
+ ret = -EINVAL;
+ goto map_phys_fmr_exit0;
+ }
+ ret = ehca_fmr_check_page_list(e_fmr, page_list, list_len);
+ if (ret)
+ goto map_phys_fmr_exit0;
+ if (iova % e_fmr->fmr_page_size) {
+ /* only whole-numbered pages */
+ EDEB_ERR(4, "bad iova, iova=%lx fmr_page_size=%x",
+ iova, e_fmr->fmr_page_size);
+ ret = -EINVAL;
+ goto map_phys_fmr_exit0;
+ }
+ if (e_fmr->fmr_map_cnt >= e_fmr->fmr_max_maps) {
+ /* HCAD does not limit the maps, however trace this anyway */
+ EDEB(6, "map limit exceeded, fmr=%p e_fmr->fmr_map_cnt=%x "
+ "e_fmr->fmr_max_maps=%x",
+ fmr, e_fmr->fmr_map_cnt, e_fmr->fmr_max_maps);
+ }
+
+ pginfo.type = EHCA_MR_PGI_FMR;
+ pginfo.num_pages = list_len;
+ pginfo.num_4k = list_len * (e_fmr->fmr_page_size / EHCA_PAGESIZE);
+ pginfo.page_list = page_list;
+ pginfo.next_4k = ((iova & (e_fmr->fmr_page_size-1)) /
+ EHCA_PAGESIZE);
+
+ ret = ehca_rereg_mr(shca, e_fmr, (u64*)iova,
+ list_len * e_fmr->fmr_page_size,
+ e_fmr->acl, e_pd, &pginfo, &tmp_lkey, &tmp_rkey);
+ if (ret)
+ goto map_phys_fmr_exit0;
+
+ /* successful reregistration */
+ e_fmr->fmr_map_cnt++;
+ e_fmr->ib.ib_fmr.lkey = tmp_lkey;
+ e_fmr->ib.ib_fmr.rkey = tmp_rkey;
+
+map_phys_fmr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x fmr=%p page_list=%p list_len=%x iova=%lx",
+ ret, fmr, page_list, list_len, iova);
+ else
+ EDEB_EX(7, "lkey=%x rkey=%x",
+ e_fmr->ib.ib_fmr.lkey, e_fmr->ib.ib_fmr.rkey);
+ return ret;
+} /* end ehca_map_phys_fmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_unmap_fmr(struct list_head *fmr_list)
+{
+ int ret = 0;
+ struct ib_fmr *ib_fmr = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ehca_shca *prev_shca = NULL;
+ struct ehca_mr *e_fmr = NULL;
+ u32 num_fmr = 0;
+ u32 unmap_fmr_cnt = 0;
+
+ EDEB_EN(7, "fmr_list=%p", fmr_list);
+
+ /* check all FMR belong to same SHCA, and check internal flag */
+ list_for_each_entry(ib_fmr, fmr_list, list) {
+ prev_shca = shca;
+ shca = container_of(ib_fmr->device, struct ehca_shca,
+ ib_device);
+ EHCA_CHECK_FMR(ib_fmr);
+ e_fmr = container_of(ib_fmr, struct ehca_mr, ib.ib_fmr);
+ if ((shca != prev_shca) && prev_shca) {
+ EDEB_ERR(4, "SHCA mismatch, shca=%p prev_shca=%p "
+ "e_fmr=%p", shca, prev_shca, e_fmr);
+ ret = -EINVAL;
+ goto unmap_fmr_exit0;
+ }
+ if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) {
+ EDEB_ERR(4, "not a FMR, e_fmr=%p e_fmr->flags=%x",
+ e_fmr, e_fmr->flags);
+ ret = -EINVAL;
+ goto unmap_fmr_exit0;
+ }
+ num_fmr++;
+ }
+
+ /* loop over all FMRs to unmap */
+ list_for_each_entry(ib_fmr, fmr_list, list) {
+ unmap_fmr_cnt++;
+ e_fmr = container_of(ib_fmr, struct ehca_mr, ib.ib_fmr);
+ shca = container_of(ib_fmr->device, struct ehca_shca,
+ ib_device);
+ ret = ehca_unmap_one_fmr(shca, e_fmr);
+ if (ret) {
+ /* unmap failed, stop unmapping of rest of FMRs */
+ EDEB_ERR(4, "unmap of one FMR failed, stop rest, "
+ "e_fmr=%p num_fmr=%x unmap_fmr_cnt=%x lkey=%x",
+ e_fmr, num_fmr, unmap_fmr_cnt,
+ e_fmr->ib.ib_fmr.lkey);
+ goto unmap_fmr_exit0;
+ }
+ }
+
+unmap_fmr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x fmr_list=%p num_fmr=%x unmap_fmr_cnt=%x",
+ ret, fmr_list, num_fmr, unmap_fmr_cnt);
+ else
+ EDEB_EX(7, "num_fmr=%x", num_fmr);
+ return ret;
+} /* end ehca_unmap_fmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_dealloc_fmr(struct ib_fmr *fmr)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ struct ehca_shca *shca = NULL;
+ struct ehca_mr *e_fmr = NULL;
+
+ EDEB_EN(7, "fmr=%p", fmr);
+
+ EHCA_CHECK_FMR(fmr);
+ e_fmr = container_of(fmr, struct ehca_mr, ib.ib_fmr);
+ shca = container_of(fmr->device, struct ehca_shca, ib_device);
+
+ if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) {
+ EDEB_ERR(4, "not a FMR, e_fmr=%p e_fmr->flags=%x",
+ e_fmr, e_fmr->flags);
+ ret = -EINVAL;
+ goto free_fmr_exit0;
+ }
+
+ h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_fmr);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_free_mr failed, h_ret=%lx e_fmr=%p "
+ "hca_hndl=%lx fmr_hndl=%lx fmr->lkey=%x",
+ h_ret, e_fmr, shca->ipz_hca_handle.handle,
+ e_fmr->ipz_mr_handle.handle, fmr->lkey);
+ ehca_mrmw_map_hrc_free_mr(h_ret);
+ goto free_fmr_exit0;
+ }
+ /* successful deregistration */
+ ehca_mr_delete(e_fmr);
+
+free_fmr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x fmr=%p", ret, fmr);
+ else
+ EDEB_EX(7, "");
+ return ret;
+} /* end ehca_dealloc_fmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_mr(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ u64 *iova_start,
+ u64 size,
+ int acl,
+ struct ehca_pd *e_pd,
+ struct ehca_mr_pginfo *pginfo,
+ u32 *lkey, /*OUT*/
+ u32 *rkey) /*OUT*/
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ u32 hipz_acl = 0;
+ struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0};
+
+ EDEB_EN(7, "shca=%p e_mr=%p iova_start=%p size=%lx acl=%x e_pd=%p "
+ "pginfo=%p num_pages=%lx num_4k=%lx", shca, e_mr, iova_start,
+ size, acl, e_pd, pginfo, pginfo->num_pages, pginfo->num_4k);
+
+ ehca_mrmw_map_acl(acl, &hipz_acl);
+ ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+ if (ehca_use_hp_mr == 1)
+ hipz_acl |= 0x00000001;
+
+ h_ret = hipz_h_alloc_resource_mr(shca->ipz_hca_handle, e_mr,
+ (u64)iova_start, size, hipz_acl,
+ e_pd->fw_pd, &hipzout);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_alloc_mr failed, h_ret=%lx hca_hndl=%lx",
+ h_ret, shca->ipz_hca_handle.handle);
+ ret = ehca_mrmw_map_hrc_alloc(h_ret);
+ goto ehca_reg_mr_exit0;
+ }
+
+ e_mr->ipz_mr_handle = hipzout.handle;
+
+ ret = ehca_reg_mr_rpages(shca, e_mr, pginfo);
+ if (ret)
+ goto ehca_reg_mr_exit1;
+
+ /* successful registration */
+ e_mr->num_pages = pginfo->num_pages;
+ e_mr->num_4k = pginfo->num_4k;
+ e_mr->start = iova_start;
+ e_mr->size = size;
+ e_mr->acl = acl;
+ *lkey = hipzout.lkey;
+ *rkey = hipzout.rkey;
+ goto ehca_reg_mr_exit0;
+
+ehca_reg_mr_exit1:
+ h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_mr);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(1, "h_ret=%lx shca=%p e_mr=%p iova_start=%p "
+ "size=%lx acl=%x e_pd=%p lkey=%x pginfo=%p "
+ "num_pages=%lx num_4k=%lx ret=%x", h_ret, shca, e_mr,
+ iova_start, size, acl, e_pd, hipzout.lkey, pginfo,
+ pginfo->num_pages, pginfo->num_4k, ret);
+ EDEB_ERR(1, "internal error in ehca_reg_mr, not recoverable");
+ }
+ehca_reg_mr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x shca=%p e_mr=%p iova_start=%p size=%lx "
+ "acl=%x e_pd=%p pginfo=%p num_pages=%lx num_4k=%lx",
+ ret, shca, e_mr, iova_start, size, acl, e_pd, pginfo,
+ pginfo->num_pages, pginfo->num_4k);
+ else
+ EDEB_EX(7, "ret=%x lkey=%x rkey=%x", ret, *lkey, *rkey);
+ return ret;
+} /* end ehca_reg_mr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_mr_rpages(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ struct ehca_mr_pginfo *pginfo)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ u32 rnum = 0;
+ u64 rpage = 0;
+ u32 i;
+ u64 *kpage = NULL;
+
+ EDEB_EN(7, "shca=%p e_mr=%p pginfo=%p num_pages=%lx num_4k=%lx",
+ shca, e_mr, pginfo, pginfo->num_pages, pginfo->num_4k);
+
+ kpage = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+ if (!kpage) {
+ EDEB_ERR(4, "kpage alloc failed");
+ ret = -ENOMEM;
+ goto ehca_reg_mr_rpages_exit0;
+ }
+
+ /* max 512 pages per shot */
+ for (i = 0; i < ((pginfo->num_4k + 512 - 1) / 512); i++) {
+
+ if (i == ((pginfo->num_4k + 512 - 1) / 512) - 1) {
+ rnum = pginfo->num_4k % 512; /* last shot */
+ if (rnum == 0)
+ rnum = 512; /* last shot is full */
+ } else
+ rnum = 512;
+
+ if (rnum > 1) {
+ ret = ehca_set_pagebuf(e_mr, pginfo, rnum, kpage);
+ if (ret) {
+ EDEB_ERR(4, "ehca_set_pagebuf bad rc, ret=%x "
+ "rnum=%x kpage=%p", ret, rnum, kpage);
+ ret = -EFAULT;
+ goto ehca_reg_mr_rpages_exit1;
+ }
+ rpage = virt_to_abs(kpage);
+ if (!rpage) {
+ EDEB_ERR(4, "kpage=%p i=%x", kpage, i);
+ ret = -EFAULT;
+ goto ehca_reg_mr_rpages_exit1;
+ }
+ } else { /* rnum==1 */
+ ret = ehca_set_pagebuf_1(e_mr, pginfo, &rpage);
+ if (ret) {
+ EDEB_ERR(4, "ehca_set_pagebuf_1 bad rc, "
+ "ret=%x i=%x", ret, i);
+ ret = -EFAULT;
+ goto ehca_reg_mr_rpages_exit1;
+ }
+ }
+
+ EDEB(9, "i=%x rnum=%x rpage=%lx", i, rnum, rpage);
+
+ h_ret = hipz_h_register_rpage_mr(shca->ipz_hca_handle, e_mr,
+ 0, /* pagesize 4k */
+ 0, rpage, rnum);
+
+ if (i == ((pginfo->num_4k + 512 - 1) / 512) - 1) {
+ /*
+ * check for 'registration complete'==H_SUCCESS
+ * and for 'page registered'==H_PAGE_REGISTERED
+ */
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "last hipz_reg_rpage_mr failed, "
+ "h_ret=%lx e_mr=%p i=%x hca_hndl=%lx "
+ "mr_hndl=%lx lkey=%x", h_ret, e_mr, i,
+ shca->ipz_hca_handle.handle,
+ e_mr->ipz_mr_handle.handle,
+ e_mr->ib.ib_mr.lkey);
+ ret = ehca_mrmw_map_hrc_rrpg_last(h_ret);
+ break;
+ } else
+ ret = 0;
+ } else if (h_ret != H_PAGE_REGISTERED) {
+ EDEB_ERR(4, "hipz_reg_rpage_mr failed, h_ret=%lx "
+ "e_mr=%p i=%x lkey=%x hca_hndl=%lx "
+ "mr_hndl=%lx", h_ret, e_mr, i,
+ e_mr->ib.ib_mr.lkey,
+ shca->ipz_hca_handle.handle,
+ e_mr->ipz_mr_handle.handle);
+ ret = ehca_mrmw_map_hrc_rrpg_notlast(h_ret);
+ break;
+ } else
+ ret = 0;
+ } /* end for(i) */
+
+
+ehca_reg_mr_rpages_exit1:
+ kfree(kpage);
+ehca_reg_mr_rpages_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x shca=%p e_mr=%p pginfo=%p num_pages=%lx "
+ "num_4k=%lx", ret, shca, e_mr, pginfo,
+ pginfo->num_pages, pginfo->num_4k);
+ else
+ EDEB_EX(7, "ret=%x", ret);
+ return ret;
+} /* end ehca_reg_mr_rpages() */
+
+/*----------------------------------------------------------------------*/
+
+inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ u64 *iova_start,
+ u64 size,
+ u32 acl,
+ struct ehca_pd *e_pd,
+ struct ehca_mr_pginfo *pginfo,
+ u32 *lkey, /*OUT*/
+ u32 *rkey) /*OUT*/
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ u32 hipz_acl = 0;
+ u64 *kpage = NULL;
+ u64 rpage = 0;
+ struct ehca_mr_pginfo pginfo_save;
+ struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0};
+
+ EDEB_EN(7, "shca=%p e_mr=%p iova_start=%p size=%lx acl=%x "
+ "e_pd=%p pginfo=%p num_pages=%lx num_4k=%lx", shca, e_mr,
+ iova_start, size, acl, e_pd, pginfo, pginfo->num_pages,
+ pginfo->num_4k);
+
+ ehca_mrmw_map_acl(acl, &hipz_acl);
+ ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+
+ kpage = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+ if (!kpage) {
+ EDEB_ERR(4, "kpage alloc failed");
+ ret = -ENOMEM;
+ goto ehca_rereg_mr_rereg1_exit0;
+ }
+
+ pginfo_save = *pginfo;
+ ret = ehca_set_pagebuf(e_mr, pginfo, pginfo->num_4k, kpage);
+ if (ret) {
+ EDEB_ERR(4, "set pagebuf failed, e_mr=%p pginfo=%p type=%x "
+ "num_pages=%lx num_4k=%lx kpage=%p", e_mr, pginfo,
+ pginfo->type, pginfo->num_pages, pginfo->num_4k,kpage);
+ goto ehca_rereg_mr_rereg1_exit1;
+ }
+ rpage = virt_to_abs(kpage);
+ if (!rpage) {
+ EDEB_ERR(4, "kpage=%p", kpage);
+ ret = -EFAULT;
+ goto ehca_rereg_mr_rereg1_exit1;
+ }
+ h_ret = hipz_h_reregister_pmr(shca->ipz_hca_handle, e_mr,
+ (u64)iova_start, size, hipz_acl,
+ e_pd->fw_pd, rpage, &hipzout);
+ if (h_ret != H_SUCCESS) {
+ /*
+ * reregistration unsuccessful, try it again with the 3 hCalls,
+ * e.g. this is required in case H_MR_CONDITION
+ * (MW bound or MR is shared)
+ */
+ EDEB(6, "hipz_h_reregister_pmr failed (Rereg1), h_ret=%lx "
+ "e_mr=%p", h_ret, e_mr);
+ *pginfo = pginfo_save;
+ ret = -EAGAIN;
+ } else if ((u64*)hipzout.vaddr != iova_start) {
+ EDEB_ERR(4, "PHYP changed iova_start in rereg_pmr, "
+ "iova_start=%p iova_start_out=%lx e_mr=%p "
+ "mr_handle=%lx lkey=%x lkey_out=%x", iova_start,
+ hipzout.vaddr, e_mr, e_mr->ipz_mr_handle.handle,
+ e_mr->ib.ib_mr.lkey, hipzout.lkey);
+ ret = -EFAULT;
+ } else {
+ /*
+ * successful reregistration
+ * note: start and start_out are identical for eServer HCAs
+ */
+ e_mr->num_pages = pginfo->num_pages;
+ e_mr->num_4k = pginfo->num_4k;
+ e_mr->start = iova_start;
+ e_mr->size = size;
+ e_mr->acl = acl;
+ *lkey = hipzout.lkey;
+ *rkey = hipzout.rkey;
+ }
+
+ehca_rereg_mr_rereg1_exit1:
+ kfree(kpage);
+ehca_rereg_mr_rereg1_exit0:
+ if ( ret && (ret != -EAGAIN) )
+ EDEB_EX(4, "ret=%x h_ret=%lx lkey=%x rkey=%x pginfo=%p "
+ "num_pages=%lx num_4k=%lx", ret, h_ret, *lkey, *rkey,
+ pginfo, pginfo->num_pages, pginfo->num_4k);
+ else
+ EDEB_EX(7, "ret=%x h_ret=%lx lkey=%x rkey=%x pginfo=%p "
+ "num_pages=%lx num_4k=%lx", ret, h_ret, *lkey, *rkey,
+ pginfo, pginfo->num_pages, pginfo->num_4k);
+ return ret;
+} /* end ehca_rereg_mr_rereg1() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_rereg_mr(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ u64 *iova_start,
+ u64 size,
+ int acl,
+ struct ehca_pd *e_pd,
+ struct ehca_mr_pginfo *pginfo,
+ u32 *lkey,
+ u32 *rkey)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ int rereg_1_hcall = 1; /* 1: use hipz_h_reregister_pmr directly */
+ int rereg_3_hcall = 0; /* 1: use 3 hipz calls for reregistration */
+
+ EDEB_EN(7, "shca=%p e_mr=%p iova_start=%p size=%lx acl=%x "
+ "e_pd=%p pginfo=%p num_pages=%lx num_4k=%lx", shca, e_mr,
+ iova_start, size, acl, e_pd, pginfo, pginfo->num_pages,
+ pginfo->num_4k);
+
+ /* first determine reregistration hCall(s) */
+ if ((pginfo->num_4k > 512) || (e_mr->num_4k > 512) ||
+ (pginfo->num_4k > e_mr->num_4k)) {
+ EDEB(7, "Rereg3 case, pginfo->num_4k=%lx "
+ "e_mr->num_4k=%x", pginfo->num_4k, e_mr->num_4k);
+ rereg_1_hcall = 0;
+ rereg_3_hcall = 1;
+ }
+
+ if (e_mr->flags & EHCA_MR_FLAG_MAXMR) { /* check for max-MR */
+ rereg_1_hcall = 0;
+ rereg_3_hcall = 1;
+ e_mr->flags &= ~EHCA_MR_FLAG_MAXMR;
+ EDEB(4, "Rereg MR for max-MR! e_mr=%p", e_mr);
+ }
+
+ if (rereg_1_hcall) {
+ ret = ehca_rereg_mr_rereg1(shca, e_mr, iova_start, size,
+ acl, e_pd, pginfo, lkey, rkey);
+ if (ret) {
+ if (ret == -EAGAIN)
+ rereg_3_hcall = 1;
+ else
+ goto ehca_rereg_mr_exit0;
+ }
+ }
+
+ if (rereg_3_hcall) {
+ struct ehca_mr save_mr;
+
+ /* first deregister old MR */
+ h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_mr);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_free_mr failed, h_ret=%lx e_mr=%p "
+ "hca_hndl=%lx mr_hndl=%lx mr->lkey=%x",
+ h_ret, e_mr, shca->ipz_hca_handle.handle,
+ e_mr->ipz_mr_handle.handle,
+ e_mr->ib.ib_mr.lkey);
+ ret = ehca_mrmw_map_hrc_free_mr(h_ret);
+ goto ehca_rereg_mr_exit0;
+ }
+ /* clean ehca_mr_t, without changing struct ib_mr and lock */
+ save_mr = *e_mr;
+ ehca_mr_deletenew(e_mr);
+
+ /* set some MR values */
+ e_mr->flags = save_mr.flags;
+ e_mr->fmr_page_size = save_mr.fmr_page_size;
+ e_mr->fmr_max_pages = save_mr.fmr_max_pages;
+ e_mr->fmr_max_maps = save_mr.fmr_max_maps;
+ e_mr->fmr_map_cnt = save_mr.fmr_map_cnt;
+
+ ret = ehca_reg_mr(shca, e_mr, iova_start, size, acl,
+ e_pd, pginfo, lkey, rkey);
+ if (ret) {
+ u32 offset = (u64)(&e_mr->flags) - (u64)e_mr;
+ memcpy(&e_mr->flags, &(save_mr.flags),
+ sizeof(struct ehca_mr) - offset);
+ goto ehca_rereg_mr_exit0;
+ }
+ }
+
+ehca_rereg_mr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x shca=%p e_mr=%p iova_start=%p size=%lx "
+ "acl=%x e_pd=%p pginfo=%p num_pages=%lx lkey=%x rkey=%x"
+ " rereg_1_hcall=%x rereg_3_hcall=%x", ret, shca, e_mr,
+ iova_start, size, acl, e_pd, pginfo, pginfo->num_pages,
+ *lkey, *rkey, rereg_1_hcall, rereg_3_hcall);
+ else
+ EDEB_EX(7, "ret=%x shca=%p e_mr=%p iova_start=%p size=%lx "
+ "acl=%x e_pd=%p pginfo=%p num_pages=%lx lkey=%x rkey=%x"
+ " rereg_1_hcall=%x rereg_3_hcall=%x", ret, shca, e_mr,
+ iova_start, size, acl, e_pd, pginfo, pginfo->num_pages,
+ *lkey, *rkey, rereg_1_hcall, rereg_3_hcall);
+
+ return ret;
+} /* end ehca_rereg_mr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_unmap_one_fmr(struct ehca_shca *shca,
+ struct ehca_mr *e_fmr)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ int rereg_1_hcall = 1; /* 1: use hipz_mr_reregister directly */
+ int rereg_3_hcall = 0; /* 1: use 3 hipz calls for unmapping */
+ struct ehca_pd *e_pd = NULL;
+ struct ehca_mr save_fmr;
+ u32 tmp_lkey = 0;
+ u32 tmp_rkey = 0;
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+ struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0};
+
+ EDEB_EN(7, "shca=%p e_fmr=%p", shca, e_fmr);
+
+ /* first check if reregistration hCall can be used for unmap */
+ if (e_fmr->fmr_max_pages > 512) {
+ rereg_1_hcall = 0;
+ rereg_3_hcall = 1;
+ }
+
+ e_pd = container_of(e_fmr->ib.ib_fmr.pd, struct ehca_pd, ib_pd);
+
+ if (rereg_1_hcall) {
+ /*
+ * note: after using rereg hcall with len=0,
+ * rereg hcall must be used again for registering pages
+ */
+ h_ret = hipz_h_reregister_pmr(shca->ipz_hca_handle, e_fmr, 0,
+ 0, 0, e_pd->fw_pd, 0, &hipzout);
+ if (h_ret != H_SUCCESS) {
+ /*
+ * should not happen, because length checked above,
+ * FMRs are not shared and no MW bound to FMRs
+ */
+ EDEB_ERR(4, "hipz_reregister_pmr failed (Rereg1), "
+ "h_ret=%lx e_fmr=%p hca_hndl=%lx mr_hndl=%lx "
+ "lkey=%x lkey_out=%x", h_ret, e_fmr,
+ shca->ipz_hca_handle.handle,
+ e_fmr->ipz_mr_handle.handle,
+ e_fmr->ib.ib_fmr.lkey, hipzout.lkey);
+ rereg_3_hcall = 1;
+ } else {
+ /* successful reregistration */
+ e_fmr->start = NULL;
+ e_fmr->size = 0;
+ tmp_lkey = hipzout.lkey;
+ tmp_rkey = hipzout.rkey;
+ }
+ }
+
+ if (rereg_3_hcall) {
+ struct ehca_mr save_mr;
+
+ /* first free old FMR */
+ h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_fmr);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_free_mr failed, h_ret=%lx e_fmr=%p "
+ "hca_hndl=%lx mr_hndl=%lx lkey=%x", h_ret,
+ e_fmr, shca->ipz_hca_handle.handle,
+ e_fmr->ipz_mr_handle.handle,
+ e_fmr->ib.ib_fmr.lkey);
+ ret = ehca_mrmw_map_hrc_free_mr(h_ret);
+ goto ehca_unmap_one_fmr_exit0;
+ }
+ /* clean ehca_mr_t, without changing lock */
+ save_fmr = *e_fmr;
+ ehca_mr_deletenew(e_fmr);
+
+ /* set some MR values */
+ e_fmr->flags = save_fmr.flags;
+ e_fmr->fmr_page_size = save_fmr.fmr_page_size;
+ e_fmr->fmr_max_pages = save_fmr.fmr_max_pages;
+ e_fmr->fmr_max_maps = save_fmr.fmr_max_maps;
+ e_fmr->fmr_map_cnt = save_fmr.fmr_map_cnt;
+ e_fmr->acl = save_fmr.acl;
+
+ pginfo.type = EHCA_MR_PGI_FMR;
+ pginfo.num_pages = 0;
+ pginfo.num_4k = 0;
+ ret = ehca_reg_mr(shca, e_fmr, NULL,
+ (e_fmr->fmr_max_pages * e_fmr->fmr_page_size),
+ e_fmr->acl, e_pd, &pginfo, &tmp_lkey,
+ &tmp_rkey);
+ if (ret) {
+ u32 offset = (u64)(&e_fmr->flags) - (u64)e_fmr;
+ memcpy(&e_fmr->flags, &(save_mr.flags),
+ sizeof(struct ehca_mr) - offset);
+ goto ehca_unmap_one_fmr_exit0;
+ }
+ }
+
+ehca_unmap_one_fmr_exit0:
+ EDEB_EX(7, "ret=%x tmp_lkey=%x tmp_rkey=%x fmr_max_pages=%x "
+ "rereg_1_hcall=%x rereg_3_hcall=%x", ret, tmp_lkey, tmp_rkey,
+ e_fmr->fmr_max_pages, rereg_1_hcall, rereg_3_hcall);
+ return ret;
+} /* end ehca_unmap_one_fmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_smr(struct ehca_shca *shca,
+ struct ehca_mr *e_origmr,
+ struct ehca_mr *e_newmr,
+ u64 *iova_start,
+ int acl,
+ struct ehca_pd *e_pd,
+ u32 *lkey, /*OUT*/
+ u32 *rkey) /*OUT*/
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ u32 hipz_acl = 0;
+ struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0};
+
+ EDEB_EN(7,"shca=%p e_origmr=%p e_newmr=%p iova_start=%p acl=%x e_pd=%p",
+ shca, e_origmr, e_newmr, iova_start, acl, e_pd);
+
+ ehca_mrmw_map_acl(acl, &hipz_acl);
+ ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+
+ h_ret = hipz_h_register_smr(shca->ipz_hca_handle, e_newmr, e_origmr,
+ (u64)iova_start, hipz_acl, e_pd->fw_pd,
+ &hipzout);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_reg_smr failed, h_ret=%lx shca=%p e_origmr=%p"
+ " e_newmr=%p iova_start=%p acl=%x e_pd=%p hca_hndl=%lx"
+ " mr_hndl=%lx lkey=%x", h_ret, shca, e_origmr, e_newmr,
+ iova_start, acl, e_pd, shca->ipz_hca_handle.handle,
+ e_origmr->ipz_mr_handle.handle,
+ e_origmr->ib.ib_mr.lkey);
+ ret = ehca_mrmw_map_hrc_reg_smr(h_ret);
+ goto ehca_reg_smr_exit0;
+ }
+ /* successful registration */
+ e_newmr->num_pages = e_origmr->num_pages;
+ e_newmr->num_4k = e_origmr->num_4k;
+ e_newmr->start = iova_start;
+ e_newmr->size = e_origmr->size;
+ e_newmr->acl = acl;
+ e_newmr->ipz_mr_handle = hipzout.handle;
+ *lkey = hipzout.lkey;
+ *rkey = hipzout.rkey;
+ goto ehca_reg_smr_exit0;
+
+ehca_reg_smr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x shca=%p e_origmr=%p e_newmr=%p "
+ "iova_start=%p acl=%x e_pd=%p",
+ ret, shca, e_origmr, e_newmr, iova_start, acl, e_pd);
+ else
+ EDEB_EX(7, "ret=%x lkey=%x rkey=%x", ret, *lkey, *rkey);
+ return ret;
+} /* end ehca_reg_smr() */
+
+/*----------------------------------------------------------------------*/
+
+/* register internal max-MR to internal SHCA */
+int ehca_reg_internal_maxmr(
+ struct ehca_shca *shca,
+ struct ehca_pd *e_pd,
+ struct ehca_mr **e_maxmr) /*OUT*/
+{
+ int ret = 0;
+ struct ehca_mr *e_mr = NULL;
+ u64 *iova_start = NULL;
+ u64 size_maxmr = 0;
+ struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0};
+ struct ib_phys_buf ib_pbuf;
+ u32 num_pages_mr = 0;
+ u32 num_pages_4k = 0; /* 4k portion "pages" */
+
+ EDEB_EN(7, "shca=%p e_pd=%p e_maxmr=%p", shca, e_pd, e_maxmr);
+
+ if (ehca_adr_bad(shca) || ehca_adr_bad(e_pd) || ehca_adr_bad(e_maxmr)) {
+ EDEB_ERR(4, "bad input values: shca=%p e_pd=%p e_maxmr=%p",
+ shca, e_pd, e_maxmr);
+ ret = -EINVAL;
+ goto ehca_reg_internal_maxmr_exit0;
+ }
+
+ e_mr = ehca_mr_new();
+ if (!e_mr) {
+ EDEB_ERR(4, "out of memory");
+ ret = -ENOMEM;
+ goto ehca_reg_internal_maxmr_exit0;
+ }
+ e_mr->flags |= EHCA_MR_FLAG_MAXMR;
+
+ /* register internal max-MR on HCA */
+ size_maxmr = (u64)high_memory - PAGE_OFFSET;
+ EDEB(7, "high_memory=%p PAGE_OFFSET=%lx", high_memory, PAGE_OFFSET);
+ iova_start = (u64*)KERNELBASE;
+ ib_pbuf.addr = 0;
+ ib_pbuf.size = size_maxmr;
+ num_pages_mr = ((((u64)iova_start % PAGE_SIZE) + size_maxmr +
+ PAGE_SIZE - 1) / PAGE_SIZE);
+ num_pages_4k = ((((u64)iova_start % EHCA_PAGESIZE) + size_maxmr +
+ EHCA_PAGESIZE - 1) / EHCA_PAGESIZE);
+
+ pginfo.type = EHCA_MR_PGI_PHYS;
+ pginfo.num_pages = num_pages_mr;
+ pginfo.num_4k = num_pages_4k;
+ pginfo.num_phys_buf = 1;
+ pginfo.phys_buf_array = &ib_pbuf;
+
+ ret = ehca_reg_mr(shca, e_mr, iova_start, size_maxmr, 0, e_pd,
+ &pginfo, &e_mr->ib.ib_mr.lkey,
+ &e_mr->ib.ib_mr.rkey);
+ if (ret) {
+ EDEB_ERR(4, "reg of internal max MR failed, e_mr=%p "
+ "iova_start=%p size_maxmr=%lx num_pages_mr=%x "
+ "num_pages_4k=%x", e_mr, iova_start, size_maxmr,
+ num_pages_mr, num_pages_4k);
+ goto ehca_reg_internal_maxmr_exit1;
+ }
+
+ /* successful registration of all pages */
+ e_mr->ib.ib_mr.device = e_pd->ib_pd.device;
+ e_mr->ib.ib_mr.pd = &e_pd->ib_pd;
+ e_mr->ib.ib_mr.uobject = NULL;
+ atomic_inc(&(e_pd->ib_pd.usecnt));
+ atomic_set(&(e_mr->ib.ib_mr.usecnt), 0);
+ *e_maxmr = e_mr;
+ goto ehca_reg_internal_maxmr_exit0;
+
+ehca_reg_internal_maxmr_exit1:
+ ehca_mr_delete(e_mr);
+ehca_reg_internal_maxmr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x shca=%p e_pd=%p e_maxmr=%p",
+ ret, shca, e_pd, e_maxmr);
+ else
+ EDEB_EX(7, "*e_maxmr=%p lkey=%x rkey=%x",
+ *e_maxmr, (*e_maxmr)->ib.ib_mr.lkey,
+ (*e_maxmr)->ib.ib_mr.rkey);
+ return ret;
+} /* end ehca_reg_internal_maxmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_reg_maxmr(struct ehca_shca *shca,
+ struct ehca_mr *e_newmr,
+ u64 *iova_start,
+ int acl,
+ struct ehca_pd *e_pd,
+ u32 *lkey,
+ u32 *rkey)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ struct ehca_mr *e_origmr = shca->maxmr;
+ u32 hipz_acl = 0;
+ struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0};
+
+ EDEB_EN(7,"shca=%p e_origmr=%p e_newmr=%p iova_start=%p acl=%x e_pd=%p",
+ shca, e_origmr, e_newmr, iova_start, acl, e_pd);
+
+ ehca_mrmw_map_acl(acl, &hipz_acl);
+ ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl);
+
+ h_ret = hipz_h_register_smr(shca->ipz_hca_handle, e_newmr, e_origmr,
+ (u64)iova_start, hipz_acl, e_pd->fw_pd,
+ &hipzout);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_reg_smr failed, h_ret=%lx e_origmr=%p "
+ "hca_hndl=%lx mr_hndl=%lx lkey=%x",
+ h_ret, e_origmr, shca->ipz_hca_handle.handle,
+ e_origmr->ipz_mr_handle.handle,
+ e_origmr->ib.ib_mr.lkey);
+ ret = ehca_mrmw_map_hrc_reg_smr(h_ret);
+ goto ehca_reg_maxmr_exit0;
+ }
+ /* successful registration */
+ e_newmr->num_pages = e_origmr->num_pages;
+ e_newmr->num_4k = e_origmr->num_4k;
+ e_newmr->start = iova_start;
+ e_newmr->size = e_origmr->size;
+ e_newmr->acl = acl;
+ e_newmr->ipz_mr_handle = hipzout.handle;
+ *lkey = hipzout.lkey;
+ *rkey = hipzout.rkey;
+
+ehca_reg_maxmr_exit0:
+ EDEB_EX(7, "ret=%x lkey=%x rkey=%x", ret, *lkey, *rkey);
+ return ret;
+} /* end ehca_reg_maxmr() */
+
+/*----------------------------------------------------------------------*/
+
+int ehca_dereg_internal_maxmr(struct ehca_shca *shca)
+{
+ int ret = 0;
+ struct ehca_mr *e_maxmr = NULL;
+ struct ib_pd *ib_pd = NULL;
+
+ EDEB_EN(7, "shca=%p shca->maxmr=%p", shca, shca->maxmr);
+
+ if (!shca->maxmr) {
+ EDEB_ERR(4, "bad call, shca=%p", shca);
+ ret = -EINVAL;
+ goto ehca_dereg_internal_maxmr_exit0;
+ }
+
+ e_maxmr = shca->maxmr;
+ ib_pd = e_maxmr->ib.ib_mr.pd;
+ shca->maxmr = NULL; /* remove internal max-MR indication from SHCA */
+
+ ret = ehca_dereg_mr(&e_maxmr->ib.ib_mr);
+ if (ret) {
+ EDEB_ERR(3, "dereg internal max-MR failed, "
+ "ret=%x e_maxmr=%p shca=%p lkey=%x",
+ ret, e_maxmr, shca, e_maxmr->ib.ib_mr.lkey);
+ shca->maxmr = e_maxmr;
+ goto ehca_dereg_internal_maxmr_exit0;
+ }
+
+ atomic_dec(&ib_pd->usecnt);
+
+ehca_dereg_internal_maxmr_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x shca=%p shca->maxmr=%p",
+ ret, shca, shca->maxmr);
+ else
+ EDEB_EX(7, "");
+ return ret;
+} /* end ehca_dereg_internal_maxmr() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * check physical buffer array of MR verbs for validness and
+ * calculates MR size
+ */
+int ehca_mr_chk_buf_and_calc_size(struct ib_phys_buf *phys_buf_array,
+ int num_phys_buf,
+ u64 *iova_start,
+ u64 *size)
+{
+ struct ib_phys_buf *pbuf = phys_buf_array;
+ u64 size_count = 0;
+ u32 i;
+
+ if (num_phys_buf == 0) {
+ EDEB_ERR(4, "bad phys buf array len, num_phys_buf=0");
+ return -EINVAL;
+ }
+ /* check first buffer */
+ if (((u64)iova_start & ~PAGE_MASK) != (pbuf->addr & ~PAGE_MASK)) {
+ EDEB_ERR(4, "iova_start/addr mismatch, iova_start=%p "
+ "pbuf->addr=%lx pbuf->size=%lx",
+ iova_start, pbuf->addr, pbuf->size);
+ return -EINVAL;
+ }
+ if (((pbuf->addr + pbuf->size) % PAGE_SIZE) &&
+ (num_phys_buf > 1)) {
+ EDEB_ERR(4, "addr/size mismatch in 1st buf, pbuf->addr=%lx "
+ "pbuf->size=%lx", pbuf->addr, pbuf->size);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_phys_buf; i++) {
+ if ((i > 0) && (pbuf->addr % PAGE_SIZE)) {
+ EDEB_ERR(4, "bad address, i=%x pbuf->addr=%lx "
+ "pbuf->size=%lx", i, pbuf->addr, pbuf->size);
+ return -EINVAL;
+ }
+ if (((i > 0) && /* not 1st */
+ (i < (num_phys_buf - 1)) && /* not last */
+ (pbuf->size % PAGE_SIZE)) || (pbuf->size == 0)) {
+ EDEB_ERR(4, "bad size, i=%x pbuf->size=%lx",
+ i, pbuf->size);
+ return -EINVAL;
+ }
+ size_count += pbuf->size;
+ pbuf++;
+ }
+
+ *size = size_count;
+ return 0;
+} /* end ehca_mr_chk_buf_and_calc_size() */
+
+/*----------------------------------------------------------------------*/
+
+/* check page list of map FMR verb for validness */
+int ehca_fmr_check_page_list(struct ehca_mr *e_fmr,
+ u64 *page_list,
+ int list_len)
+{
+ u32 i;
+ u64 *page = NULL;
+
+ if (ehca_adr_bad(page_list)) {
+ EDEB_ERR(4, "bad page_list, page_list=%p fmr=%p",
+ page_list, e_fmr);
+ return -EINVAL;
+ }
+
+ if ((list_len == 0) || (list_len > e_fmr->fmr_max_pages)) {
+ EDEB_ERR(4, "bad list_len, list_len=%x e_fmr->fmr_max_pages=%x "
+ "fmr=%p", list_len, e_fmr->fmr_max_pages, e_fmr);
+ return -EINVAL;
+ }
+
+ /* each page must be aligned */
+ page = page_list;
+ for (i = 0; i < list_len; i++) {
+ if (*page % e_fmr->fmr_page_size) {
+ EDEB_ERR(4, "bad page, i=%x *page=%lx page=%p "
+ "fmr=%p fmr_page_size=%x",
+ i, *page, page, e_fmr, e_fmr->fmr_page_size);
+ return -EINVAL;
+ }
+ page++;
+ }
+
+ return 0;
+} /* end ehca_fmr_check_page_list() */
+
+/*----------------------------------------------------------------------*/
+
+/* setup page buffer from page info */
+int ehca_set_pagebuf(struct ehca_mr *e_mr,
+ struct ehca_mr_pginfo *pginfo,
+ u32 number,
+ u64 *kpage)
+{
+ int ret = 0;
+ struct ib_umem_chunk *prev_chunk = NULL;
+ struct ib_umem_chunk *chunk = NULL;
+ struct ib_phys_buf *pbuf = NULL;
+ u64 *fmrlist = NULL;
+ u64 num4k = 0;
+ u64 pgaddr = 0;
+ u64 offs4k = 0;
+ u32 i = 0;
+ u32 j = 0;
+
+ EDEB_EN(7, "pginfo=%p type=%x num_pages=%lx num_4k=%lx next_buf=%lx "
+ "next_4k=%lx number=%x kpage=%p page_cnt=%lx page_4k_cnt=%lx "
+ "next_listelem=%lx region=%p next_chunk=%p next_nmap=%lx",
+ pginfo, pginfo->type, pginfo->num_pages, pginfo->num_4k,
+ pginfo->next_buf, pginfo->next_4k, number, kpage,
+ pginfo->page_cnt, pginfo->page_4k_cnt, pginfo->next_listelem,
+ pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+
+ if (pginfo->type == EHCA_MR_PGI_PHYS) {
+ /* loop over desired phys_buf_array entries */
+ while (i < number) {
+ pbuf = pginfo->phys_buf_array + pginfo->next_buf;
+ num4k = ((pbuf->addr % EHCA_PAGESIZE) + pbuf->size +
+ EHCA_PAGESIZE - 1) / EHCA_PAGESIZE;
+ offs4k = (pbuf->addr & ~PAGE_MASK) / EHCA_PAGESIZE;
+ while (pginfo->next_4k < offs4k + num4k) {
+ /* sanity check */
+ if ((pginfo->page_cnt >= pginfo->num_pages) ||
+ (pginfo->page_4k_cnt >= pginfo->num_4k)) {
+ EDEB_ERR(4, "page_cnt >= num_pages, "
+ "page_cnt=%lx num_pages=%lx "
+ "page_4k_cnt=%lx num_4k=%lx "
+ "i=%x", pginfo->page_cnt,
+ pginfo->num_pages,
+ pginfo->page_4k_cnt,
+ pginfo->num_4k, i);
+ ret = -EFAULT;
+ }
+ *kpage = phys_to_abs(
+ (pbuf->addr & EHCA_PAGEMASK)
+ + (pginfo->next_4k * EHCA_PAGESIZE));
+ if ( !(*kpage) && pbuf->addr ) {
+ EDEB_ERR(4, "pbuf->addr=%lx "
+ "pbuf->size=%lx next_4k=%lx",
+ pbuf->addr, pbuf->size,
+ pginfo->next_4k);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_exit0;
+ }
+ (pginfo->page_4k_cnt)++;
+ (pginfo->next_4k)++;
+ if (pginfo->next_4k %
+ (PAGE_SIZE / EHCA_PAGESIZE) == 0)
+ (pginfo->page_cnt)++;
+ kpage++;
+ i++;
+ if (i >= number) break;
+ }
+ if (pginfo->next_4k >= offs4k + num4k) {
+ (pginfo->next_buf)++;
+ pginfo->next_4k = 0;
+ }
+ }
+ } else if (pginfo->type == EHCA_MR_PGI_USER) {
+ /* loop over desired chunk entries */
+ chunk = pginfo->next_chunk;
+ prev_chunk = pginfo->next_chunk;
+ list_for_each_entry_continue(chunk,
+ (&(pginfo->region->chunk_list)),
+ list) {
+ EDEB(9, "chunk->page_list[0]=%lx",
+ (u64)sg_dma_address(&chunk->page_list[0]));
+ for (i = pginfo->next_nmap; i < chunk->nmap; ) {
+ pgaddr = ( page_to_pfn(chunk->page_list[i].page)
+ << PAGE_SHIFT );
+ *kpage = phys_to_abs(pgaddr +
+ (pginfo->next_4k *
+ EHCA_PAGESIZE));
+ EDEB(9,"pgaddr=%lx *kpage=%lx next_4k=%lx",
+ pgaddr, *kpage, pginfo->next_4k);
+ if ( !(*kpage) ) {
+ EDEB_ERR(4, "pgaddr=%lx "
+ "chunk->page_list[i]=%lx i=%x "
+ "next_4k=%lx mr=%p", pgaddr,
+ (u64)sg_dma_address(
+ &chunk->page_list[i]),
+ i, pginfo->next_4k, e_mr);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_exit0;
+ }
+ (pginfo->page_4k_cnt)++;
+ (pginfo->next_4k)++;
+ kpage++;
+ if (pginfo->next_4k %
+ (PAGE_SIZE / EHCA_PAGESIZE) == 0) {
+ (pginfo->page_cnt)++;
+ (pginfo->next_nmap)++;
+ pginfo->next_4k = 0;
+ i++;
+ }
+ j++;
+ if (j >= number) break;
+ }
+ if ((pginfo->next_nmap >= chunk->nmap) &&
+ (j >= number)) {
+ pginfo->next_nmap = 0;
+ prev_chunk = chunk;
+ break;
+ } else if (pginfo->next_nmap >= chunk->nmap) {
+ pginfo->next_nmap = 0;
+ prev_chunk = chunk;
+ } else if (j >= number)
+ break;
+ else
+ prev_chunk = chunk;
+ }
+ pginfo->next_chunk =
+ list_prepare_entry(prev_chunk,
+ (&(pginfo->region->chunk_list)),
+ list);
+ } else if (pginfo->type == EHCA_MR_PGI_FMR) {
+ /* loop over desired page_list entries */
+ fmrlist = pginfo->page_list + pginfo->next_listelem;
+ for (i = 0; i < number; i++) {
+ *kpage = phys_to_abs((*fmrlist & EHCA_PAGEMASK) +
+ pginfo->next_4k * EHCA_PAGESIZE);
+ if ( !(*kpage) ) {
+ EDEB_ERR(4, "*fmrlist=%lx fmrlist=%p "
+ "next_listelem=%lx next_4k=%lx",
+ *fmrlist, fmrlist,
+ pginfo->next_listelem,pginfo->next_4k);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_exit0;
+ }
+ (pginfo->page_4k_cnt)++;
+ (pginfo->next_4k)++;
+ kpage++;
+ if (pginfo->next_4k %
+ (e_mr->fmr_page_size / EHCA_PAGESIZE) == 0) {
+ (pginfo->page_cnt)++;
+ (pginfo->next_listelem)++;
+ fmrlist++;
+ pginfo->next_4k = 0;
+ }
+ }
+ } else {
+ EDEB_ERR(4, "bad pginfo->type=%x", pginfo->type);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_exit0;
+ }
+
+ehca_set_pagebuf_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+ "num_4k=%lx next_buf=%lx next_4k=%lx number=%x "
+ "kpage=%p page_cnt=%lx page_4k_cnt=%lx i=%x "
+ "next_listelem=%lx region=%p next_chunk=%p "
+ "next_nmap=%lx", ret, e_mr, pginfo, pginfo->type,
+ pginfo->num_pages, pginfo->num_4k, pginfo->next_buf,
+ pginfo->next_4k, number, kpage, pginfo->page_cnt,
+ pginfo->page_4k_cnt, i, pginfo->next_listelem,
+ pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+ else
+ EDEB_EX(7, "ret=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+ "num_4k=%lx next_buf=%lx next_4k=%lx number=%x "
+ "kpage=%p page_cnt=%lx page_4k_cnt=%lx i=%x "
+ "next_listelem=%lx region=%p next_chunk=%p "
+ "next_nmap=%lx", ret, e_mr, pginfo, pginfo->type,
+ pginfo->num_pages, pginfo->num_4k, pginfo->next_buf,
+ pginfo->next_4k, number, kpage, pginfo->page_cnt,
+ pginfo->page_4k_cnt, i, pginfo->next_listelem,
+ pginfo->region, pginfo->next_chunk, pginfo->next_nmap);
+ return ret;
+} /* end ehca_set_pagebuf() */
+
+/*----------------------------------------------------------------------*/
+
+/* setup 1 page from page info page buffer */
+int ehca_set_pagebuf_1(struct ehca_mr *e_mr,
+ struct ehca_mr_pginfo *pginfo,
+ u64 *rpage)
+{
+ int ret = 0;
+ struct ib_phys_buf *tmp_pbuf = NULL;
+ u64 *fmrlist = NULL;
+ struct ib_umem_chunk *chunk = NULL;
+ struct ib_umem_chunk *prev_chunk = NULL;
+ u64 pgaddr = 0;
+ u64 num4k = 0;
+ u64 offs4k = 0;
+
+ EDEB_EN(7, "pginfo=%p type=%x num_pages=%lx num_4k=%lx next_buf=%lx "
+ "next_4k=%lx rpage=%p page_cnt=%lx page_4k_cnt=%lx "
+ "next_listelem=%lx region=%p next_chunk=%p next_nmap=%lx",
+ pginfo, pginfo->type, pginfo->num_pages, pginfo->num_4k,
+ pginfo->next_buf, pginfo->next_4k, rpage, pginfo->page_cnt,
+ pginfo->page_4k_cnt, pginfo->next_listelem, pginfo->region,
+ pginfo->next_chunk, pginfo->next_nmap);
+
+ if (pginfo->type == EHCA_MR_PGI_PHYS) {
+ /* sanity check */
+ if ((pginfo->page_cnt >= pginfo->num_pages) ||
+ (pginfo->page_4k_cnt >= pginfo->num_4k)) {
+ EDEB_ERR(4, "page_cnt >= num_pages, page_cnt=%lx "
+ "num_pages=%lx page_4k_cnt=%lx num_4k=%lx",
+ pginfo->page_cnt, pginfo->num_pages,
+ pginfo->page_4k_cnt, pginfo->num_4k);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_1_exit0;
+ }
+ tmp_pbuf = pginfo->phys_buf_array + pginfo->next_buf;
+ num4k = ((tmp_pbuf->addr % EHCA_PAGESIZE) + tmp_pbuf->size +
+ EHCA_PAGESIZE - 1) / EHCA_PAGESIZE;
+ offs4k = (tmp_pbuf->addr & ~PAGE_MASK) / EHCA_PAGESIZE;
+ *rpage = phys_to_abs((tmp_pbuf->addr & EHCA_PAGEMASK) +
+ (pginfo->next_4k * EHCA_PAGESIZE));
+ if ( !(*rpage) && tmp_pbuf->addr ) {
+ EDEB_ERR(4, "tmp_pbuf->addr=%lx"
+ " tmp_pbuf->size=%lx next_4k=%lx",
+ tmp_pbuf->addr, tmp_pbuf->size,
+ pginfo->next_4k);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_1_exit0;
+ }
+ (pginfo->page_4k_cnt)++;
+ (pginfo->next_4k)++;
+ if (pginfo->next_4k % (PAGE_SIZE / EHCA_PAGESIZE) == 0)
+ (pginfo->page_cnt)++;
+ if (pginfo->next_4k >= offs4k + num4k) {
+ (pginfo->next_buf)++;
+ pginfo->next_4k = 0;
+ }
+ } else if (pginfo->type == EHCA_MR_PGI_USER) {
+ chunk = pginfo->next_chunk;
+ prev_chunk = pginfo->next_chunk;
+ list_for_each_entry_continue(chunk,
+ (&(pginfo->region->chunk_list)),
+ list) {
+ pgaddr = ( page_to_pfn(chunk->page_list[
+ pginfo->next_nmap].page)
+ << PAGE_SHIFT);
+ *rpage = phys_to_abs(pgaddr +
+ (pginfo->next_4k * EHCA_PAGESIZE));
+ EDEB(9,"pgaddr=%lx *rpage=%lx next_4k=%lx", pgaddr,
+ *rpage, pginfo->next_4k);
+ if ( !(*rpage) ) {
+ EDEB_ERR(4, "pgaddr=%lx chunk->page_list[]=%lx "
+ "next_nmap=%lx next_4k=%lx mr=%p",
+ pgaddr, (u64)sg_dma_address(
+ &chunk->page_list[
+ pginfo->next_nmap]),
+ pginfo->next_nmap, pginfo->next_4k,
+ e_mr);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_1_exit0;
+ }
+ (pginfo->page_4k_cnt)++;
+ (pginfo->next_4k)++;
+ if (pginfo->next_4k %
+ (PAGE_SIZE / EHCA_PAGESIZE) == 0) {
+ (pginfo->page_cnt)++;
+ (pginfo->next_nmap)++;
+ pginfo->next_4k = 0;
+ }
+ if (pginfo->next_nmap >= chunk->nmap) {
+ pginfo->next_nmap = 0;
+ prev_chunk = chunk;
+ }
+ break;
+ }
+ pginfo->next_chunk =
+ list_prepare_entry(prev_chunk,
+ (&(pginfo->region->chunk_list)),
+ list);
+ } else if (pginfo->type == EHCA_MR_PGI_FMR) {
+ fmrlist = pginfo->page_list + pginfo->next_listelem;
+ *rpage = phys_to_abs((*fmrlist & EHCA_PAGEMASK) +
+ pginfo->next_4k * EHCA_PAGESIZE);
+ if ( !(*rpage) ) {
+ EDEB_ERR(4, "*fmrlist=%lx fmrlist=%p next_listelem=%lx "
+ "next_4k=%lx", *fmrlist, fmrlist,
+ pginfo->next_listelem, pginfo->next_4k);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_1_exit0;
+ }
+ (pginfo->page_4k_cnt)++;
+ (pginfo->next_4k)++;
+ if (pginfo->next_4k %
+ (e_mr->fmr_page_size / EHCA_PAGESIZE) == 0) {
+ (pginfo->page_cnt)++;
+ (pginfo->next_listelem)++;
+ pginfo->next_4k = 0;
+ }
+ } else {
+ EDEB_ERR(4, "bad pginfo->type=%x", pginfo->type);
+ ret = -EFAULT;
+ goto ehca_set_pagebuf_1_exit0;
+ }
+
+ehca_set_pagebuf_1_exit0:
+ if (ret)
+ EDEB_EX(4, "ret=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+ "num_4k=%lx next_buf=%lx next_4k=%lx rpage=%p "
+ "page_cnt=%lx page_4k_cnt=%lx next_listelem=%lx "
+ "region=%p next_chunk=%p next_nmap=%lx", ret, e_mr,
+ pginfo, pginfo->type, pginfo->num_pages, pginfo->num_4k,
+ pginfo->next_buf, pginfo->next_4k, rpage,
+ pginfo->page_cnt, pginfo->page_4k_cnt,
+ pginfo->next_listelem, pginfo->region,
+ pginfo->next_chunk, pginfo->next_nmap);
+ else
+ EDEB_EX(7, "ret=%x e_mr=%p pginfo=%p type=%x num_pages=%lx "
+ "num_4k=%lx next_buf=%lx next_4k=%lx rpage=%p "
+ "page_cnt=%lx page_4k_cnt=%lx next_listelem=%lx "
+ "region=%p next_chunk=%p next_nmap=%lx", ret, e_mr,
+ pginfo, pginfo->type, pginfo->num_pages, pginfo->num_4k,
+ pginfo->next_buf, pginfo->next_4k, rpage,
+ pginfo->page_cnt, pginfo->page_4k_cnt,
+ pginfo->next_listelem, pginfo->region,
+ pginfo->next_chunk, pginfo->next_nmap);
+ return ret;
+} /* end ehca_set_pagebuf_1() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * check MR if it is a max-MR, i.e. uses whole memory
+ * in case it's a max-MR 1 is returned, else 0
+ */
+int ehca_mr_is_maxmr(u64 size,
+ u64 *iova_start)
+{
+ /* a MR is treated as max-MR only if it fits following: */
+ if ((size == ((u64)high_memory - PAGE_OFFSET)) &&
+ (iova_start == (void*)KERNELBASE)) {
+ EDEB(6, "this is a max-MR");
+ return 1;
+ } else
+ return 0;
+} /* end ehca_mr_is_maxmr() */
+
+/*----------------------------------------------------------------------*/
+
+/* map access control for MR/MW. This routine is used for MR and MW. */
+void ehca_mrmw_map_acl(int ib_acl,
+ u32 *hipz_acl)
+{
+ *hipz_acl = 0;
+ if (ib_acl & IB_ACCESS_REMOTE_READ)
+ *hipz_acl |= HIPZ_ACCESSCTRL_R_READ;
+ if (ib_acl & IB_ACCESS_REMOTE_WRITE)
+ *hipz_acl |= HIPZ_ACCESSCTRL_R_WRITE;
+ if (ib_acl & IB_ACCESS_REMOTE_ATOMIC)
+ *hipz_acl |= HIPZ_ACCESSCTRL_R_ATOMIC;
+ if (ib_acl & IB_ACCESS_LOCAL_WRITE)
+ *hipz_acl |= HIPZ_ACCESSCTRL_L_WRITE;
+ if (ib_acl & IB_ACCESS_MW_BIND)
+ *hipz_acl |= HIPZ_ACCESSCTRL_MW_BIND;
+} /* end ehca_mrmw_map_acl() */
+
+/*----------------------------------------------------------------------*/
+
+/* sets page size in hipz access control for MR/MW. */
+void ehca_mrmw_set_pgsize_hipz_acl(u32 *hipz_acl) /*INOUT*/
+{
+ return; /* HCA supports only 4k */
+} /* end ehca_mrmw_set_pgsize_hipz_acl() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * reverse map access control for MR/MW.
+ * This routine is used for MR and MW.
+ */
+void ehca_mrmw_reverse_map_acl(const u32 *hipz_acl,
+ int *ib_acl) /*OUT*/
+{
+ *ib_acl = 0;
+ if (*hipz_acl & HIPZ_ACCESSCTRL_R_READ)
+ *ib_acl |= IB_ACCESS_REMOTE_READ;
+ if (*hipz_acl & HIPZ_ACCESSCTRL_R_WRITE)
+ *ib_acl |= IB_ACCESS_REMOTE_WRITE;
+ if (*hipz_acl & HIPZ_ACCESSCTRL_R_ATOMIC)
+ *ib_acl |= IB_ACCESS_REMOTE_ATOMIC;
+ if (*hipz_acl & HIPZ_ACCESSCTRL_L_WRITE)
+ *ib_acl |= IB_ACCESS_LOCAL_WRITE;
+ if (*hipz_acl & HIPZ_ACCESSCTRL_MW_BIND)
+ *ib_acl |= IB_ACCESS_MW_BIND;
+} /* end ehca_mrmw_reverse_map_acl() */
+
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * map HIPZ rc to IB retcodes for MR/MW allocations
+ * Used for hipz_mr_reg_alloc and hipz_mw_alloc.
+ */
+int ehca_mrmw_map_hrc_alloc(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_SUCCESS: /* successful completion */
+ return 0;
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RT_PARM: /* invalid resource type */
+ case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */
+ case H_MLENGTH_PARM: /* invalid memory length */
+ case H_MEM_ACCESS_PARM: /* invalid access controls */
+ case H_CONSTRAINED: /* resource constraint */
+ return -EINVAL;
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_alloc() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * map HIPZ rc to IB retcodes for MR register rpage
+ * Used for hipz_h_register_rpage_mr at registering last page
+ */
+int ehca_mrmw_map_hrc_rrpg_last(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_SUCCESS: /* registration complete */
+ return 0;
+ case H_PAGE_REGISTERED: /* page registered */
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RH_PARM: /* invalid resource handle */
+/* case H_QT_PARM: invalid queue type */
+ case H_PARAMETER: /*
+ * invalid logical address,
+ * or count zero or greater 512
+ */
+ case H_TABLE_FULL: /* page table full */
+ case H_HARDWARE: /* HCA not operational */
+ return -EINVAL;
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_rrpg_last() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * map HIPZ rc to IB retcodes for MR register rpage
+ * Used for hipz_h_register_rpage_mr at registering one page, but not last page
+ */
+int ehca_mrmw_map_hrc_rrpg_notlast(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_PAGE_REGISTERED: /* page registered */
+ return 0;
+ case H_SUCCESS: /* registration complete */
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RH_PARM: /* invalid resource handle */
+/* case H_QT_PARM: invalid queue type */
+ case H_PARAMETER: /*
+ * invalid logical address,
+ * or count zero or greater 512
+ */
+ case H_TABLE_FULL: /* page table full */
+ case H_HARDWARE: /* HCA not operational */
+ return -EINVAL;
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_rrpg_notlast() */
+
+/*----------------------------------------------------------------------*/
+
+/* map HIPZ rc to IB retcodes for MR query. Used for hipz_mr_query. */
+int ehca_mrmw_map_hrc_query_mr(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_SUCCESS: /* successful completion */
+ return 0;
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RH_PARM: /* invalid resource handle */
+ return -EINVAL;
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_query_mr() */
+
+/*----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------*/
+
+/*
+ * map HIPZ rc to IB retcodes for freeing MR resource
+ * Used for hipz_h_free_resource_mr
+ */
+int ehca_mrmw_map_hrc_free_mr(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_SUCCESS: /* resource freed */
+ return 0;
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RH_PARM: /* invalid resource handle */
+ case H_R_STATE: /* invalid resource state */
+ case H_HARDWARE: /* HCA not operational */
+ return -EINVAL;
+ case H_RESOURCE: /* Resource in use */
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_free_mr() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * map HIPZ rc to IB retcodes for freeing MW resource
+ * Used for hipz_h_free_resource_mw
+ */
+int ehca_mrmw_map_hrc_free_mw(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_SUCCESS: /* resource freed */
+ return 0;
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RH_PARM: /* invalid resource handle */
+ case H_R_STATE: /* invalid resource state */
+ case H_HARDWARE: /* HCA not operational */
+ return -EINVAL;
+ case H_RESOURCE: /* Resource in use */
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_free_mw() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * map HIPZ rc to IB retcodes for SMR registrations
+ * Used for hipz_h_register_smr.
+ */
+int ehca_mrmw_map_hrc_reg_smr(const u64 hipz_rc)
+{
+ switch (hipz_rc) {
+ case H_SUCCESS: /* successful completion */
+ return 0;
+ case H_ADAPTER_PARM: /* invalid adapter handle */
+ case H_RH_PARM: /* invalid resource handle */
+ case H_MEM_PARM: /* invalid MR virtual address */
+ case H_MEM_ACCESS_PARM: /* invalid access controls */
+ case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */
+ return -EINVAL;
+ case H_BUSY: /* long busy */
+ return -EBUSY;
+ default:
+ return -EINVAL;
+ }
+} /* end ehca_mrmw_map_hrc_reg_smr() */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * MR destructor and constructor
+ * used in Reregister MR verb, sets all fields in ehca_mr_t to 0,
+ * except struct ib_mr and spinlock
+ */
+void ehca_mr_deletenew(struct ehca_mr *mr)
+{
+ mr->flags = 0;
+ mr->num_pages = 0;
+ mr->num_4k = 0;
+ mr->acl = 0;
+ mr->start = NULL;
+ mr->fmr_page_size = 0;
+ mr->fmr_max_pages = 0;
+ mr->fmr_max_maps = 0;
+ mr->fmr_map_cnt = 0;
+ memset(&mr->ipz_mr_handle, 0, sizeof(mr->ipz_mr_handle));
+ memset(&mr->galpas, 0, sizeof(mr->galpas));
+ mr->nr_of_pages = 0;
+ mr->pagearray = NULL;
+} /* end ehca_mr_deletenew() */
diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.h b/drivers/infiniband/hw/ehca/ehca_mrmw.h
new file mode 100644
index 0000000..01899b4
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_mrmw.h
@@ -0,0 +1,143 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * MR/MW declarations and inline functions
+ *
+ * Authors: Dietmar Decker <[email protected]>
+ * Christoph Raisch <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef _EHCA_MRMW_H_
+#define _EHCA_MRMW_H_
+
+#undef DEB_PREFIX
+#define DEB_PREFIX "mrmw"
+
+int ehca_reg_mr(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ u64 *iova_start,
+ u64 size,
+ int acl,
+ struct ehca_pd *e_pd,
+ struct ehca_mr_pginfo *pginfo,
+ u32 *lkey,
+ u32 *rkey);
+
+int ehca_reg_mr_rpages(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ struct ehca_mr_pginfo *pginfo);
+
+int ehca_rereg_mr(struct ehca_shca *shca,
+ struct ehca_mr *e_mr,
+ u64 *iova_start,
+ u64 size,
+ int mr_access_flags,
+ struct ehca_pd *e_pd,
+ struct ehca_mr_pginfo *pginfo,
+ u32 *lkey,
+ u32 *rkey);
+
+int ehca_unmap_one_fmr(struct ehca_shca *shca,
+ struct ehca_mr *e_fmr);
+
+int ehca_reg_smr(struct ehca_shca *shca,
+ struct ehca_mr *e_origmr,
+ struct ehca_mr *e_newmr,
+ u64 *iova_start,
+ int acl,
+ struct ehca_pd *e_pd,
+ u32 *lkey,
+ u32 *rkey);
+
+int ehca_reg_internal_maxmr(struct ehca_shca *shca,
+ struct ehca_pd *e_pd,
+ struct ehca_mr **maxmr);
+
+int ehca_reg_maxmr(struct ehca_shca *shca,
+ struct ehca_mr *e_newmr,
+ u64 *iova_start,
+ int acl,
+ struct ehca_pd *e_pd,
+ u32 *lkey,
+ u32 *rkey);
+
+int ehca_dereg_internal_maxmr(struct ehca_shca *shca);
+
+int ehca_mr_chk_buf_and_calc_size(struct ib_phys_buf *phys_buf_array,
+ int num_phys_buf,
+ u64 *iova_start,
+ u64 *size);
+
+int ehca_fmr_check_page_list(struct ehca_mr *e_fmr,
+ u64 *page_list,
+ int list_len);
+
+int ehca_set_pagebuf(struct ehca_mr *e_mr,
+ struct ehca_mr_pginfo *pginfo,
+ u32 number,
+ u64 *kpage);
+
+int ehca_set_pagebuf_1(struct ehca_mr *e_mr,
+ struct ehca_mr_pginfo *pginfo,
+ u64 *rpage);
+
+int ehca_mr_is_maxmr(u64 size,
+ u64 *iova_start);
+
+void ehca_mrmw_map_acl(int ib_acl,
+ u32 *hipz_acl);
+
+void ehca_mrmw_set_pgsize_hipz_acl(u32 *hipz_acl);
+
+void ehca_mrmw_reverse_map_acl(const u32 *hipz_acl,
+ int *ib_acl);
+
+int ehca_mrmw_map_hrc_alloc(const u64 hipz_rc);
+
+int ehca_mrmw_map_hrc_rrpg_last(const u64 hipz_rc);
+
+int ehca_mrmw_map_hrc_rrpg_notlast(const u64 hipz_rc);
+
+int ehca_mrmw_map_hrc_query_mr(const u64 hipz_rc);
+
+int ehca_mrmw_map_hrc_free_mr(const u64 hipz_rc);
+
+int ehca_mrmw_map_hrc_free_mw(const u64 hipz_rc);
+
+int ehca_mrmw_map_hrc_reg_smr(const u64 hipz_rc);
+
+void ehca_mr_deletenew(struct ehca_mr *mr);
+
+#endif /*_EHCA_MRMW_H_*/
--
1.4.1
drivers/infiniband/hw/ehca/hcp_if.c | 1473 +++++++++++++++++++++++++++++++++++
drivers/infiniband/hw/ehca/hcp_if.h | 261 ++++++
2 files changed, 1734 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c
new file mode 100644
index 0000000..2407eb6
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hcp_if.c
@@ -0,0 +1,1473 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * Firmware Infiniband Interface code for POWER
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ * Gerd Bayer <[email protected]>
+ * Waleri Fomin <[email protected]>
+ *
+ * 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.
+ */
+
+#define DEB_PREFIX "hcpi"
+
+#include <asm/hvcall.h>
+#include "ehca_tools.h"
+#include "hcp_if.h"
+#include "hcp_phyp.h"
+#include "hipz_fns.h"
+
+#define H_ALL_RES_QP_ENHANCED_OPS EHCA_BMASK_IBM(9,11)
+#define H_ALL_RES_QP_PTE_PIN EHCA_BMASK_IBM(12,12)
+#define H_ALL_RES_QP_SERVICE_TYPE EHCA_BMASK_IBM(13,15)
+#define H_ALL_RES_QP_LL_RQ_CQE_POSTING EHCA_BMASK_IBM(18,18)
+#define H_ALL_RES_QP_LL_SQ_CQE_POSTING EHCA_BMASK_IBM(19,21)
+#define H_ALL_RES_QP_SIGNALING_TYPE EHCA_BMASK_IBM(22,23)
+#define H_ALL_RES_QP_UD_AV_LKEY_CTRL EHCA_BMASK_IBM(31,31)
+#define H_ALL_RES_QP_RESOURCE_TYPE EHCA_BMASK_IBM(56,63)
+
+#define H_ALL_RES_QP_MAX_OUTST_SEND_WR EHCA_BMASK_IBM(0,15)
+#define H_ALL_RES_QP_MAX_OUTST_RECV_WR EHCA_BMASK_IBM(16,31)
+#define H_ALL_RES_QP_MAX_SEND_SGE EHCA_BMASK_IBM(32,39)
+#define H_ALL_RES_QP_MAX_RECV_SGE EHCA_BMASK_IBM(40,47)
+
+#define H_ALL_RES_QP_ACT_OUTST_SEND_WR EHCA_BMASK_IBM(16,31)
+#define H_ALL_RES_QP_ACT_OUTST_RECV_WR EHCA_BMASK_IBM(48,63)
+#define H_ALL_RES_QP_ACT_SEND_SGE EHCA_BMASK_IBM(8,15)
+#define H_ALL_RES_QP_ACT_RECV_SGE EHCA_BMASK_IBM(24,31)
+
+#define H_ALL_RES_QP_SQUEUE_SIZE_PAGES EHCA_BMASK_IBM(0,31)
+#define H_ALL_RES_QP_RQUEUE_SIZE_PAGES EHCA_BMASK_IBM(32,63)
+
+/* direct access qp controls */
+#define DAQP_CTRL_ENABLE 0x01
+#define DAQP_CTRL_SEND_COMP 0x20
+#define DAQP_CTRL_RECV_COMP 0x40
+
+static u32 get_longbusy_msecs(int longbusy_rc)
+{
+ switch (longbusy_rc) {
+ case H_LONG_BUSY_ORDER_1_MSEC:
+ return 1;
+ case H_LONG_BUSY_ORDER_10_MSEC:
+ return 10;
+ case H_LONG_BUSY_ORDER_100_MSEC:
+ return 100;
+ case H_LONG_BUSY_ORDER_1_SEC:
+ return 1000;
+ case H_LONG_BUSY_ORDER_10_SEC:
+ return 10000;
+ case H_LONG_BUSY_ORDER_100_SEC:
+ return 100000;
+ default:
+ return 1;
+ }
+}
+
+static long ehca_hcall_7arg_7ret(unsigned long opcode,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6,
+ unsigned long arg7,
+ unsigned long *out1,
+ unsigned long *out2,
+ unsigned long *out3,
+ unsigned long *out4,
+ unsigned long *out5,
+ unsigned long *out6,
+ unsigned long *out7)
+{
+ long ret = H_SUCCESS;
+ int i, sleep_msecs;
+
+ EDEB_EN(7, "opcode=%lx arg1=%lx arg2=%lx arg3=%lx arg4=%lx arg5=%lx"
+ " arg6=%lx arg7=%lx", opcode, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
+
+ for (i = 0; i < 5; i++) {
+ ret = plpar_hcall_7arg_7ret(opcode,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7,
+ out1, out2, out3, out4,
+ out5, out6,out7);
+
+ if (H_IS_LONG_BUSY(ret)) {
+ sleep_msecs = get_longbusy_msecs(ret);
+ msleep_interruptible(sleep_msecs);
+ continue;
+ }
+
+ if (ret < H_SUCCESS)
+ EDEB_ERR(4, "opcode=%lx ret=%lx"
+ " arg1=%lx arg2=%lx arg3=%lx arg4=%lx"
+ " arg5=%lx arg6=%lx arg7=%lx"
+ " out1=%lx out2=%lx out3=%lx out4=%lx"
+ " out5=%lx out6=%lx out7=%lx",
+ opcode, ret,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7,
+ *out1, *out2, *out3, *out4,
+ *out5, *out6, *out7);
+
+ EDEB_EX(7, "opcode=%lx ret=%lx out1=%lx out2=%lx out3=%lx "
+ "out4=%lx out5=%lx out6=%lx out7=%lx",
+ opcode, ret, *out1, *out2, *out3, *out4, *out5,
+ *out6, *out7);
+ return ret;
+ }
+
+ EDEB_EX(7, "opcode=%lx ret=H_BUSY", opcode);
+
+ return H_BUSY;
+}
+
+static long ehca_hcall_9arg_9ret(unsigned long opcode,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6,
+ unsigned long arg7,
+ unsigned long arg8,
+ unsigned long arg9,
+ unsigned long *out1,
+ unsigned long *out2,
+ unsigned long *out3,
+ unsigned long *out4,
+ unsigned long *out5,
+ unsigned long *out6,
+ unsigned long *out7,
+ unsigned long *out8,
+ unsigned long *out9)
+{
+ long ret = H_SUCCESS;
+ int i, sleep_msecs;
+
+ EDEB_EN(7, "opcode=%lx arg1=%lx arg2=%lx arg3=%lx arg4=%lx "
+ "arg5=%lx arg6=%lx arg7=%lx arg8=%lx arg9=%lx",
+ opcode, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9);
+
+
+ for (i = 0; i < 5; i++) {
+ ret = plpar_hcall_9arg_9ret(opcode,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8,
+ arg9,
+ out1, out2, out3, out4,
+ out5, out6, out7, out8,
+ out9);
+
+ if (H_IS_LONG_BUSY(ret)) {
+ sleep_msecs = get_longbusy_msecs(ret);
+ msleep_interruptible(sleep_msecs);
+ continue;
+ }
+
+ if (ret < H_SUCCESS)
+ EDEB_ERR(4, "opcode=%lx ret=%lx"
+ " arg1=%lx arg2=%lx arg3=%lx arg4=%lx"
+ " arg5=%lx arg6=%lx arg7=%lx arg8=%lx"
+ " arg9=%lx"
+ " out1=%lx out2=%lx out3=%lx out4=%lx"
+ " out5=%lx out6=%lx out7=%lx out8=%lx"
+ " out9=%lx",
+ opcode, ret,
+ arg1, arg2, arg3, arg4,
+ arg5, arg6, arg7, arg8,
+ arg9,
+ *out1, *out2, *out3, *out4,
+ *out5, *out6, *out7, *out8,
+ *out9);
+
+ EDEB_EX(7, "opcode=%lx ret=%lx out1=%lx out2=%lx out3=%lx "
+ "out4=%lx out5=%lx out6=%lx out7=%lx out8=%lx out9=%lx",
+ opcode, ret,*out1, *out2, *out3, *out4, *out5, *out6,
+ *out7, *out8, *out9);
+ return ret;
+
+ }
+
+ EDEB_EX(7, "opcode=%lx ret=H_BUSY", opcode);
+ return H_BUSY;
+}
+u64 hipz_h_alloc_resource_eq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_pfeq *pfeq,
+ const u32 neq_control,
+ const u32 number_of_entries,
+ struct ipz_eq_handle *eq_handle,
+ u32 * act_nr_of_entries,
+ u32 * act_pages,
+ u32 * eq_ist)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 act_nr_of_entries_out = 0;
+ u64 act_pages_out = 0;
+ u64 eq_ist_out = 0;
+ u64 allocate_controls = 0;
+ u32 x = (u64)(&x);
+
+ EDEB_EN(7, "pfeq=%p adapter_handle=%lx new_control=%x"
+ " number_of_entries=%x",
+ pfeq, adapter_handle.handle, neq_control,
+ number_of_entries);
+
+ /* resource type */
+ allocate_controls = 3ULL;
+
+ /* ISN is associated */
+ if (neq_control != 1)
+ allocate_controls = (1ULL << (63 - 7)) | allocate_controls;
+ else /* notification event queue */
+ allocate_controls = (1ULL << 63) | allocate_controls;
+
+ ret = ehca_hcall_7arg_7ret(H_ALLOC_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ allocate_controls, /* r5 */
+ number_of_entries, /* r6 */
+ 0, 0, 0, 0,
+ &eq_handle->handle, /* r4 */
+ &dummy, /* r5 */
+ &dummy, /* r6 */
+ &act_nr_of_entries_out, /* r7 */
+ &act_pages_out, /* r8 */
+ &eq_ist_out, /* r8 */
+ &dummy);
+
+ *act_nr_of_entries = (u32)act_nr_of_entries_out;
+ *act_pages = (u32)act_pages_out;
+ *eq_ist = (u32)eq_ist_out;
+
+ if (ret == H_NOT_ENOUGH_RESOURCES)
+ EDEB_ERR(4, "Not enough resource - ret=%lx ", ret);
+
+ EDEB_EX(7, "act_nr_of_entries=%x act_pages=%x eq_ist=%x",
+ *act_nr_of_entries, *act_pages, *eq_ist);
+
+ return ret;
+}
+
+u64 hipz_h_reset_event(const struct ipz_adapter_handle adapter_handle,
+ struct ipz_eq_handle eq_handle,
+ const u64 event_mask)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "eq_handle=%lx, adapter_handle=%lx event_mask=%lx",
+ eq_handle.handle, adapter_handle.handle, event_mask);
+
+ ret = ehca_hcall_7arg_7ret(H_RESET_EVENTS,
+ adapter_handle.handle, /* r4 */
+ eq_handle.handle, /* r5 */
+ event_mask, /* r6 */
+ 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_alloc_resource_cq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_cq *cq,
+ struct ehca_alloc_cq_parms *param)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 act_nr_of_entries_out;
+ u64 act_pages_out;
+ u64 g_la_privileged_out;
+ u64 g_la_user_out;
+
+ EDEB_EN(7, "Adapter_handle=%lx eq_handle=%lx cq_token=%x"
+ " cq_number_of_entries=%x",
+ adapter_handle.handle, param->eq_handle.handle,
+ cq->token, param->nr_cqe);
+
+ ret = ehca_hcall_7arg_7ret(H_ALLOC_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ 2, /* r5 */
+ param->eq_handle.handle, /* r6 */
+ cq->token, /* r7 */
+ param->nr_cqe, /* r8 */
+ 0, 0,
+ &cq->ipz_cq_handle.handle, /* r4 */
+ &dummy, /* r5 */
+ &dummy, /* r6 */
+ &act_nr_of_entries_out, /* r7 */
+ &act_pages_out, /* r8 */
+ &g_la_privileged_out, /* r9 */
+ &g_la_user_out); /* r10 */
+
+ param->act_nr_of_entries = (u32)act_nr_of_entries_out;
+ param->act_pages = (u32)act_pages_out;
+
+ if (ret == H_SUCCESS)
+ hcp_galpas_ctor(&cq->galpas, g_la_privileged_out, g_la_user_out);
+
+ if (ret == H_NOT_ENOUGH_RESOURCES)
+ EDEB_ERR(4, "Not enough resources. ret=%lx", ret);
+
+ EDEB_EX(7, "cq_handle=%lx act_nr_of_entries=%x act_pages=%x",
+ cq->ipz_cq_handle.handle, param->act_nr_of_entries, param->act_pages);
+
+ return ret;
+}
+
+u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_qp *qp,
+ struct ehca_alloc_qp_parms *parms)
+{
+ u64 ret = H_SUCCESS;
+ u64 allocate_controls;
+ u64 max_r10_reg;
+ u64 dummy = 0;
+ u64 qp_nr_out = 0;
+ u64 r6_out = 0;
+ u64 r7_out = 0;
+ u64 r8_out = 0;
+ u64 g_la_user_out = 0;
+ u64 r11_out = 0;
+ u16 max_nr_receive_wqes = qp->init_attr.cap.max_recv_wr + 1;
+ u16 max_nr_send_wqes = qp->init_attr.cap.max_send_wr + 1;
+ int daqp_ctrl = parms->daqp_ctrl;
+
+ EDEB_EN(7, "Adapter_handle=%lx servicetype=%x signalingtype=%x"
+ " ud_av_l_key=%x send_cq_handle=%lx receive_cq_handle=%lx"
+ " async_eq_handle=%lx qp_token=%x pd=%x max_nr_send_wqes=%x"
+ " max_nr_receive_wqes=%x max_nr_send_sges=%x"
+ " max_nr_receive_sges=%x ud_av_l_key=%x galpa.pid=%x",
+ adapter_handle.handle, parms->servicetype, parms->sigtype,
+ parms->ud_av_l_key_ctl, qp->send_cq->ipz_cq_handle.handle,
+ qp->recv_cq->ipz_cq_handle.handle, parms->ipz_eq_handle.handle,
+ qp->token, parms->pd.value, max_nr_send_wqes,
+ max_nr_receive_wqes, parms->max_send_sge, parms->max_recv_sge,
+ parms->ud_av_l_key_ctl, qp->galpas.pid);
+
+ allocate_controls =
+ EHCA_BMASK_SET(H_ALL_RES_QP_ENHANCED_OPS,
+ (daqp_ctrl & DAQP_CTRL_ENABLE) ? 1 : 0)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_PTE_PIN, 0)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_SERVICE_TYPE, parms->servicetype)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_SIGNALING_TYPE, parms->sigtype)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_LL_RQ_CQE_POSTING,
+ (daqp_ctrl & DAQP_CTRL_RECV_COMP) ? 1 : 0)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_LL_SQ_CQE_POSTING,
+ (daqp_ctrl & DAQP_CTRL_SEND_COMP) ? 1 : 0)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_UD_AV_LKEY_CTRL,
+ parms->ud_av_l_key_ctl)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_RESOURCE_TYPE, 1);
+
+ max_r10_reg =
+ EHCA_BMASK_SET(H_ALL_RES_QP_MAX_OUTST_SEND_WR,
+ max_nr_send_wqes)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_MAX_OUTST_RECV_WR,
+ max_nr_receive_wqes)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_MAX_SEND_SGE,
+ parms->max_send_sge)
+ | EHCA_BMASK_SET(H_ALL_RES_QP_MAX_RECV_SGE,
+ parms->max_recv_sge);
+
+
+ ret = ehca_hcall_9arg_9ret(H_ALLOC_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ allocate_controls, /* r5 */
+ qp->send_cq->ipz_cq_handle.handle,
+ qp->recv_cq->ipz_cq_handle.handle,
+ parms->ipz_eq_handle.handle,
+ ((u64)qp->token << 32) | parms->pd.value,
+ max_r10_reg, /* r10 */
+ parms->ud_av_l_key_ctl, /* r11 */
+ 0,
+ &qp->ipz_qp_handle.handle,
+ &qp_nr_out, /* r5 */
+ &r6_out, /* r6 */
+ &r7_out, /* r7 */
+ &r8_out, /* r8 */
+ &dummy, /* r9 */
+ &g_la_user_out, /* r10 */
+ &r11_out,
+ &dummy);
+
+ /* extract outputs */
+ qp->real_qp_num = (u32)qp_nr_out;
+
+ parms->act_nr_send_sges =
+ (u16)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_OUTST_SEND_WR, r6_out);
+ parms->act_nr_recv_wqes =
+ (u16)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_OUTST_RECV_WR, r6_out);
+ parms->act_nr_send_sges =
+ (u8)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_SEND_SGE, r7_out);
+ parms->act_nr_recv_sges =
+ (u8)EHCA_BMASK_GET(H_ALL_RES_QP_ACT_RECV_SGE, r7_out);
+ parms->nr_sq_pages =
+ (u32)EHCA_BMASK_GET(H_ALL_RES_QP_SQUEUE_SIZE_PAGES, r8_out);
+ parms->nr_rq_pages =
+ (u32)EHCA_BMASK_GET(H_ALL_RES_QP_RQUEUE_SIZE_PAGES, r8_out);
+
+ if (ret == H_SUCCESS)
+ hcp_galpas_ctor(&qp->galpas, g_la_user_out, g_la_user_out);
+
+ if (ret == H_NOT_ENOUGH_RESOURCES)
+ EDEB_ERR(4, "Not enough resources. ret=%lx",ret);
+
+ EDEB_EX(7, "qp_nr=%x act_nr_send_wqes=%x"
+ " act_nr_receive_wqes=%x act_nr_send_sges=%x"
+ " act_nr_receive_sges=%x nr_sq_pages=%x"
+ " nr_rq_pages=%x galpa.user=%lx galpa.kernel=%lx",
+ qp->real_qp_num, parms->act_nr_send_wqes,
+ parms->act_nr_recv_wqes, parms->act_nr_send_sges,
+ parms->act_nr_recv_sges, parms->nr_sq_pages,
+ parms->nr_rq_pages, qp->galpas.user.fw_handle,
+ qp->galpas.kernel.fw_handle);
+
+ return ret;
+}
+
+u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle,
+ const u8 port_id,
+ struct hipz_query_port *query_port_response_block)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 r_cb;
+
+ EDEB_EN(7, "adapter_handle=%lx port_id %x",
+ adapter_handle.handle, port_id);
+
+ if (((u64)query_port_response_block) & 0xfff) {
+ EDEB_ERR(4, "response block not page aligned");
+ return H_PARAMETER;
+ }
+
+ r_cb = virt_to_abs(query_port_response_block);
+
+ ret = ehca_hcall_7arg_7ret(H_QUERY_PORT,
+ adapter_handle.handle, /* r4 */
+ port_id, /* r5 */
+ r_cb, /* r6 */
+ 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB_DMP(7, query_port_response_block, 64, "query_port_response_block");
+ EDEB(7, "offset31=%x offset35=%x offset36=%x",
+ ((u32*)query_port_response_block)[32],
+ ((u32*)query_port_response_block)[36],
+ ((u32*)query_port_response_block)[37]);
+ EDEB(7, "offset200=%x offset201=%x offset202=%x "
+ "offset203=%x",
+ ((u32*)query_port_response_block)[0x200],
+ ((u32*)query_port_response_block)[0x201],
+ ((u32*)query_port_response_block)[0x202],
+ ((u32*)query_port_response_block)[0x203]);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle,
+ struct hipz_query_hca *query_hca_rblock)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 r_cb;
+ EDEB_EN(7, "adapter_handle=%lx", adapter_handle.handle);
+
+ if (((u64)query_hca_rblock) & 0xfff) {
+ EDEB_ERR(4, "response_block=%p not page aligned",
+ query_hca_rblock);
+ return H_PARAMETER;
+ }
+
+ r_cb = virt_to_abs(query_hca_rblock);
+
+ ret = ehca_hcall_7arg_7ret(H_QUERY_HCA,
+ adapter_handle.handle, /* r4 */
+ r_cb, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB(7, "offset0=%x offset1=%x offset2=%x offset3=%x",
+ ((u32*)query_hca_rblock)[0],
+ ((u32*)query_hca_rblock)[1],
+ ((u32*)query_hca_rblock)[2], ((u32*)query_hca_rblock)[3]);
+ EDEB(7, "offset4=%x offset5=%x offset6=%x offset7=%x",
+ ((u32*)query_hca_rblock)[4],
+ ((u32*)query_hca_rblock)[5],
+ ((u32*)query_hca_rblock)[6], ((u32*)query_hca_rblock)[7]);
+ EDEB(7, "offset8=%x offset9=%x offseta=%x offsetb=%x",
+ ((u32*)query_hca_rblock)[8],
+ ((u32*)query_hca_rblock)[9],
+ ((u32*)query_hca_rblock)[10], ((u32*)query_hca_rblock)[11]);
+ EDEB(7, "offsetc=%x offsetd=%x offsete=%x offsetf=%x",
+ ((u32*)query_hca_rblock)[12],
+ ((u32*)query_hca_rblock)[13],
+ ((u32*)query_hca_rblock)[14], ((u32*)query_hca_rblock)[15]);
+ EDEB(7, "offset136=%x offset192=%x offset204=%x",
+ ((u32*)query_hca_rblock)[32],
+ ((u32*)query_hca_rblock)[48], ((u32*)query_hca_rblock)[51]);
+ EDEB(7, "offset231=%x offset235=%x",
+ ((u32*)query_hca_rblock)[57], ((u32*)query_hca_rblock)[58]);
+ EDEB(7, "offset200=%x offset201=%x offset202=%x offset203=%x",
+ ((u32*)query_hca_rblock)[0x201],
+ ((u32*)query_hca_rblock)[0x202],
+ ((u32*)query_hca_rblock)[0x203],
+ ((u32*)query_hca_rblock)[0x204]);
+
+ EDEB_EX(7, "ret=%lx adapter_handle=%lx",
+ ret, adapter_handle.handle);
+
+ return ret;
+}
+
+u64 hipz_h_register_rpage(const struct ipz_adapter_handle adapter_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 resource_handle,
+ const u64 logical_address_of_page,
+ u64 count)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "adapter_handle=%lx pagesize=%x queue_type=%x"
+ " resource_handle=%lx logical_address_of_page=%lx count=%lx",
+ adapter_handle.handle, pagesize, queue_type,
+ resource_handle, logical_address_of_page, count);
+
+ ret = ehca_hcall_7arg_7ret(H_REGISTER_RPAGES,
+ adapter_handle.handle, /* r4 */
+ queue_type | pagesize << 8, /* r5 */
+ resource_handle, /* r6 */
+ logical_address_of_page, /* r7 */
+ count, /* r8 */
+ 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_register_rpage_eq(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_eq_handle eq_handle,
+ struct ehca_pfeq *pfeq,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count)
+{
+ u64 ret = H_SUCCESS;
+
+ EDEB_EN(7, "pfeq=%p adapter_handle=%lx eq_handle=%lx pagesize=%x"
+ " queue_type=%x logical_address_of_page=%lx count=%lx",
+ pfeq, adapter_handle.handle, eq_handle.handle, pagesize,
+ queue_type,logical_address_of_page, count);
+
+ if (count != 1) {
+ EDEB_ERR(4, "Ppage counter=%lx", count);
+ return H_PARAMETER;
+ }
+ ret = hipz_h_register_rpage(adapter_handle,
+ pagesize,
+ queue_type,
+ eq_handle.handle,
+ logical_address_of_page, count);
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u32 hipz_h_query_int_state(const struct ipz_adapter_handle adapter_handle,
+ u32 ist)
+{
+ u32 ret = H_SUCCESS;
+ u64 dummy = 0;
+
+ EDEB_EN(7, "ist=%x", ist);
+
+ ret = ehca_hcall_7arg_7ret(H_QUERY_INT_STATE,
+ adapter_handle.handle, /* r4 */
+ ist, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ if (ret != H_SUCCESS && ret != H_BUSY)
+ EDEB_ERR(4, "Could not query interrupt state.");
+
+ EDEB_EX(7, "interrupt state: %x", ret);
+
+ return ret;
+}
+
+u64 hipz_h_register_rpage_cq(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_cq_handle cq_handle,
+ struct ehca_pfcq *pfcq,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count,
+ const struct h_galpa gal)
+{
+ u64 ret = H_SUCCESS;
+
+ EDEB_EN(7, "pfcq=%p adapter_handle=%lx cq_handle=%lx pagesize=%x"
+ " queue_type=%x logical_address_of_page=%lx count=%lx",
+ pfcq, adapter_handle.handle, cq_handle.handle, pagesize,
+ queue_type, logical_address_of_page, count);
+
+ if (count != 1) {
+ EDEB_ERR(4, "Page counter=%lx", count);
+ return H_PARAMETER;
+ }
+
+ ret = hipz_h_register_rpage(adapter_handle, pagesize, queue_type,
+ cq_handle.handle, logical_address_of_page,
+ count);
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_register_rpage_qp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count,
+ const struct h_galpa galpa)
+{
+ u64 ret = H_SUCCESS;
+
+ EDEB_EN(7, "pfqp=%p adapter_handle=%lx qp_handle=%lx pagesize=%x"
+ " queue_type=%x logical_address_of_page=%lx count=%lx",
+ pfqp, adapter_handle.handle, qp_handle.handle, pagesize,
+ queue_type, logical_address_of_page, count);
+
+ if (count != 1) {
+ EDEB_ERR(4, "Page counter=%lx", count);
+ return H_PARAMETER;
+ }
+
+ ret = hipz_h_register_rpage(adapter_handle,pagesize,queue_type,
+ qp_handle.handle,logical_address_of_page,
+ count);
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_disable_and_get_wqe(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ void **log_addr_next_sq_wqe2processed,
+ void **log_addr_next_rq_wqe2processed,
+ int dis_and_get_function_code)
+{
+ u64 ret = H_SUCCESS;
+ u8 function_code = 1;
+ u64 dummy, dummy1, dummy2;
+
+ EDEB_EN(7, "pfqp=%p adapter_handle=%lx function=%x qp_handle=%lx",
+ pfqp, adapter_handle.handle, function_code, qp_handle.handle);
+
+ if (!log_addr_next_sq_wqe2processed)
+ log_addr_next_sq_wqe2processed = (void**)&dummy1;
+ if (!log_addr_next_rq_wqe2processed)
+ log_addr_next_rq_wqe2processed = (void**)&dummy2;
+
+ ret = ehca_hcall_7arg_7ret(H_DISABLE_AND_GETC,
+ adapter_handle.handle, /* r4 */
+ dis_and_get_function_code, /* r5 */
+ qp_handle.handle, /* r6 */
+ 0, 0, 0, 0,
+ (void*)log_addr_next_sq_wqe2processed,
+ (void*)log_addr_next_rq_wqe2processed,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+ EDEB_EX(7, "ret=%lx ladr_next_rq_wqe_out=%p"
+ " ladr_next_sq_wqe_out=%p", ret,
+ *log_addr_next_sq_wqe2processed,
+ *log_addr_next_rq_wqe2processed);
+
+ return ret;
+}
+
+u64 hipz_h_modify_qp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ const u64 update_mask,
+ struct hcp_modify_qp_control_block *mqpcb,
+ struct h_galpa gal)
+{
+ u64 ret = H_SUCCESS;
+ u64 invalid_attribute_identifier = 0;
+ u64 rc_attrib_mask = 0;
+ u64 dummy;
+ u64 r_cb;
+ EDEB_EN(7, "pfqp=%p adapter_handle=%lx qp_handle=%lx"
+ " update_mask=%lx qp_state=%x mqpcb=%p",
+ pfqp, adapter_handle.handle, qp_handle.handle,
+ update_mask, mqpcb->qp_state, mqpcb);
+
+ r_cb = virt_to_abs(mqpcb);
+ ret = ehca_hcall_7arg_7ret(H_MODIFY_QP,
+ adapter_handle.handle, /* r4 */
+ qp_handle.handle, /* r5 */
+ update_mask, /* r6 */
+ r_cb, /* r7 */
+ 0, 0, 0,
+ &invalid_attribute_identifier, /* r4 */
+ &dummy, /* r5 */
+ &dummy, /* r6 */
+ &dummy, /* r7 */
+ &dummy, /* r8 */
+ &rc_attrib_mask, /* r9 */
+ &dummy);
+ if (ret == H_NOT_ENOUGH_RESOURCES)
+ EDEB_ERR(4, "Insufficient resources ret=%lx", ret);
+
+ EDEB_EX(7, "ret=%lx invalid_attribute_identifier=%lx"
+ " invalid_attribute_MASK=%lx", ret,
+ invalid_attribute_identifier, rc_attrib_mask);
+
+ return ret;
+}
+
+u64 hipz_h_query_qp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ struct hcp_modify_qp_control_block *qqpcb,
+ struct h_galpa gal)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 r_cb;
+ EDEB_EN(7, "adapter_handle=%lx qp_handle=%lx",
+ adapter_handle.handle, qp_handle.handle);
+
+ r_cb = virt_to_abs(qqpcb);
+ EDEB(7, "r_cb=%lx", r_cb);
+
+ ret = ehca_hcall_7arg_7ret(H_QUERY_QP,
+ adapter_handle.handle, /* r4 */
+ qp_handle.handle, /* r5 */
+ r_cb, /* r6 */
+ 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_destroy_qp(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_qp *qp)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 ladr_next_sq_wqe_out;
+ u64 ladr_next_rq_wqe_out;
+
+ EDEB_EN(7, "qp=%p ipz_qp_handle=%lx adapter_handle=%lx",
+ qp, qp->ipz_qp_handle.handle, adapter_handle.handle);
+
+ ret = hcp_galpas_dtor(&qp->galpas);
+ if (ret) {
+ EDEB_ERR(4, "Could not destruct qp->galpas");
+ return H_RESOURCE;
+ }
+ ret = ehca_hcall_7arg_7ret(H_DISABLE_AND_GETC,
+ adapter_handle.handle, /* r4 */
+ /* function code */
+ 1, /* r5 */
+ qp->ipz_qp_handle.handle, /* r6 */
+ 0, 0, 0, 0,
+ &ladr_next_sq_wqe_out, /* r4 */
+ &ladr_next_rq_wqe_out, /* r5 */
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+ if (ret == H_HARDWARE)
+ EDEB_ERR(4, "HCA not operational. ret=%lx", ret);
+
+ ret = ehca_hcall_7arg_7ret(H_FREE_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ qp->ipz_qp_handle.handle, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ if (ret == H_RESOURCE)
+ EDEB_ERR(4, "Resource still in use. ret=%lx", ret);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_define_aqp0(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u32 port)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "port=%x ipz_qp_handle=%lx adapter_handle=%lx",
+ port, qp_handle.handle, adapter_handle.handle);
+
+ ret = ehca_hcall_7arg_7ret(H_DEFINE_AQP0,
+ adapter_handle.handle, /* r4 */
+ qp_handle.handle, /* r5 */
+ port, /* r6 */
+ 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_define_aqp1(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u32 port, u32 * pma_qp_nr,
+ u32 * bma_qp_nr)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 pma_qp_nr_out;
+ u64 bma_qp_nr_out;
+
+ EDEB_EN(7, "port=%x qp_handle=%lx adapter_handle=%lx",
+ port, qp_handle.handle, adapter_handle.handle);
+
+ ret = ehca_hcall_7arg_7ret(H_DEFINE_AQP1,
+ adapter_handle.handle, /* r4 */
+ qp_handle.handle, /* r5 */
+ port, /* r6 */
+ 0, 0, 0, 0,
+ &pma_qp_nr_out, /* r4 */
+ &bma_qp_nr_out, /* r5 */
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ *pma_qp_nr = (u32)pma_qp_nr_out;
+ *bma_qp_nr = (u32)bma_qp_nr_out;
+
+ if (ret == H_ALIAS_EXIST)
+ EDEB_ERR(4, "AQP1 already exists. ret=%lx", ret);
+
+ EDEB_EX(7, "ret=%lx pma_qp_nr=%i bma_qp_nr=%i",
+ ret, (int)*pma_qp_nr, (int)*bma_qp_nr);
+
+ return ret;
+}
+
+u64 hipz_h_attach_mcqp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u16 mcg_dlid,
+ u64 subnet_prefix, u64 interface_id)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u8 *dgid_sp = (u8*)&subnet_prefix;
+ u8 *dgid_ii = (u8*)&interface_id;
+
+ EDEB_EN(7, "qp_handle=%lx adapter_handle=%lx\nMCG_DGID ="
+ " %d.%d.%d.%d.%d.%d.%d.%d."
+ " %d.%d.%d.%d.%d.%d.%d.%d",
+ qp_handle.handle, adapter_handle.handle,
+ dgid_sp[0], dgid_sp[1],
+ dgid_sp[2], dgid_sp[3],
+ dgid_sp[4], dgid_sp[5],
+ dgid_sp[6], dgid_sp[7],
+ dgid_ii[0], dgid_ii[1],
+ dgid_ii[2], dgid_ii[3],
+ dgid_ii[4], dgid_ii[5],
+ dgid_ii[6], dgid_ii[7]);
+
+ ret = ehca_hcall_7arg_7ret(H_ATTACH_MCQP,
+ adapter_handle.handle, /* r4 */
+ qp_handle.handle, /* r5 */
+ mcg_dlid, /* r6 */
+ interface_id, /* r7 */
+ subnet_prefix, /* r8 */
+ 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ if (ret == H_NOT_ENOUGH_RESOURCES)
+ EDEB_ERR(4, "Not enough resources. ret=%lx", ret);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_detach_mcqp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u16 mcg_dlid,
+ u64 subnet_prefix, u64 interface_id)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u8 *dgid_sp = (u8*)&subnet_prefix;
+ u8 *dgid_ii = (u8*)&interface_id;
+
+ EDEB_EN(7, "qp_handle=%lx adapter_handle=%lx\nMCG_DGID ="
+ " %d.%d.%d.%d.%d.%d.%d.%d."
+ " %d.%d.%d.%d.%d.%d.%d.%d",
+ qp_handle.handle, adapter_handle.handle,
+ dgid_sp[0], dgid_sp[1],
+ dgid_sp[2], dgid_sp[3],
+ dgid_sp[4], dgid_sp[5],
+ dgid_sp[6], dgid_sp[7],
+ dgid_ii[0], dgid_ii[1],
+ dgid_ii[2], dgid_ii[3],
+ dgid_ii[4], dgid_ii[5],
+ dgid_ii[6], dgid_ii[7]);
+ ret = ehca_hcall_7arg_7ret(H_DETACH_MCQP,
+ adapter_handle.handle, /* r4 */
+ qp_handle.handle, /* r5 */
+ mcg_dlid, /* r6 */
+ interface_id, /* r7 */
+ subnet_prefix, /* r8 */
+ 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_destroy_cq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_cq *cq,
+ u8 force_flag)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "cq->pf=%p cq=.%p ipz_cq_handle=%lx adapter_handle=%lx",
+ &cq->pf, cq, cq->ipz_cq_handle.handle, adapter_handle.handle);
+
+ ret = hcp_galpas_dtor(&cq->galpas);
+ if (ret) {
+ EDEB_ERR(4, "Could not destruct cp->galpas");
+ return H_RESOURCE;
+ }
+
+ ret = ehca_hcall_7arg_7ret(H_FREE_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ cq->ipz_cq_handle.handle, /* r5 */
+ force_flag != 0 ? 1L : 0L, /* r6 */
+ 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ if (ret == H_RESOURCE)
+ EDEB(4, "ret=%lx ", ret);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_destroy_eq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_eq *eq)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "eq->pf=%p eq=%p ipz_eq_handle=%lx adapter_handle=%lx",
+ &eq->pf, eq, eq->ipz_eq_handle.handle,
+ adapter_handle.handle);
+
+ ret = hcp_galpas_dtor(&eq->galpas);
+ if (ret) {
+ EDEB_ERR(4, "Could not destruct eq->galpas");
+ return H_RESOURCE;
+ }
+
+ ret = ehca_hcall_7arg_7ret(H_FREE_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ eq->ipz_eq_handle.handle, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+
+ if (ret == H_RESOURCE)
+ EDEB_ERR(4, "Resource in use. ret=%lx ", ret);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_alloc_resource_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const u64 vaddr,
+ const u64 length,
+ const u32 access_ctrl,
+ const struct ipz_pd pd,
+ struct ehca_mr_hipzout_parms *outparms)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 lkey_out;
+ u64 rkey_out;
+
+ EDEB_EN(7, "adapter_handle=%lx mr=%p vaddr=%lx length=%lx"
+ " access_ctrl=%x pd=%x",
+ adapter_handle.handle, mr, vaddr, length, access_ctrl,
+ pd.value);
+
+ ret = ehca_hcall_7arg_7ret(H_ALLOC_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ 5, /* r5 */
+ vaddr, /* r6 */
+ length, /* r7 */
+ (((u64)access_ctrl) << 32ULL), /* r8 */
+ pd.value, /* r9 */
+ 0,
+ &(outparms->handle.handle), /* r4 */
+ &dummy, /* r5 */
+ &lkey_out, /* r6 */
+ &rkey_out, /* r7 */
+ &dummy,
+ &dummy,
+ &dummy);
+ outparms->lkey = (u32)lkey_out;
+ outparms->rkey = (u32)rkey_out;
+
+ EDEB_EX(7, "ret=%lx mr_handle=%lx lkey=%x rkey=%x",
+ ret, outparms->handle.handle, outparms->lkey, outparms->rkey);
+
+ return ret;
+}
+
+u64 hipz_h_register_rpage_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count)
+{
+ u64 ret = H_SUCCESS;
+
+ EDEB_EN(7, "adapter_handle=%lx mr=%p mr_handle=%lx pagesize=%x"
+ " queue_type=%x logical_address_of_page=%lx count=%lx",
+ adapter_handle.handle, mr, mr->ipz_mr_handle.handle, pagesize,
+ queue_type, logical_address_of_page, count);
+
+ if ((count > 1) && (logical_address_of_page & 0xfff)) {
+ EDEB_ERR(4, "logical_address_of_page not on a 4k boundary "
+ "adapter_handle=%lx mr=%p mr_handle=%lx "
+ "pagesize=%x queue_type=%x logical_address_of_page=%lx"
+ " count=%lx",
+ adapter_handle.handle, mr, mr->ipz_mr_handle.handle,
+ pagesize, queue_type, logical_address_of_page, count);
+ ret = H_PARAMETER;
+ } else
+ ret = hipz_h_register_rpage(adapter_handle, pagesize,
+ queue_type,
+ mr->ipz_mr_handle.handle,
+ logical_address_of_page, count);
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_query_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ struct ehca_mr_hipzout_parms *outparms)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 remote_len_out;
+ u64 remote_vaddr_out;
+ u64 acc_ctrl_pd_out;
+ u64 r9_out;
+
+ EDEB_EN(7, "adapter_handle=%lx mr=%p mr_handle=%lx",
+ adapter_handle.handle, mr, mr->ipz_mr_handle.handle);
+
+ ret = ehca_hcall_7arg_7ret(H_QUERY_MR,
+ adapter_handle.handle, /* r4 */
+ mr->ipz_mr_handle.handle, /* r5 */
+ 0, 0, 0, 0, 0,
+ &outparms->len, /* r4 */
+ &outparms->vaddr, /* r5 */
+ &remote_len_out, /* r6 */
+ &remote_vaddr_out, /* r7 */
+ &acc_ctrl_pd_out, /* r8 */
+ &r9_out,
+ &dummy);
+
+ outparms->acl = acc_ctrl_pd_out >> 32;
+ outparms->lkey = (u32)(r9_out >> 32);
+ outparms->rkey = (u32)(r9_out & (0xffffffff));
+
+ EDEB_EX(7, "ret=%lx mr_local_length=%lx mr_local_vaddr=%lx "
+ "mr_remote_length=%lx mr_remote_vaddr=%lx access_ctrl=%x "
+ "pd=%x lkey=%x rkey=%x", ret, outparms->len,
+ outparms->vaddr, remote_len_out, remote_vaddr_out,
+ outparms->acl, outparms->acl, outparms->lkey, outparms->rkey);
+
+ return ret;
+}
+
+u64 hipz_h_free_resource_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "adapter_handle=%lx mr=%p mr_handle=%lx",
+ adapter_handle.handle, mr, mr->ipz_mr_handle.handle);
+
+ ret = ehca_hcall_7arg_7ret(H_FREE_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ mr->ipz_mr_handle.handle, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_reregister_pmr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const u64 vaddr_in,
+ const u64 length,
+ const u32 access_ctrl,
+ const struct ipz_pd pd,
+ const u64 mr_addr_cb,
+ struct ehca_mr_hipzout_parms *outparms)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 lkey_out;
+ u64 rkey_out;
+
+ EDEB_EN(7, "adapter_handle=%lx mr=%p mr_handle=%lx vaddr_in=%lx "
+ "length=%lx access_ctrl=%x pd=%x mr_addr_cb=%lx",
+ adapter_handle.handle, mr, mr->ipz_mr_handle.handle, vaddr_in,
+ length, access_ctrl, pd.value, mr_addr_cb);
+
+ ret = ehca_hcall_7arg_7ret(H_REREGISTER_PMR,
+ adapter_handle.handle, /* r4 */
+ mr->ipz_mr_handle.handle, /* r5 */
+ vaddr_in, /* r6 */
+ length, /* r7 */
+ /* r8 */
+ ((((u64)access_ctrl) << 32ULL) | pd.value),
+ mr_addr_cb, /* r9 */
+ 0,
+ &dummy, /* r4 */
+ &outparms->vaddr, /* r5 */
+ &lkey_out, /* r6 */
+ &rkey_out, /* r7 */
+ &dummy,
+ &dummy,
+ &dummy);
+
+ outparms->lkey = (u32)lkey_out;
+ outparms->rkey = (u32)rkey_out;
+
+ EDEB_EX(7, "ret=%lx vaddr=%lx lkey=%x rkey=%x",
+ ret, outparms->vaddr, outparms->lkey, outparms->rkey);
+ return ret;
+}
+
+u64 hipz_h_register_smr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const struct ehca_mr *orig_mr,
+ const u64 vaddr_in,
+ const u32 access_ctrl,
+ const struct ipz_pd pd,
+ struct ehca_mr_hipzout_parms *outparms)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 lkey_out;
+ u64 rkey_out;
+
+ EDEB_EN(7, "adapter_handle=%lx orig_mr=%p orig_mr_handle=%lx "
+ "vaddr_in=%lx access_ctrl=%x pd=%x", adapter_handle.handle,
+ orig_mr, orig_mr->ipz_mr_handle.handle, vaddr_in, access_ctrl,
+ pd.value);
+
+
+ ret = ehca_hcall_7arg_7ret(H_REGISTER_SMR,
+ adapter_handle.handle, /* r4 */
+ orig_mr->ipz_mr_handle.handle, /* r5 */
+ vaddr_in, /* r6 */
+ (((u64)access_ctrl) << 32ULL), /* r7 */
+ pd.value, /* r8 */
+ 0, 0,
+ &(outparms->handle.handle), /* r4 */
+ &dummy, /* r5 */
+ &lkey_out, /* r6 */
+ &rkey_out, /* r7 */
+ &dummy,
+ &dummy,
+ &dummy);
+ outparms->lkey = (u32)lkey_out;
+ outparms->rkey = (u32)rkey_out;
+
+ EDEB_EX(7, "ret=%lx mr_handle=%lx lkey=%x rkey=%x",
+ ret, outparms->handle.handle, outparms->lkey, outparms->rkey);
+
+ return ret;
+}
+
+u64 hipz_h_alloc_resource_mw(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mw *mw,
+ const struct ipz_pd pd,
+ struct ehca_mw_hipzout_parms *outparms)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 rkey_out;
+
+ EDEB_EN(7, "adapter_handle=%lx mw=%p pd=%x",
+ adapter_handle.handle, mw, pd.value);
+
+ ret = ehca_hcall_7arg_7ret(H_ALLOC_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ 6, /* r5 */
+ pd.value, /* r6 */
+ 0, 0, 0, 0,
+ &(outparms->handle.handle), /* r4 */
+ &dummy, /* r5 */
+ &dummy, /* r6 */
+ &rkey_out, /* r7 */
+ &dummy,
+ &dummy,
+ &dummy);
+
+ outparms->rkey = (u32)rkey_out;
+
+ EDEB_EX(7, "ret=%lx mw_handle=%lx rkey=%x",
+ ret, outparms->handle.handle, outparms->rkey);
+ return ret;
+}
+
+u64 hipz_h_query_mw(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mw *mw,
+ struct ehca_mw_hipzout_parms *outparms)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 pd_out;
+ u64 rkey_out;
+
+ EDEB_EN(7, "adapter_handle=%lx mw=%p mw_handle=%lx",
+ adapter_handle.handle, mw, mw->ipz_mw_handle.handle);
+
+ ret = ehca_hcall_7arg_7ret(H_QUERY_MW,
+ adapter_handle.handle, /* r4 */
+ mw->ipz_mw_handle.handle, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy, /* r4 */
+ &dummy, /* r5 */
+ &dummy, /* r6 */
+ &rkey_out, /* r7 */
+ &pd_out, /* r8 */
+ &dummy,
+ &dummy);
+ outparms->rkey = (u32)rkey_out;
+
+ EDEB_EX(7, "ret=%lx rkey=%x pd=%lx", ret, outparms->rkey, pd_out);
+
+ return ret;
+}
+
+u64 hipz_h_free_resource_mw(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mw *mw)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+
+ EDEB_EN(7, "adapter_handle=%lx mw=%p mw_handle=%lx",
+ adapter_handle.handle, mw, mw->ipz_mw_handle.handle);
+
+ ret = ehca_hcall_7arg_7ret(H_FREE_RESOURCE,
+ adapter_handle.handle, /* r4 */
+ mw->ipz_mw_handle.handle, /* r5 */
+ 0, 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
+
+u64 hipz_h_error_data(const struct ipz_adapter_handle adapter_handle,
+ const u64 ressource_handle,
+ void *rblock,
+ unsigned long *byte_count)
+{
+ u64 ret = H_SUCCESS;
+ u64 dummy;
+ u64 r_cb;
+
+ EDEB_EN(7, "adapter_handle=%lx ressource_handle=%lx rblock=%p",
+ adapter_handle.handle, ressource_handle, rblock);
+
+ if (((u64)rblock) & 0xfff) {
+ EDEB_ERR(4, "rblock not page aligned.");
+ return H_PARAMETER;
+ }
+
+ r_cb = virt_to_abs(rblock);
+
+ ret = ehca_hcall_7arg_7ret(H_ERROR_DATA,
+ adapter_handle.handle,
+ ressource_handle,
+ r_cb,
+ 0, 0, 0, 0,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy,
+ &dummy);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h
new file mode 100644
index 0000000..39956d8
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hcp_if.h
@@ -0,0 +1,261 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * Firmware Infiniband Interface code for POWER
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ * Gerd Bayer <[email protected]>
+ * Waleri Fomin <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __HCP_IF_H__
+#define __HCP_IF_H__
+
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "hipz_hw.h"
+
+/*
+ * hipz_h_alloc_resource_eq allocates EQ resources in HW and FW, initalize
+ * resources, create the empty EQPT (ring).
+ */
+u64 hipz_h_alloc_resource_eq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_pfeq *pfeq,
+ const u32 neq_control,
+ const u32 number_of_entries,
+ struct ipz_eq_handle *eq_handle,
+ u32 * act_nr_of_entries,
+ u32 * act_pages,
+ u32 * eq_ist);
+
+u64 hipz_h_reset_event(const struct ipz_adapter_handle adapter_handle,
+ struct ipz_eq_handle eq_handle,
+ const u64 event_mask);
+/*
+ * hipz_h_allocate_resource_cq allocates CQ resources in HW and FW, initialize
+ * resources, create the empty CQPT (ring).
+ */
+u64 hipz_h_alloc_resource_cq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_cq *cq,
+ struct ehca_alloc_cq_parms *param);
+
+
+/*
+ * hipz_h_alloc_resource_qp allocates QP resources in HW and FW,
+ * initialize resources, create empty QPPTs (2 rings).
+ */
+u64 hipz_h_alloc_resource_qp(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_qp *qp,
+ struct ehca_alloc_qp_parms *parms);
+
+u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle,
+ const u8 port_id,
+ struct hipz_query_port *query_port_response_block);
+
+u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle,
+ struct hipz_query_hca *query_hca_rblock);
+
+/*
+ * hipz_h_register_rpage internal function in hcp_if.h for all
+ * hcp_H_REGISTER_RPAGE calls.
+ */
+u64 hipz_h_register_rpage(const struct ipz_adapter_handle adapter_handle,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 resource_handle,
+ const u64 logical_address_of_page,
+ u64 count);
+
+u64 hipz_h_register_rpage_eq(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_eq_handle eq_handle,
+ struct ehca_pfeq *pfeq,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count);
+
+u32 hipz_h_query_int_state(const struct ipz_adapter_handle
+ hcp_adapter_handle,
+ u32 ist);
+
+u64 hipz_h_register_rpage_cq(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_cq_handle cq_handle,
+ struct ehca_pfcq *pfcq,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count,
+ const struct h_galpa gal);
+
+u64 hipz_h_register_rpage_qp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count,
+ const struct h_galpa galpa);
+
+u64 hipz_h_disable_and_get_wqe(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ void **log_addr_next_sq_wqe_tb_processed,
+ void **log_addr_next_rq_wqe_tb_processed,
+ int dis_and_get_function_code);
+enum hcall_sigt {
+ HCALL_SIGT_NO_CQE = 0,
+ HCALL_SIGT_BY_WQE = 1,
+ HCALL_SIGT_EVERY = 2
+};
+
+u64 hipz_h_modify_qp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ const u64 update_mask,
+ struct hcp_modify_qp_control_block *mqpcb,
+ struct h_galpa gal);
+
+u64 hipz_h_query_qp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct ehca_pfqp *pfqp,
+ struct hcp_modify_qp_control_block *qqpcb,
+ struct h_galpa gal);
+
+u64 hipz_h_destroy_qp(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_qp *qp);
+
+u64 hipz_h_define_aqp0(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u32 port);
+
+u64 hipz_h_define_aqp1(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u32 port, u32 * pma_qp_nr,
+ u32 * bma_qp_nr);
+
+u64 hipz_h_attach_mcqp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u16 mcg_dlid,
+ u64 subnet_prefix, u64 interface_id);
+
+u64 hipz_h_detach_mcqp(const struct ipz_adapter_handle adapter_handle,
+ const struct ipz_qp_handle qp_handle,
+ struct h_galpa gal,
+ u16 mcg_dlid,
+ u64 subnet_prefix, u64 interface_id);
+
+u64 hipz_h_destroy_cq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_cq *cq,
+ u8 force_flag);
+
+u64 hipz_h_destroy_eq(const struct ipz_adapter_handle adapter_handle,
+ struct ehca_eq *eq);
+
+/*
+ * hipz_h_alloc_resource_mr allocates MR resources in HW and FW, initialize
+ * resources.
+ */
+u64 hipz_h_alloc_resource_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const u64 vaddr,
+ const u64 length,
+ const u32 access_ctrl,
+ const struct ipz_pd pd,
+ struct ehca_mr_hipzout_parms *outparms);
+
+/* hipz_h_register_rpage_mr registers MR resource pages in HW and FW */
+u64 hipz_h_register_rpage_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const u8 pagesize,
+ const u8 queue_type,
+ const u64 logical_address_of_page,
+ const u64 count);
+
+/* hipz_h_query_mr queries MR in HW and FW */
+u64 hipz_h_query_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ struct ehca_mr_hipzout_parms *outparms);
+
+/* hipz_h_free_resource_mr frees MR resources in HW and FW */
+u64 hipz_h_free_resource_mr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr);
+
+/* hipz_h_reregister_pmr reregisters MR in HW and FW */
+u64 hipz_h_reregister_pmr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const u64 vaddr_in,
+ const u64 length,
+ const u32 access_ctrl,
+ const struct ipz_pd pd,
+ const u64 mr_addr_cb,
+ struct ehca_mr_hipzout_parms *outparms);
+
+/* hipz_h_register_smr register shared MR in HW and FW */
+u64 hipz_h_register_smr(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mr *mr,
+ const struct ehca_mr *orig_mr,
+ const u64 vaddr_in,
+ const u32 access_ctrl,
+ const struct ipz_pd pd,
+ struct ehca_mr_hipzout_parms *outparms);
+
+/*
+ * hipz_h_alloc_resource_mw allocates MW resources in HW and FW, initialize
+ * resources.
+ */
+u64 hipz_h_alloc_resource_mw(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mw *mw,
+ const struct ipz_pd pd,
+ struct ehca_mw_hipzout_parms *outparms);
+
+/* hipz_h_query_mw queries MW in HW and FW */
+u64 hipz_h_query_mw(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mw *mw,
+ struct ehca_mw_hipzout_parms *outparms);
+
+/* hipz_h_free_resource_mw frees MW resources in HW and FW */
+u64 hipz_h_free_resource_mw(const struct ipz_adapter_handle adapter_handle,
+ const struct ehca_mw *mw);
+
+u64 hipz_h_error_data(const struct ipz_adapter_handle adapter_handle,
+ const u64 ressource_handle,
+ void *rblock,
+ unsigned long *byte_count);
+
+#endif /* __HCP_IF_H__ */
--
1.4.1
drivers/infiniband/hw/ehca/ehca_cq.c | 449 ++++++++++++++++++++++++++++++++++
1 files changed, 449 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c
new file mode 100644
index 0000000..c52d1c3
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_cq.c
@@ -0,0 +1,449 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * Completion queue handling
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Khadija Souissi <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Heiko J Schick <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ *
+ *
+ * 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.
+ */
+
+#define DEB_PREFIX "e_cq"
+
+#include <asm/current.h>
+
+#include "ehca_iverbs.h"
+#include "ehca_classes.h"
+#include "ehca_irq.h"
+#include "hcp_if.h"
+
+int ehca_cq_assign_qp(struct ehca_cq *cq, struct ehca_qp *qp)
+{
+ unsigned int qp_num = qp->real_qp_num;
+ unsigned int key = qp_num & (QP_HASHTAB_LEN-1);
+ unsigned long spl_flags = 0;
+
+ spin_lock_irqsave(&cq->spinlock, spl_flags);
+ hlist_add_head(&qp->list_entries, &cq->qp_hashtab[key]);
+ spin_unlock_irqrestore(&cq->spinlock, spl_flags);
+
+ EDEB(7, "cq_num=%x real_qp_num=%x", cq->cq_number, qp_num);
+
+ return 0;
+}
+
+int ehca_cq_unassign_qp(struct ehca_cq *cq, unsigned int real_qp_num)
+{
+ int ret = -EINVAL;
+ unsigned int key = real_qp_num & (QP_HASHTAB_LEN-1);
+ struct hlist_node *iter = NULL;
+ struct ehca_qp *qp = NULL;
+ unsigned long spl_flags = 0;
+
+ spin_lock_irqsave(&cq->spinlock, spl_flags);
+ hlist_for_each(iter, &cq->qp_hashtab[key]) {
+ qp = hlist_entry(iter, struct ehca_qp, list_entries);
+ if (qp->real_qp_num == real_qp_num) {
+ hlist_del(iter);
+ EDEB(7, "removed qp from cq .cq_num=%x real_qp_num=%x",
+ cq->cq_number, real_qp_num);
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&cq->spinlock, spl_flags);
+ if (ret) {
+ EDEB_ERR(4, "qp not found cq_num=%x real_qp_num=%x",
+ cq->cq_number, real_qp_num);
+ }
+
+ return ret;
+}
+
+struct ehca_qp* ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num)
+{
+ struct ehca_qp *ret = NULL;
+ unsigned int key = real_qp_num & (QP_HASHTAB_LEN-1);
+ struct hlist_node *iter = NULL;
+ struct ehca_qp *qp = NULL;
+ hlist_for_each(iter, &cq->qp_hashtab[key]) {
+ qp = hlist_entry(iter, struct ehca_qp, list_entries);
+ if (qp->real_qp_num == real_qp_num) {
+ ret = qp;
+ break;
+ }
+ }
+ return ret;
+}
+
+struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe,
+ struct ib_ucontext *context,
+ struct ib_udata *udata)
+{
+ extern struct ehca_module ehca_module;
+ struct ib_cq *cq = NULL;
+ struct ehca_cq *my_cq = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ipz_adapter_handle adapter_handle;
+ /* h_call's out parameters */
+ struct ehca_alloc_cq_parms param;
+ u32 counter = 0;
+ void *vpage = NULL;
+ u64 rpage = 0;
+ struct h_galpa gal;
+ u64 cqx_fec = 0;
+ u64 h_ret = 0;
+ int ipz_rc = 0;
+ int ret = 0;
+ const u32 additional_cqe=20;
+ int i= 0;
+ unsigned long flags;
+
+ EHCA_CHECK_DEVICE_P(device);
+ EDEB_EN(7, "device=%p cqe=%x context=%p", device, cqe, context);
+
+ if (cqe >= 0xFFFFFFFF - 64 - additional_cqe)
+ return ERR_PTR(-EINVAL);
+
+ my_cq = kmem_cache_alloc(ehca_module.cache_cq, SLAB_KERNEL);
+ if (!my_cq) {
+ cq = ERR_PTR(-ENOMEM);
+ EDEB_ERR(4, "Out of memory for ehca_cq struct device=%p",
+ device);
+ goto create_cq_exit0;
+ }
+
+ memset(my_cq, 0, sizeof(struct ehca_cq));
+ memset(¶m, 0, sizeof(struct ehca_alloc_cq_parms));
+
+ spin_lock_init(&my_cq->spinlock);
+ spin_lock_init(&my_cq->cb_lock);
+ spin_lock_init(&my_cq->task_lock);
+ my_cq->ownpid = current->tgid;
+
+ cq = &my_cq->ib_cq;
+
+ shca = container_of(device, struct ehca_shca, ib_device);
+ adapter_handle = shca->ipz_hca_handle;
+ param.eq_handle = shca->eq.ipz_eq_handle;
+
+
+ do {
+ if (!idr_pre_get(&ehca_cq_idr, GFP_KERNEL)) {
+ cq = ERR_PTR(-ENOMEM);
+ EDEB_ERR(4,
+ "Can't reserve idr resources. "
+ "device=%p", device);
+ goto create_cq_exit1;
+ }
+
+ spin_lock_irqsave(&ehca_cq_idr_lock, flags);
+ ret = idr_get_new(&ehca_cq_idr, my_cq, &my_cq->token);
+ spin_unlock_irqrestore(&ehca_cq_idr_lock, flags);
+
+ } while (ret == -EAGAIN);
+
+ if (ret) {
+ cq = ERR_PTR(-ENOMEM);
+ EDEB_ERR(4,
+ "Can't allocate new idr entry. "
+ "device=%p", device);
+ goto create_cq_exit1;
+ }
+
+ /*
+ * CQs maximum depth is 4GB-64, but we need additional 20 as buffer
+ * for receiving errors CQEs.
+ */
+ param.nr_cqe = cqe + additional_cqe;
+ h_ret = hipz_h_alloc_resource_cq(adapter_handle, my_cq, ¶m);
+
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4,"hipz_h_alloc_resource_cq() failed "
+ "h_ret=%lx device=%p", h_ret, device);
+ cq = ERR_PTR(ehca2ib_return_code(h_ret));
+ goto create_cq_exit2;
+ }
+
+ ipz_rc = ipz_queue_ctor(&my_cq->ipz_queue, param.act_pages,
+ EHCA_PAGESIZE, sizeof(struct ehca_cqe), 0);
+ if (!ipz_rc) {
+ EDEB_ERR(4,
+ "ipz_queue_ctor() failed "
+ "ipz_rc=%x device=%p", ipz_rc, device);
+ cq = ERR_PTR(-EINVAL);
+ goto create_cq_exit3;
+ }
+
+ for (counter = 0; counter < param.act_pages; counter++) {
+ vpage = ipz_qpageit_get_inc(&my_cq->ipz_queue);
+ if (!vpage) {
+ EDEB_ERR(4, "ipz_qpageit_get_inc() "
+ "returns NULL device=%p", device);
+ cq = ERR_PTR(-EAGAIN);
+ goto create_cq_exit4;
+ }
+ rpage = virt_to_abs(vpage);
+
+ h_ret = hipz_h_register_rpage_cq(adapter_handle,
+ my_cq->ipz_cq_handle,
+ &my_cq->pf,
+ 0,
+ 0,
+ rpage,
+ 1,
+ my_cq->galpas.
+ kernel);
+
+ if (h_ret < H_SUCCESS) {
+ EDEB_ERR(4, "hipz_h_register_rpage_cq() failed "
+ "ehca_cq=%p cq_num=%x h_ret=%lx "
+ "counter=%i act_pages=%i",
+ my_cq, my_cq->cq_number,
+ h_ret, counter, param.act_pages);
+ cq = ERR_PTR(-EINVAL);
+ goto create_cq_exit4;
+ }
+
+ if (counter == (param.act_pages - 1)) {
+ vpage = ipz_qpageit_get_inc(&my_cq->ipz_queue);
+ if ((h_ret != H_SUCCESS) || vpage) {
+ EDEB_ERR(4, "Registration of pages not "
+ "complete ehca_cq=%p cq_num=%x "
+ "h_ret=%lx",
+ my_cq, my_cq->cq_number, h_ret);
+ cq = ERR_PTR(-EAGAIN);
+ goto create_cq_exit4;
+ }
+ } else {
+ if (h_ret != H_PAGE_REGISTERED) {
+ EDEB_ERR(4, "Registration of page failed "
+ "ehca_cq=%p cq_num=%x h_ret=%lx"
+ "counter=%i act_pages=%i",
+ my_cq, my_cq->cq_number,
+ h_ret, counter, param.act_pages);
+ cq = ERR_PTR(-ENOMEM);
+ goto create_cq_exit4;
+ }
+ }
+ }
+
+ ipz_qeit_reset(&my_cq->ipz_queue);
+
+ gal = my_cq->galpas.kernel;
+ cqx_fec = hipz_galpa_load(gal, CQTEMM_OFFSET(cqx_fec));
+ EDEB(8, "ehca_cq=%p cq_num=%x CQX_FEC=%lx",
+ my_cq, my_cq->cq_number, cqx_fec);
+
+ my_cq->ib_cq.cqe = my_cq->nr_of_entries =
+ param.act_nr_of_entries - additional_cqe;
+ my_cq->cq_number = (my_cq->ipz_cq_handle.handle) & 0xffff;
+
+ for (i = 0; i < QP_HASHTAB_LEN; i++)
+ INIT_HLIST_HEAD(&my_cq->qp_hashtab[i]);
+
+ if (context) {
+ struct ipz_queue *ipz_queue = &my_cq->ipz_queue;
+ struct ehca_create_cq_resp resp;
+ struct vm_area_struct *vma = NULL;
+ memset(&resp, 0, sizeof(resp));
+ resp.cq_number = my_cq->cq_number;
+ resp.token = my_cq->token;
+ resp.ipz_queue.qe_size = ipz_queue->qe_size;
+ resp.ipz_queue.act_nr_of_sg = ipz_queue->act_nr_of_sg;
+ resp.ipz_queue.queue_length = ipz_queue->queue_length;
+ resp.ipz_queue.pagesize = ipz_queue->pagesize;
+ resp.ipz_queue.toggle_state = ipz_queue->toggle_state;
+ ret = ehca_mmap_nopage(((u64)(my_cq->token) << 32) | 0x12000000,
+ ipz_queue->queue_length,
+ (void**)&resp.ipz_queue.queue,
+ &vma);
+ if (ret) {
+ EDEB_ERR(4, "Could not mmap queue pages");
+ cq = ERR_PTR(ret);
+ goto create_cq_exit4;
+ }
+ my_cq->uspace_queue = resp.ipz_queue.queue;
+ resp.galpas = my_cq->galpas;
+ ret = ehca_mmap_register(my_cq->galpas.user.fw_handle,
+ (void**)&resp.galpas.kernel.fw_handle,
+ &vma);
+ if (ret) {
+ EDEB_ERR(4, "Could not mmap fw_handle");
+ cq = ERR_PTR(ret);
+ goto create_cq_exit5;
+ }
+ my_cq->uspace_fwh = (u64)resp.galpas.kernel.fw_handle;
+ if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {
+ EDEB_ERR(4, "Copy to udata failed.");
+ goto create_cq_exit6;
+ }
+ }
+
+ EDEB_EX(7,"retcode=%p ehca_cq=%p cq_num=%x cq_size=%x",
+ cq, my_cq, my_cq->cq_number, param.act_nr_of_entries);
+ return cq;
+
+create_cq_exit6:
+ ehca_munmap(my_cq->uspace_fwh, EHCA_PAGESIZE);
+
+create_cq_exit5:
+ ehca_munmap(my_cq->uspace_queue, my_cq->ipz_queue.queue_length);
+
+create_cq_exit4:
+ ipz_queue_dtor(&my_cq->ipz_queue);
+
+create_cq_exit3:
+ h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 1);
+ if (h_ret != H_SUCCESS)
+ EDEB(4, "hipz_h_destroy_cq() failed ehca_cq=%p cq_num=%x "
+ "h_ret=%lx", my_cq, my_cq->cq_number, h_ret);
+
+create_cq_exit2:
+ spin_lock_irqsave(&ehca_cq_idr_lock, flags);
+ idr_remove(&ehca_cq_idr, my_cq->token);
+ spin_unlock_irqrestore(&ehca_cq_idr_lock, flags);
+
+create_cq_exit1:
+ kmem_cache_free(ehca_module.cache_cq, my_cq);
+
+create_cq_exit0:
+ EDEB_EX(4, "An error has occured retcode=%p", cq);
+ return cq;
+}
+
+int ehca_destroy_cq(struct ib_cq *cq)
+{
+ extern struct ehca_module ehca_module;
+ u64 h_ret = 0;
+ int ret = 0;
+ struct ehca_cq *my_cq = NULL;
+ int cq_num = 0;
+ struct ib_device *device = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ipz_adapter_handle adapter_handle;
+ u32 cur_pid = current->tgid;
+ unsigned long flags;
+
+ EHCA_CHECK_CQ(cq);
+ my_cq = container_of(cq, struct ehca_cq, ib_cq);
+ cq_num = my_cq->cq_number;
+ device = cq->device;
+ EHCA_CHECK_DEVICE(device);
+ shca = container_of(device, struct ehca_shca, ib_device);
+ adapter_handle = shca->ipz_hca_handle;
+ EDEB_EN(7, "ehca_cq=%p cq_num=%x",
+ my_cq, my_cq->cq_number);
+
+ spin_lock_irqsave(&ehca_cq_idr_lock, flags);
+ while (my_cq->nr_callbacks)
+ yield();
+
+ idr_remove(&ehca_cq_idr, my_cq->token);
+ spin_unlock_irqrestore(&ehca_cq_idr_lock, flags);
+
+ if (my_cq->uspace_queue && my_cq->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_cq->ownpid);
+ return -EINVAL;
+ }
+
+ /* un-mmap if vma alloc */
+ if (my_cq->uspace_queue ) {
+ ret = ehca_munmap(my_cq->uspace_queue,
+ my_cq->ipz_queue.queue_length);
+ ret = ehca_munmap(my_cq->uspace_fwh, EHCA_PAGESIZE);
+ }
+
+ h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 0);
+ if (h_ret == H_R_STATE) {
+ /* cq in err: read err data and destroy it forcibly */
+ EDEB(4, "ehca_cq=%p cq_num=%x ressource=%lx in err state. "
+ "Try to delete it forcibly.",
+ my_cq, my_cq->cq_number, my_cq->ipz_cq_handle.handle);
+ ehca_error_data(shca, my_cq, my_cq->ipz_cq_handle.handle);
+ h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 1);
+ if (h_ret == H_SUCCESS)
+ EDEB(4, "ehca_cq=%p cq_num=%x deleted successfully.",
+ my_cq, my_cq->cq_number);
+ }
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4,"hipz_h_destroy_cq() failed "
+ "h_ret=%lx ehca_cq=%p cq_num=%x",
+ h_ret, my_cq, my_cq->cq_number);
+ ret = ehca2ib_return_code(h_ret);
+ goto destroy_cq_exit0;
+ }
+ ipz_queue_dtor(&my_cq->ipz_queue);
+ kmem_cache_free(ehca_module.cache_cq, my_cq);
+
+destroy_cq_exit0:
+ EDEB_EX(7, "ehca_cq=%p cq_num=%x ret=%x ",
+ my_cq, cq_num, ret);
+ return ret;
+}
+
+int ehca_resize_cq(struct ib_cq *cq, int cqe, struct ib_udata *udata)
+{
+ int ret = 0;
+ struct ehca_cq *my_cq = NULL;
+ u32 cur_pid = current->tgid;
+
+ if (unlikely(!cq)) {
+ EDEB_ERR(4, "cq is NULL");
+ return -EFAULT;
+ }
+
+ my_cq = container_of(cq, struct ehca_cq, ib_cq);
+ EDEB_EN(7, "ehca_cq=%p cq_num=%x",
+ my_cq, my_cq->cq_number);
+
+ if (my_cq->uspace_queue && my_cq->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_cq->ownpid);
+ return -EINVAL;
+ }
+
+ /* TODO: proper resize needs to be done */
+ ret = -EFAULT;
+ EDEB_ERR(4, "not implemented yet");
+
+ EDEB_EX(7, "ehca_cq=%p cq_num=%x",
+ my_cq, my_cq->cq_number);
+ return ret;
+}
--
1.4.1
drivers/infiniband/hw/ehca/ehca_av.c | 303 ++++++++++++++++++++++++++++++++++
drivers/infiniband/hw/ehca/ehca_pd.c | 120 +++++++++++++
2 files changed, 423 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ehca_av.c b/drivers/infiniband/hw/ehca/ehca_av.c
new file mode 100644
index 0000000..fd9fc6d
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_av.c
@@ -0,0 +1,303 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * adress vector functions
+ *
+ * Authors: Hoang-Nam Nguyen <[email protected]>
+ * Khadija Souissi <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Christoph Raisch <[email protected]>
+ *
+ * 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.
+ */
+
+
+#define DEB_PREFIX "ehav"
+
+#include <asm/current.h>
+
+#include "ehca_tools.h"
+#include "ehca_iverbs.h"
+#include "hcp_if.h"
+
+struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr)
+{
+ extern struct ehca_module ehca_module;
+ extern int ehca_static_rate;
+ int ret = 0;
+ struct ehca_av *av = NULL;
+ struct ehca_shca *shca = NULL;
+
+ EHCA_CHECK_PD_P(pd);
+ EHCA_CHECK_ADR_P(ah_attr);
+
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+
+ EDEB_EN(7, "pd=%p ah_attr=%p", pd, ah_attr);
+
+ av = kmem_cache_alloc(ehca_module.cache_av, SLAB_KERNEL);
+ if (!av) {
+ EDEB_ERR(4, "Out of memory pd=%p ah_attr=%p", pd, ah_attr);
+ ret = -ENOMEM;
+ goto create_ah_exit0;
+ }
+
+ av->av.sl = ah_attr->sl;
+ av->av.dlid = ah_attr->dlid;
+ av->av.slid_path_bits = ah_attr->src_path_bits;
+
+ if (ehca_static_rate < 0) {
+ int ah_mult = ib_rate_to_mult(ah_attr->static_rate);
+ int ehca_mult =
+ ib_rate_to_mult(shca->sport[ah_attr->port_num].rate );
+
+ if (ah_mult >= ehca_mult)
+ av->av.ipd = 0;
+ else
+ av->av.ipd = (ah_mult > 0) ?
+ ((ehca_mult - 1) / ah_mult) : 0;
+ } else
+ av->av.ipd = ehca_static_rate;
+
+ EDEB(7, "IPD av->av.ipd set =%x ah_attr->static_rate=%x "
+ "shca_ib_rate=%x ",av->av.ipd, ah_attr->static_rate,
+ shca->sport[ah_attr->port_num].rate);
+
+ av->av.lnh = ah_attr->ah_flags;
+ av->av.grh.word_0 = EHCA_BMASK_SET(GRH_IPVERSION_MASK, 6);
+ av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_TCLASS_MASK,
+ ah_attr->grh.traffic_class);
+ av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK,
+ ah_attr->grh.flow_label);
+ av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK,
+ ah_attr->grh.hop_limit);
+ av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1B);
+ /* set sgid in grh.word_1 */
+ if (ah_attr->ah_flags & IB_AH_GRH) {
+ int rc = 0;
+ struct ib_port_attr port_attr;
+ union ib_gid gid;
+ memset(&port_attr, 0, sizeof(port_attr));
+ rc = ehca_query_port(pd->device, ah_attr->port_num,
+ &port_attr);
+ if (rc) { /* invalid port number */
+ ret = -EINVAL;
+ EDEB_ERR(4, "Invalid port number "
+ "ehca_query_port() returned %x "
+ "pd=%p ah_attr=%p", rc, pd, ah_attr);
+ goto create_ah_exit1;
+ }
+ memset(&gid, 0, sizeof(gid));
+ rc = ehca_query_gid(pd->device,
+ ah_attr->port_num,
+ ah_attr->grh.sgid_index, &gid);
+ if (rc) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "Failed to retrieve sgid "
+ "ehca_query_gid() returned %x "
+ "pd=%p ah_attr=%p", rc, pd, ah_attr);
+ goto create_ah_exit1;
+ }
+ memcpy(&av->av.grh.word_1, &gid, sizeof(gid));
+ }
+ /* for the time being we use a hard coded PMTU of 2048 Bytes */
+ av->av.pmtu = 4;
+
+ /* dgid comes in grh.word_3 */
+ memcpy(&av->av.grh.word_3, &ah_attr->grh.dgid,
+ sizeof(ah_attr->grh.dgid));
+
+ EHCA_REGISTER_AV(device, pd);
+
+ EDEB_EX(7, "pd=%p ah_attr=%p av=%p", pd, ah_attr, av);
+ return &av->ib_ah;
+
+create_ah_exit1:
+ kmem_cache_free(ehca_module.cache_av, av);
+
+create_ah_exit0:
+ EDEB_EX(7, "ret=%x pd=%p ah_attr=%p", ret, pd, ah_attr);
+
+ return ERR_PTR(ret);
+}
+
+int ehca_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
+{
+ struct ehca_av *av = NULL;
+ struct ehca_ud_av new_ehca_av;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+ int ret = 0;
+
+ EHCA_CHECK_AV(ah);
+ EHCA_CHECK_ADR(ah_attr);
+
+ EDEB_EN(7, "ah=%p ah_attr=%p", ah, ah_attr);
+
+ my_pd = container_of(ah->pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ return -EINVAL;
+ }
+
+ memset(&new_ehca_av, 0, sizeof(new_ehca_av));
+ new_ehca_av.sl = ah_attr->sl;
+ new_ehca_av.dlid = ah_attr->dlid;
+ new_ehca_av.slid_path_bits = ah_attr->src_path_bits;
+ new_ehca_av.ipd = ah_attr->static_rate;
+ new_ehca_av.lnh = EHCA_BMASK_SET(GRH_FLAG_MASK,
+ (ah_attr->ah_flags & IB_AH_GRH) > 0);
+ new_ehca_av.grh.word_0 = EHCA_BMASK_SET(GRH_TCLASS_MASK,
+ ah_attr->grh.traffic_class);
+ new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK,
+ ah_attr->grh.flow_label);
+ new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK,
+ ah_attr->grh.hop_limit);
+ new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1b);
+
+ /* set sgid in grh.word_1 */
+ if (ah_attr->ah_flags & IB_AH_GRH) {
+ int rc = 0;
+ struct ib_port_attr port_attr;
+ union ib_gid gid;
+ memset(&port_attr, 0, sizeof(port_attr));
+ rc = ehca_query_port(ah->device, ah_attr->port_num,
+ &port_attr);
+ if (rc) { /* invalid port number */
+ ret = -EINVAL;
+ EDEB_ERR(4, "Invalid port number "
+ "ehca_query_port() returned %x "
+ "ah=%p ah_attr=%p port_num=%x",
+ rc, ah, ah_attr, ah_attr->port_num);
+ goto modify_ah_exit1;
+ }
+ memset(&gid, 0, sizeof(gid));
+ rc = ehca_query_gid(ah->device,
+ ah_attr->port_num,
+ ah_attr->grh.sgid_index, &gid);
+ if (rc) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "Failed to retrieve sgid "
+ "ehca_query_gid() returned %x "
+ "ah=%p ah_attr=%p port_num=%x "
+ "sgid_index=%x",
+ rc, ah, ah_attr, ah_attr->port_num,
+ ah_attr->grh.sgid_index);
+ goto modify_ah_exit1;
+ }
+ memcpy(&new_ehca_av.grh.word_1, &gid, sizeof(gid));
+ }
+
+ new_ehca_av.pmtu = 4; /* see also comment in create_ah() */
+
+ memcpy(&new_ehca_av.grh.word_3, &ah_attr->grh.dgid,
+ sizeof(ah_attr->grh.dgid));
+
+ av = container_of(ah, struct ehca_av, ib_ah);
+ av->av = new_ehca_av;
+
+modify_ah_exit1:
+ EDEB_EX(7, "ret=%x ah=%p ah_attr=%p", ret, ah, ah_attr);
+
+ return ret;
+}
+
+int ehca_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr)
+{
+ int ret = 0;
+ struct ehca_av *av = NULL;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+
+ EHCA_CHECK_AV(ah);
+ EHCA_CHECK_ADR(ah_attr);
+
+ EDEB_EN(7, "ah=%p ah_attr=%p", ah, ah_attr);
+
+ my_pd = container_of(ah->pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ return -EINVAL;
+ }
+
+ av = container_of(ah, struct ehca_av, ib_ah);
+ memcpy(&ah_attr->grh.dgid, &av->av.grh.word_3,
+ sizeof(ah_attr->grh.dgid));
+ ah_attr->sl = av->av.sl;
+
+ ah_attr->dlid = av->av.dlid;
+
+ ah_attr->src_path_bits = av->av.slid_path_bits;
+ ah_attr->static_rate = av->av.ipd;
+ ah_attr->ah_flags = EHCA_BMASK_GET(GRH_FLAG_MASK, av->av.lnh);
+ ah_attr->grh.traffic_class = EHCA_BMASK_GET(GRH_TCLASS_MASK,
+ av->av.grh.word_0);
+ ah_attr->grh.hop_limit = EHCA_BMASK_GET(GRH_HOPLIMIT_MASK,
+ av->av.grh.word_0);
+ ah_attr->grh.flow_label = EHCA_BMASK_GET(GRH_FLOWLABEL_MASK,
+ av->av.grh.word_0);
+
+ EDEB_EX(7, "ah=%p ah_attr=%p ret=%x", ah, ah_attr, ret);
+ return ret;
+}
+
+int ehca_destroy_ah(struct ib_ah *ah)
+{
+ extern struct ehca_module ehca_module;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+ int ret = 0;
+
+ EHCA_CHECK_AV(ah);
+ EHCA_DEREGISTER_AV(ah);
+
+ EDEB_EN(7, "ah=%p", ah);
+
+ my_pd = container_of(ah->pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ return -EINVAL;
+ }
+
+ kmem_cache_free(ehca_module.cache_av,
+ container_of(ah, struct ehca_av, ib_ah));
+
+ EDEB_EX(7, "ret=%x ah=%p", ret, ah);
+ return ret;
+}
diff --git a/drivers/infiniband/hw/ehca/ehca_pd.c b/drivers/infiniband/hw/ehca/ehca_pd.c
new file mode 100644
index 0000000..afcbe59
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_pd.c
@@ -0,0 +1,120 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * PD functions
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ *
+ * 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.
+ */
+
+
+#define DEB_PREFIX "vpd "
+
+#include <asm/current.h>
+
+#include "ehca_tools.h"
+#include "ehca_iverbs.h"
+
+struct ib_pd *ehca_alloc_pd(struct ib_device *device,
+ struct ib_ucontext *context, struct ib_udata *udata)
+{
+ extern struct ehca_module ehca_module;
+ struct ib_pd *mypd = NULL;
+ struct ehca_pd *pd = NULL;
+
+ EDEB_EN(7, "device=%p context=%p udata=%p", device, context, udata);
+
+ EHCA_CHECK_DEVICE_P(device);
+
+ pd = kmem_cache_alloc(ehca_module.cache_pd, SLAB_KERNEL);
+ if (!pd) {
+ EDEB_ERR(4, "ERROR device=%p context=%p pd=%p"
+ " out of memory", device, context, mypd);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memset(pd, 0, sizeof(struct ehca_pd));
+ pd->ownpid = current->tgid;
+
+ /*
+ * Kernel PD: when device = -1, 0
+ * User PD: when context != -1
+ */
+ if (!context) {
+ /*
+ * Kernel PDs after init reuses always
+ * the one created in ehca_shca_reopen()
+ */
+ struct ehca_shca *shca = container_of(device, struct ehca_shca,
+ ib_device);
+ pd->fw_pd.value = shca->pd->fw_pd.value;
+ } else
+ pd->fw_pd.value = (u64)pd;
+
+ mypd = &pd->ib_pd;
+
+ EHCA_REGISTER_PD(device, pd);
+
+ EDEB_EX(7, "device=%p context=%p pd=%p", device, context, mypd);
+
+ return mypd;
+}
+
+int ehca_dealloc_pd(struct ib_pd *pd)
+{
+ extern struct ehca_module ehca_module;
+ int ret = 0;
+ u32 cur_pid = current->tgid;
+ struct ehca_pd *my_pd = NULL;
+
+ EDEB_EN(7, "pd=%p", pd);
+
+ EHCA_CHECK_PD(pd);
+ my_pd = container_of(pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ return -EINVAL;
+ }
+
+ EHCA_DEREGISTER_PD(pd);
+
+ kmem_cache_free(ehca_module.cache_pd,
+ container_of(pd, struct ehca_pd, ib_pd));
+
+ EDEB_EX(7, "pd=%p", pd);
+
+ return ret;
+}
--
1.4.1
drivers/infiniband/hw/ehca/ipz_pt_fn.c | 166 +++++++++++++++++++++
drivers/infiniband/hw/ehca/ipz_pt_fn.h | 253 ++++++++++++++++++++++++++++++++
2 files changed, 419 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/infiniband/hw/ehca/ipz_pt_fn.c
new file mode 100644
index 0000000..a14f957
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.c
@@ -0,0 +1,166 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * internal queue handling
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Christoph Raisch <[email protected]>
+ *
+ * Copyright (c) 2005 IBM Corporation
+ *
+ * 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.
+ */
+
+#define DEB_PREFIX "iptz"
+
+#include "ehca_tools.h"
+#include "ipz_pt_fn.h"
+
+extern int ehca_hwlevel;
+
+void *ipz_qpageit_get_inc(struct ipz_queue *queue)
+{
+ void *ret = ipz_qeit_get(queue);
+ queue->current_q_offset += queue->pagesize;
+ if (queue->current_q_offset > queue->queue_length) {
+ queue->current_q_offset -= queue->pagesize;
+ ret = NULL;
+ }
+ if (((u64)ret) % EHCA_PAGESIZE) {
+ EDEB(4, "ERROR!! not at PAGE-Boundary");
+ return NULL;
+ }
+ EDEB(7, "queue=%p ret=%p", queue, ret);
+ return ret;
+}
+
+void *ipz_qeit_eq_get_inc(struct ipz_queue *queue)
+{
+ void *ret = ipz_qeit_get(queue);
+ u64 last_entry_in_q = queue->queue_length - queue->qe_size;
+ queue->current_q_offset += queue->qe_size;
+ if (queue->current_q_offset > last_entry_in_q) {
+ queue->current_q_offset = 0;
+ queue->toggle_state = (~queue->toggle_state) & 1;
+ }
+
+ EDEB(7, "queue=%p ret=%p new current_q_offset=%lx qe_size=%x",
+ queue, ret, queue->current_q_offset, queue->qe_size);
+
+ return ret;
+}
+
+int ipz_queue_ctor(struct ipz_queue *queue,
+ const u32 nr_of_pages,
+ const u32 pagesize, const u32 qe_size, const u32 nr_of_sg)
+{
+ int pages_per_kpage = PAGE_SIZE >> EHCA_PAGESHIFT;
+ int f;
+
+ EDEB_EN(7, "nr_of_pages=%x pagesize=%x qe_size=%x pages_per_kpage=%x",
+ nr_of_pages, pagesize, qe_size, pages_per_kpage);
+ if (pagesize > PAGE_SIZE) {
+ EDEB_ERR(4, "FATAL ERROR: pagesize=%x is greater than "
+ "kernel page size", pagesize);
+ return 0;
+ }
+ if (!pages_per_kpage) {
+ EDEB_ERR(4, "FATAL ERROR: invalid kernel page size. "
+ "pages_per_kpage=%x", pages_per_kpage);
+ return 0;
+ }
+ queue->queue_length = nr_of_pages * pagesize;
+ queue->queue_pages = vmalloc(nr_of_pages * sizeof(void *));
+ if (!queue->queue_pages) {
+ EDEB(4, "ERROR!! didn't get the memory");
+ return 0;
+ }
+ memset(queue->queue_pages, 0, nr_of_pages * sizeof(void *));
+ /*
+ * allocate pages for queue:
+ * outer loop allocates whole kernel pages (page aligned) and
+ * inner loop divides a kernel page into smaller hca queue pages
+ */
+ f = 0;
+ while (f < nr_of_pages) {
+ u8 *kpage = (u8*)get_zeroed_page(GFP_KERNEL);
+ int k;
+ if (!kpage)
+ goto ipz_queue_ctor_exit0; /*NOMEM*/
+ for (k = 0; k < pages_per_kpage && f < nr_of_pages; k++) {
+ (queue->queue_pages)[f] = (struct ipz_page *)kpage;
+ kpage += EHCA_PAGESIZE;
+ f++;
+ }
+ }
+
+ queue->current_q_offset = 0;
+ queue->qe_size = qe_size;
+ queue->act_nr_of_sg = nr_of_sg;
+ queue->pagesize = pagesize;
+ queue->toggle_state = 1;
+ EDEB_EX(7, "queue_length=%x queue_pages=%p qe_size=%x"
+ " act_nr_of_sg=%x", queue->queue_length, queue->queue_pages,
+ queue->qe_size, queue->act_nr_of_sg);
+ return 1;
+
+ ipz_queue_ctor_exit0:
+ EDEB_ERR(4, "Couldn't get alloc pages queue=%p f=%x nr_of_pages=%x",
+ queue, f, nr_of_pages);
+ for (f = 0; f < nr_of_pages; f += pages_per_kpage) {
+ if (!(queue->queue_pages)[f])
+ break;
+ free_page((unsigned long)(queue->queue_pages)[f]);
+ }
+ return 0;
+}
+
+int ipz_queue_dtor(struct ipz_queue *queue)
+{
+ int pages_per_kpage = PAGE_SIZE >> EHCA_PAGESHIFT;
+ int g;
+ int nr_pages;
+
+ EDEB_EN(7, "ipz_queue pointer=%p", queue);
+ if (!queue || !queue->queue_pages) {
+ EDEB_ERR(4, "queue or queue_pages is NULL");
+ return 0;
+ }
+ EDEB(7, "destructing a queue with the following "
+ "properties:\n nr_of_pages=%x pagesize=%x qe_size=%x",
+ queue->act_nr_of_sg, queue->pagesize, queue->qe_size);
+ nr_pages = queue->queue_length / queue->pagesize;
+ for (g = 0; g < nr_pages; g += pages_per_kpage)
+ free_page((unsigned long)(queue->queue_pages)[g]);
+ vfree(queue->queue_pages);
+
+ EDEB_EX(7, "queue freed!");
+ return 1;
+}
diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.h b/drivers/infiniband/hw/ehca/ipz_pt_fn.h
new file mode 100644
index 0000000..fdd139b
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.h
@@ -0,0 +1,253 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * internal queue handling
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Christoph Raisch <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __IPZ_PT_FN_H__
+#define __IPZ_PT_FN_H__
+
+#include "ehca_qes.h"
+#define EHCA_PAGESHIFT 12
+#define EHCA_PAGESIZE 4096UL
+#define EHCA_PAGEMASK (~(EHCA_PAGESIZE-1))
+#define EHCA_PT_ENTRIES 512UL
+
+#include "ehca_tools.h"
+#include "ehca_qes.h"
+
+/* struct generic ehca page */
+struct ipz_page {
+ u8 entries[EHCA_PAGESIZE];
+};
+
+/* struct generic queue in linux kernel virtual memory (kv) */
+struct ipz_queue {
+ u64 current_q_offset; /* current queue entry */
+
+ struct ipz_page **queue_pages; /* array of pages belonging to queue */
+ u32 qe_size; /* queue entry size */
+ u32 act_nr_of_sg;
+ u32 queue_length; /* queue length allocated in bytes */
+ u32 pagesize;
+ u32 toggle_state; /* toggle flag - per page */
+ u32 dummy3; /* 64 bit alignment */
+};
+
+/*
+ * return current Queue Entry for a certain q_offset
+ * returns address (kv) of Queue Entry
+ */
+static inline void *ipz_qeit_calc(struct ipz_queue *queue, u64 q_offset)
+{
+ struct ipz_page *current_page = NULL;
+ if (q_offset >= queue->queue_length)
+ return NULL;
+ current_page = (queue->queue_pages)[q_offset >> EHCA_PAGESHIFT];
+ return ¤t_page->entries[q_offset & (EHCA_PAGESIZE - 1)];
+}
+
+/*
+ * return current Queue Entry
+ * returns address (kv) of Queue Entry
+ */
+static inline void *ipz_qeit_get(struct ipz_queue *queue)
+{
+ return ipz_qeit_calc(queue, queue->current_q_offset);
+}
+
+/*
+ * return current Queue Page , increment Queue Page iterator from
+ * page to page in struct ipz_queue, last increment will return 0! and
+ * NOT wrap
+ * returns address (kv) of Queue Page
+ * warning don't use in parallel with ipz_QE_get_inc()
+ */
+void *ipz_qpageit_get_inc(struct ipz_queue *queue);
+
+/*
+ * return current Queue Entry, increment Queue Entry iterator by one
+ * step in struct ipz_queue, will wrap in ringbuffer
+ * returns address (kv) of Queue Entry BEFORE increment
+ * warning don't use in parallel with ipz_qpageit_get_inc()
+ * warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ */
+static inline void *ipz_qeit_get_inc(struct ipz_queue *queue)
+{
+ void *ret = ipz_qeit_get(queue);
+ queue->current_q_offset += queue->qe_size;
+ if (queue->current_q_offset >= queue->queue_length) {
+ queue->current_q_offset = 0;
+ /* toggle the valid flag */
+ queue->toggle_state = (~queue->toggle_state) & 1;
+ }
+
+ EDEB(7, "queue=%p ret=%p new current_q_addr=%lx qe_size=%x",
+ queue, ret, queue->current_q_offset, queue->qe_size);
+
+ return ret;
+}
+
+/*
+ * return current Queue Entry, increment Queue Entry iterator by one
+ * step in struct ipz_queue, will wrap in ringbuffer
+ * returns address (kv) of Queue Entry BEFORE increment
+ * returns 0 and does not increment, if wrong valid state
+ * warning don't use in parallel with ipz_qpageit_get_inc()
+ * warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ */
+static inline void *ipz_qeit_get_inc_valid(struct ipz_queue *queue)
+{
+ struct ehca_cqe *cqe = ipz_qeit_get(queue);
+ u32 cqe_flags = cqe->cqe_flags;
+
+ if ((cqe_flags >> 7) != (queue->toggle_state & 1))
+ return NULL;
+
+ ipz_qeit_get_inc(queue);
+ return cqe;
+}
+
+/*
+ * returns and resets Queue Entry iterator
+ * returns address (kv) of first Queue Entry
+ */
+static inline void *ipz_qeit_reset(struct ipz_queue *queue)
+{
+ queue->current_q_offset = 0;
+ return ipz_qeit_get(queue);
+}
+
+/* struct generic page table */
+struct ipz_pt {
+ u64 entries[EHCA_PT_ENTRIES];
+};
+
+/* struct page table for a queue, only to be used in pf */
+struct ipz_qpt {
+ /* queue page tables (kv), use u64 because we know the element length */
+ u64 *qpts;
+ u32 n_qpts;
+ u32 n_ptes; /* number of page table entries */
+ u64 *current_pte_addr;
+};
+
+/*
+ * constructor for a ipz_queue_t, placement new for ipz_queue_t,
+ * new for all dependent datastructors
+ * all QP Tables are the same
+ * flow:
+ * allocate+pin queue
+ * see ipz_qpt_ctor()
+ * returns true if ok, false if out of memory
+ */
+int ipz_queue_ctor(struct ipz_queue *queue, const u32 nr_of_pages,
+ const u32 pagesize, const u32 qe_size,
+ const u32 nr_of_sg);
+
+/*
+ * destructor for a ipz_queue_t
+ * -# free queue
+ * see ipz_queue_ctor()
+ * returns true if ok, false if queue was NULL-ptr of free failed
+ */
+int ipz_queue_dtor(struct ipz_queue *queue);
+
+/*
+ * constructor for a ipz_qpt_t,
+ * placement new for struct ipz_queue, new for all dependent datastructors
+ * all QP Tables are the same,
+ * flow:
+ * -# allocate+pin queue
+ * -# initialise ptcb
+ * -# allocate+pin PTs
+ * -# link PTs to a ring, according to HCA Arch, set bit62 id needed
+ * -# the ring must have room for exactly nr_of_PTEs
+ * see ipz_qpt_ctor()
+ */
+void ipz_qpt_ctor(struct ipz_qpt *qpt,
+ const u32 nr_of_qes,
+ const u32 pagesize,
+ const u32 qe_size,
+ const u8 lowbyte, const u8 toggle,
+ u32 * act_nr_of_QEs, u32 * act_nr_of_pages);
+
+/*
+ * return current Queue Entry, increment Queue Entry iterator by one
+ * step in struct ipz_queue, will wrap in ringbuffer
+ * returns address (kv) of Queue Entry BEFORE increment
+ * warning don't use in parallel with ipz_qpageit_get_inc()
+ * warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ * fix EQ page problems
+ */
+void *ipz_qeit_eq_get_inc(struct ipz_queue *queue);
+
+/*
+ * return current Event Queue Entry, increment Queue Entry iterator
+ * by one step in struct ipz_queue if valid, will wrap in ringbuffer
+ * returns address (kv) of Queue Entry BEFORE increment
+ * returns 0 and does not increment, if wrong valid state
+ * warning don't use in parallel with ipz_queue_QPageit_get_inc()
+ * warning unpredictable results may occur if steps>act_nr_of_queue_entries
+ */
+static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue)
+{
+ void *ret = ipz_qeit_get(queue);
+ u32 qe = *(u8 *) ret;
+ EDEB(7, "ipz_QEit_EQ_get_inc_valid qe=%x", qe);
+ if ((qe >> 7) == (queue->toggle_state & 1))
+ ipz_qeit_eq_get_inc(queue); /* this is a good one */
+ else
+ ret = NULL;
+ return ret;
+}
+
+/* returns address (GX) of first queue entry */
+static inline u64 ipz_qpt_get_firstpage(struct ipz_qpt *qpt)
+{
+ return be64_to_cpu(qpt->qpts[0]);
+}
+
+/* returns address (kv) of first page of queue page table */
+static inline void *ipz_qpt_get_qpt(struct ipz_qpt *qpt)
+{
+ return qpt->qpts;
+}
+
+#endif /* __IPZ_PT_FN_H__ */
--
1.4.1
drivers/infiniband/hw/ehca/ehca_eq.c | 222 ++++++++++++++++++++++++++++++++++
1 files changed, 222 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c
new file mode 100644
index 0000000..080ed02
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_eq.c
@@ -0,0 +1,222 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * Event queue handling
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Khadija Souissi <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Heiko J Schick <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ *
+ *
+ * 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.
+ */
+
+#define DEB_PREFIX "e_eq"
+
+#include "ehca_classes.h"
+#include "ehca_irq.h"
+#include "ehca_iverbs.h"
+#include "ehca_qes.h"
+#include "hcp_if.h"
+#include "ipz_pt_fn.h"
+
+int ehca_create_eq(struct ehca_shca *shca,
+ struct ehca_eq *eq,
+ const enum ehca_eq_type type, const u32 length)
+{
+ u64 ret = H_SUCCESS;
+ u32 nr_pages = 0;
+ u32 i;
+ void *vpage = NULL;
+
+ EDEB_EN(7, "shca=%p eq=%p length=%x", shca, eq, length);
+ EHCA_CHECK_ADR(shca);
+ EHCA_CHECK_ADR(eq);
+
+ spin_lock_init(&eq->spinlock);
+ eq->is_initialized = 0;
+
+ if (type != EHCA_EQ && type != EHCA_NEQ) {
+ EDEB_ERR(4, "Invalid EQ type %x. eq=%p", type, eq);
+ return -EINVAL;
+ }
+ if (length == 0) {
+ EDEB_ERR(4, "EQ length must not be zero. eq=%p", eq);
+ return -EINVAL;
+ }
+
+ ret = hipz_h_alloc_resource_eq(shca->ipz_hca_handle,
+ &eq->pf,
+ type,
+ length,
+ &eq->ipz_eq_handle,
+ &eq->length,
+ &nr_pages, &eq->ist);
+
+ if (ret != H_SUCCESS) {
+ EDEB_ERR(4, "Can't allocate EQ / NEQ. eq=%p", eq);
+ return -EINVAL;
+ }
+
+ ret = ipz_queue_ctor(&eq->ipz_queue, nr_pages,
+ EHCA_PAGESIZE, sizeof(struct ehca_eqe), 0);
+ if (!ret) {
+ EDEB_ERR(4, "Can't allocate EQ pages. eq=%p", eq);
+ goto create_eq_exit1;
+ }
+
+ for (i = 0; i < nr_pages; i++) {
+ u64 rpage;
+
+ if (!(vpage = ipz_qpageit_get_inc(&eq->ipz_queue))) {
+ ret = H_RESOURCE;
+ goto create_eq_exit2;
+ }
+
+ rpage = virt_to_abs(vpage);
+ ret = hipz_h_register_rpage_eq(shca->ipz_hca_handle,
+ eq->ipz_eq_handle,
+ &eq->pf,
+ 0, 0, rpage, 1);
+
+ if (i == (nr_pages - 1)) {
+ /* last page */
+ vpage = ipz_qpageit_get_inc(&eq->ipz_queue);
+ if (ret != H_SUCCESS || vpage)
+ goto create_eq_exit2;
+ } else {
+ if (ret != H_PAGE_REGISTERED || !vpage)
+ goto create_eq_exit2;
+ }
+ }
+
+ ipz_qeit_reset(&eq->ipz_queue);
+
+ /* register interrupt handlers and initialize work queues */
+ if (type == EHCA_EQ) {
+ ret = ibmebus_request_irq(NULL, eq->ist, ehca_interrupt_eq,
+ SA_INTERRUPT, "ehca_eq",
+ (void *)shca);
+ if (ret < 0)
+ EDEB_ERR(4, "Can't map interrupt handler.");
+
+ tasklet_init(&eq->interrupt_task, ehca_tasklet_eq, (long)shca);
+ } else if (type == EHCA_NEQ) {
+ ret = ibmebus_request_irq(NULL, eq->ist, ehca_interrupt_neq,
+ SA_INTERRUPT, "ehca_neq",
+ (void *)shca);
+ if (ret < 0)
+ EDEB_ERR(4, "Can't map interrupt handler.");
+
+ tasklet_init(&eq->interrupt_task, ehca_tasklet_neq, (long)shca);
+ }
+
+ eq->is_initialized = 1;
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return 0;
+
+create_eq_exit2:
+ ipz_queue_dtor(&eq->ipz_queue);
+
+create_eq_exit1:
+ hipz_h_destroy_eq(shca->ipz_hca_handle, eq);
+
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return -EINVAL;
+}
+
+void *ehca_poll_eq(struct ehca_shca *shca, struct ehca_eq *eq)
+{
+ unsigned long flags = 0;
+ void *eqe = NULL;
+
+ EDEB_EN(7, "shca=%p eq=%p", shca, eq);
+ EHCA_CHECK_ADR_P(shca);
+ EHCA_CHECK_EQ_P(eq);
+
+ spin_lock_irqsave(&eq->spinlock, flags);
+ eqe = ipz_eqit_eq_get_inc_valid(&eq->ipz_queue);
+ spin_unlock_irqrestore(&eq->spinlock, flags);
+
+ EDEB_EX(7, "eq=%p eqe=%p", eq, eqe);
+
+ return eqe;
+}
+
+void ehca_poll_eqs(unsigned long data)
+{
+ struct ehca_shca *shca;
+ struct ehca_module *module = (struct ehca_module*)data;
+
+ spin_lock(&module->shca_lock);
+ list_for_each_entry(shca, &module->shca_list, shca_list) {
+ if (shca->eq.is_initialized)
+ ehca_tasklet_eq((unsigned long)(void*)shca);
+ }
+ mod_timer(&module->timer, jiffies + HZ);
+ spin_unlock(&module->shca_lock);
+
+ return;
+}
+
+int ehca_destroy_eq(struct ehca_shca *shca, struct ehca_eq *eq)
+{
+ unsigned long flags = 0;
+ u64 h_ret = H_SUCCESS;
+
+ EDEB_EN(7, "shca=%p eq=%p", shca, eq);
+ EHCA_CHECK_ADR(shca);
+ EHCA_CHECK_EQ(eq);
+
+ spin_lock_irqsave(&eq->spinlock, flags);
+ ibmebus_free_irq(NULL, eq->ist, (void *)shca);
+
+ h_ret = hipz_h_destroy_eq(shca->ipz_hca_handle, eq);
+
+ spin_unlock_irqrestore(&eq->spinlock, flags);
+
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "Can't free EQ resources.");
+ return -EINVAL;
+ }
+ ipz_queue_dtor(&eq->ipz_queue);
+
+ EDEB_EX(7, "h_ret=%lx", h_ret);
+
+ return h_ret;
+}
--
1.4.1
drivers/infiniband/hw/ehca/ehca_qes.h | 259 +++++
drivers/infiniband/hw/ehca/ehca_qp.c | 1594 ++++++++++++++++++++++++++++++++
drivers/infiniband/hw/ehca/ehca_reqs.c | 694 ++++++++++++++
drivers/infiniband/hw/ehca/ehca_sqp.c | 123 ++
4 files changed, 2670 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/ehca_qes.h b/drivers/infiniband/hw/ehca/ehca_qes.h
new file mode 100644
index 0000000..8707d29
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_qes.h
@@ -0,0 +1,259 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * Hardware request structures
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Christoph Raisch <[email protected]>
+ *
+ * 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.
+ */
+
+
+#ifndef _EHCA_QES_H_
+#define _EHCA_QES_H_
+
+#include "ehca_tools.h"
+
+/* virtual scatter gather entry to specify remote adresses with length */
+struct ehca_vsgentry {
+ u64 vaddr;
+ u32 lkey;
+ u32 length;
+};
+
+#define GRH_FLAG_MASK EHCA_BMASK_IBM(7,7)
+#define GRH_IPVERSION_MASK EHCA_BMASK_IBM(0,3)
+#define GRH_TCLASS_MASK EHCA_BMASK_IBM(4,12)
+#define GRH_FLOWLABEL_MASK EHCA_BMASK_IBM(13,31)
+#define GRH_PAYLEN_MASK EHCA_BMASK_IBM(32,47)
+#define GRH_NEXTHEADER_MASK EHCA_BMASK_IBM(48,55)
+#define GRH_HOPLIMIT_MASK EHCA_BMASK_IBM(56,63)
+
+/*
+ * Unreliable Datagram Address Vector Format
+ * see IBTA Vol1 chapter 8.3 Global Routing Header
+ */
+struct ehca_ud_av {
+ u8 sl;
+ u8 lnh;
+ u16 dlid;
+ u8 reserved1;
+ u8 reserved2;
+ u8 reserved3;
+ u8 slid_path_bits;
+ u8 reserved4;
+ u8 ipd;
+ u8 reserved5;
+ u8 pmtu;
+ u32 reserved6;
+ u64 reserved7;
+ union {
+ struct {
+ u64 word_0; /* always set to 6 */
+ /*should be 0x1B for IB transport */
+ u64 word_1;
+ u64 word_2;
+ u64 word_3;
+ u64 word_4;
+ } grh;
+ struct {
+ u32 wd_0;
+ u32 wd_1;
+ /* DWord_1 --> SGID */
+
+ u32 sgid_wd3;
+ u32 sgid_wd2;
+
+ u32 sgid_wd1;
+ u32 sgid_wd0;
+ /* DWord_3 --> DGID */
+
+ u32 dgid_wd3;
+ u32 dgid_wd2;
+
+ u32 dgid_wd1;
+ u32 dgid_wd0;
+ } grh_l;
+ };
+};
+
+/* maximum number of sg entries allowed in a WQE */
+#define MAX_WQE_SG_ENTRIES 252
+
+#define WQE_OPTYPE_SEND 0x80
+#define WQE_OPTYPE_RDMAREAD 0x40
+#define WQE_OPTYPE_RDMAWRITE 0x20
+#define WQE_OPTYPE_CMPSWAP 0x10
+#define WQE_OPTYPE_FETCHADD 0x08
+#define WQE_OPTYPE_BIND 0x04
+
+#define WQE_WRFLAG_REQ_SIGNAL_COM 0x80
+#define WQE_WRFLAG_FENCE 0x40
+#define WQE_WRFLAG_IMM_DATA_PRESENT 0x20
+#define WQE_WRFLAG_SOLIC_EVENT 0x10
+
+#define WQEF_CACHE_HINT 0x80
+#define WQEF_CACHE_HINT_RD_WR 0x40
+#define WQEF_TIMED_WQE 0x20
+#define WQEF_PURGE 0x08
+#define WQEF_HIGH_NIBBLE 0xF0
+
+#define MW_BIND_ACCESSCTRL_R_WRITE 0x40
+#define MW_BIND_ACCESSCTRL_R_READ 0x20
+#define MW_BIND_ACCESSCTRL_R_ATOMIC 0x10
+
+struct ehca_wqe {
+ u64 work_request_id;
+ u8 optype;
+ u8 wr_flag;
+ u16 pkeyi;
+ u8 wqef;
+ u8 nr_of_data_seg;
+ u16 wqe_provided_slid;
+ u32 destination_qp_number;
+ u32 resync_psn_sqp;
+ u32 local_ee_context_qkey;
+ u32 immediate_data;
+ union {
+ struct {
+ u64 remote_virtual_adress;
+ u32 rkey;
+ u32 reserved;
+ u64 atomic_1st_op_dma_len;
+ u64 atomic_2nd_op;
+ struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES];
+
+ } nud;
+ struct {
+ u64 ehca_ud_av_ptr;
+ u64 reserved1;
+ u64 reserved2;
+ u64 reserved3;
+ struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES];
+ } ud_avp;
+ struct {
+ struct ehca_ud_av ud_av;
+ struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES -
+ 2];
+ } ud_av;
+ struct {
+ u64 reserved0;
+ u64 reserved1;
+ u64 reserved2;
+ u64 reserved3;
+ struct ehca_vsgentry sg_list[MAX_WQE_SG_ENTRIES];
+ } all_rcv;
+
+ struct {
+ u64 reserved;
+ u32 rkey;
+ u32 old_rkey;
+ u64 reserved1;
+ u64 reserved2;
+ u64 virtual_address;
+ u32 reserved3;
+ u32 length;
+ u32 reserved4;
+ u16 reserved5;
+ u8 reserved6;
+ u8 lr_ctl;
+ u32 lkey;
+ u32 reserved7;
+ u64 reserved8;
+ u64 reserved9;
+ u64 reserved10;
+ u64 reserved11;
+ } bind;
+ struct {
+ u64 reserved12;
+ u64 reserved13;
+ u32 size;
+ u32 start;
+ } inline_data;
+ } u;
+
+};
+
+#define WC_SEND_RECEIVE EHCA_BMASK_IBM(0,0)
+#define WC_IMM_DATA EHCA_BMASK_IBM(1,1)
+#define WC_GRH_PRESENT EHCA_BMASK_IBM(2,2)
+#define WC_SE_BIT EHCA_BMASK_IBM(3,3)
+#define WC_STATUS_ERROR_BIT 0x80000000
+#define WC_STATUS_REMOTE_ERROR_FLAGS 0x0000F800
+#define WC_STATUS_PURGE_BIT 0x10
+
+struct ehca_cqe {
+ u64 work_request_id;
+ u8 optype;
+ u8 w_completion_flags;
+ u16 reserved1;
+ u32 nr_bytes_transferred;
+ u32 immediate_data;
+ u32 local_qp_number;
+ u8 freed_resource_count;
+ u8 service_level;
+ u16 wqe_count;
+ u32 qp_token;
+ u32 qkey_ee_token;
+ u32 remote_qp_number;
+ u16 dlid;
+ u16 rlid;
+ u16 reserved2;
+ u16 pkey_index;
+ u32 cqe_timestamp;
+ u32 wqe_timestamp;
+ u8 wqe_timestamp_valid;
+ u8 reserved3;
+ u8 reserved4;
+ u8 cqe_flags;
+ u32 status;
+};
+
+struct ehca_eqe {
+ u64 entry;
+};
+
+struct ehca_mrte {
+ u64 starting_va;
+ u64 length; /* length of memory region in bytes*/
+ u32 pd;
+ u8 key_instance;
+ u8 pagesize;
+ u8 mr_control;
+ u8 local_remote_access_ctrl;
+ u8 reserved[0x20 - 0x18];
+ u64 at_pointer[4];
+};
+#endif /*_EHCA_QES_H_*/
diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c
new file mode 100644
index 0000000..011afa6
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_qp.c
@@ -0,0 +1,1594 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * QP functions
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ * Heiko J Schick <[email protected]>
+ *
+ * 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.
+ */
+
+
+#define DEB_PREFIX "e_qp"
+
+#include <asm/current.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"
+
+/*
+ * attributes not supported by query qp
+ */
+#define QP_ATTR_QUERY_NOT_SUPPORTED (IB_QP_MAX_DEST_RD_ATOMIC | \
+ IB_QP_MAX_QP_RD_ATOMIC | \
+ 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:
+ EDEB_ERR(4, "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:
+ EDEB_ERR(4,"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:
+ EDEB_ERR(4,"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:
+ if (ib_fromstate == IB_QPS_RESET)
+ index = IB_QPST_RESET2INIT;
+ else if (ib_fromstate == IB_QPS_INIT)
+ index = IB_QPST_INIT2INIT;
+ break;
+ case IB_QPS_RTR:
+ if (ib_fromstate == IB_QPS_INIT)
+ index = IB_QPST_INIT2RTR;
+ break;
+ case IB_QPS_RTS:
+ if (ib_fromstate == IB_QPS_RTR)
+ index = IB_QPST_RTR2RTS;
+ else if (ib_fromstate == IB_QPS_RTS)
+ index = IB_QPST_RTS2RTS;
+ else if (ib_fromstate == IB_QPS_SQD)
+ index = IB_QPST_SQD2RTS;
+ else if (ib_fromstate == IB_QPS_SQE)
+ index = IB_QPST_SQE2RTS;
+ 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;
+}
+
+enum ehca_service_type {
+ ST_RC = 0,
+ ST_UC = 1,
+ ST_RD = 2,
+ ST_UD = 3
+};
+
+/*
+ * 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_ETY:
+ return -EINVAL;
+ default:
+ EDEB_ERR(4, "Invalid ibqptype=%x", ibqptype);
+ return -EINVAL;
+ }
+}
+
+/*
+ * init_qp_queues initializes/constructs r/squeue and registers queue pages.
+ */
+static inline int init_qp_queues(struct ipz_adapter_handle ipz_hca_handle,
+ struct ehca_qp *my_qp,
+ int nr_sq_pages,
+ int nr_rq_pages,
+ int swqe_size,
+ int rwqe_size,
+ int nr_send_sges, int nr_receive_sges)
+{
+ int ret = -EINVAL;
+ int cnt = 0;
+ void *vpage = NULL;
+ u64 rpage = 0;
+ int ipz_rc = -1;
+ u64 h_ret = H_PARAMETER;
+
+ ipz_rc = ipz_queue_ctor(&my_qp->ipz_squeue,
+ nr_sq_pages,
+ EHCA_PAGESIZE, swqe_size, nr_send_sges);
+ if (!ipz_rc) {
+ EDEB_ERR(4, "Cannot allocate page for squeue. ipz_rc=%x",
+ ipz_rc);
+ ret = -EBUSY;
+ return ret;
+ }
+
+ ipz_rc = ipz_queue_ctor(&my_qp->ipz_rqueue,
+ nr_rq_pages,
+ EHCA_PAGESIZE, rwqe_size, nr_receive_sges);
+ if (!ipz_rc) {
+ EDEB_ERR(4, "Cannot allocate page for rqueue. ipz_rc=%x",
+ ipz_rc);
+ ret = -EBUSY;
+ goto init_qp_queues0;
+ }
+ /* register SQ pages */
+ for (cnt = 0; cnt < nr_sq_pages; cnt++) {
+ vpage = ipz_qpageit_get_inc(&my_qp->ipz_squeue);
+ if (!vpage) {
+ EDEB_ERR(4, "SQ ipz_qpageit_get_inc() "
+ "failed p_vpage= %p", vpage);
+ ret = -EINVAL;
+ goto init_qp_queues1;
+ }
+ rpage = virt_to_abs(vpage);
+
+ h_ret = hipz_h_register_rpage_qp(ipz_hca_handle,
+ my_qp->ipz_qp_handle,
+ &my_qp->pf, 0, 0,
+ rpage, 1,
+ my_qp->galpas.kernel);
+ if (h_ret < H_SUCCESS) {
+ EDEB_ERR(4,"SQ hipz_qp_register_rpage() faield "
+ "rc=%lx", h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto init_qp_queues1;
+ }
+ }
+
+ ipz_qeit_reset(&my_qp->ipz_squeue);
+
+ /* register RQ pages */
+ for (cnt = 0; cnt < nr_rq_pages; cnt++) {
+ vpage = ipz_qpageit_get_inc(&my_qp->ipz_rqueue);
+ if (!vpage) {
+ EDEB_ERR(4,"RQ ipz_qpageit_get_inc() "
+ "failed p_vpage = %p", vpage);
+ h_ret = H_RESOURCE;
+ ret = -EINVAL;
+ goto init_qp_queues1;
+ }
+
+ rpage = virt_to_abs(vpage);
+
+ h_ret = hipz_h_register_rpage_qp(ipz_hca_handle,
+ my_qp->ipz_qp_handle,
+ &my_qp->pf, 0, 1,
+ rpage, 1,my_qp->galpas.kernel);
+ if (h_ret < H_SUCCESS) {
+ EDEB_ERR(4, "RQ hipz_qp_register_rpage() failed "
+ "rc=%lx", h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto init_qp_queues1;
+ }
+ if (cnt == (nr_rq_pages - 1)) { /* last page! */
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4,"RQ hipz_qp_register_rpage() "
+ "h_ret= %lx ", h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto init_qp_queues1;
+ }
+ vpage = ipz_qpageit_get_inc(&my_qp->ipz_rqueue);
+ if (vpage) {
+ EDEB_ERR(4,"ipz_qpageit_get_inc() "
+ "should not succeed vpage=%p",
+ vpage);
+ ret = -EINVAL;
+ goto init_qp_queues1;
+ }
+ } else {
+ if (h_ret != H_PAGE_REGISTERED) {
+ EDEB_ERR(4,"RQ hipz_qp_register_rpage() "
+ "h_ret= %lx ", h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto init_qp_queues1;
+ }
+ }
+ }
+
+ ipz_qeit_reset(&my_qp->ipz_rqueue);
+
+ return 0;
+
+init_qp_queues1:
+ ipz_queue_dtor(&my_qp->ipz_rqueue);
+init_qp_queues0:
+ ipz_queue_dtor(&my_qp->ipz_squeue);
+ return ret;
+}
+
+
+struct ib_qp *ehca_create_qp(struct ib_pd *pd,
+ struct ib_qp_init_attr *init_attr,
+ struct ib_udata *udata)
+{
+ extern struct ehca_module ehca_module;
+ static int da_msg_size[]={ 128, 256, 512, 1024, 2048, 4096 };
+ int ret = -EINVAL;
+
+ struct ehca_qp *my_qp = NULL;
+ struct ehca_pd *my_pd = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ib_ucontext *context = NULL;
+ u64 h_ret = H_PARAMETER;
+ int max_send_sge;
+ int max_recv_sge;
+
+ /* h_call's out parameters */
+ struct ehca_alloc_qp_parms parms;
+ u32 qp_nr = 0, swqe_size = 0, rwqe_size = 0;
+ u8 daqp_completion, isdaqp;
+ unsigned long flags;
+
+ EDEB_EN(7,"pd=%p init_attr=%p", pd, init_attr);
+ EHCA_CHECK_PD_P(pd);
+ EHCA_CHECK_ADR_P(init_attr);
+
+ if (init_attr->sq_sig_type != IB_SIGNAL_REQ_WR &&
+ init_attr->sq_sig_type != IB_SIGNAL_ALL_WR) {
+ EDEB_ERR(4, "init_attr->sg_sig_type=%x not allowed",
+ init_attr->sq_sig_type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* save daqp completion bits */
+ daqp_completion = init_attr->qp_type & 0x60;
+ /* save daqp bit */
+ isdaqp = (init_attr->qp_type & 0x80) ? 1 : 0;
+ init_attr->qp_type = init_attr->qp_type & 0x1F;
+
+ if (init_attr->qp_type != IB_QPT_UD &&
+ init_attr->qp_type != IB_QPT_SMI &&
+ init_attr->qp_type != IB_QPT_GSI &&
+ init_attr->qp_type != IB_QPT_UC &&
+ init_attr->qp_type != IB_QPT_RC) {
+ EDEB_ERR(4,"wrong QP Type=%x",init_attr->qp_type);
+ return ERR_PTR(-EINVAL);
+ }
+ if (init_attr->qp_type != IB_QPT_RC && isdaqp != 0) {
+ EDEB_ERR(4,"unsupported LL QP Type=%x",init_attr->qp_type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (pd->uobject && udata)
+ context = pd->uobject->context;
+
+ my_qp = kmem_cache_alloc(ehca_module.cache_qp, SLAB_KERNEL);
+ if (!my_qp) {
+ EDEB_ERR(4, "pd=%p not enough memory to alloc qp", pd);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memset(my_qp, 0, sizeof(struct ehca_qp));
+ memset (&parms, 0, sizeof(struct ehca_alloc_qp_parms));
+ spin_lock_init(&my_qp->spinlock_s);
+ spin_lock_init(&my_qp->spinlock_r);
+
+ my_pd = container_of(pd, struct ehca_pd, ib_pd);
+
+ shca = container_of(pd->device, struct ehca_shca, ib_device);
+ my_qp->recv_cq =
+ container_of(init_attr->recv_cq, struct ehca_cq, ib_cq);
+ my_qp->send_cq =
+ container_of(init_attr->send_cq, struct ehca_cq, ib_cq);
+
+ my_qp->init_attr = *init_attr;
+
+ do {
+ if (!idr_pre_get(&ehca_qp_idr, GFP_KERNEL)) {
+ ret = -ENOMEM;
+ EDEB_ERR(4, "Can't reserve idr resources.");
+ goto create_qp_exit0;
+ }
+
+ spin_lock_irqsave(&ehca_qp_idr_lock, flags);
+ ret = idr_get_new(&ehca_qp_idr, my_qp, &my_qp->token);
+ spin_unlock_irqrestore(&ehca_qp_idr_lock, flags);
+
+ } while (ret == -EAGAIN);
+
+ if (ret) {
+ ret = -ENOMEM;
+ EDEB_ERR(4, "Can't allocate new idr entry.");
+ goto create_qp_exit0;
+ }
+
+ parms.servicetype = ibqptype2servicetype(init_attr->qp_type);
+ if (parms.servicetype < 0) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "Invalid qp_type=%x", init_attr->qp_type);
+ goto create_qp_exit0;
+ }
+
+ if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR)
+ parms.sigtype = HCALL_SIGT_EVERY;
+ else
+ 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 (IB_QPT_UD == init_attr->qp_type ||
+ IB_QPT_GSI == init_attr->qp_type ||
+ IB_QPT_SMI == init_attr->qp_type) {
+ max_send_sge += 2;
+ max_recv_sge += 2;
+ }
+
+ EDEB(7, "isdaqp=%x daqp_completion=%x", isdaqp, daqp_completion);
+
+ parms.ipz_eq_handle = shca->eq.ipz_eq_handle;
+ parms.daqp_ctrl = isdaqp | daqp_completion;
+ parms.pd = my_pd->fw_pd;
+ parms.max_recv_sge = max_recv_sge;
+ parms.max_send_sge = max_send_sge;
+
+ h_ret = hipz_h_alloc_resource_qp(shca->ipz_hca_handle, my_qp, &parms);
+
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "h_alloc_resource_qp() failed h_ret=%lx", h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto create_qp_exit1;
+ }
+
+ switch (init_attr->qp_type) {
+ case IB_QPT_RC:
+ if (isdaqp == 0) {
+ swqe_size = offsetof(struct ehca_wqe, u.nud.sg_list[
+ (parms.act_nr_send_sges)]);
+ rwqe_size = offsetof(struct ehca_wqe, u.nud.sg_list[
+ (parms.act_nr_recv_sges)]);
+ } else { /* for daqp we need to use msg size, not wqe size */
+ swqe_size = da_msg_size[max_send_sge];
+ rwqe_size = da_msg_size[max_recv_sge];
+ parms.act_nr_send_sges = 1;
+ parms.act_nr_recv_sges = 1;
+ }
+ break;
+ case IB_QPT_UC:
+ swqe_size = offsetof(struct ehca_wqe,
+ u.nud.sg_list[parms.act_nr_send_sges]);
+ rwqe_size = offsetof(struct ehca_wqe,
+ u.nud.sg_list[parms.act_nr_recv_sges]);
+ break;
+
+ case IB_QPT_UD:
+ case IB_QPT_GSI:
+ case IB_QPT_SMI:
+ /* UD circumvention */
+ parms.act_nr_recv_sges -= 2;
+ parms.act_nr_send_sges -= 2;
+ swqe_size = offsetof(struct ehca_wqe,
+ u.ud_av.sg_list[parms.act_nr_send_sges]);
+ rwqe_size = offsetof(struct ehca_wqe,
+ u.ud_av.sg_list[parms.act_nr_recv_sges]);
+
+ if (IB_QPT_GSI == init_attr->qp_type ||
+ IB_QPT_SMI == init_attr->qp_type) {
+ parms.act_nr_send_wqes = init_attr->cap.max_send_wr;
+ parms.act_nr_recv_wqes = init_attr->cap.max_recv_wr;
+ parms.act_nr_send_sges = init_attr->cap.max_send_sge;
+ parms.act_nr_recv_sges = init_attr->cap.max_recv_sge;
+ my_qp->real_qp_num =
+ (init_attr->qp_type == IB_QPT_SMI) ? 0 : 1;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* initializes r/squeue and registers queue pages */
+ ret = init_qp_queues(shca->ipz_hca_handle, my_qp,
+ parms.nr_sq_pages, parms.nr_rq_pages,
+ swqe_size, rwqe_size,
+ parms.act_nr_send_sges, parms.act_nr_recv_sges);
+ if (ret) {
+ EDEB_ERR(4,"Couldn't initialize r/squeue and pages ret=%x",
+ ret);
+ goto create_qp_exit2;
+ }
+
+ 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_num = my_qp->real_qp_num;
+ my_qp->ib_qp.qp_type = init_attr->qp_type;
+
+ my_qp->qp_type = init_attr->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.act_nr_recv_sges;
+ init_attr->cap.max_recv_wr = parms.act_nr_recv_wqes;
+ init_attr->cap.max_send_sge = parms.act_nr_send_sges;
+ init_attr->cap.max_send_wr = parms.act_nr_send_wqes;
+
+ /* NOTE: define_apq0() not supported yet */
+ if (init_attr->qp_type == IB_QPT_GSI) {
+ h_ret = ehca_define_sqp(shca, my_qp, init_attr);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "ehca_define_sqp() failed rc=%lx",h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto create_qp_exit3;
+ }
+ }
+ if (init_attr->send_cq) {
+ struct ehca_cq *cq = container_of(init_attr->send_cq,
+ struct ehca_cq, ib_cq);
+ ret = ehca_cq_assign_qp(cq, my_qp);
+ if (ret) {
+ EDEB_ERR(4, "Couldn't assign qp to send_cq ret=%x",
+ ret);
+ goto create_qp_exit3;
+ }
+ my_qp->send_cq = cq;
+ }
+ /* copy queues, galpa data to user space */
+ if (context && udata) {
+ struct ipz_queue *ipz_rqueue = &my_qp->ipz_rqueue;
+ struct ipz_queue *ipz_squeue = &my_qp->ipz_squeue;
+ struct ehca_create_qp_resp resp;
+ struct vm_area_struct * vma;
+ 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.qkey = my_qp->qkey;
+ resp.real_qp_num = my_qp->real_qp_num;
+ /* rqueue properties */
+ resp.ipz_rqueue.qe_size = ipz_rqueue->qe_size;
+ resp.ipz_rqueue.act_nr_of_sg = ipz_rqueue->act_nr_of_sg;
+ resp.ipz_rqueue.queue_length = ipz_rqueue->queue_length;
+ resp.ipz_rqueue.pagesize = ipz_rqueue->pagesize;
+ resp.ipz_rqueue.toggle_state = ipz_rqueue->toggle_state;
+ ret = ehca_mmap_nopage(((u64)(my_qp->token) << 32) | 0x22000000,
+ ipz_rqueue->queue_length,
+ (void**)&resp.ipz_rqueue.queue,
+ &vma);
+ if (ret) {
+ EDEB_ERR(4, "Could not mmap rqueue pages");
+ goto create_qp_exit3;
+ }
+ my_qp->uspace_rqueue = resp.ipz_rqueue.queue;
+ /* squeue properties */
+ resp.ipz_squeue.qe_size = ipz_squeue->qe_size;
+ resp.ipz_squeue.act_nr_of_sg = ipz_squeue->act_nr_of_sg;
+ resp.ipz_squeue.queue_length = ipz_squeue->queue_length;
+ resp.ipz_squeue.pagesize = ipz_squeue->pagesize;
+ resp.ipz_squeue.toggle_state = ipz_squeue->toggle_state;
+ ret = ehca_mmap_nopage(((u64)(my_qp->token) << 32) | 0x23000000,
+ ipz_squeue->queue_length,
+ (void**)&resp.ipz_squeue.queue,
+ &vma);
+ if (ret) {
+ EDEB_ERR(4, "Could not mmap squeue pages");
+ goto create_qp_exit4;
+ }
+ my_qp->uspace_squeue = resp.ipz_squeue.queue;
+ /* fw_handle */
+ resp.galpas = my_qp->galpas;
+ ret = ehca_mmap_register(my_qp->galpas.user.fw_handle,
+ (void**)&resp.galpas.kernel.fw_handle,
+ &vma);
+ if (ret) {
+ EDEB_ERR(4, "Could not mmap fw_handle");
+ goto create_qp_exit5;
+ }
+ my_qp->uspace_fwh = (u64)resp.galpas.kernel.fw_handle;
+
+ if (ib_copy_to_udata(udata, &resp, sizeof resp)) {
+ EDEB_ERR(4, "Copy to udata failed");
+ ret = -EINVAL;
+ goto create_qp_exit6;
+ }
+ }
+
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x, token=%x",
+ my_qp, qp_nr, my_qp->token);
+ return &my_qp->ib_qp;
+
+create_qp_exit6:
+ ehca_munmap(my_qp->uspace_fwh, EHCA_PAGESIZE);
+
+create_qp_exit5:
+ ehca_munmap(my_qp->uspace_squeue, my_qp->ipz_squeue.queue_length);
+
+create_qp_exit4:
+ ehca_munmap(my_qp->uspace_rqueue, my_qp->ipz_rqueue.queue_length);
+
+create_qp_exit3:
+ ipz_queue_dtor(&my_qp->ipz_rqueue);
+ ipz_queue_dtor(&my_qp->ipz_squeue);
+
+create_qp_exit2:
+ hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp);
+
+create_qp_exit1:
+ spin_lock_irqsave(&ehca_qp_idr_lock, flags);
+ idr_remove(&ehca_qp_idr, my_qp->token);
+ spin_unlock_irqrestore(&ehca_qp_idr_lock, flags);
+
+create_qp_exit0:
+ kmem_cache_free(ehca_module.cache_qp, my_qp);
+ EDEB_EX(4, "failed ret=%x", ret);
+ return ERR_PTR(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)
+{
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ struct ipz_queue *squeue = NULL;
+ void *bad_send_wqe_p = NULL;
+ void *bad_send_wqe_v = NULL;
+ void *squeue_start_p = NULL;
+ void *squeue_end_p = NULL;
+ void *squeue_start_v = NULL;
+ void *squeue_end_v = NULL;
+ struct ehca_wqe *wqe = NULL;
+ int qp_num = my_qp->ib_qp.qp_num;
+
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x ", my_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) {
+ EDEB_ERR(4, "hipz_h_disable_and_get_wqe() failed "
+ "ehca_qp=%p qp_num=%x h_ret=%lx",my_qp, qp_num, h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto prepare_sqe_rts_exit1;
+ }
+ bad_send_wqe_p = (void*)((u64)bad_send_wqe_p & (~(1L<<63)));
+ EDEB(7, "qp_num=%x bad_send_wqe_p=%p", qp_num, bad_send_wqe_p);
+ /* convert wqe pointer to vadr */
+ bad_send_wqe_v = abs_to_virt((u64)bad_send_wqe_p);
+ EDEB_DMP(6, bad_send_wqe_v, 32, "qp_num=%x bad_wqe", qp_num);
+ squeue = &my_qp->ipz_squeue;
+ squeue_start_p = (void*)virt_to_abs(ipz_qeit_calc(squeue, 0L));
+ squeue_end_p = squeue_start_p+squeue->queue_length;
+ squeue_start_v = abs_to_virt((u64)squeue_start_p);
+ squeue_end_v = abs_to_virt((u64)squeue_end_p);
+ EDEB(6, "qp_num=%x squeue_start_v=%p squeue_end_v=%p",
+ qp_num, squeue_start_v, squeue_end_v);
+
+ /* loop sets wqe's purge bit */
+ wqe = (struct ehca_wqe*)bad_send_wqe_v;
+ *bad_wqe_cnt = 0;
+ while (wqe->optype != 0xff && wqe->wqef != 0xff) {
+ EDEB_DMP(6, 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 */
+ wqe = (struct ehca_wqe*)((u8*)wqe+squeue->qe_size);
+ *bad_wqe_cnt = (*bad_wqe_cnt)+1;
+ if ((void*)wqe >= squeue_end_v) {
+ wqe = squeue_start_v;
+ }
+ }
+ /*
+ * bad wqe will be reprocessed and ignored when pol_cq() is called,
+ * i.e. nr of wqes with flush error status is one less
+ */
+ EDEB(6, "qp_num=%x flusherr_wqe_cnt=%x", qp_num, (*bad_wqe_cnt)-1);
+ wqe->wqef = 0;
+
+prepare_sqe_rts_exit1:
+
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x", my_qp, qp_num, ret);
+ return ret;
+}
+
+/*
+ * 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 = 0, qp_new_state = 0;
+ int cnt = 0, qp_attr_idx = 0, ret = 0;
+
+ enum ib_qp_statetrans statetrans;
+ struct hcp_modify_qp_control_block *mqpcb = NULL;
+ struct ehca_qp *my_qp = NULL;
+ struct ehca_shca *shca = NULL;
+ u64 update_mask = 0;
+ u64 h_ret = H_SUCCESS;
+ int bad_wqe_cnt = 0;
+ int squeue_locked = 0;
+ unsigned long spl_flags = 0;
+
+ my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+ shca = container_of(ibqp->pd->device, struct ehca_shca, ib_device);
+
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x ibqp_type=%x "
+ "new qp_state=%x attribute_mask=%x",
+ my_qp, ibqp->qp_num, ibqp->qp_type,
+ attr->qp_state, attr_mask);
+
+ /* do query_qp to obtain current attr values */
+ mqpcb = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL);
+ if (mqpcb == NULL) {
+ ret = -ENOMEM;
+ EDEB_ERR(4, "Could not get zeroed page for mqpcb "
+ "ehca_qp=%p qp_num=%x ", my_qp, ibqp->qp_num);
+ goto modify_qp_exit0;
+ }
+
+ 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) {
+ EDEB_ERR(4, "hipz_h_query_qp() failed "
+ "ehca_qp=%p qp_num=%x h_ret=%lx",
+ my_qp, ibqp->qp_num, h_ret);
+ ret = ehca2ib_return_code(h_ret);
+ goto modify_qp_exit1;
+ }
+ EDEB(7, "ehca_qp=%p qp_num=%x ehca_qp_state=%x",
+ my_qp, ibqp->qp_num, mqpcb->qp_state);
+
+ qp_cur_state = ehca2ib_qp_state(mqpcb->qp_state);
+
+ if (qp_cur_state == -EINVAL) { /* invalid qp state */
+ ret = -EINVAL;
+ EDEB_ERR(4, "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) {
+ EDEB_ERR(4, "SMI RESET -> INIT failed. "
+ "ehca_modify_qp() rc=%x", smirc);
+ ret = H_PARAMETER;
+ goto modify_qp_exit1;
+ }
+ qp_cur_state = IB_QPS_INIT;
+ EDEB(7, "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;
+ EDEB_ERR(4, "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;
+ }
+
+ EDEB(7, "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)) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "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;
+ }
+
+ if ((mqpcb->qp_state = ib2ehca_qp_state(qp_new_state)))
+ update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_STATE, 1);
+ else {
+ ret = -EINVAL;
+ EDEB_ERR(4, "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;
+ EDEB_ERR(4, "<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;
+ EDEB_ERR(4, "Invalid QP type=%x ehca_qp=%p qp_num=%x",
+ ibqp->qp_type, my_qp, ibqp->qp_num);
+ goto modify_qp_exit1;
+ }
+
+ EDEB(7, "ehca_qp=%p qp_num=%x <VALID STATE CHANGE> qp_state_xsit=%x",
+ my_qp, ibqp->qp_num, statetrans);
+
+ /* 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 (my_qp->uspace_squeue == 0) {
+ struct ehca_wqe *wqe = NULL;
+ /* lock send queue */
+ spin_lock_irqsave(&my_qp->spinlock_s, spl_flags);
+ squeue_locked = 1;
+ /* mark next free wqe */
+ wqe = (struct ehca_wqe*)
+ ipz_qeit_get(&my_qp->ipz_squeue);
+ wqe->optype = wqe->wqef = 0xff;
+ EDEB(7, "qp_num=%x next_free_wqe=%p",
+ ibqp->qp_num, wqe);
+ }
+ ret = prepare_sqe_rts(my_qp, shca, &bad_wqe_cnt);
+ if (ret) {
+ EDEB_ERR(4, "prepare_sqe_rts() failed "
+ "ehca_qp=%p qp_num=%x ret=%x",
+ 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) {
+ mqpcb->prim_p_key_idx = attr->pkey_index;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_P_KEY_IDX, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "IB_QP_PKEY_INDEX update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_PORT) {
+ if (attr->port_num < 1 || attr->port_num > shca->num_ports) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "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;
+ }
+ mqpcb->prim_phys_port = attr->port_num;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PRIM_PHYS_PORT, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_PORT update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_QKEY) {
+ mqpcb->qkey = attr->qkey;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_QKEY, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_QKEY update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_AV) {
+ int ah_mult = ib_rate_to_mult(attr->ah_attr.static_rate);
+ int ehca_mult = ib_rate_to_mult(shca->sport[my_qp->
+ init_attr.port_num].rate);
+
+ 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 (ah_mult < ehca_mult)
+ mqpcb->max_static_rate = (ah_mult > 0) ?
+ ((ehca_mult - 1) / ah_mult) : 0;
+ else
+ mqpcb->max_static_rate = 0;
+
+ EDEB(7, " ipd=mqpcb->max_static_rate set %x "
+ " ah_mult=%x ehca_mult=%x "
+ " attr->ah_attr.static_rate=%x",
+ mqpcb->max_static_rate,ah_mult,ehca_mult,
+ attr->ah_attr.static_rate);
+
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_MAX_STATIC_RATE, 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 << 31;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_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);
+ }
+
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_AV update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+
+ if (attr_mask & IB_QP_PATH_MTU) {
+ mqpcb->path_mtu = attr->path_mtu;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_PATH_MTU, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_PATH_MTU update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_TIMEOUT) {
+ mqpcb->timeout = attr->timeout;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_TIMEOUT, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_TIMEOUT update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_RETRY_CNT) {
+ mqpcb->retry_count = attr->retry_cnt;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RETRY_COUNT, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_RETRY_CNT update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ 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);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_RNR_RETRY update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_RQ_PSN) {
+ mqpcb->receive_psn = attr->rq_psn;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_RECEIVE_PSN, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_RQ_PSN update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+ 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; /* max is 2 */
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_MAX_DEST_RD_ATOMIC "
+ "update_mask=%lx", my_qp, ibqp->qp_num, update_mask);
+ }
+ 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);
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_MAX_QP_RD_ATOMIC "
+ "update_mask=%lx", my_qp, ibqp->qp_num, update_mask);
+ }
+ if (attr_mask & IB_QP_ALT_PATH) {
+ int ah_mult = ib_rate_to_mult(attr->alt_ah_attr.static_rate);
+ int ehca_mult = ib_rate_to_mult(
+ shca->sport[my_qp->init_attr.port_num].rate);
+
+ mqpcb->dlid_al = attr->alt_ah_attr.dlid;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_DLID_AL, 1);
+ mqpcb->source_path_bits_al = attr->alt_ah_attr.src_path_bits;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_SOURCE_PATH_BITS_AL, 1);
+ mqpcb->service_level_al = attr->alt_ah_attr.sl;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SERVICE_LEVEL_AL, 1);
+
+ if (ah_mult < ehca_mult)
+ mqpcb->max_static_rate = (ah_mult > 0) ?
+ ((ehca_mult - 1) / ah_mult) : 0;
+ else
+ mqpcb->max_static_rate_al = 0;
+
+ EDEB(7, " ipd=mqpcb->max_static_rate set %x,"
+ " ah_mult=%x ehca_mult=%x",
+ mqpcb->max_static_rate,ah_mult,ehca_mult);
+
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_MAX_STATIC_RATE_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 << 31;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_SEND_GRH_FLAG_AL, 1);
+ mqpcb->source_gid_idx_al =
+ attr->alt_ah_attr.grh.sgid_index;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_SOURCE_GID_IDX_AL, 1);
+
+ for (cnt = 0; cnt < 16; cnt++)
+ mqpcb->dest_gid_al.byte[cnt] =
+ attr->alt_ah_attr.grh.dgid.raw[cnt];
+
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_DEST_GID_AL, 1);
+ mqpcb->flow_label_al = attr->alt_ah_attr.grh.flow_label;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_FLOW_LABEL_AL, 1);
+ mqpcb->hop_limit_al = attr->alt_ah_attr.grh.hop_limit;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_HOP_LIMIT_AL, 1);
+ mqpcb->traffic_class_al =
+ attr->alt_ah_attr.grh.traffic_class;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_TRAFFIC_CLASS_AL, 1);
+ }
+
+ EDEB(7, "ehca_qp=%p qp_num=%x IB_QP_ALT_PATH update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+
+ 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);
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "IB_QP_MIN_RNR_TIMER update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+
+ if (attr_mask & IB_QP_SQ_PSN) {
+ mqpcb->send_psn = attr->sq_psn;
+ update_mask |= EHCA_BMASK_SET(MQPCB_MASK_SEND_PSN, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "IB_QP_SQ_PSN update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+
+ 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);
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "IB_QP_DEST_QPN update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ }
+
+ if (attr_mask & IB_QP_PATH_MIG_STATE) {
+ mqpcb->path_migration_state = attr->path_mig_state;
+ update_mask |=
+ EHCA_BMASK_SET(MQPCB_MASK_PATH_MIGRATION_STATE, 1);
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "IB_QP_PATH_MIG_STATE update_mask=%lx", my_qp,
+ ibqp->qp_num, update_mask);
+ }
+
+ 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);
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "IB_QP_CAP update_mask=%lx",
+ my_qp, ibqp->qp_num, update_mask);
+ /* no support for max_send/recv_sge yet */
+ }
+
+ EDEB_DMP(7, mqpcb, 4*70, "ehca_qp=%p qp_num=%x", my_qp, 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);
+ EDEB_ERR(4, "hipz_h_modify_qp() failed rc=%lx "
+ "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);
+ EDEB(6, "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);
+
+ EDEB(7, "ehca_qp=%p qp_num=%x "
+ "RESET_2_INIT needs an additional enable "
+ "-> update_mask=%lx", my_qp, ibqp->qp_num, update_mask);
+
+ 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);
+ EDEB_ERR(4, "ENABLE in context of "
+ "RESET_2_INIT failed! "
+ "Maybe you didn't get a LID"
+ "h_ret=%lx ehca_qp=%p qp_num=%x",
+ h_ret, my_qp, ibqp->qp_num);
+ goto modify_qp_exit2;
+ }
+ }
+
+ if (statetrans == IB_QPST_ANY2RESET) {
+ ipz_qeit_reset(&my_qp->ipz_rqueue);
+ ipz_qeit_reset(&my_qp->ipz_squeue);
+ }
+
+ 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, spl_flags);
+ my_qp->sqerr_purgeflag = 1;
+ }
+
+modify_qp_exit1:
+ kfree(mqpcb);
+
+modify_qp_exit0:
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x ibqp_type=%x ret=%x",
+ my_qp, ibqp->qp_num, ibqp->qp_type, ret);
+ return ret;
+}
+
+int ehca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask)
+{
+ int ret = 0;
+ struct ehca_qp *my_qp = NULL;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+
+ EHCA_CHECK_ADR(ibqp);
+ EHCA_CHECK_ADR(attr);
+ EHCA_CHECK_ADR(ibqp->device);
+
+ my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x ibqp_type=%x attr_mask=%x",
+ my_qp, ibqp->qp_num, ibqp->qp_type, attr_mask);
+
+ my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ ret = -EINVAL;
+ } else
+ ret = internal_modify_qp(ibqp, attr, attr_mask, 0);
+
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x ibqp_type=%x ret=%x",
+ my_qp, ibqp->qp_num, ibqp->qp_type, ret);
+ return ret;
+}
+
+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 = NULL;
+ struct ehca_shca *shca = NULL;
+ struct hcp_modify_qp_control_block *qpcb = NULL;
+ struct ipz_adapter_handle adapter_handle;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+ int cnt = 0, ret = 0;
+ u64 h_ret = H_SUCCESS;
+
+ EHCA_CHECK_ADR(qp);
+ EHCA_CHECK_ADR(qp_attr);
+ EHCA_CHECK_DEVICE(qp->device);
+
+ my_qp = container_of(qp, struct ehca_qp, ib_qp);
+
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x "
+ "qp_attr=%p qp_attr_mask=%x qp_init_attr=%p",
+ my_qp, qp->qp_num, qp_attr, qp_attr_mask, qp_init_attr);
+
+ my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ ret = -EINVAL;
+ goto query_qp_exit0;
+ }
+
+ shca = container_of(qp->device, struct ehca_shca, ib_device);
+ adapter_handle = shca->ipz_hca_handle;
+
+ if (qp_attr_mask & QP_ATTR_QUERY_NOT_SUPPORTED) {
+ ret = -EINVAL;
+ EDEB_ERR(4,"Invalid attribute mask "
+ "ehca_qp=%p qp_num=%x qp_attr_mask=%x ",
+ my_qp, qp->qp_num, qp_attr_mask);
+ goto query_qp_exit0;
+ }
+
+ qpcb = kzalloc(H_CB_ALIGNMENT, GFP_KERNEL );
+ if (!qpcb) {
+ ret = -ENOMEM;
+ EDEB_ERR(4,"Out of memory for qpcb "
+ "ehca_qp=%p qp_num=%x", my_qp, qp->qp_num);
+ goto query_qp_exit0;
+ }
+
+ 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);
+ EDEB_ERR(4,"hipz_h_query_qp() failed "
+ "ehca_qp=%p qp_num=%x h_ret=%lx",
+ 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;
+ EDEB_ERR(4,"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;
+ 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 =
+ EHCA_BMASK_GET(MQPCB_PRIM_P_KEY_IDX, qpcb->prim_p_key_idx);
+
+ qp_attr->port_num =
+ EHCA_BMASK_GET(MQPCB_PRIM_PHYS_PORT, 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 =
+ EHCA_BMASK_GET(MQPCB_PRIM_P_KEY_IDX, qpcb->alt_p_key_idx);
+
+ qp_attr->alt_port_num = qpcb->alt_phys_port;
+ qp_attr->alt_timeout = qpcb->timeout_al;
+
+ /* 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;
+
+ EDEB(7, "ehca_qp=%p qp_number=%x dest_qp_number=%x "
+ "dlid=%x path_mtu=%x dest_gid=%lx_%lx "
+ "service_level=%x qp_state=%x",
+ my_qp, qpcb->qp_number, qpcb->dest_qp_nr,
+ qpcb->dlid, qpcb->path_mtu,
+ qpcb->dest_gid.dw[0], qpcb->dest_gid.dw[1],
+ qpcb->service_level, qpcb->qp_state);
+
+ EDEB_DMP(7, qpcb, 4*70, "ehca_qp=%p qp_num=%x", my_qp, qp->qp_num);
+
+query_qp_exit1:
+ kfree(qpcb);
+
+query_qp_exit0:
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x",
+ my_qp, qp->qp_num, ret);
+ return ret;
+}
+
+int ehca_destroy_qp(struct ib_qp *ibqp)
+{
+ extern struct ehca_module ehca_module;
+ struct ehca_qp *my_qp = NULL;
+ struct ehca_shca *shca = NULL;
+ struct ehca_pfqp *qp_pf = NULL;
+ struct ehca_pd *my_pd = NULL;
+ u32 cur_pid = current->tgid;
+ u32 qp_num = 0;
+ int ret = 0;
+ u64 h_ret = H_SUCCESS;
+ u8 port_num = 0;
+ enum ib_qp_type qp_type;
+ unsigned long flags;
+
+ EHCA_CHECK_ADR(ibqp);
+
+ my_qp = container_of(ibqp, struct ehca_qp, ib_qp);
+ qp_num = ibqp->qp_num;
+ qp_pf = &my_qp->pf;
+
+ shca = container_of(ibqp->device, struct ehca_shca, ib_device);
+
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x", my_qp, ibqp->qp_num);
+
+ my_pd = container_of(my_qp->ib_qp.pd, struct ehca_pd, ib_pd);
+ if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context &&
+ my_pd->ownpid != cur_pid) {
+ EDEB_ERR(4, "Invalid caller pid=%x ownpid=%x",
+ cur_pid, my_pd->ownpid);
+ return -EINVAL;
+ }
+
+ if (my_qp->send_cq) {
+ ret = ehca_cq_unassign_qp(my_qp->send_cq,
+ my_qp->real_qp_num);
+ if (ret) {
+ EDEB_ERR(4, "Couldn't unassign qp from send_cq "
+ "ret=%x qp_num=%x cq_num=%x",
+ ret, my_qp->ib_qp.qp_num,
+ my_qp->send_cq->cq_number);
+ goto destroy_qp_exit0;
+ }
+ }
+
+ spin_lock_irqsave(&ehca_qp_idr_lock, flags);
+ idr_remove(&ehca_qp_idr, my_qp->token);
+ spin_unlock_irqrestore(&ehca_qp_idr_lock, flags);
+
+ /* un-mmap if vma alloc */
+ if (my_qp->uspace_rqueue) {
+ ret = ehca_munmap(my_qp->uspace_rqueue,
+ my_qp->ipz_rqueue.queue_length);
+ ret = ehca_munmap(my_qp->uspace_squeue,
+ my_qp->ipz_squeue.queue_length);
+ ret = ehca_munmap(my_qp->uspace_fwh, EHCA_PAGESIZE);
+ }
+
+ h_ret = hipz_h_destroy_qp(shca->ipz_hca_handle, my_qp);
+ if (h_ret != H_SUCCESS) {
+ EDEB_ERR(4, "hipz_h_destroy_qp() failed "
+ "rc=%lx ehca_qp=%p qp_num=%x",
+ h_ret, qp_pf, qp_num);
+ goto destroy_qp_exit0;
+ }
+
+ port_num = my_qp->init_attr.port_num;
+ qp_type = my_qp->init_attr.qp_type;
+
+ /* no support for IB_QPT_SMI yet */
+ if (qp_type == IB_QPT_GSI) {
+ struct ib_event event;
+
+ EDEB(4, "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);
+ }
+
+ ipz_queue_dtor(&my_qp->ipz_rqueue);
+ ipz_queue_dtor(&my_qp->ipz_squeue);
+ kmem_cache_free(ehca_module.cache_qp, my_qp);
+
+destroy_qp_exit0:
+ ret = ehca2ib_return_code(h_ret);
+ EDEB_EX(7,"ret=%x", ret);
+ return ret;
+}
diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/infiniband/hw/ehca/ehca_reqs.c
new file mode 100644
index 0000000..f1afb94
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_reqs.c
@@ -0,0 +1,694 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * post_send/recv, poll_cq, req_notify
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ *
+ * 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.
+ */
+
+
+#define DEB_PREFIX "reqs"
+
+#include <asm-powerpc/system.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 inline int ehca_write_rwqe(struct ipz_queue *ipz_rqueue,
+ struct ehca_wqe *wqe_p,
+ struct ib_recv_wr *recv_wr)
+{
+ u8 cnt_ds;
+ if (unlikely((recv_wr->num_sge < 0) ||
+ (recv_wr->num_sge > ipz_rqueue->act_nr_of_sg))) {
+ EDEB_ERR(4, "Invalid number of WQE SGE. "
+ "num_sqe=%x max_nr_of_sg=%x",
+ recv_wr->num_sge, ipz_rqueue->act_nr_of_sg);
+ return -EINVAL; /* invalid SG list length */
+ }
+
+ /* clear wqe header until sglist */
+ memset(wqe_p, 0, offsetof(struct ehca_wqe, u.ud_av.sg_list));
+
+ wqe_p->work_request_id = recv_wr->wr_id;
+ wqe_p->nr_of_data_seg = recv_wr->num_sge;
+
+ for (cnt_ds = 0; cnt_ds < recv_wr->num_sge; cnt_ds++) {
+ wqe_p->u.all_rcv.sg_list[cnt_ds].vaddr =
+ recv_wr->sg_list[cnt_ds].addr;
+ wqe_p->u.all_rcv.sg_list[cnt_ds].lkey =
+ recv_wr->sg_list[cnt_ds].lkey;
+ wqe_p->u.all_rcv.sg_list[cnt_ds].length =
+ recv_wr->sg_list[cnt_ds].length;
+ }
+
+ if (IS_EDEB_ON(7)) {
+ EDEB(7, "RECEIVE WQE written into ipz_rqueue=%p", ipz_rqueue);
+ EDEB_DMP(7, wqe_p, 16*(6 + wqe_p->nr_of_data_seg), "recv wqe");
+ }
+
+ return 0;
+}
+
+#if defined(DEBUG_GSI_SEND_WR)
+
+/* need ib_mad struct */
+#include <rdma/ib_mad.h>
+
+static void trace_send_wr_ud(const struct ib_send_wr *send_wr)
+{
+ int idx = 0;
+ int j = 0;
+ while (send_wr) {
+ struct ib_mad_hdr *mad_hdr = send_wr->wr.ud.mad_hdr;
+ struct ib_sge *sge = send_wr->sg_list;
+ EDEB(4, "send_wr#%x wr_id=%lx num_sge=%x "
+ "send_flags=%x opcode=%x",idx, send_wr->wr_id,
+ send_wr->num_sge, send_wr->send_flags, send_wr->opcode);
+ if (mad_hdr) {
+ EDEB(4, "send_wr#%x mad_hdr base_version=%x "
+ "mgmt_class=%x class_version=%x method=%x "
+ "status=%x class_specific=%x tid=%lx attr_id=%x "
+ "resv=%x attr_mod=%x",
+ idx, mad_hdr->base_version, mad_hdr->mgmt_class,
+ mad_hdr->class_version, mad_hdr->method,
+ mad_hdr->status, mad_hdr->class_specific,
+ mad_hdr->tid, mad_hdr->attr_id, mad_hdr->resv,
+ mad_hdr->attr_mod);
+ }
+ for (j = 0; j < send_wr->num_sge; j++) {
+ u8 *data = (u8 *) abs_to_virt(sge->addr);
+ EDEB(4, "send_wr#%x sge#%x addr=%p length=%x lkey=%x",
+ idx, j, data, sge->length, sge->lkey);
+ /* assume length is n*16 */
+ EDEB_DMP(4, data, sge->length, "send_wr#%x sge#%x",
+ idx, j);
+ sge++;
+ } /* eof for j */
+ idx++;
+ send_wr = send_wr->next;
+ } /* eof while send_wr */
+}
+
+#endif /* DEBUG_GSI_SEND_WR */
+
+static inline int ehca_write_swqe(struct ehca_qp *qp,
+ struct ehca_wqe *wqe_p,
+ const struct ib_send_wr *send_wr)
+{
+ u32 idx;
+ u64 dma_length;
+ struct ehca_av *my_av;
+ u32 remote_qkey = send_wr->wr.ud.remote_qkey;
+
+ if (unlikely((send_wr->num_sge < 0) ||
+ (send_wr->num_sge > qp->ipz_squeue.act_nr_of_sg))) {
+ EDEB_ERR(4, "Invalid number of WQE SGE. "
+ "num_sqe=%x max_nr_of_sg=%x",
+ send_wr->num_sge, qp->ipz_squeue.act_nr_of_sg);
+ return -EINVAL; /* invalid SG list length */
+ }
+
+ /* clear wqe header until sglist */
+ memset(wqe_p, 0, offsetof(struct ehca_wqe, u.ud_av.sg_list));
+
+ wqe_p->work_request_id = send_wr->wr_id;
+
+ switch (send_wr->opcode) {
+ case IB_WR_SEND:
+ case IB_WR_SEND_WITH_IMM:
+ wqe_p->optype = WQE_OPTYPE_SEND;
+ break;
+ case IB_WR_RDMA_WRITE:
+ case IB_WR_RDMA_WRITE_WITH_IMM:
+ wqe_p->optype = WQE_OPTYPE_RDMAWRITE;
+ break;
+ case IB_WR_RDMA_READ:
+ wqe_p->optype = WQE_OPTYPE_RDMAREAD;
+ break;
+ default:
+ EDEB_ERR(4, "Invalid opcode=%x", send_wr->opcode);
+ return -EINVAL; /* invalid opcode */
+ }
+
+ wqe_p->wqef = (send_wr->opcode) & WQEF_HIGH_NIBBLE;
+
+ wqe_p->wr_flag = 0;
+
+ if (send_wr->send_flags & IB_SEND_SIGNALED)
+ wqe_p->wr_flag |= WQE_WRFLAG_REQ_SIGNAL_COM;
+
+ if (send_wr->opcode == IB_WR_SEND_WITH_IMM ||
+ send_wr->opcode == IB_WR_RDMA_WRITE_WITH_IMM) {
+ /* this might not work as long as HW does not support it */
+ wqe_p->immediate_data = be32_to_cpu(send_wr->imm_data);
+ wqe_p->wr_flag |= WQE_WRFLAG_IMM_DATA_PRESENT;
+ }
+
+ wqe_p->nr_of_data_seg = send_wr->num_sge;
+
+ switch (qp->qp_type) {
+ case IB_QPT_SMI:
+ case IB_QPT_GSI:
+ /* no break is intential here */
+ case IB_QPT_UD:
+ /* IB 1.2 spec C10-15 compliance */
+ if (send_wr->wr.ud.remote_qkey & 0x80000000)
+ remote_qkey = qp->qkey;
+
+ wqe_p->destination_qp_number = send_wr->wr.ud.remote_qpn << 8;
+ wqe_p->local_ee_context_qkey = remote_qkey;
+ if (!send_wr->wr.ud.ah) {
+ EDEB_ERR(4, "wr.ud.ah is NULL. qp=%p", qp);
+ return -EINVAL;
+ }
+ my_av = container_of(send_wr->wr.ud.ah, struct ehca_av, ib_ah);
+ wqe_p->u.ud_av.ud_av = my_av->av;
+
+ /*
+ * omitted check of IB_SEND_INLINE
+ * since HW does not support it
+ */
+ for (idx = 0; idx < send_wr->num_sge; idx++) {
+ wqe_p->u.ud_av.sg_list[idx].vaddr =
+ send_wr->sg_list[idx].addr;
+ wqe_p->u.ud_av.sg_list[idx].lkey =
+ send_wr->sg_list[idx].lkey;
+ wqe_p->u.ud_av.sg_list[idx].length =
+ send_wr->sg_list[idx].length;
+ } /* eof for idx */
+ if (qp->qp_type == IB_QPT_SMI ||
+ qp->qp_type == IB_QPT_GSI)
+ wqe_p->u.ud_av.ud_av.pmtu = 1;
+ if (qp->qp_type == IB_QPT_GSI) {
+ wqe_p->pkeyi = send_wr->wr.ud.pkey_index;
+#ifdef DEBUG_GSI_SEND_WR
+ trace_send_wr_ud(send_wr);
+#endif /* DEBUG_GSI_SEND_WR */
+ }
+ break;
+
+ case IB_QPT_UC:
+ if (send_wr->send_flags & IB_SEND_FENCE)
+ wqe_p->wr_flag |= WQE_WRFLAG_FENCE;
+ /* no break is intentional here */
+ case IB_QPT_RC:
+ /* TODO: atomic not implemented */
+ wqe_p->u.nud.remote_virtual_adress =
+ send_wr->wr.rdma.remote_addr;
+ wqe_p->u.nud.rkey = send_wr->wr.rdma.rkey;
+
+ /*
+ * omitted checking of IB_SEND_INLINE
+ * since HW does not support it
+ */
+ dma_length = 0;
+ for (idx = 0; idx < send_wr->num_sge; idx++) {
+ wqe_p->u.nud.sg_list[idx].vaddr =
+ send_wr->sg_list[idx].addr;
+ wqe_p->u.nud.sg_list[idx].lkey =
+ send_wr->sg_list[idx].lkey;
+ wqe_p->u.nud.sg_list[idx].length =
+ send_wr->sg_list[idx].length;
+ dma_length += send_wr->sg_list[idx].length;
+ } /* eof idx */
+ wqe_p->u.nud.atomic_1st_op_dma_len = dma_length;
+
+ break;
+
+ default:
+ EDEB_ERR(4, "Invalid qptype=%x", qp->qp_type);
+ return -EINVAL;
+ }
+
+ if (IS_EDEB_ON(7)) {
+ EDEB(7, "SEND WQE written into queue qp=%p ", qp);
+ EDEB_DMP(7, wqe_p, 16*(6 + wqe_p->nr_of_data_seg), "send wqe");
+ }
+ return 0;
+}
+
+/* map_ib_wc_status converts raw cqe_status to ib_wc_status */
+static inline void map_ib_wc_status(u32 cqe_status,
+ enum ib_wc_status *wc_status)
+{
+ if (unlikely(cqe_status & WC_STATUS_ERROR_BIT)) {
+ switch (cqe_status & 0x3F) {
+ case 0x01:
+ case 0x21:
+ *wc_status = IB_WC_LOC_LEN_ERR;
+ break;
+ case 0x02:
+ case 0x22:
+ *wc_status = IB_WC_LOC_QP_OP_ERR;
+ break;
+ case 0x03:
+ case 0x23:
+ *wc_status = IB_WC_LOC_EEC_OP_ERR;
+ break;
+ case 0x04:
+ case 0x24:
+ *wc_status = IB_WC_LOC_PROT_ERR;
+ break;
+ case 0x05:
+ case 0x25:
+ *wc_status = IB_WC_WR_FLUSH_ERR;
+ break;
+ case 0x06:
+ *wc_status = IB_WC_MW_BIND_ERR;
+ break;
+ case 0x07: /* remote error - look into bits 20:24 */
+ switch ((cqe_status
+ & WC_STATUS_REMOTE_ERROR_FLAGS) >> 11) {
+ case 0x0:
+ /*
+ * PSN Sequence Error!
+ * couldn't find a matching status!
+ */
+ *wc_status = IB_WC_GENERAL_ERR;
+ break;
+ case 0x1:
+ *wc_status = IB_WC_REM_INV_REQ_ERR;
+ break;
+ case 0x2:
+ *wc_status = IB_WC_REM_ACCESS_ERR;
+ break;
+ case 0x3:
+ *wc_status = IB_WC_REM_OP_ERR;
+ break;
+ case 0x4:
+ *wc_status = IB_WC_REM_INV_RD_REQ_ERR;
+ break;
+ }
+ break;
+ case 0x08:
+ *wc_status = IB_WC_RETRY_EXC_ERR;
+ break;
+ case 0x09:
+ *wc_status = IB_WC_RNR_RETRY_EXC_ERR;
+ break;
+ case 0x0A:
+ case 0x2D:
+ *wc_status = IB_WC_REM_ABORT_ERR;
+ break;
+ case 0x0B:
+ case 0x2E:
+ *wc_status = IB_WC_INV_EECN_ERR;
+ break;
+ case 0x0C:
+ case 0x2F:
+ *wc_status = IB_WC_INV_EEC_STATE_ERR;
+ break;
+ case 0x0D:
+ *wc_status = IB_WC_BAD_RESP_ERR;
+ break;
+ case 0x10:
+ /* WQE purged */
+ *wc_status = IB_WC_WR_FLUSH_ERR;
+ break;
+ default:
+ *wc_status = IB_WC_FATAL_ERR;
+
+ }
+ } else
+ *wc_status = IB_WC_SUCCESS;
+}
+
+int ehca_post_send(struct ib_qp *qp,
+ struct ib_send_wr *send_wr,
+ struct ib_send_wr **bad_send_wr)
+{
+ struct ehca_qp *my_qp = NULL;
+ struct ib_send_wr *cur_send_wr = NULL;
+ struct ehca_wqe *wqe_p = NULL;
+ int wqe_cnt = 0;
+ int ret = 0;
+ unsigned long spl_flags = 0;
+
+ EHCA_CHECK_ADR(qp);
+ my_qp = container_of(qp, struct ehca_qp, ib_qp);
+ EHCA_CHECK_QP(my_qp);
+ EHCA_CHECK_ADR(send_wr);
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x send_wr=%p bad_send_wr=%p",
+ my_qp, qp->qp_num, send_wr, bad_send_wr);
+
+ /* LOCK the QUEUE */
+ spin_lock_irqsave(&my_qp->spinlock_s, spl_flags);
+
+ /* loop processes list of send reqs */
+ for (cur_send_wr = send_wr; cur_send_wr != NULL;
+ cur_send_wr = cur_send_wr->next) {
+ u64 start_offset = my_qp->ipz_squeue.current_q_offset;
+ /* get pointer next to free WQE */
+ wqe_p = ipz_qeit_get_inc(&my_qp->ipz_squeue);
+ if (unlikely(!wqe_p)) {
+ /* too many posted work requests: queue overflow */
+ if (bad_send_wr)
+ *bad_send_wr = cur_send_wr;
+ if (wqe_cnt == 0) {
+ ret = -ENOMEM;
+ EDEB_ERR(4, "Too many posted WQEs qp_num=%x",
+ qp->qp_num);
+ }
+ goto post_send_exit0;
+ }
+ /* write a SEND WQE into the QUEUE */
+ ret = ehca_write_swqe(my_qp, wqe_p, cur_send_wr);
+ /*
+ * if something failed,
+ * reset the free entry pointer to the start value
+ */
+ if (unlikely(ret)) {
+ my_qp->ipz_squeue.current_q_offset = start_offset;
+ *bad_send_wr = cur_send_wr;
+ if (wqe_cnt == 0) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "Could not write WQE qp_num=%x",
+ qp->qp_num);
+ }
+ goto post_send_exit0;
+ }
+ wqe_cnt++;
+ EDEB(7, "ehca_qp=%p qp_num=%x wqe_cnt=%d",
+ my_qp, qp->qp_num, wqe_cnt);
+ } /* eof for cur_send_wr */
+
+post_send_exit0:
+ /* UNLOCK the QUEUE */
+ spin_unlock_irqrestore(&my_qp->spinlock_s, spl_flags);
+ iosync(); /* serialize GAL register access */
+ hipz_update_sqa(my_qp, wqe_cnt);
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x wqe_cnt=%d",
+ my_qp, qp->qp_num, ret, wqe_cnt);
+ return ret;
+}
+
+int ehca_post_recv(struct ib_qp *qp,
+ struct ib_recv_wr *recv_wr,
+ struct ib_recv_wr **bad_recv_wr)
+{
+ struct ehca_qp *my_qp = NULL;
+ struct ib_recv_wr *cur_recv_wr = NULL;
+ struct ehca_wqe *wqe_p = NULL;
+ int wqe_cnt = 0;
+ int ret = 0;
+ unsigned long spl_flags = 0;
+
+ EHCA_CHECK_ADR(qp);
+ my_qp = container_of(qp, struct ehca_qp, ib_qp);
+ EHCA_CHECK_QP(my_qp);
+ EHCA_CHECK_ADR(recv_wr);
+ EDEB_EN(7, "ehca_qp=%p qp_num=%x recv_wr=%p bad_recv_wr=%p",
+ my_qp, qp->qp_num, recv_wr, bad_recv_wr);
+
+ /* LOCK the QUEUE */
+ spin_lock_irqsave(&my_qp->spinlock_r, spl_flags);
+
+ /* loop processes list of send reqs */
+ for (cur_recv_wr = recv_wr; cur_recv_wr != NULL;
+ cur_recv_wr = cur_recv_wr->next) {
+ u64 start_offset = my_qp->ipz_rqueue.current_q_offset;
+ /* get pointer next to free WQE */
+ wqe_p = ipz_qeit_get_inc(&my_qp->ipz_rqueue);
+ if (unlikely(!wqe_p)) {
+ /* too many posted work requests: queue overflow */
+ if (bad_recv_wr)
+ *bad_recv_wr = cur_recv_wr;
+ if (wqe_cnt == 0) {
+ ret = -ENOMEM;
+ EDEB_ERR(4, "Too many posted WQEs qp_num=%x",
+ qp->qp_num);
+ }
+ goto post_recv_exit0;
+ }
+ /* write a RECV WQE into the QUEUE */
+ ret = ehca_write_rwqe(&my_qp->ipz_rqueue, wqe_p,
+ cur_recv_wr);
+ /*
+ * if something failed,
+ * reset the free entry pointer to the start value
+ */
+ if (unlikely(ret)) {
+ my_qp->ipz_rqueue.current_q_offset = start_offset;
+ *bad_recv_wr = cur_recv_wr;
+ if (wqe_cnt == 0) {
+ ret = -EINVAL;
+ EDEB_ERR(4, "Could not write WQE qp_num=%x",
+ qp->qp_num);
+ }
+ goto post_recv_exit0;
+ }
+ wqe_cnt++;
+ EDEB(7, "ehca_qp=%p qp_num=%x wqe_cnt=%d",
+ my_qp, qp->qp_num, wqe_cnt);
+ } /* eof for cur_recv_wr */
+
+post_recv_exit0:
+ spin_unlock_irqrestore(&my_qp->spinlock_r, spl_flags);
+ iosync(); /* serialize GAL register access */
+ hipz_update_rqa(my_qp, wqe_cnt);
+ EDEB_EX(7, "ehca_qp=%p qp_num=%x ret=%x wqe_cnt=%d",
+ my_qp, qp->qp_num, ret, wqe_cnt);
+ return ret;
+}
+
+/*
+ * ib_wc_opcode table converts ehca wc opcode to ib
+ * Since we use zero to indicate invalid opcode, the actual ib opcode must
+ * be decremented!!!
+ */
+static const u8 ib_wc_opcode[255] = {
+ [0x01] = IB_WC_RECV+1,
+ [0x02] = IB_WC_RECV_RDMA_WITH_IMM+1,
+ [0x04] = IB_WC_BIND_MW+1,
+ [0x08] = IB_WC_FETCH_ADD+1,
+ [0x10] = IB_WC_COMP_SWAP+1,
+ [0x20] = IB_WC_RDMA_WRITE+1,
+ [0x40] = IB_WC_RDMA_READ+1,
+ [0x80] = IB_WC_SEND+1
+};
+
+/* internal function to poll one entry of cq */
+static inline int ehca_poll_cq_one(struct ib_cq *cq, struct ib_wc *wc)
+{
+ int ret = 0;
+ struct ehca_cq *my_cq = container_of(cq, struct ehca_cq, ib_cq);
+ struct ehca_cqe *cqe = NULL;
+ int cqe_count = 0;
+
+ EDEB_EN(7, "ehca_cq=%p cq_num=%x wc=%p", my_cq, my_cq->cq_number, wc);
+
+poll_cq_one_read_cqe:
+ cqe = (struct ehca_cqe *)
+ ipz_qeit_get_inc_valid(&my_cq->ipz_queue);
+ if (!cqe) {
+ ret = -EAGAIN;
+ EDEB(7, "Completion queue is empty ehca_cq=%p cq_num=%x "
+ "ret=%x", my_cq, my_cq->cq_number, ret);
+ goto poll_cq_one_exit0;
+ }
+
+ /* prevents loads being reordered across this point */
+ rmb();
+
+ cqe_count++;
+ if (unlikely(cqe->status & WC_STATUS_PURGE_BIT)) {
+ struct ehca_qp *qp=ehca_cq_get_qp(my_cq, cqe->local_qp_number);
+ int purgeflag = 0;
+ unsigned long spl_flags = 0;
+ if (!qp) {
+ EDEB_ERR(4, "cq_num=%x qp_num=%x "
+ "could not find qp -> ignore cqe",
+ my_cq->cq_number, cqe->local_qp_number);
+ EDEB_DMP(4, cqe, 64, "cq_num=%x qp_num=%x",
+ my_cq->cq_number, cqe->local_qp_number);
+ /* ignore this purged cqe */
+ goto poll_cq_one_read_cqe;
+ }
+ spin_lock_irqsave(&qp->spinlock_s, spl_flags);
+ purgeflag = qp->sqerr_purgeflag;
+ spin_unlock_irqrestore(&qp->spinlock_s, spl_flags);
+
+ if (purgeflag) {
+ EDEB(6, "Got CQE with purged bit qp_num=%x src_qp=%x",
+ cqe->local_qp_number, cqe->remote_qp_number);
+ EDEB_DMP(6, cqe, 64, "qp_num=%x src_qp=%x",
+ cqe->local_qp_number, cqe->remote_qp_number);
+ /*
+ * ignore this to avoid double cqes of bad wqe
+ * that caused sqe and turn off purge flag
+ */
+ qp->sqerr_purgeflag = 0;
+ goto poll_cq_one_read_cqe;
+ }
+ }
+
+ /* tracing cqe */
+ if (IS_EDEB_ON(7)) {
+ EDEB(7, "Received COMPLETION ehca_cq=%p cq_num=%x -----",
+ my_cq, my_cq->cq_number);
+ EDEB_DMP(7, cqe, 64, "ehca_cq=%p cq_num=%x",
+ my_cq, my_cq->cq_number);
+ EDEB(7, "ehca_cq=%p cq_num=%x -------------------------",
+ my_cq, my_cq->cq_number);
+ }
+
+ /* we got a completion! */
+ wc->wr_id = cqe->work_request_id;
+
+ /* eval ib_wc_opcode */
+ wc->opcode = ib_wc_opcode[cqe->optype]-1;
+ if (unlikely(wc->opcode == -1)) {
+ EDEB_ERR(4, "Invalid cqe->OPType=%x cqe->status=%x "
+ "ehca_cq=%p cq_num=%x",
+ cqe->optype, cqe->status, my_cq, my_cq->cq_number);
+ /* dump cqe for other infos */
+ EDEB_DMP(4, cqe, 64, "ehca_cq=%p cq_num=%x",
+ my_cq, my_cq->cq_number);
+ /* update also queue adder to throw away this entry!!! */
+ goto poll_cq_one_exit0;
+ }
+ /* eval ib_wc_status */
+ if (unlikely(cqe->status & WC_STATUS_ERROR_BIT)) {
+ /* complete with errors */
+ map_ib_wc_status(cqe->status, &wc->status);
+ wc->vendor_err = wc->status;
+ } else
+ wc->status = IB_WC_SUCCESS;
+
+ wc->qp_num = cqe->local_qp_number;
+ wc->byte_len = cqe->nr_bytes_transferred;
+ wc->pkey_index = cqe->pkey_index;
+ wc->slid = cqe->rlid;
+ wc->dlid_path_bits = cqe->dlid;
+ wc->src_qp = cqe->remote_qp_number;
+ wc->wc_flags = cqe->w_completion_flags;
+ wc->imm_data = cpu_to_be32(cqe->immediate_data);
+ wc->sl = cqe->service_level;
+
+ if (wc->status != IB_WC_SUCCESS)
+ EDEB(6, "ehca_cq=%p cq_num=%x WARNING unsuccessful cqe "
+ "OPType=%x status=%x qp_num=%x src_qp=%x wr_id=%lx cqe=%p",
+ my_cq, my_cq->cq_number, cqe->optype, cqe->status,
+ cqe->local_qp_number, cqe->remote_qp_number,
+ cqe->work_request_id, cqe);
+
+poll_cq_one_exit0:
+ if (cqe_count > 0)
+ hipz_update_feca(my_cq, cqe_count);
+
+ EDEB_EX(7, "ret=%x ehca_cq=%p cq_number=%x wc=%p "
+ "status=%x opcode=%x qp_num=%x byte_len=%x",
+ ret, my_cq, my_cq->cq_number, wc, wc->status,
+ wc->opcode, wc->qp_num, wc->byte_len);
+
+ return ret;
+}
+
+int ehca_poll_cq(struct ib_cq *cq, int num_entries, struct ib_wc *wc)
+{
+ struct ehca_cq *my_cq = NULL;
+ int nr = 0;
+ struct ib_wc *current_wc = NULL;
+ int ret = 0;
+ unsigned long spl_flags = 0;
+
+ EHCA_CHECK_CQ(cq);
+ EHCA_CHECK_ADR(wc);
+
+ my_cq = container_of(cq, struct ehca_cq, ib_cq);
+ EHCA_CHECK_CQ(my_cq);
+
+ EDEB_EN(7, "ehca_cq=%p cq_num=%x num_entries=%d wc=%p",
+ my_cq, my_cq->cq_number, num_entries, wc);
+
+ if (num_entries < 1) {
+ EDEB_ERR(4, "Invalid num_entries=%d ehca_cq=%p cq_num=%x",
+ num_entries, my_cq, my_cq->cq_number);
+ ret = -EINVAL;
+ goto poll_cq_exit0;
+ }
+
+ current_wc = wc;
+ spin_lock_irqsave(&my_cq->spinlock, spl_flags);
+ for (nr = 0; nr < num_entries; nr++) {
+ ret = ehca_poll_cq_one(cq, current_wc);
+ if (ret)
+ break;
+ current_wc++;
+ } /* eof for nr */
+ spin_unlock_irqrestore(&my_cq->spinlock, spl_flags);
+ if (ret == -EAGAIN || !ret)
+ ret = nr;
+
+poll_cq_exit0:
+ EDEB_EX(7, "ehca_cq=%p cq_num=%x ret=%x wc=%p nr_entries=%d",
+ my_cq, my_cq->cq_number, ret, wc, nr);
+
+ return ret;
+}
+
+int ehca_req_notify_cq(struct ib_cq *cq, enum ib_cq_notify cq_notify)
+{
+ struct ehca_cq *my_cq = NULL;
+ int ret = 0;
+
+ EHCA_CHECK_CQ(cq);
+ my_cq = container_of(cq, struct ehca_cq, ib_cq);
+ EHCA_CHECK_CQ(my_cq);
+ EDEB_EN(7, "ehca_cq=%p cq_num=%x cq_notif=%x",
+ my_cq, my_cq->cq_number, cq_notify);
+
+ switch (cq_notify) {
+ case IB_CQ_SOLICITED:
+ hipz_set_cqx_n0(my_cq, 1);
+ break;
+ case IB_CQ_NEXT_COMP:
+ hipz_set_cqx_n1(my_cq, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ EDEB_EX(7, "ehca_cq=%p cq_num=%x ret=%x",
+ my_cq, my_cq->cq_number, ret);
+
+ return ret;
+}
diff --git a/drivers/infiniband/hw/ehca/ehca_sqp.c b/drivers/infiniband/hw/ehca/ehca_sqp.c
new file mode 100644
index 0000000..d2c5552
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/ehca_sqp.c
@@ -0,0 +1,123 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * SQP functions
+ *
+ * Authors: Khadija Souissi <[email protected]>
+ * Heiko J Schick <[email protected]>
+ *
+ * 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.
+ */
+
+
+#define DEB_PREFIX "e_qp"
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include "ehca_classes.h"
+#include "ehca_tools.h"
+#include "ehca_qes.h"
+#include "ehca_iverbs.h"
+#include "hcp_if.h"
+
+
+extern int ehca_create_aqp1(struct ehca_shca *shca, struct ehca_sport *sport);
+extern int ehca_destroy_aqp1(struct ehca_sport *sport);
+
+extern int ehca_port_act_time;
+
+/**
+ * ehca_define_sqp - Defines special queue pair 1 (GSI QP). When special queue
+ * pair is created successfully, the corresponding port gets active.
+ *
+ * Define Special Queue pair 0 (SMI QP) is still not supported.
+ *
+ * @qp_init_attr: Queue pair init attributes with port and queue pair type
+ */
+
+u64 ehca_define_sqp(struct ehca_shca *shca,
+ struct ehca_qp *ehca_qp,
+ struct ib_qp_init_attr *qp_init_attr)
+{
+
+ u32 pma_qp_nr = 0;
+ u32 bma_qp_nr = 0;
+ u64 ret = H_SUCCESS;
+ u8 port = qp_init_attr->port_num;
+ int counter = 0;
+
+ EDEB_EN(7, "port=%x qp_type=%x",
+ port, qp_init_attr->qp_type);
+
+ shca->sport[port - 1].port_state = IB_PORT_DOWN;
+
+ switch (qp_init_attr->qp_type) {
+ case IB_QPT_SMI:
+ /* function not supported yet */
+ break;
+ case IB_QPT_GSI:
+ ret = hipz_h_define_aqp1(shca->ipz_hca_handle,
+ ehca_qp->ipz_qp_handle,
+ ehca_qp->galpas.kernel,
+ (u32) qp_init_attr->port_num,
+ &pma_qp_nr, &bma_qp_nr);
+
+ if (ret != H_SUCCESS) {
+ EDEB_ERR(4, "Can't define AQP1 for port %x. rc=%lx",
+ port, ret);
+ goto ehca_define_aqp1;
+ }
+ break;
+ default:
+ ret = H_PARAMETER;
+ goto ehca_define_aqp1;
+ }
+
+ while ((shca->sport[port - 1].port_state != IB_PORT_ACTIVE) &&
+ (counter < ehca_port_act_time)) {
+ EDEB(6, "... wait until port %x is active",
+ port);
+ msleep_interruptible(1000);
+ counter++;
+ }
+
+ if (counter == ehca_port_act_time) {
+ EDEB_ERR(4, "Port %x is not active.", port);
+ ret = H_HARDWARE;
+ }
+
+ehca_define_aqp1:
+ EDEB_EX(7, "ret=%lx", ret);
+
+ return ret;
+}
--
1.4.1
drivers/infiniband/hw/ehca/hipz_fns.h | 68 +++++
drivers/infiniband/hw/ehca/hipz_fns_core.h | 122 +++++++++
drivers/infiniband/hw/ehca/hipz_hw.h | 390 ++++++++++++++++++++++++++++
3 files changed, 580 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/hipz_fns.h b/drivers/infiniband/hw/ehca/hipz_fns.h
new file mode 100644
index 0000000..9dac93d
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hipz_fns.h
@@ -0,0 +1,68 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * HW abstraction register functions
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __HIPZ_FNS_H__
+#define __HIPZ_FNS_H__
+
+#include "ehca_classes.h"
+#include "hipz_hw.h"
+
+#include "hipz_fns_core.h"
+
+#define hipz_galpa_store_eq(gal, offset, value) \
+ hipz_galpa_store(gal, EQTEMM_OFFSET(offset), value)
+
+#define hipz_galpa_load_eq(gal, offset) \
+ hipz_galpa_load(gal, EQTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_qped(gal, offset, value) \
+ hipz_galpa_store(gal, QPEDMM_OFFSET(offset), value)
+
+#define hipz_galpa_load_qped(gal, offset) \
+ hipz_galpa_load(gal, QPEDMM_OFFSET(offset))
+
+#define hipz_galpa_store_mrmw(gal, offset, value) \
+ hipz_galpa_store(gal, MRMWMM_OFFSET(offset), value)
+
+#define hipz_galpa_load_mrmw(gal, offset) \
+ hipz_galpa_load(gal, MRMWMM_OFFSET(offset))
+
+#endif
diff --git a/drivers/infiniband/hw/ehca/hipz_fns_core.h b/drivers/infiniband/hw/ehca/hipz_fns_core.h
new file mode 100644
index 0000000..ff8d7ed
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hipz_fns_core.h
@@ -0,0 +1,122 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * HW abstraction register functions
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ * Heiko J Schick <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __HIPZ_FNS_CORE_H__
+#define __HIPZ_FNS_CORE_H__
+
+#include "hcp_phyp.h"
+#include "hipz_hw.h"
+
+#define hipz_galpa_store_cq(gal, offset, value) \
+ hipz_galpa_store(gal, CQTEMM_OFFSET(offset), value)
+
+#define hipz_galpa_load_cq(gal, offset) \
+ hipz_galpa_load(gal, CQTEMM_OFFSET(offset))
+
+#define hipz_galpa_store_qp(gal,offset, value) \
+ hipz_galpa_store(gal, QPTEMM_OFFSET(offset), value)
+#define hipz_galpa_load_qp(gal, offset) \
+ hipz_galpa_load(gal,QPTEMM_OFFSET(offset))
+
+static inline void hipz_update_sqa(struct ehca_qp *qp, u16 nr_wqes)
+{
+ struct h_galpa gal;
+
+ EDEB_EN(7, "qp=%p", qp);
+ gal = qp->galpas.kernel;
+ /* ringing doorbell :-) */
+ hipz_galpa_store_qp(gal, qpx_sqa, EHCA_BMASK_SET(QPX_SQADDER, nr_wqes));
+ EDEB_EX(7, "qp=%p QPx_SQA = %i", qp, nr_wqes);
+}
+
+static inline void hipz_update_rqa(struct ehca_qp *qp, u16 nr_wqes)
+{
+ struct h_galpa gal;
+
+ EDEB_EN(7, "qp=%p", qp);
+ gal = qp->galpas.kernel;
+ /* ringing doorbell :-) */
+ hipz_galpa_store_qp(gal, qpx_rqa, EHCA_BMASK_SET(QPX_RQADDER, nr_wqes));
+ EDEB_EX(7, "qp=%p QPx_RQA = %i", qp, nr_wqes);
+}
+
+static inline void hipz_update_feca(struct ehca_cq *cq, u32 nr_cqes)
+{
+ struct h_galpa gal;
+
+ EDEB_EN(7, "cq=%p", cq);
+ gal = cq->galpas.kernel;
+ hipz_galpa_store_cq(gal, cqx_feca,
+ EHCA_BMASK_SET(CQX_FECADDER, nr_cqes));
+ EDEB_EX(7, "cq=%p CQx_FECA = %i", cq, nr_cqes);
+}
+
+static inline void hipz_set_cqx_n0(struct ehca_cq *cq, u32 value)
+{
+ struct h_galpa gal;
+ u64 CQx_N0_reg = 0;
+
+ EDEB_EN(7, "cq=%p event on solicited completion -- write CQx_N0", cq);
+ gal = cq->galpas.kernel;
+ hipz_galpa_store_cq(gal, cqx_n0,
+ EHCA_BMASK_SET(CQX_N0_GENERATE_SOLICITED_COMP_EVENT,
+ value));
+ CQx_N0_reg = hipz_galpa_load_cq(gal, cqx_n0);
+ EDEB_EX(7, "cq=%p loaded CQx_N0=%lx", cq, (unsigned long)CQx_N0_reg);
+}
+
+static inline void hipz_set_cqx_n1(struct ehca_cq *cq, u32 value)
+{
+ struct h_galpa gal;
+ u64 CQx_N1_reg = 0;
+
+ EDEB_EN(7, "cq=%p event on completion -- write CQx_N1",
+ cq);
+ gal = cq->galpas.kernel;
+ hipz_galpa_store_cq(gal, cqx_n1,
+ EHCA_BMASK_SET(CQX_N1_GENERATE_COMP_EVENT, value));
+ CQx_N1_reg = hipz_galpa_load_cq(gal, cqx_n1);
+ EDEB_EX(7, "cq=%p loaded CQx_N1=%lx", cq, (unsigned long)CQx_N1_reg);
+}
+
+#endif /* __HIPZ_FNC_CORE_H__ */
diff --git a/drivers/infiniband/hw/ehca/hipz_hw.h b/drivers/infiniband/hw/ehca/hipz_hw.h
new file mode 100644
index 0000000..f5f4871
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hipz_hw.h
@@ -0,0 +1,390 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * eHCA register definitions
+ *
+ * Authors: Waleri Fomin <[email protected]>
+ * Christoph Raisch <[email protected]>
+ * Reinhard Ernst <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __HIPZ_HW_H__
+#define __HIPZ_HW_H__
+
+#include "ehca_tools.h"
+
+/* QP Table Entry Memory Map */
+struct hipz_qptemm {
+ u64 qpx_hcr;
+ u64 qpx_c;
+ u64 qpx_herr;
+ u64 qpx_aer;
+/* 0x20*/
+ u64 qpx_sqa;
+ u64 qpx_sqc;
+ u64 qpx_rqa;
+ u64 qpx_rqc;
+/* 0x40*/
+ u64 qpx_st;
+ u64 qpx_pmstate;
+ u64 qpx_pmfa;
+ u64 qpx_pkey;
+/* 0x60*/
+ u64 qpx_pkeya;
+ u64 qpx_pkeyb;
+ u64 qpx_pkeyc;
+ u64 qpx_pkeyd;
+/* 0x80*/
+ u64 qpx_qkey;
+ u64 qpx_dqp;
+ u64 qpx_dlidp;
+ u64 qpx_portp;
+/* 0xa0*/
+ u64 qpx_slidp;
+ u64 qpx_slidpp;
+ u64 qpx_dlida;
+ u64 qpx_porta;
+/* 0xc0*/
+ u64 qpx_slida;
+ u64 qpx_slidpa;
+ u64 qpx_slvl;
+ u64 qpx_ipd;
+/* 0xe0*/
+ u64 qpx_mtu;
+ u64 qpx_lato;
+ u64 qpx_rlimit;
+ u64 qpx_rnrlimit;
+/* 0x100*/
+ u64 qpx_t;
+ u64 qpx_sqhp;
+ u64 qpx_sqptp;
+ u64 qpx_nspsn;
+/* 0x120*/
+ u64 qpx_nspsnhwm;
+ u64 reserved1;
+ u64 qpx_sdsi;
+ u64 qpx_sdsbc;
+/* 0x140*/
+ u64 qpx_sqwsize;
+ u64 qpx_sqwts;
+ u64 qpx_lsn;
+ u64 qpx_nssn;
+/* 0x160 */
+ u64 qpx_mor;
+ u64 qpx_cor;
+ u64 qpx_sqsize;
+ u64 qpx_erc;
+/* 0x180*/
+ u64 qpx_rnrrc;
+ u64 qpx_ernrwt;
+ u64 qpx_rnrresp;
+ u64 qpx_lmsna;
+/* 0x1a0 */
+ u64 qpx_sqhpc;
+ u64 qpx_sqcptp;
+ u64 qpx_sigt;
+ u64 qpx_wqecnt;
+/* 0x1c0*/
+ u64 qpx_rqhp;
+ u64 qpx_rqptp;
+ u64 qpx_rqsize;
+ u64 qpx_nrr;
+/* 0x1e0*/
+ u64 qpx_rdmac;
+ u64 qpx_nrpsn;
+ u64 qpx_lapsn;
+ u64 qpx_lcr;
+/* 0x200*/
+ u64 qpx_rwc;
+ u64 qpx_rwva;
+ u64 qpx_rdsi;
+ u64 qpx_rdsbc;
+/* 0x220*/
+ u64 qpx_rqwsize;
+ u64 qpx_crmsn;
+ u64 qpx_rdd;
+ u64 qpx_larpsn;
+/* 0x240*/
+ u64 qpx_pd;
+ u64 qpx_scqn;
+ u64 qpx_rcqn;
+ u64 qpx_aeqn;
+/* 0x260*/
+ u64 qpx_aaelog;
+ u64 qpx_ram;
+ u64 qpx_rdmaqe0;
+ u64 qpx_rdmaqe1;
+/* 0x280*/
+ u64 qpx_rdmaqe2;
+ u64 qpx_rdmaqe3;
+ u64 qpx_nrpsnhwm;
+/* 0x298*/
+ u64 reserved[(0x400 - 0x298) / 8];
+/* 0x400 extended data */
+ u64 reserved_ext[(0x500 - 0x400) / 8];
+/* 0x500 */
+ u64 reserved2[(0x1000 - 0x500) / 8];
+/* 0x1000 */
+};
+
+#define QPX_SQADDER EHCA_BMASK_IBM(48,63)
+#define QPX_RQADDER EHCA_BMASK_IBM(48,63)
+
+#define QPTEMM_OFFSET(x) offsetof(struct hipz_qptemm,x)
+
+/* MRMWPT Entry Memory Map */
+struct hipz_mrmwmm {
+ /* 0x00 */
+ u64 mrx_hcr;
+
+ u64 mrx_c;
+ u64 mrx_herr;
+ u64 mrx_aer;
+ /* 0x20 */
+ u64 mrx_pp;
+ u64 reserved1;
+ u64 reserved2;
+ u64 reserved3;
+ /* 0x40 */
+ u64 reserved4[(0x200 - 0x40) / 8];
+ /* 0x200 */
+ u64 mrx_ctl[64];
+
+};
+
+#define MRX_HCR_LPARID_VALID EHCA_BMASK_IBM(0,0)
+
+#define MRMWMM_OFFSET(x) offsetof(struct hipz_mrmwmm,x)
+
+struct hipz_qpedmm {
+ /* 0x00 */
+ u64 reserved0[(0x400) / 8];
+ /* 0x400 */
+ u64 qpedx_phh;
+ u64 qpedx_ppsgp;
+ /* 0x410 */
+ u64 qpedx_ppsgu;
+ u64 qpedx_ppdgp;
+ /* 0x420 */
+ u64 qpedx_ppdgu;
+ u64 qpedx_aph;
+ /* 0x430 */
+ u64 qpedx_apsgp;
+ u64 qpedx_apsgu;
+ /* 0x440 */
+ u64 qpedx_apdgp;
+ u64 qpedx_apdgu;
+ /* 0x450 */
+ u64 qpedx_apav;
+ u64 qpedx_apsav;
+ /* 0x460 */
+ u64 qpedx_hcr;
+ u64 reserved1[4];
+ /* 0x488 */
+ u64 qpedx_rrl0;
+ /* 0x490 */
+ u64 qpedx_rrrkey0;
+ u64 qpedx_rrva0;
+ /* 0x4a0 */
+ u64 reserved2;
+ u64 qpedx_rrl1;
+ /* 0x4b0 */
+ u64 qpedx_rrrkey1;
+ u64 qpedx_rrva1;
+ /* 0x4c0 */
+ u64 reserved3;
+ u64 qpedx_rrl2;
+ /* 0x4d0 */
+ u64 qpedx_rrrkey2;
+ u64 qpedx_rrva2;
+ /* 0x4e0 */
+ u64 reserved4;
+ u64 qpedx_rrl3;
+ /* 0x4f0 */
+ u64 qpedx_rrrkey3;
+ u64 qpedx_rrva3;
+};
+
+#define QPEDMM_OFFSET(x) offsetof(struct hipz_qpedmm,x)
+
+/* CQ Table Entry Memory Map */
+struct hipz_cqtemm {
+ u64 cqx_hcr;
+ u64 cqx_c;
+ u64 cqx_herr;
+ u64 cqx_aer;
+/* 0x20 */
+ u64 cqx_ptp;
+ u64 cqx_tp;
+ u64 cqx_fec;
+ u64 cqx_feca;
+/* 0x40 */
+ u64 cqx_ep;
+ u64 cqx_eq;
+/* 0x50 */
+ u64 reserved1;
+ u64 cqx_n0;
+/* 0x60 */
+ u64 cqx_n1;
+ u64 reserved2[(0x1000 - 0x60) / 8];
+/* 0x1000 */
+};
+
+#define CQX_FEC_CQE_CNT EHCA_BMASK_IBM(32,63)
+#define CQX_FECADDER EHCA_BMASK_IBM(32,63)
+#define CQX_N0_GENERATE_SOLICITED_COMP_EVENT EHCA_BMASK_IBM(0,0)
+#define CQX_N1_GENERATE_COMP_EVENT EHCA_BMASK_IBM(0,0)
+
+#define CQTEMM_OFFSET(x) offsetof(struct hipz_cqtemm,x)
+
+/* EQ Table Entry Memory Map */
+struct hipz_eqtemm {
+ u64 eqx_hcr;
+ u64 eqx_c;
+
+ u64 eqx_herr;
+ u64 eqx_aer;
+/* 0x20 */
+ u64 eqx_ptp;
+ u64 eqx_tp;
+ u64 eqx_ssba;
+ u64 eqx_psba;
+
+/* 0x40 */
+ u64 eqx_cec;
+ u64 eqx_meql;
+ u64 eqx_xisbi;
+ u64 eqx_xisc;
+/* 0x60 */
+ u64 eqx_it;
+
+};
+
+#define EQTEMM_OFFSET(x) offsetof(struct hipz_eqtemm,x)
+
+/* access control defines for MR/MW */
+#define HIPZ_ACCESSCTRL_L_WRITE 0x00800000
+#define HIPZ_ACCESSCTRL_R_WRITE 0x00400000
+#define HIPZ_ACCESSCTRL_R_READ 0x00200000
+#define HIPZ_ACCESSCTRL_R_ATOMIC 0x00100000
+#define HIPZ_ACCESSCTRL_MW_BIND 0x00080000
+
+/* query hca response block */
+struct hipz_query_hca {
+ u32 cur_reliable_dg;
+ u32 cur_qp;
+ u32 cur_cq;
+ u32 cur_eq;
+ u32 cur_mr;
+ u32 cur_mw;
+ u32 cur_ee_context;
+ u32 cur_mcast_grp;
+ u32 cur_qp_attached_mcast_grp;
+ u32 reserved1;
+ u32 cur_ipv6_qp;
+ u32 cur_eth_qp;
+ u32 cur_hp_mr;
+ u32 reserved2[3];
+ u32 max_rd_domain;
+ u32 max_qp;
+ u32 max_cq;
+ u32 max_eq;
+ u32 max_mr;
+ u32 max_hp_mr;
+ u32 max_mw;
+ u32 max_mrwpte;
+ u32 max_special_mrwpte;
+ u32 max_rd_ee_context;
+ u32 max_mcast_grp;
+ u32 max_total_mcast_qp_attach;
+ u32 max_mcast_qp_attach;
+ u32 max_raw_ipv6_qp;
+ u32 max_raw_ethy_qp;
+ u32 internal_clock_frequency;
+ u32 max_pd;
+ u32 max_ah;
+ u32 max_cqe;
+ u32 max_wqes_wq;
+ u32 max_partitions;
+ u32 max_rr_ee_context;
+ u32 max_rr_qp;
+ u32 max_rr_hca;
+ u32 max_act_wqs_ee_context;
+ u32 max_act_wqs_qp;
+ u32 max_sge;
+ u32 max_sge_rd;
+ u32 memory_page_size_supported;
+ u64 max_mr_size;
+ u32 local_ca_ack_delay;
+ u32 num_ports;
+ u32 vendor_id;
+ u32 vendor_part_id;
+ u32 hw_ver;
+ u64 node_guid;
+ u64 hca_cap_indicators;
+ u32 data_counter_register_size;
+ u32 max_shared_rq;
+ u32 max_isns_eq;
+ u32 max_neq;
+} __attribute__ ((packed));
+
+/* query port response block */
+struct hipz_query_port {
+ u32 state;
+ u32 bad_pkey_cntr;
+ u32 lmc;
+ u32 lid;
+ u32 subnet_timeout;
+ u32 qkey_viol_cntr;
+ u32 sm_sl;
+ u32 sm_lid;
+ u32 capability_mask;
+ u32 init_type_reply;
+ u32 pkey_tbl_len;
+ u32 gid_tbl_len;
+ u64 gid_prefix;
+ u32 port_nr;
+ u16 pkey_entries[16];
+ u8 reserved1[32];
+ u32 trent_size;
+ u32 trbuf_size;
+ u64 max_msg_sz;
+ u32 max_mtu;
+ u32 vl_cap;
+ u8 reserved2[1900];
+ u64 guid_entries[255];
+} __attribute__ ((packed));
+
+#endif
--
1.4.1
drivers/infiniband/hw/ehca/hcp_phyp.c | 92 ++++++++++++++++++++++++++++++++
drivers/infiniband/hw/ehca/hcp_phyp.h | 96 +++++++++++++++++++++++++++++++++
2 files changed, 188 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.c b/drivers/infiniband/hw/ehca/hcp_phyp.c
new file mode 100644
index 0000000..d522d50
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hcp_phyp.c
@@ -0,0 +1,92 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * load store abstraction for ehca register access with tracing
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ *
+ * 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.
+ */
+
+#define DEB_PREFIX "PHYP"
+
+#include "ehca_classes.h"
+#include "hipz_hw.h"
+
+int hcall_map_page(u64 physaddr, u64 *mapaddr)
+{
+ *mapaddr = (u64)(ioremap(physaddr, EHCA_PAGESIZE));
+
+ EDEB(7, "ioremap physaddr=%lx mapaddr=%lx", physaddr, *mapaddr);
+ return 0;
+}
+
+int hcall_unmap_page(u64 mapaddr)
+{
+ EDEB(7, "mapaddr=%lx", mapaddr);
+ iounmap((volatile void __iomem*)mapaddr);
+ return 0;
+}
+
+int hcp_galpas_ctor(struct h_galpas *galpas,
+ u64 paddr_kernel, u64 paddr_user)
+{
+ int ret = hcall_map_page(paddr_kernel, &galpas->kernel.fw_handle);
+ if (ret)
+ return ret;
+
+ galpas->user.fw_handle = paddr_user;
+
+ EDEB(7, "paddr_kernel=%lx paddr_user=%lx galpas->kernel=%lx"
+ " galpas->user=%lx",
+ paddr_kernel, paddr_user, galpas->kernel.fw_handle,
+ galpas->user.fw_handle);
+
+ return ret;
+}
+
+int hcp_galpas_dtor(struct h_galpas *galpas)
+{
+ int ret = 0;
+
+ if (galpas->kernel.fw_handle)
+ ret = hcall_unmap_page(galpas->kernel.fw_handle);
+
+ if (ret)
+ return ret;
+
+ galpas->user.fw_handle = galpas->kernel.fw_handle = 0;
+
+ return ret;
+}
diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.h b/drivers/infiniband/hw/ehca/hcp_phyp.h
new file mode 100644
index 0000000..ecb1117
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/hcp_phyp.h
@@ -0,0 +1,96 @@
+/*
+ * IBM eServer eHCA Infiniband device driver for Linux on POWER
+ *
+ * Firmware calls
+ *
+ * Authors: Christoph Raisch <[email protected]>
+ * Hoang-Nam Nguyen <[email protected]>
+ * Waleri Fomin <[email protected]>
+ * Gerd Bayer <[email protected]>
+ *
+ * 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.
+ */
+
+#ifndef __HCP_PHYP_H__
+#define __HCP_PHYP_H__
+
+
+/*
+ * eHCA page (mapped into memory)
+ * resource to access eHCA register pages in CPU address space
+*/
+struct h_galpa {
+ u64 fw_handle;
+ /* for pSeries this is a 64bit memory address where
+ I/O memory is mapped into CPU address space (kv) */
+};
+
+/*
+ * resource to access eHCA address space registers, all types
+ */
+struct h_galpas {
+ u32 pid; /*PID of userspace galpa checking */
+ struct h_galpa user; /* user space accessible resource,
+ set to 0 if unused */
+ struct h_galpa kernel; /* kernel space accessible resource,
+ set to 0 if unused */
+};
+
+static inline u64 hipz_galpa_load(struct h_galpa galpa, u32 offset)
+{
+ u64 addr = galpa.fw_handle + offset;
+ u64 out;
+ EDEB_EN(7, "addr=%lx offset=%x ", addr, offset);
+ out = *(u64 *) addr;
+ EDEB_EX(7, "addr=%lx value=%lx", addr, out);
+ return out;
+}
+
+static inline void hipz_galpa_store(struct h_galpa galpa, u32 offset, u64 value)
+{
+ u64 addr = galpa.fw_handle + offset;
+ EDEB(7, "addr=%lx offset=%x value=%lx", addr,
+ offset, value);
+ *(u64 *) addr = value;
+}
+
+int hcp_galpas_ctor(struct h_galpas *galpas,
+ u64 paddr_kernel, u64 paddr_user);
+
+int hcp_galpas_dtor(struct h_galpas *galpas);
+
+int hcall_map_page(u64 physaddr, u64 * mapaddr);
+
+int hcall_unmap_page(u64 mapaddr);
+
+#endif
--
1.4.1
drivers/infiniband/Kconfig | 1 +
drivers/infiniband/Makefile | 1 +
drivers/infiniband/hw/ehca/Kconfig | 12 ++++++++++++
drivers/infiniband/hw/ehca/Makefile | 18 ++++++++++++++++++
4 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index 69a53d4..fd2d528 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -36,6 +36,7 @@ config INFINIBAND_ADDR_TRANS
source "drivers/infiniband/hw/mthca/Kconfig"
source "drivers/infiniband/hw/ipath/Kconfig"
+source "drivers/infiniband/hw/ehca/Kconfig"
source "drivers/infiniband/ulp/ipoib/Kconfig"
diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile
index c7ff58c..893bee0 100644
--- a/drivers/infiniband/Makefile
+++ b/drivers/infiniband/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_INFINIBAND) += core/
obj-$(CONFIG_INFINIBAND_MTHCA) += hw/mthca/
obj-$(CONFIG_IPATH_CORE) += hw/ipath/
+obj-$(CONFIG_INFINIBAND_EHCA) += hw/ehca/
obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/
obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/
obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/
diff --git a/drivers/infiniband/hw/ehca/Kconfig b/drivers/infiniband/hw/ehca/Kconfig
new file mode 100644
index 0000000..12285d0
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/Kconfig
@@ -0,0 +1,12 @@
+config INFINIBAND_EHCA
+ tristate "eHCA support"
+ depends on IBMEBUS && INFINIBAND
+ ---help---
+ This is a low level device driver for the IBM GX based Host channel
+ adapters (HCAs).
+
+config INFINIBAND_EHCA_SCALING
+ bool "Scaling support (EXPERIMENTAL)"
+ depends on IBMEBUS && INFINIBAND_EHCA && HOTPLUG_CPU && EXPERIMENTAL
+ ---help---
+ eHCA scaling support schedules the CQ callbacks to different CPUs.
diff --git a/drivers/infiniband/hw/ehca/Makefile b/drivers/infiniband/hw/ehca/Makefile
new file mode 100644
index 0000000..70032cf
--- /dev/null
+++ b/drivers/infiniband/hw/ehca/Makefile
@@ -0,0 +1,18 @@
+# Authors: Heiko J Schick <[email protected]>
+# Christoph Raisch <[email protected]>
+# Joachim Fenkes <[email protected]>
+#
+# Copyright (c) 2005 IBM Corporation
+#
+# All rights reserved.
+#
+# This source code is distributed under a dual license of GPL v2.0 and OpenIB BSD.
+
+obj-$(CONFIG_INFINIBAND_EHCA) += hcad_mod.o
+
+
+hcad_mod-objs = ehca_main.o ehca_hca.o ehca_mcast.o ehca_pd.o ehca_av.o ehca_eq.o \
+ ehca_cq.o ehca_qp.o ehca_sqp.o ehca_mrmw.o ehca_reqs.o ehca_irq.o \
+ ehca_uverbs.o ipz_pt_fn.o hcp_if.o hcp_phyp.o
+
+CFLAGS += -DEHCA_USE_HCALL -DEHCA_USE_HCALL_KERNEL
--
1.4.1
On Thursday 17 August 2006 22:11, Roland Dreier wrote:
> +
> +CFLAGS += -DEHCA_USE_HCALL -DEHCA_USE_HCALL_KERNEL
This seems really pointless, since you're always defining these
macros to the same value.
Just drop the CFLAGS and remove the code that depends on them
being different.
Arnd <><
[email protected] wrote on 18.08.2006 01:34:37:
> On Thursday 17 August 2006 22:11, Roland Dreier wrote:
> > +
> > +CFLAGS += -DEHCA_USE_HCALL -DEHCA_USE_HCALL_KERNEL
>
> This seems really pointless, since you're always defining these
> macros to the same value.
>
> Just drop the CFLAGS and remove the code that depends on them
> being different.
Yes, that's true. Those defines are unnecessary. We'll throw them out.
Thx
Hoang-Nam Nguyen
On Thu, 2006-08-17 at 16:11, Roland Dreier wrote:
[snip...]
> diff --git a/drivers/infiniband/hw/ehca/ehca_sqp.c b/drivers/infiniband/hw/ehca/ehca_sqp.c
> new file mode 100644
> index 0000000..d2c5552
> --- /dev/null
> +++ b/drivers/infiniband/hw/ehca/ehca_sqp.c
> @@ -0,0 +1,123 @@
> +/*
> + * IBM eServer eHCA Infiniband device driver for Linux on POWER
> + *
> + * SQP functions
> + *
> + * Authors: Khadija Souissi <[email protected]>
> + * Heiko J Schick <[email protected]>
[snip...]
> +
> +extern int ehca_create_aqp1(struct ehca_shca *shca, struct ehca_sport *sport);
> +extern int ehca_destroy_aqp1(struct ehca_sport *sport);
> +
> +extern int ehca_port_act_time;
> +
> +/**
> + * ehca_define_sqp - Defines special queue pair 1 (GSI QP). When special queue
> + * pair is created successfully, the corresponding port gets active.
> + *
> + * Define Special Queue pair 0 (SMI QP) is still not supported.
> + *
> + * @qp_init_attr: Queue pair init attributes with port and queue pair type
> + */
> +
> +u64 ehca_define_sqp(struct ehca_shca *shca,
> + struct ehca_qp *ehca_qp,
> + struct ib_qp_init_attr *qp_init_attr)
> +{
> +
> + u32 pma_qp_nr = 0;
> + u32 bma_qp_nr = 0;
> + u64 ret = H_SUCCESS;
> + u8 port = qp_init_attr->port_num;
> + int counter = 0;
> +
> + EDEB_EN(7, "port=%x qp_type=%x",
> + port, qp_init_attr->qp_type);
> +
> + shca->sport[port - 1].port_state = IB_PORT_DOWN;
> +
> + switch (qp_init_attr->qp_type) {
> + case IB_QPT_SMI:
> + /* function not supported yet */
> + break;
> + case IB_QPT_GSI:
> + ret = hipz_h_define_aqp1(shca->ipz_hca_handle,
> + ehca_qp->ipz_qp_handle,
> + ehca_qp->galpas.kernel,
> + (u32) qp_init_attr->port_num,
> + &pma_qp_nr, &bma_qp_nr);
> +
> + if (ret != H_SUCCESS) {
> + EDEB_ERR(4, "Can't define AQP1 for port %x. rc=%lx",
> + port, ret);
> + goto ehca_define_aqp1;
> + }
> + break;
> + default:
> + ret = H_PARAMETER;
> + goto ehca_define_aqp1;
> + }
> +
> + while ((shca->sport[port - 1].port_state != IB_PORT_ACTIVE) &&
> + (counter < ehca_port_act_time)) {
> + EDEB(6, "... wait until port %x is active",
> + port);
> + msleep_interruptible(1000);
> + counter++;
> + }
> +
> + if (counter == ehca_port_act_time) {
> + EDEB_ERR(4, "Port %x is not active.", port);
> + ret = H_HARDWARE;
> + }
> +
> +ehca_define_aqp1:
> + EDEB_EX(7, "ret=%lx", ret);
> +
> + return ret;
> +}
I, for one, was hoping that the timer based transition to active for QP1
would have been resolved before being submitted. Any idea on the plan to
resolve this ?
-- Hal
>
> I, for one, was hoping that the timer based transition to active for QP1
> would have been resolved before being submitted. Any idea on the plan to
> resolve this ?
>
> -- Hal
>
>
>
We're testing it. As I mentioned before, this requires a change in the
system firmware. I personally don't think this will show up in firmware in
time for 2.6.19 merge window.
So unfortunately we still need that loop.
Regards . . . Christoph R