Merge tag 'rdma-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[firefly-linux-kernel-4.4.55.git] / drivers / infiniband / hw / qib / qib_keys.c
index 8fd19a47df0c76a7d7527f302d32449c6bdbec4d..e9486c74c2262b5997aca9599971d625e9399677 100644 (file)
 
 /**
  * qib_alloc_lkey - allocate an lkey
- * @rkt: lkey table in which to allocate the lkey
  * @mr: memory region that this lkey protects
+ * @dma_region: 0->normal key, 1->restricted DMA key
+ *
+ * Returns 0 if successful, otherwise returns -errno.
+ *
+ * Increments mr reference count as required.
+ *
+ * Sets the lkey field mr for non-dma regions.
  *
- * Returns 1 if successful, otherwise returns 0.
  */
 
-int qib_alloc_lkey(struct qib_lkey_table *rkt, struct qib_mregion *mr)
+int qib_alloc_lkey(struct qib_mregion *mr, int dma_region)
 {
        unsigned long flags;
        u32 r;
        u32 n;
-       int ret;
+       int ret = 0;
+       struct qib_ibdev *dev = to_idev(mr->pd->device);
+       struct qib_lkey_table *rkt = &dev->lk_table;
 
        spin_lock_irqsave(&rkt->lock, flags);
 
+       /* special case for dma_mr lkey == 0 */
+       if (dma_region) {
+               struct qib_mregion *tmr;
+
+               tmr = rcu_dereference(dev->dma_mr);
+               if (!tmr) {
+                       qib_get_mr(mr);
+                       rcu_assign_pointer(dev->dma_mr, mr);
+                       mr->lkey_published = 1;
+               }
+               goto success;
+       }
+
        /* Find the next available LKEY */
        r = rkt->next;
        n = r;
@@ -57,11 +77,8 @@ int qib_alloc_lkey(struct qib_lkey_table *rkt, struct qib_mregion *mr)
                if (rkt->table[r] == NULL)
                        break;
                r = (r + 1) & (rkt->max - 1);
-               if (r == n) {
-                       spin_unlock_irqrestore(&rkt->lock, flags);
-                       ret = 0;
+               if (r == n)
                        goto bail;
-               }
        }
        rkt->next = (r + 1) & (rkt->max - 1);
        /*
@@ -76,57 +93,58 @@ int qib_alloc_lkey(struct qib_lkey_table *rkt, struct qib_mregion *mr)
                mr->lkey |= 1 << 8;
                rkt->gen++;
        }
-       rkt->table[r] = mr;
+       qib_get_mr(mr);
+       rcu_assign_pointer(rkt->table[r], mr);
+       mr->lkey_published = 1;
+success:
        spin_unlock_irqrestore(&rkt->lock, flags);
-
-       ret = 1;
-
-bail:
+out:
        return ret;
+bail:
+       spin_unlock_irqrestore(&rkt->lock, flags);
+       ret = -ENOMEM;
+       goto out;
 }
 
 /**
  * qib_free_lkey - free an lkey
- * @rkt: table from which to free the lkey
- * @lkey: lkey id to free
+ * @mr: mr to free from tables
  */
