| From d8f9cc328c8888369880e2527e9186d745f2bbf6 Mon Sep 17 00:00:00 2001 |
| From: Jack Morgenstein <jackm@dev.mellanox.co.il> |
| Date: Wed, 23 May 2018 15:30:31 +0300 |
| Subject: IB/mlx4: Mark user MR as writable if actual virtual memory is writable |
| |
| From: Jack Morgenstein <jackm@dev.mellanox.co.il> |
| |
| commit d8f9cc328c8888369880e2527e9186d745f2bbf6 upstream. |
| |
| To allow rereg_user_mr to modify the MR from read-only to writable without |
| using get_user_pages again, we needed to define the initial MR as writable. |
| However, this was originally done unconditionally, without taking into |
| account the writability of the underlying virtual memory. |
| |
| As a result, any attempt to register a read-only MR over read-only |
| virtual memory failed. |
| |
| To fix this, do not add the writable flag bit when the user virtual memory |
| is not writable (e.g. const memory). |
| |
| However, when the underlying memory is NOT writable (and we therefore |
| do not define the initial MR as writable), the IB core adds a |
| "force writable" flag to its user-pages request. If this succeeds, |
| the reg_user_mr caller gets a writable copy of the original pages. |
| |
| If the user-space caller then does a rereg_user_mr operation to enable |
| writability, this will succeed. This should not be allowed, since |
| the original virtual memory was not writable. |
| |
| Cc: <stable@vger.kernel.org> |
| Fixes: 9376932d0c26 ("IB/mlx4_ib: Add support for user MR re-registration") |
| Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> |
| Signed-off-by: Jack Morgenstein <jackm@dev.mellanox.co.il> |
| Signed-off-by: Leon Romanovsky <leonro@mellanox.com> |
| Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/infiniband/hw/mlx4/mr.c | 50 +++++++++++++++++++++++++++++++++------- |
| 1 file changed, 42 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/infiniband/hw/mlx4/mr.c |
| +++ b/drivers/infiniband/hw/mlx4/mr.c |
| @@ -131,6 +131,40 @@ out: |
| return err; |
| } |
| |
| +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(¤t->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(¤t->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) |
| @@ -145,10 +179,8 @@ struct ib_mr *mlx4_ib_reg_user_mr(struct |
| 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; |
| @@ -215,6 +247,9 @@ int mlx4_ib_rereg_user_mr(struct ib_mr * |
| } |
| |
| 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)); |
| |
| @@ -228,10 +263,9 @@ int mlx4_ib_rereg_user_mr(struct ib_mr * |
| |
| 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 */ |