Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1bf66a30 authored by Roland Dreier's avatar Roland Dreier
Browse files

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: default avatarRoland Dreier <rolandd@cisco.com>
parent f7c6a7b5
Loading
Loading
Loading
Loading
+13 −28
Original line number Diff line number Diff line
@@ -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 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
}
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(&work->work, ib_umem_account);
		work->mm   = mm;
		work->diff = diff;
		INIT_WORK(&umem->work, ib_umem_account);
		umem->mm   = mm;
		umem->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);

+3 −0
Original line number Diff line number Diff line
@@ -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 {