-int qib_free_lkey(struct qib_ibdev *dev, struct qib_mregion *mr)
+void qib_free_lkey(struct qib_mregion *mr)
 {
        unsigned long flags;
        u32 lkey = mr->lkey;
        u32 r;
-       int ret;
+       struct qib_ibdev *dev = to_idev(mr->pd->device);
+       struct qib_lkey_table *rkt = &dev->lk_table;
 
-       spin_lock_irqsave(&dev->lk_table.lock, flags);
-       if (lkey == 0) {
-               if (dev->dma_mr && dev->dma_mr == mr) {
-                       ret = atomic_read(&dev->dma_mr->refcount);
-                       if (!ret)
-                               dev->dma_mr = NULL;
-               } else
-                       ret = 0;
-       } else {
+       spin_lock_irqsave(&rkt->lock, flags);
+       if (!mr->lkey_published)
+               goto out;
+       if (lkey == 0)
+               rcu_assign_pointer(dev->dma_mr, NULL);
+       else {
                r = lkey >> (32 - ib_qib_lkey_table_size);
-               ret = atomic_read(&dev->lk_table.table[r]->refcount);
-               if (!ret)
-                       dev->lk_table.table[r] = NULL;
+               rcu_assign_pointer(rkt->table[r], NULL);
        }
-       spin_unlock_irqrestore(&dev->lk_table.lock, flags);
-
-       if (ret)
-               ret = -EBUSY;
-       return ret;
+       qib_put_mr(mr);
+       mr->lkey_published = 0;
+out:
+       spin_unlock_irqrestore(&rkt->lock, flags);
 }
 
 /**
  * qib_lkey_ok - check IB SGE for validity and initialize
  * @rkt: table containing lkey to check SGE against
+ * @pd: protection domain
  * @isge: outgoing internal SGE
  * @sge: SGE to check
  * @acc: access flags
  *
  * Return 1 if valid and successful, otherwise returns 0.
  *
+ * increments the reference count upon success
+ *
  * Check the IB SGE for validity and initialize our internal version
  * of it.
  */
@@ -136,24 +154,25 @@ int qib_lkey_ok(struct qib_lkey_table *rkt, struct qib_pd *pd,
        struct qib_mregion *mr;
        unsigned n, m;
        size_t off;
-       unsigned long flags;
 
        /*
         * We use LKEY == zero for kernel virtual addresses
         * (see qib_get_dma_mr and qib_dma.c).
         */
-       spin_lock_irqsave(&rkt->lock, flags);
+       rcu_read_lock();
        if (sge->lkey == 0) {
                struct qib_ibdev *dev = to_idev(pd->ibpd.device);
 
                if (pd->user)
                        goto bail;
-               if (!dev->dma_mr)
+               mr = rcu_dereference(dev->dma_mr);
+               if (!mr)
                        goto bail;
-               atomic_inc(&dev->dma_mr->refcount);
-               spin_unlock_irqrestore(&rkt->lock, flags);
+               if (unlikely(!atomic_inc_not_zero(&mr->refcount)))
+                       goto bail;
+               rcu_read_unlock();
 
-               isge->mr = dev->dma_mr;
+               isge->mr = mr;
                isge->vaddr = (void *) sge->addr;
                isge->length = sge->length;
                isge->sge_length = sge->length;
@@ -161,18 +180,18 @@ int qib_lkey_ok(struct qib_lkey_table *rkt, struct qib_pd *pd,
                isge->n = 0;
                goto ok;
        }
-       mr = rkt->table[(sge->lkey >> (32 - ib_qib_lkey_table_size))];
-       if (unlikely(mr == NULL || mr->lkey != sge->lkey ||
-                    mr->pd != &pd->ibpd))
+       mr = rcu_dereference(
+               rkt->table[(sge->lkey >> (32 - ib_qib_lkey_table_size))]);
+       if (unlikely(!mr || mr->lkey != sge->lkey || mr->pd != &pd->ibpd))
                goto bail;
 
        off = sge->addr - mr->user_base;
-       if (unlikely(sge->addr < mr->user_base ||
-                    off + sge->length > mr->length ||
-                    (mr->access_flags & acc) != acc))
+       if (unlikely(sge->addr < mr->iova || off + sge->length > mr->length ||
+                    (mr->access_flags & acc) == 0))
                goto bail;
-       atomic_inc(&mr->refcount);
-       spin_unlock_irqrestore(&rkt->lock, flags);
+       if (unlikely(!atomic_inc_not_zero(&mr->refcount)))
+               goto bail;
+       rcu_read_unlock();
 
        off += mr->offset;
        if (mr->page_shift) {
@@ -208,20 +227,22 @@ int qib_lkey_ok(struct qib_lkey_table *rkt, struct qib_pd *pd,
 ok:
        return 1;
 bail:
-       spin_unlock_irqrestore(&rkt->lock, flags);
+       rcu_read_unlock();
        return 0;
 }
 
 /**
  * qib_rkey_ok - check the IB virtual address, length, and RKEY
- * @dev: infiniband device
- * @ss: SGE state
+ * @qp: qp for validation
+ * @sge: SGE state
  * @len: length of data
  * @vaddr: virtual address to place data
  * @rkey: rkey to check
  * @acc: access flags
  *
  * Return 1 if successful, otherwise 0.
+ *
+ * increments the reference count upon success
  */
 int qib_rkey_ok(struct qib_qp *qp, struct qib_sge *sge,
                u32 len, u64 vaddr, u32 rkey, int acc)
@@ -230,25 +251,26 @@ int qib_rkey_ok(struct qib_qp *qp, struct qib_sge *sge,
        struct qib_mregion *mr;
        unsigned n, m;
        size_t off;
-       unsigned long flags;
 
        /*
         * We use RKEY == zero for kernel virtual addresses
         * (see qib_get_dma_mr and qib_dma.c).
         */
-       spin_lock_irqsave(&rkt->lock, flags);
+       rcu_read_lock();
        if (rkey == 0) {
                struct qib_pd *pd = to_ipd(qp->ibqp.pd);
                struct qib_ibdev *dev = to_idev(pd->ibpd.device);
 
                if (pd->user)
                        goto bail;
-               if (!dev->dma_mr)
+               mr = rcu_dereference(dev->dma_mr);
+               if (!mr)
                        goto bail;
-               atomic_inc(&dev->dma_mr->refcount);
-               spin_unlock_irqrestore(&rkt->lock, flags);
+               if (unlikely(!atomic_inc_not_zero(&mr->refcount)))
+                       goto bail;
+               rcu_read_unlock();
 
-               sge->mr = dev->dma_mr;
+               sge->mr = mr;
                sge->vaddr = (void *) vaddr;
                sge->length = len;
                sge->sge_length = len;
@@ -257,16 +279,18 @@ int qib_rkey_ok(struct qib_qp *qp, struct qib_sge *sge,
                goto ok;
        }
 
-       mr = rkt->table[(rkey >> (32 - ib_qib_lkey_table_size))];
-       if (unlikely(mr == NULL || mr->lkey != rkey || qp->ibqp.pd != mr->pd))
+       mr = rcu_dereference(
+               rkt->table[(rkey >> (32 - ib_qib_lkey_table_size))]);
+       if (unlikely(!mr || mr->lkey != rkey || qp->ibqp.pd != mr->pd))
                goto bail;
 
        off = vaddr - mr->iova;
        if (unlikely(vaddr < mr->iova || off + len > mr->length ||
                     (mr->access_flags & acc) == 0))
                goto bail;
-       atomic_inc(&mr->refcount);
-       spin_unlock_irqrestore(&rkt->lock, flags);
+       if (unlikely(!atomic_inc_not_zero(&mr->refcount)))
+               goto bail;
+       rcu_read_unlock();
 
        off += mr->offset;
        if (mr->page_shift) {
@@ -302,7 +326,7 @@ int qib_rkey_ok(struct qib_qp *qp, struct qib_sge *sge,
 ok:
        return 1;
 bail:
-       spin_unlock_irqrestore(&rkt->lock, flags);
+       rcu_read_unlock();
        return 0;
 }
 
@@ -325,7 +349,9 @@ int qib_fast_reg_mr(struct qib_qp *qp, struct ib_send_wr *wr)
        if (pd->user || rkey == 0)
                goto bail;
 
-       mr = rkt->table[(rkey >> (32 - ib_qib_lkey_table_size))];
+       mr = rcu_dereference_protected(
+               rkt->table[(rkey >> (32 - ib_qib_lkey_table_size))],
+               lockdep_is_held(&rkt->lock));
        if (unlikely(mr == NULL || qp->ibqp.pd != mr->pd))
                goto bail;