Something like this should work...

 - R.

--- infiniband/core/uverbs_mem.c        (revision 2710)
+++ infiniband/core/uverbs_mem.c        (working copy)
@@ -37,6 +37,13 @@
 
 #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;
@@ -160,21 +167,53 @@ out:
        return ret;
 }
 
-void ib_umem_release(struct ib_device *dev, struct ib_umem *umem)
+static void ib_umem_account(void *work_ptr)
 {
-       struct mm_struct *mm;
+       struct ib_umem_account_work *work = work_ptr;
 
-       mm = get_task_mm(current);
+       down_write(&work->mm->mmap_sem);
+       work->mm->locked_vm -= work->diff;
+       up_write(&work->mm->mmap_sem);
+       mmput(work->mm);
+       kfree(work);
+}
 
-       if (mm) {
-               down_write(&mm->mmap_sem);
-               mm->locked_vm -= PAGE_ALIGN(umem->length + umem->offset) >> 
PAGE_SHIFT;
-       }
+void ib_umem_release(struct ib_device *dev, struct ib_umem *umem)
+{
+       struct mm_struct *mm;
 
        __ib_umem_release(dev, umem, 1);
 
+       mm = get_task_mm(current);
        if (mm) {
-               up_write(&mm->mmap_sem);
-               mmput(mm);
+               /*
+                * We may be called with the mm's mmap_sem already
+                * held.  This can happen when a userspace munmap() is
+                * the call that drops the last reference to our file
+                * and calls our release method.  If there are memory
+                * regions to destroy, we'll end up here and not be
+                * able to take the mmap_sem.
+                *
+                * To handle this, we try to grab the mmap_sem, and if
+                * we can't get it immediately, we defer the
+                * accounting to the system workqueue.
+                */
+               if (down_write_trylock(&mm->mmap_sem)) {
+                       mm->locked_vm -= PAGE_ALIGN(umem->length + 
umem->offset) >> PAGE_SHIFT;
+                       up_write(&mm->mmap_sem);
+                       mmput(mm);
+               } else {
+                       struct ib_umem_account_work *work;
+
+                       work = kmalloc(sizeof *work, GFP_KERNEL);
+                       if (!work)
+                               return;
+
+                       INIT_WORK(&work->work, ib_umem_account, work);
+                       work->mm   = mm;
+                       work->diff = PAGE_ALIGN(umem->length + umem->offset) >> 
PAGE_SHIFT;
+
+                       schedule_work(&work->work);
+               }
        }
 }
_______________________________________________
openib-general mailing list
[email protected]
http://openib.org/mailman/listinfo/openib-general

To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general

Reply via email to