[openib-general] [PATCH] process locked in D state.

Roland Dreier rolandd at cisco.com
Mon Jun 27 16:59:54 PDT 2005


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);
+		}
 	}
 }



More information about the general mailing list