Path: blob/master/drivers/infiniband/hw/ehca/ehca_cq.c
15112 views
/*1* IBM eServer eHCA Infiniband device driver for Linux on POWER2*3* Completion queue handling4*5* Authors: Waleri Fomin <[email protected]>6* Khadija Souissi <[email protected]>7* Reinhard Ernst <[email protected]>8* Heiko J Schick <[email protected]>9* Hoang-Nam Nguyen <[email protected]>10*11*12* Copyright (c) 2005 IBM Corporation13*14* All rights reserved.15*16* This source code is distributed under a dual license of GPL v2.0 and OpenIB17* BSD.18*19* OpenIB BSD License20*21* Redistribution and use in source and binary forms, with or without22* modification, are permitted provided that the following conditions are met:23*24* Redistributions of source code must retain the above copyright notice, this25* list of conditions and the following disclaimer.26*27* Redistributions in binary form must reproduce the above copyright notice,28* this list of conditions and the following disclaimer in the documentation29* and/or other materials30* provided with the distribution.31*32* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"33* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE34* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE35* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE36* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR37* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF38* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR39* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER40* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)41* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE42* POSSIBILITY OF SUCH DAMAGE.43*/4445#include <linux/slab.h>4647#include "ehca_iverbs.h"48#include "ehca_classes.h"49#include "ehca_irq.h"50#include "hcp_if.h"5152static struct kmem_cache *cq_cache;5354int ehca_cq_assign_qp(struct ehca_cq *cq, struct ehca_qp *qp)55{56unsigned int qp_num = qp->real_qp_num;57unsigned int key = qp_num & (QP_HASHTAB_LEN-1);58unsigned long flags;5960spin_lock_irqsave(&cq->spinlock, flags);61hlist_add_head(&qp->list_entries, &cq->qp_hashtab[key]);62spin_unlock_irqrestore(&cq->spinlock, flags);6364ehca_dbg(cq->ib_cq.device, "cq_num=%x real_qp_num=%x",65cq->cq_number, qp_num);6667return 0;68}6970int ehca_cq_unassign_qp(struct ehca_cq *cq, unsigned int real_qp_num)71{72int ret = -EINVAL;73unsigned int key = real_qp_num & (QP_HASHTAB_LEN-1);74struct hlist_node *iter;75struct ehca_qp *qp;76unsigned long flags;7778spin_lock_irqsave(&cq->spinlock, flags);79hlist_for_each(iter, &cq->qp_hashtab[key]) {80qp = hlist_entry(iter, struct ehca_qp, list_entries);81if (qp->real_qp_num == real_qp_num) {82hlist_del(iter);83ehca_dbg(cq->ib_cq.device,84"removed qp from cq .cq_num=%x real_qp_num=%x",85cq->cq_number, real_qp_num);86ret = 0;87break;88}89}90spin_unlock_irqrestore(&cq->spinlock, flags);91if (ret)92ehca_err(cq->ib_cq.device,93"qp not found cq_num=%x real_qp_num=%x",94cq->cq_number, real_qp_num);9596return ret;97}9899struct ehca_qp *ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num)100{101struct ehca_qp *ret = NULL;102unsigned int key = real_qp_num & (QP_HASHTAB_LEN-1);103struct hlist_node *iter;104struct ehca_qp *qp;105hlist_for_each(iter, &cq->qp_hashtab[key]) {106qp = hlist_entry(iter, struct ehca_qp, list_entries);107if (qp->real_qp_num == real_qp_num) {108ret = qp;109break;110}111}112return ret;113}114115struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, int comp_vector,116struct ib_ucontext *context,117struct ib_udata *udata)118{119static const u32 additional_cqe = 20;120struct ib_cq *cq;121struct ehca_cq *my_cq;122struct ehca_shca *shca =123container_of(device, struct ehca_shca, ib_device);124struct ipz_adapter_handle adapter_handle;125struct ehca_alloc_cq_parms param; /* h_call's out parameters */126struct h_galpa gal;127void *vpage;128u32 counter;129u64 rpage, cqx_fec, h_ret;130int ipz_rc, ret, i;131unsigned long flags;132133if (cqe >= 0xFFFFFFFF - 64 - additional_cqe)134return ERR_PTR(-EINVAL);135136if (!atomic_add_unless(&shca->num_cqs, 1, shca->max_num_cqs)) {137ehca_err(device, "Unable to create CQ, max number of %i "138"CQs reached.", shca->max_num_cqs);139ehca_err(device, "To increase the maximum number of CQs "140"use the number_of_cqs module parameter.\n");141return ERR_PTR(-ENOSPC);142}143144my_cq = kmem_cache_zalloc(cq_cache, GFP_KERNEL);145if (!my_cq) {146ehca_err(device, "Out of memory for ehca_cq struct device=%p",147device);148atomic_dec(&shca->num_cqs);149return ERR_PTR(-ENOMEM);150}151152memset(¶m, 0, sizeof(struct ehca_alloc_cq_parms));153154spin_lock_init(&my_cq->spinlock);155spin_lock_init(&my_cq->cb_lock);156spin_lock_init(&my_cq->task_lock);157atomic_set(&my_cq->nr_events, 0);158init_waitqueue_head(&my_cq->wait_completion);159160cq = &my_cq->ib_cq;161162adapter_handle = shca->ipz_hca_handle;163param.eq_handle = shca->eq.ipz_eq_handle;164165do {166if (!idr_pre_get(&ehca_cq_idr, GFP_KERNEL)) {167cq = ERR_PTR(-ENOMEM);168ehca_err(device, "Can't reserve idr nr. device=%p",169device);170goto create_cq_exit1;171}172173write_lock_irqsave(&ehca_cq_idr_lock, flags);174ret = idr_get_new(&ehca_cq_idr, my_cq, &my_cq->token);175write_unlock_irqrestore(&ehca_cq_idr_lock, flags);176} while (ret == -EAGAIN);177178if (ret) {179cq = ERR_PTR(-ENOMEM);180ehca_err(device, "Can't allocate new idr entry. device=%p",181device);182goto create_cq_exit1;183}184185if (my_cq->token > 0x1FFFFFF) {186cq = ERR_PTR(-ENOMEM);187ehca_err(device, "Invalid number of cq. device=%p", device);188goto create_cq_exit2;189}190191/*192* CQs maximum depth is 4GB-64, but we need additional 20 as buffer193* for receiving errors CQEs.194*/195param.nr_cqe = cqe + additional_cqe;196h_ret = hipz_h_alloc_resource_cq(adapter_handle, my_cq, ¶m);197198if (h_ret != H_SUCCESS) {199ehca_err(device, "hipz_h_alloc_resource_cq() failed "200"h_ret=%lli device=%p", h_ret, device);201cq = ERR_PTR(ehca2ib_return_code(h_ret));202goto create_cq_exit2;203}204205ipz_rc = ipz_queue_ctor(NULL, &my_cq->ipz_queue, param.act_pages,206EHCA_PAGESIZE, sizeof(struct ehca_cqe), 0, 0);207if (!ipz_rc) {208ehca_err(device, "ipz_queue_ctor() failed ipz_rc=%i device=%p",209ipz_rc, device);210cq = ERR_PTR(-EINVAL);211goto create_cq_exit3;212}213214for (counter = 0; counter < param.act_pages; counter++) {215vpage = ipz_qpageit_get_inc(&my_cq->ipz_queue);216if (!vpage) {217ehca_err(device, "ipz_qpageit_get_inc() "218"returns NULL device=%p", device);219cq = ERR_PTR(-EAGAIN);220goto create_cq_exit4;221}222rpage = virt_to_abs(vpage);223224h_ret = hipz_h_register_rpage_cq(adapter_handle,225my_cq->ipz_cq_handle,226&my_cq->pf,2270,2280,229rpage,2301,231my_cq->galpas.232kernel);233234if (h_ret < H_SUCCESS) {235ehca_err(device, "hipz_h_register_rpage_cq() failed "236"ehca_cq=%p cq_num=%x h_ret=%lli counter=%i "237"act_pages=%i", my_cq, my_cq->cq_number,238h_ret, counter, param.act_pages);239cq = ERR_PTR(-EINVAL);240goto create_cq_exit4;241}242243if (counter == (param.act_pages - 1)) {244vpage = ipz_qpageit_get_inc(&my_cq->ipz_queue);245if ((h_ret != H_SUCCESS) || vpage) {246ehca_err(device, "Registration of pages not "247"complete ehca_cq=%p cq_num=%x "248"h_ret=%lli", my_cq, my_cq->cq_number,249h_ret);250cq = ERR_PTR(-EAGAIN);251goto create_cq_exit4;252}253} else {254if (h_ret != H_PAGE_REGISTERED) {255ehca_err(device, "Registration of page failed "256"ehca_cq=%p cq_num=%x h_ret=%lli "257"counter=%i act_pages=%i",258my_cq, my_cq->cq_number,259h_ret, counter, param.act_pages);260cq = ERR_PTR(-ENOMEM);261goto create_cq_exit4;262}263}264}265266ipz_qeit_reset(&my_cq->ipz_queue);267268gal = my_cq->galpas.kernel;269cqx_fec = hipz_galpa_load(gal, CQTEMM_OFFSET(cqx_fec));270ehca_dbg(device, "ehca_cq=%p cq_num=%x CQX_FEC=%llx",271my_cq, my_cq->cq_number, cqx_fec);272273my_cq->ib_cq.cqe = my_cq->nr_of_entries =274param.act_nr_of_entries - additional_cqe;275my_cq->cq_number = (my_cq->ipz_cq_handle.handle) & 0xffff;276277for (i = 0; i < QP_HASHTAB_LEN; i++)278INIT_HLIST_HEAD(&my_cq->qp_hashtab[i]);279280INIT_LIST_HEAD(&my_cq->sqp_err_list);281INIT_LIST_HEAD(&my_cq->rqp_err_list);282283if (context) {284struct ipz_queue *ipz_queue = &my_cq->ipz_queue;285struct ehca_create_cq_resp resp;286memset(&resp, 0, sizeof(resp));287resp.cq_number = my_cq->cq_number;288resp.token = my_cq->token;289resp.ipz_queue.qe_size = ipz_queue->qe_size;290resp.ipz_queue.act_nr_of_sg = ipz_queue->act_nr_of_sg;291resp.ipz_queue.queue_length = ipz_queue->queue_length;292resp.ipz_queue.pagesize = ipz_queue->pagesize;293resp.ipz_queue.toggle_state = ipz_queue->toggle_state;294resp.fw_handle_ofs = (u32)295(my_cq->galpas.user.fw_handle & (PAGE_SIZE - 1));296if (ib_copy_to_udata(udata, &resp, sizeof(resp))) {297ehca_err(device, "Copy to udata failed.");298goto create_cq_exit4;299}300}301302return cq;303304create_cq_exit4:305ipz_queue_dtor(NULL, &my_cq->ipz_queue);306307create_cq_exit3:308h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 1);309if (h_ret != H_SUCCESS)310ehca_err(device, "hipz_h_destroy_cq() failed ehca_cq=%p "311"cq_num=%x h_ret=%lli", my_cq, my_cq->cq_number, h_ret);312313create_cq_exit2:314write_lock_irqsave(&ehca_cq_idr_lock, flags);315idr_remove(&ehca_cq_idr, my_cq->token);316write_unlock_irqrestore(&ehca_cq_idr_lock, flags);317318create_cq_exit1:319kmem_cache_free(cq_cache, my_cq);320321atomic_dec(&shca->num_cqs);322return cq;323}324325int ehca_destroy_cq(struct ib_cq *cq)326{327u64 h_ret;328struct ehca_cq *my_cq = container_of(cq, struct ehca_cq, ib_cq);329int cq_num = my_cq->cq_number;330struct ib_device *device = cq->device;331struct ehca_shca *shca = container_of(device, struct ehca_shca,332ib_device);333struct ipz_adapter_handle adapter_handle = shca->ipz_hca_handle;334unsigned long flags;335336if (cq->uobject) {337if (my_cq->mm_count_galpa || my_cq->mm_count_queue) {338ehca_err(device, "Resources still referenced in "339"user space cq_num=%x", my_cq->cq_number);340return -EINVAL;341}342}343344/*345* remove the CQ from the idr first to make sure346* no more interrupt tasklets will touch this CQ347*/348write_lock_irqsave(&ehca_cq_idr_lock, flags);349idr_remove(&ehca_cq_idr, my_cq->token);350write_unlock_irqrestore(&ehca_cq_idr_lock, flags);351352/* now wait until all pending events have completed */353wait_event(my_cq->wait_completion, !atomic_read(&my_cq->nr_events));354355/* nobody's using our CQ any longer -- we can destroy it */356h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 0);357if (h_ret == H_R_STATE) {358/* cq in err: read err data and destroy it forcibly */359ehca_dbg(device, "ehca_cq=%p cq_num=%x resource=%llx in err "360"state. Try to delete it forcibly.",361my_cq, cq_num, my_cq->ipz_cq_handle.handle);362ehca_error_data(shca, my_cq, my_cq->ipz_cq_handle.handle);363h_ret = hipz_h_destroy_cq(adapter_handle, my_cq, 1);364if (h_ret == H_SUCCESS)365ehca_dbg(device, "cq_num=%x deleted successfully.",366cq_num);367}368if (h_ret != H_SUCCESS) {369ehca_err(device, "hipz_h_destroy_cq() failed h_ret=%lli "370"ehca_cq=%p cq_num=%x", h_ret, my_cq, cq_num);371return ehca2ib_return_code(h_ret);372}373ipz_queue_dtor(NULL, &my_cq->ipz_queue);374kmem_cache_free(cq_cache, my_cq);375376atomic_dec(&shca->num_cqs);377return 0;378}379380int ehca_resize_cq(struct ib_cq *cq, int cqe, struct ib_udata *udata)381{382/* TODO: proper resize needs to be done */383ehca_err(cq->device, "not implemented yet");384385return -EFAULT;386}387388int ehca_init_cq_cache(void)389{390cq_cache = kmem_cache_create("ehca_cache_cq",391sizeof(struct ehca_cq), 0,392SLAB_HWCACHE_ALIGN,393NULL);394if (!cq_cache)395return -ENOMEM;396return 0;397}398399void ehca_cleanup_cq_cache(void)400{401if (cq_cache)402kmem_cache_destroy(cq_cache);403}404405406