linux/drivers/infiniband/hw/qib/qib_keys.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006, 2007, 2009 QLogic Corporation. All rights reserved.
   3 * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved.
   4 *
   5 * This software is available to you under a choice of one of two
   6 * licenses.  You may choose to be licensed under the terms of the GNU
   7 * General Public License (GPL) Version 2, available from the file
   8 * COPYING in the main directory of this source tree, or the
   9 * OpenIB.org BSD license below:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      - Redistributions of source code must retain the above
  16 *        copyright notice, this list of conditions and the following
  17 *        disclaimer.
  18 *
  19 *      - Redistributions in binary form must reproduce the above
  20 *        copyright notice, this list of conditions and the following
  21 *        disclaimer in the documentation and/or other materials
  22 *        provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34#include "qib.h"
  35
  36/**
  37 * qib_alloc_lkey - allocate an lkey
  38 * @mr: memory region that this lkey protects
  39 * @dma_region: 0->normal key, 1->restricted DMA key
  40 *
  41 * Returns 0 if successful, otherwise returns -errno.
  42 *
  43 * Increments mr reference count as required.
  44 *
  45 * Sets the lkey field mr for non-dma regions.
  46 *
  47 */
  48
  49int qib_alloc_lkey(struct rvt_mregion *mr, int dma_region)
  50{
  51        unsigned long flags;
  52        u32 r;
  53        u32 n;
  54        int ret = 0;
  55        struct qib_ibdev *dev = to_idev(mr->pd->device);
  56        struct rvt_lkey_table *rkt = &dev->lk_table;
  57
  58        spin_lock_irqsave(&rkt->lock, flags);
  59
  60        /* special case for dma_mr lkey == 0 */
  61        if (dma_region) {
  62                struct rvt_mregion *tmr;
  63
  64                tmr = rcu_access_pointer(dev->dma_mr);
  65                if (!tmr) {
  66                        qib_get_mr(mr);
  67                        rcu_assign_pointer(dev->dma_mr, mr);
  68                        mr->lkey_published = 1;
  69                }
  70                goto success;
  71        }
  72
  73        /* Find the next available LKEY */
  74        r = rkt->next;
  75        n = r;
  76        for (;;) {
  77                if (rkt->table[r] == NULL)
  78                        break;
  79                r = (r + 1) & (rkt->max - 1);
  80                if (r == n)
  81                        goto bail;
  82        }
  83        rkt->next = (r + 1) & (rkt->max - 1);
  84        /*
  85         * Make sure lkey is never zero which is reserved to indicate an
  86         * unrestricted LKEY.
  87         */
  88        rkt->gen++;
  89        /*
  90         * bits are capped in qib_verbs.c to insure enough bits
  91         * for generation number
  92         */
  93        mr->lkey = (r << (32 - ib_rvt_lkey_table_size)) |
  94                ((((1 << (24 - ib_rvt_lkey_table_size)) - 1) & rkt->gen)
  95                 << 8);
  96        if (mr->lkey == 0) {
  97                mr->lkey |= 1 << 8;
  98                rkt->gen++;
  99        }
 100        qib_get_mr(mr);
 101        rcu_assign_pointer(rkt->table[r], mr);
 102        mr->lkey_published = 1;
 103success:
 104        spin_unlock_irqrestore(&rkt->lock, flags);
 105out:
 106        return ret;
 107bail:
 108        spin_unlock_irqrestore(&rkt->lock, flags);
 109        ret = -ENOMEM;
 110        goto out;
 111}
 112
 113/**
 114 * qib_free_lkey - free an lkey
 115 * @mr: mr to free from tables
 116 */
 117void qib_free_lkey(struct rvt_mregion *mr)
 118{
 119        unsigned long flags;
 120        u32 lkey = mr->lkey;
 121        u32 r;
 122        struct qib_ibdev *dev = to_idev(mr->pd->device);
 123        struct rvt_lkey_table *rkt = &dev->lk_table;
 124
 125        spin_lock_irqsave(&rkt->lock, flags);
 126        if (!mr->lkey_published)
 127                goto out;
 128        if (lkey == 0)
 129                RCU_INIT_POINTER(dev->dma_mr, NULL);
 130        else {
 131                r = lkey >> (32 - ib_rvt_lkey_table_size);
 132                RCU_INIT_POINTER(rkt->table[r], NULL);
 133        }
 134        qib_put_mr(mr);
 135        mr->lkey_published = 0;
 136out:
 137        spin_unlock_irqrestore(&rkt->lock, flags);
 138}
 139
 140/**
 141 * qib_rkey_ok - check the IB virtual address, length, and RKEY
 142 * @qp: qp for validation
 143 * @sge: SGE state
 144 * @len: length of data
 145 * @vaddr: virtual address to place data
 146 * @rkey: rkey to check
 147 * @acc: access flags
 148 *
 149 * Return 1 if successful, otherwise 0.
 150 *
 151 * increments the reference count upon success
 152 */
 153int qib_rkey_ok(struct rvt_qp *qp, struct rvt_sge *sge,
 154                u32 len, u64 vaddr, u32 rkey, int acc)
 155{
 156        struct rvt_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;
 157        struct rvt_mregion *mr;
 158        unsigned n, m;
 159        size_t off;
 160
 161        /*
 162         * We use RKEY == zero for kernel virtual addresses
 163         * (see qib_get_dma_mr and qib_dma.c).
 164         */
 165        rcu_read_lock();
 166        if (rkey == 0) {
 167                struct rvt_pd *pd = ibpd_to_rvtpd(qp->ibqp.pd);
 168                struct qib_ibdev *dev = to_idev(pd->ibpd.device);
 169
 170                if (pd->user)
 171                        goto bail;
 172                mr = rcu_dereference(dev->dma_mr);
 173                if (!mr)
 174                        goto bail;
 175                if (unlikely(!atomic_inc_not_zero(&mr->refcount)))
 176                        goto bail;
 177                rcu_read_unlock();
 178
 179                sge->mr = mr;
 180                sge->vaddr = (void *) vaddr;
 181                sge->length = len;
 182                sge->sge_length = len;
 183                sge->m = 0;
 184                sge->n = 0;
 185                goto ok;
 186        }
 187
 188        mr = rcu_dereference(
 189                rkt->table[(rkey >> (32 - ib_rvt_lkey_table_size))]);
 190        if (unlikely(!mr || mr->lkey != rkey || qp->ibqp.pd != mr->pd))
 191                goto bail;
 192
 193        off = vaddr - mr->iova;
 194        if (unlikely(vaddr < mr->iova || off + len > mr->length ||
 195                     (mr->access_flags & acc) == 0))
 196                goto bail;
 197        if (unlikely(!atomic_inc_not_zero(&mr->refcount)))
 198                goto bail;
 199        rcu_read_unlock();
 200
 201        off += mr->offset;
 202        if (mr->page_shift) {
 203                /*
 204                page sizes are uniform power of 2 so no loop is necessary
 205                entries_spanned_by_off is the number of times the loop below
 206                would have executed.
 207                */
 208                size_t entries_spanned_by_off;
 209
 210                entries_spanned_by_off = off >> mr->page_shift;
 211                off -= (entries_spanned_by_off << mr->page_shift);
 212                m = entries_spanned_by_off / RVT_SEGSZ;
 213                n = entries_spanned_by_off % RVT_SEGSZ;
 214        } else {
 215                m = 0;
 216                n = 0;
 217                while (off >= mr->map[m]->segs[n].length) {
 218                        off -= mr->map[m]->segs[n].length;
 219                        n++;
 220                        if (n >= RVT_SEGSZ) {
 221                                m++;
 222                                n = 0;
 223                        }
 224                }
 225        }
 226        sge->mr = mr;
 227        sge->vaddr = mr->map[m]->segs[n].vaddr + off;
 228        sge->length = mr->map[m]->segs[n].length - off;
 229        sge->sge_length = len;
 230        sge->m = m;
 231        sge->n = n;
 232ok:
 233        return 1;
 234bail:
 235        rcu_read_unlock();
 236        return 0;
 237}
 238
 239