Merge tag 'renesas-fixes-for-v4.18' of https://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / infiniband / hw / mlx4 / mr.c
index 61d8b06..ed1f253 100644 (file)
@@ -367,6 +367,40 @@ end:
        return block_shift;
 }
 
+static struct ib_umem *mlx4_get_umem_mr(struct ib_ucontext *context, u64 start,
+                                       u64 length, u64 virt_addr,
+                                       int access_flags)
+{
+       /*
+        * Force registering the memory as writable if the underlying pages
+        * are writable.  This is so rereg can change the access permissions
+        * from readable to writable without having to run through ib_umem_get
+        * again
+        */
+       if (!ib_access_writable(access_flags)) {
+               struct vm_area_struct *vma;
+
+               down_read(&current->mm->mmap_sem);
+               /*
+                * FIXME: Ideally this would iterate over all the vmas that
+                * cover the memory, but for now it requires a single vma to
+                * entirely cover the MR to support RO mappings.
+                */
+               vma = find_vma(current->mm, start);
+               if (vma && vma->vm_end >= start + length &&
+                   vma->vm_start <= start) {
+                       if (vma->vm_flags & VM_WRITE)
+                               access_flags |= IB_ACCESS_LOCAL_WRITE;
+               } else {
+                       access_flags |= IB_ACCESS_LOCAL_WRITE;
+               }
+
+               up_read(&current->mm->mmap_sem);
+       }
+
+       return ib_umem_get(context, start, length, access_flags, 0);
+}
+
 struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
                                  u64 virt_addr, int access_flags,
                                  struct ib_udata *udata)
@@ -381,10 +415,8 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
        if (!mr)
                return ERR_PTR(-ENOMEM);
 
-       /* Force registering the memory as writable. */
-       /* Used for memory re-registeration. HCA protects the access */
-       mr->umem = ib_umem_get(pd->uobject->context, start, length,
-                              access_flags | IB_ACCESS_LOCAL_WRITE, 0);
+       mr->umem = mlx4_get_umem_mr(pd->uobject->context, start, length,
+                                   virt_addr, access_flags);
        if (IS_ERR(mr->umem)) {
                err = PTR_ERR(mr->umem);
                goto err_free;
@@ -454,6 +486,9 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags,
        }
 
        if (flags & IB_MR_REREG_ACCESS) {
+               if (ib_access_writable(mr_access_flags) && !mmr->umem->writable)
+                       return -EPERM;
+
                err = mlx4_mr_hw_change_access(dev->dev, *pmpt_entry,
                                               convert_access(mr_access_flags));
 
@@ -467,10 +502,9 @@ int mlx4_ib_rereg_user_mr(struct ib_mr *mr, int flags,
 
                mlx4_mr_rereg_mem_cleanup(dev->dev, &mmr->mmr);
                ib_umem_release(mmr->umem);
-               mmr->umem = ib_umem_get(mr->uobject->context, start, length,
-                                       mr_access_flags |
-                                       IB_ACCESS_LOCAL_WRITE,
-                                       0);
+               mmr->umem =
+                       mlx4_get_umem_mr(mr->uobject->context, start, length,
+                                        virt_addr, mr_access_flags);
                if (IS_ERR(mmr->umem)) {
                        err = PTR_ERR(mmr->umem);
                        /* Prevent mlx4_ib_dereg_mr from free'ing invalid pointer */