Path: blob/master/drivers/infiniband/hw/qib/qib_keys.c
15112 views
/*1* Copyright (c) 2006, 2007, 2009 QLogic Corporation. All rights reserved.2* Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.3*4* This software is available to you under a choice of one of two5* licenses. You may choose to be licensed under the terms of the GNU6* General Public License (GPL) Version 2, available from the file7* COPYING in the main directory of this source tree, or the8* OpenIB.org BSD license below:9*10* Redistribution and use in source and binary forms, with or11* without modification, are permitted provided that the following12* conditions are met:13*14* - Redistributions of source code must retain the above15* copyright notice, this list of conditions and the following16* disclaimer.17*18* - Redistributions in binary form must reproduce the above19* copyright notice, this list of conditions and the following20* disclaimer in the documentation and/or other materials21* provided with the distribution.22*23* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30* SOFTWARE.31*/3233#include "qib.h"3435/**36* qib_alloc_lkey - allocate an lkey37* @rkt: lkey table in which to allocate the lkey38* @mr: memory region that this lkey protects39*40* Returns 1 if successful, otherwise returns 0.41*/4243int qib_alloc_lkey(struct qib_lkey_table *rkt, struct qib_mregion *mr)44{45unsigned long flags;46u32 r;47u32 n;48int ret;4950spin_lock_irqsave(&rkt->lock, flags);5152/* Find the next available LKEY */53r = rkt->next;54n = r;55for (;;) {56if (rkt->table[r] == NULL)57break;58r = (r + 1) & (rkt->max - 1);59if (r == n) {60spin_unlock_irqrestore(&rkt->lock, flags);61ret = 0;62goto bail;63}64}65rkt->next = (r + 1) & (rkt->max - 1);66/*67* Make sure lkey is never zero which is reserved to indicate an68* unrestricted LKEY.69*/70rkt->gen++;71mr->lkey = (r << (32 - ib_qib_lkey_table_size)) |72((((1 << (24 - ib_qib_lkey_table_size)) - 1) & rkt->gen)73<< 8);74if (mr->lkey == 0) {75mr->lkey |= 1 << 8;76rkt->gen++;77}78rkt->table[r] = mr;79spin_unlock_irqrestore(&rkt->lock, flags);8081ret = 1;8283bail:84return ret;85}8687/**88* qib_free_lkey - free an lkey89* @rkt: table from which to free the lkey90* @lkey: lkey id to free91*/92int qib_free_lkey(struct qib_ibdev *dev, struct qib_mregion *mr)93{94unsigned long flags;95u32 lkey = mr->lkey;96u32 r;97int ret;9899spin_lock_irqsave(&dev->lk_table.lock, flags);100if (lkey == 0) {101if (dev->dma_mr && dev->dma_mr == mr) {102ret = atomic_read(&dev->dma_mr->refcount);103if (!ret)104dev->dma_mr = NULL;105} else106ret = 0;107} else {108r = lkey >> (32 - ib_qib_lkey_table_size);109ret = atomic_read(&dev->lk_table.table[r]->refcount);110if (!ret)111dev->lk_table.table[r] = NULL;112}113spin_unlock_irqrestore(&dev->lk_table.lock, flags);114115if (ret)116ret = -EBUSY;117return ret;118}119120/**121* qib_lkey_ok - check IB SGE for validity and initialize122* @rkt: table containing lkey to check SGE against123* @isge: outgoing internal SGE124* @sge: SGE to check125* @acc: access flags126*127* Return 1 if valid and successful, otherwise returns 0.128*129* Check the IB SGE for validity and initialize our internal version130* of it.131*/132int qib_lkey_ok(struct qib_lkey_table *rkt, struct qib_pd *pd,133struct qib_sge *isge, struct ib_sge *sge, int acc)134{135struct qib_mregion *mr;136unsigned n, m;137size_t off;138unsigned long flags;139140/*141* We use LKEY == zero for kernel virtual addresses142* (see qib_get_dma_mr and qib_dma.c).143*/144spin_lock_irqsave(&rkt->lock, flags);145if (sge->lkey == 0) {146struct qib_ibdev *dev = to_idev(pd->ibpd.device);147148if (pd->user)149goto bail;150if (!dev->dma_mr)151goto bail;152atomic_inc(&dev->dma_mr->refcount);153spin_unlock_irqrestore(&rkt->lock, flags);154155isge->mr = dev->dma_mr;156isge->vaddr = (void *) sge->addr;157isge->length = sge->length;158isge->sge_length = sge->length;159isge->m = 0;160isge->n = 0;161goto ok;162}163mr = rkt->table[(sge->lkey >> (32 - ib_qib_lkey_table_size))];164if (unlikely(mr == NULL || mr->lkey != sge->lkey ||165mr->pd != &pd->ibpd))166goto bail;167168off = sge->addr - mr->user_base;169if (unlikely(sge->addr < mr->user_base ||170off + sge->length > mr->length ||171(mr->access_flags & acc) != acc))172goto bail;173atomic_inc(&mr->refcount);174spin_unlock_irqrestore(&rkt->lock, flags);175176off += mr->offset;177if (mr->page_shift) {178/*179page sizes are uniform power of 2 so no loop is necessary180entries_spanned_by_off is the number of times the loop below181would have executed.182*/183size_t entries_spanned_by_off;184185entries_spanned_by_off = off >> mr->page_shift;186off -= (entries_spanned_by_off << mr->page_shift);187m = entries_spanned_by_off/QIB_SEGSZ;188n = entries_spanned_by_off%QIB_SEGSZ;189} else {190m = 0;191n = 0;192while (off >= mr->map[m]->segs[n].length) {193off -= mr->map[m]->segs[n].length;194n++;195if (n >= QIB_SEGSZ) {196m++;197n = 0;198}199}200}201isge->mr = mr;202isge->vaddr = mr->map[m]->segs[n].vaddr + off;203isge->length = mr->map[m]->segs[n].length - off;204isge->sge_length = sge->length;205isge->m = m;206isge->n = n;207ok:208return 1;209bail:210spin_unlock_irqrestore(&rkt->lock, flags);211return 0;212}213214/**215* qib_rkey_ok - check the IB virtual address, length, and RKEY216* @dev: infiniband device217* @ss: SGE state218* @len: length of data219* @vaddr: virtual address to place data220* @rkey: rkey to check221* @acc: access flags222*223* Return 1 if successful, otherwise 0.224*/225int qib_rkey_ok(struct qib_qp *qp, struct qib_sge *sge,226u32 len, u64 vaddr, u32 rkey, int acc)227{228struct qib_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;229struct qib_mregion *mr;230unsigned n, m;231size_t off;232unsigned long flags;233234/*235* We use RKEY == zero for kernel virtual addresses236* (see qib_get_dma_mr and qib_dma.c).237*/238spin_lock_irqsave(&rkt->lock, flags);239if (rkey == 0) {240struct qib_pd *pd = to_ipd(qp->ibqp.pd);241struct qib_ibdev *dev = to_idev(pd->ibpd.device);242243if (pd->user)244goto bail;245if (!dev->dma_mr)246goto bail;247atomic_inc(&dev->dma_mr->refcount);248spin_unlock_irqrestore(&rkt->lock, flags);249250sge->mr = dev->dma_mr;251sge->vaddr = (void *) vaddr;252sge->length = len;253sge->sge_length = len;254sge->m = 0;255sge->n = 0;256goto ok;257}258259mr = rkt->table[(rkey >> (32 - ib_qib_lkey_table_size))];260if (unlikely(mr == NULL || mr->lkey != rkey || qp->ibqp.pd != mr->pd))261goto bail;262263off = vaddr - mr->iova;264if (unlikely(vaddr < mr->iova || off + len > mr->length ||265(mr->access_flags & acc) == 0))266goto bail;267atomic_inc(&mr->refcount);268spin_unlock_irqrestore(&rkt->lock, flags);269270off += mr->offset;271if (mr->page_shift) {272/*273page sizes are uniform power of 2 so no loop is necessary274entries_spanned_by_off is the number of times the loop below275would have executed.276*/277size_t entries_spanned_by_off;278279entries_spanned_by_off = off >> mr->page_shift;280off -= (entries_spanned_by_off << mr->page_shift);281m = entries_spanned_by_off/QIB_SEGSZ;282n = entries_spanned_by_off%QIB_SEGSZ;283} else {284m = 0;285n = 0;286while (off >= mr->map[m]->segs[n].length) {287off -= mr->map[m]->segs[n].length;288n++;289if (n >= QIB_SEGSZ) {290m++;291n = 0;292}293}294}295sge->mr = mr;296sge->vaddr = mr->map[m]->segs[n].vaddr + off;297sge->length = mr->map[m]->segs[n].length - off;298sge->sge_length = len;299sge->m = m;300sge->n = n;301ok:302return 1;303bail:304spin_unlock_irqrestore(&rkt->lock, flags);305return 0;306}307308/*309* Initialize the memory region specified by the work reqeust.310*/311int qib_fast_reg_mr(struct qib_qp *qp, struct ib_send_wr *wr)312{313struct qib_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;314struct qib_pd *pd = to_ipd(qp->ibqp.pd);315struct qib_mregion *mr;316u32 rkey = wr->wr.fast_reg.rkey;317unsigned i, n, m;318int ret = -EINVAL;319unsigned long flags;320u64 *page_list;321size_t ps;322323spin_lock_irqsave(&rkt->lock, flags);324if (pd->user || rkey == 0)325goto bail;326327mr = rkt->table[(rkey >> (32 - ib_qib_lkey_table_size))];328if (unlikely(mr == NULL || qp->ibqp.pd != mr->pd))329goto bail;330331if (wr->wr.fast_reg.page_list_len > mr->max_segs)332goto bail;333334ps = 1UL << wr->wr.fast_reg.page_shift;335if (wr->wr.fast_reg.length > ps * wr->wr.fast_reg.page_list_len)336goto bail;337338mr->user_base = wr->wr.fast_reg.iova_start;339mr->iova = wr->wr.fast_reg.iova_start;340mr->lkey = rkey;341mr->length = wr->wr.fast_reg.length;342mr->access_flags = wr->wr.fast_reg.access_flags;343page_list = wr->wr.fast_reg.page_list->page_list;344m = 0;345n = 0;346for (i = 0; i < wr->wr.fast_reg.page_list_len; i++) {347mr->map[m]->segs[n].vaddr = (void *) page_list[i];348mr->map[m]->segs[n].length = ps;349if (++n == QIB_SEGSZ) {350m++;351n = 0;352}353}354355ret = 0;356bail:357spin_unlock_irqrestore(&rkt->lock, flags);358return ret;359}360361362