diff options
author | Roland Dreier <rolandd@cisco.com> | 2007-04-19 05:20:28 +0200 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-05-09 03:00:37 +0200 |
commit | 1bf66a30421ca772820f489d88c16d0c430d6a67 (patch) | |
tree | b1ab223e6908d772bcad7f9bc3382c33ad5a4490 | |
parent | IB/uverbs: Export ib_umem_get()/ib_umem_release() to modules (diff) | |
download | linux-1bf66a30421ca772820f489d88c16d0c430d6a67.tar.xz linux-1bf66a30421ca772820f489d88c16d0c430d6a67.zip |
IB: Put rlimit accounting struct in struct ib_umem
When memory pinned with ib_umem_get() is released, ib_umem_release()
needs to subtract the amount of memory being unpinned from
mm->locked_vm. However, ib_umem_release() may be called with
mm->mmap_sem already held for writing if the memory is being released
as part of an munmap() call, so it is sometimes necessary to defer
this accounting into a workqueue.
However, the work struct used to defer this accounting is dynamically
allocated before it is queued, so there is the possibility of failing
that allocation. If the allocation fails, then ib_umem_release has no
choice except to bail out and leave the process with a permanently
elevated locked_vm.
Fix this by allocating the structure to defer accounting as part of
the original struct ib_umem, so there's no possibility of failing a
later allocation if creating the struct ib_umem and pinning memory
succeeds.
Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r-- | drivers/infiniband/core/umem.c | 41 | ||||
-rw-r--r-- | include/rdma/ib_umem.h | 3 |
2 files changed, 16 insertions, 28 deletions
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index 48e854cf416f..f32ca5fbb26b 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c @@ -39,13 +39,6 @@ #include "uverbs.h" -struct ib_umem_account_work { - struct work_struct work; - struct mm_struct *mm; - unsigned long diff; -}; - - static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty) { struct ib_umem_chunk *chunk, *tmp; @@ -192,16 +185,15 @@ out: } EXPORT_SYMBOL(ib_umem_get); -static void ib_umem_account(struct work_struct *_work) +static void ib_umem_account(struct work_struct *work) { - struct ib_umem_account_work *work = - container_of(_work, struct ib_umem_account_work, work); - - down_write(&work->mm->mmap_sem); - work->mm->locked_vm -= work->diff; - up_write(&work->mm->mmap_sem); - mmput(work->mm); - kfree(work); + struct ib_umem *umem = container_of(work, struct ib_umem, work); + + down_write(&umem->mm->mmap_sem); + umem->mm->locked_vm -= umem->diff; + up_write(&umem->mm->mmap_sem); + mmput(umem->mm); + kfree(umem); } /** @@ -210,7 +202,6 @@ static void ib_umem_account(struct work_struct *_work) */ void ib_umem_release(struct ib_umem *umem) { - struct ib_umem_account_work *work; struct ib_ucontext *context = umem->context; struct mm_struct *mm; unsigned long diff; @@ -222,7 +213,6 @@ void ib_umem_release(struct ib_umem *umem) return; diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; - kfree(umem); /* * We may be called with the mm's mmap_sem already held. This @@ -233,17 +223,11 @@ void ib_umem_release(struct ib_umem *umem) * we defer the vm_locked accounting to the system workqueue. */ if (context->closing && !down_write_trylock(&mm->mmap_sem)) { - work = kmalloc(sizeof *work, GFP_KERNEL); - if (!work) { - mmput(mm); - return; - } + INIT_WORK(&umem->work, ib_umem_account); + umem->mm = mm; + umem->diff = diff; - INIT_WORK(&work->work, ib_umem_account); - work->mm = mm; - work->diff = diff; - - schedule_work(&work->work); + schedule_work(&umem->work); return; } else down_write(&mm->mmap_sem); @@ -251,6 +235,7 @@ void ib_umem_release(struct ib_umem *umem) current->mm->locked_vm -= diff; up_write(&mm->mmap_sem); mmput(mm); + kfree(umem); } EXPORT_SYMBOL(ib_umem_release); diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h index 06307f7e43e0..b3a36f7d79e5 100644 --- a/include/rdma/ib_umem.h +++ b/include/rdma/ib_umem.h @@ -45,6 +45,9 @@ struct ib_umem { int page_size; int writable; struct list_head chunk_list; + struct work_struct work; + struct mm_struct *mm; + unsigned long diff; }; struct ib_umem_chunk { |