Commit 87773dd5 authored by Shawn Bohrer's avatar Shawn Bohrer Committed by Roland Dreier

IB: ib_umem_release() should decrement mm->pinned_vm from ib_umem_get

In debugging an application that receives -ENOMEM from ib_reg_mr(), I
found that ib_umem_get() can fail because the pinned_vm count has
wrapped causing it to always be larger than the lock limit even with
RLIMIT_MEMLOCK set to RLIM_INFINITY.

The wrapping of pinned_vm occurs because the process that calls
ib_reg_mr() will have its mm->pinned_vm count incremented.  Later a
different process with a different mm_struct than the one that
allocated the ib_umem struct ends up releasing it which results in
decrementing the new processes mm->pinned_vm count past zero and
wrapping.

I'm not entirely sure what circumstances cause a different process to
release the ib_umem than the one that allocated it but the kernel
stack trace of the freeing process from my situation looks like the
following:

    Call Trace:
     [<ffffffff814d64b1>] dump_stack+0x19/0x1b
     [<ffffffffa0b522a5>] ib_umem_release+0x1f5/0x200 [ib_core]
     [<ffffffffa0b90681>] mlx4_ib_destroy_qp+0x241/0x440 [mlx4_ib]
     [<ffffffffa0b4d93c>] ib_destroy_qp+0x12c/0x170 [ib_core]
     [<ffffffffa0cc7129>] ib_uverbs_close+0x259/0x4e0 [ib_uverbs]
     [<ffffffff81141cba>] __fput+0xba/0x240
     [<ffffffff81141e4e>] ____fput+0xe/0x10
     [<ffffffff81060894>] task_work_run+0xc4/0xe0
     [<ffffffff810029e5>] do_notify_resume+0x95/0xa0
     [<ffffffff814e3dd0>] int_signal+0x12/0x17

The following patch fixes the issue by storing the pid struct of the
process that calls ib_umem_get() so that ib_umem_release and/or
ib_umem_account() can properly decrement the pinned_vm count of the
correct mm_struct.
Signed-off-by: default avatarShawn Bohrer <sbohrer@rgmadvisors.com>
Reviewed-by: default avatarShachar Raindel <raindel@mellanox.com>
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
parent 52addcf9
...@@ -105,6 +105,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, ...@@ -105,6 +105,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
umem->length = size; umem->length = size;
umem->offset = addr & ~PAGE_MASK; umem->offset = addr & ~PAGE_MASK;
umem->page_size = PAGE_SIZE; umem->page_size = PAGE_SIZE;
umem->pid = get_task_pid(current, PIDTYPE_PID);
/* /*
* We ask for writable memory if any access flags other than * We ask for writable memory if any access flags other than
* "remote read" are set. "Local write" and "remote write" * "remote read" are set. "Local write" and "remote write"
...@@ -198,6 +199,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, ...@@ -198,6 +199,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
if (ret < 0) { if (ret < 0) {
if (need_release) if (need_release)
__ib_umem_release(context->device, umem, 0); __ib_umem_release(context->device, umem, 0);
put_pid(umem->pid);
kfree(umem); kfree(umem);
} else } else
current->mm->pinned_vm = locked; current->mm->pinned_vm = locked;
...@@ -230,15 +232,19 @@ void ib_umem_release(struct ib_umem *umem) ...@@ -230,15 +232,19 @@ void ib_umem_release(struct ib_umem *umem)
{ {
struct ib_ucontext *context = umem->context; struct ib_ucontext *context = umem->context;
struct mm_struct *mm; struct mm_struct *mm;
struct task_struct *task;
unsigned long diff; unsigned long diff;
__ib_umem_release(umem->context->device, umem, 1); __ib_umem_release(umem->context->device, umem, 1);
mm = get_task_mm(current); task = get_pid_task(umem->pid, PIDTYPE_PID);
if (!mm) { put_pid(umem->pid);
kfree(umem); if (!task)
return; goto out;
} mm = get_task_mm(task);
put_task_struct(task);
if (!mm)
goto out;
diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT;
...@@ -262,9 +268,10 @@ void ib_umem_release(struct ib_umem *umem) ...@@ -262,9 +268,10 @@ void ib_umem_release(struct ib_umem *umem)
} else } else
down_write(&mm->mmap_sem); down_write(&mm->mmap_sem);
current->mm->pinned_vm -= diff; mm->pinned_vm -= diff;
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
mmput(mm); mmput(mm);
out:
kfree(umem); kfree(umem);
} }
EXPORT_SYMBOL(ib_umem_release); EXPORT_SYMBOL(ib_umem_release);
......
...@@ -47,6 +47,7 @@ struct ib_umem { ...@@ -47,6 +47,7 @@ struct ib_umem {
int writable; int writable;
int hugetlb; int hugetlb;
struct work_struct work; struct work_struct work;
struct pid *pid;
struct mm_struct *mm; struct mm_struct *mm;
unsigned long diff; unsigned long diff;
struct sg_table sg_head; struct sg_table sg_head;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment