linux/drivers/infiniband/hw/ipath/ipath_keys.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2006, 2007 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 <asm/io.h>
  35
  36#include "ipath_verbs.h"
  37#include "ipath_kernel.h"
  38
  39/**
  40 * ipath_alloc_lkey - allocate an lkey
  41 * @rkt: lkey table in which to allocate the lkey
  42 * @mr: memory region that this lkey protects
  43 *
  44 * Returns 1 if successful, otherwise returns 0.
  45 */
  46
  47int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr)
  48{
  49        unsigned long flags;
  50        u32 r;
  51        u32 n;
  52        int ret;
  53
  54        spin_lock_irqsave(&rkt->lock, flags);
  55
  56        /* Find the next available LKEY */
  57        r = n = rkt->next;
  58        for (;;) {
  59                if (rkt->table[r] == NULL)
  60                        break;
  61                r = (r + 1) & (rkt->max - 1);
  62                if (r == n) {
  63                        spin_unlock_irqrestore(&rkt->lock, flags);
  64                        ipath_dbg("LKEY table full\n");
  65                        ret = 0;
  66                        goto bail;
  67                }
  68        }
  69        rkt->next = (r + 1) & (rkt->max - 1);
  70        /*
  71         * Make sure lkey is never zero which is reserved to indicate an
  72         * unrestricted LKEY.
  73         */
  74        rkt->gen++;
  75        mr->lkey = (r << (32 - ib_ipath_lkey_table_size)) |
  76                ((((1 << (24 - ib_ipath_lkey_table_size)) - 1) & rkt->gen)
  77                 << 8);
  78        if (mr->lkey == 0) {
  79                mr->lkey |= 1 << 8;
  80                rkt->gen++;
  81        }
  82        rkt->table[r] = mr;
  83        spin_unlock_irqrestore(&rkt->lock, flags);
  84
  85        ret = 1;
  86
  87bail:
  88        return ret;
  89}
  90
  91/**
  92 * ipath_free_lkey - free an lkey
  93 * @rkt: table from which to free the lkey
  94 * @lkey: lkey id to free
  95 */
  96void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey)
  97{
  98        unsigned long flags;
  99        u32 r;
 100
 101        if (lkey == 0)
 102                return;
 103        r = lkey >> (32 - ib_ipath_lkey_table_size);
 104        spin_lock_irqsave(&rkt->lock, flags);
 105        rkt->table[r] = NULL;
 106        spin_unlock_irqrestore(&rkt->lock, flags);
 107}
 108
 109/**
 110 * ipath_lkey_ok - check IB SGE for validity and initialize
 111 * @rkt: table containing lkey to check SGE against
 112 * @isge: outgoing internal SGE
 113 * @sge: SGE to check
 114 * @acc: access flags
 115 *
 116 * Return 1 if valid and successful, otherwise returns 0.
 117 *
 118 * Check the IB SGE for validity and initialize our internal version
 119 * of it.
 120 */
 121int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
 122                  struct ib_sge *sge, int acc)
 123{
 124        struct ipath_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;
 125        struct ipath_mregion *mr;
 126        unsigned n, m;
 127        size_t off;
 128        int ret;
 129
 130        /*
 131         * We use LKEY == zero for kernel virtual addresses
 132         * (see ipath_get_dma_mr and ipath_dma.c).
 133         */
 134        if (sge->lkey == 0) {
 135                /* always a kernel port, no locking needed */
 136                struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
 137
 138                if (pd->user) {
 139                        ret = 0;
 140                        goto bail;
 141                }
 142                isge->mr = NULL;
 143                isge->vaddr = (void *) sge->addr;
 144                isge->length = sge->length;
 145                isge->sge_length = sge->length;
 146                ret = 1;
 147                goto bail;
 148        }
 149        mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))];
 150        if (unlikely(mr == NULL || mr->lkey != sge->lkey ||
 151                     qp->ibqp.pd != mr->pd)) {
 152                ret = 0;
 153                goto bail;
 154        }
 155
 156        off = sge->addr - mr->user_base;
 157        if (unlikely(sge->addr < mr->user_base ||
 158                     off + sge->length > mr->length ||
 159                     (mr->access_flags & acc) != acc)) {
 160                ret = 0;
 161                goto bail;
 162        }
 163
 164        off += mr->offset;
 165        m = 0;
 166        n = 0;
 167        while (off >= mr->map[m]->segs[n].length) {
 168                off -= mr->map[m]->segs[n].length;
 169                n++;
 170                if (n >= IPATH_SEGSZ) {
 171                        m++;
 172                        n = 0;
 173                }
 174        }
 175        isge->mr = mr;
 176        isge->vaddr = mr->map[m]->segs[n].vaddr + off;
 177        isge->length = mr->map[m]->segs[n].length - off;
 178        isge->sge_length = sge->length;
 179        isge->m = m;
 180        isge->n = n;
 181
 182        ret = 1;
 183
 184bail:
 185        return ret;
 186}
 187
 188/**
 189 * ipath_rkey_ok - check the IB virtual address, length, and RKEY
 190 * @dev: infiniband device
 191 * @ss: SGE state
 192 * @len: length of data
 193 * @vaddr: virtual address to place data
 194 * @rkey: rkey to check
 195 * @acc: access flags
 196 *
 197 * Return 1 if successful, otherwise 0.
 198 */
 199int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
 200                  u32 len, u64 vaddr, u32 rkey, int acc)
 201{
 202        struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
 203        struct ipath_lkey_table *rkt = &dev->lk_table;
 204        struct ipath_sge *sge = &ss->sge;
 205        struct ipath_mregion *mr;
 206        unsigned n, m;
 207        size_t off;
 208        int ret;
 209
 210        /*
 211         * We use RKEY == zero for kernel virtual addresses
 212         * (see ipath_get_dma_mr and ipath_dma.c).
 213         */
 214        if (rkey == 0) {
 215                /* always a kernel port, no locking needed */
 216                struct ipath_pd *pd = to_ipd(qp->ibqp.pd);
 217
 218                if (pd->user) {
 219                        ret = 0;
 220                        goto bail;
 221                }
 222                sge->mr = NULL;
 223                sge->vaddr = (void *) vaddr;
 224                sge->length = len;
 225                sge->sge_length = len;
 226                ss->sg_list = NULL;
 227                ss->num_sge = 1;
 228                ret = 1;
 229                goto bail;
 230        }
 231
 232        mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))];
 233        if (unlikely(mr == NULL || mr->lkey != rkey ||
 234                     qp->ibqp.pd != mr->pd)) {
 235                ret = 0;
 236                goto bail;
 237        }
 238
 239        off = vaddr - mr->iova;
 240        if (unlikely(vaddr < mr->iova || off + len > mr->length ||
 241                     (mr->access_flags & acc) == 0)) {
 242                ret = 0;
 243                goto bail;
 244        }
 245
 246        off += mr->offset;
 247        m = 0;
 248        n = 0;
 249        while (off >= mr->map[m]->segs[n].length) {
 250                off -= mr->map[m]->segs[n].length;
 251                n++;
 252                if (n >= IPATH_SEGSZ) {
 253                        m++;
 254                        n = 0;
 255                }
 256        }
 257        sge->mr = mr;
 258        sge->vaddr = mr->map[m]->segs[n].vaddr + off;
 259        sge->length = mr->map[m]->segs[n].length - off;
 260        sge->sge_length = len;
 261        sge->m = m;
 262        sge->n = n;
 263        ss->sg_list = NULL;
 264        ss->num_sge = 1;
 265
 266        ret = 1;
 267
 268bail:
 269        return ret;
 270}
 271