[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