Skip to content

Commit

Permalink
ipc,namespace: make ipc namespace allocation wait for pending free
Browse files Browse the repository at this point in the history
Currently the ipc namespace allocation will fail when there are
ipc_namespace structures pending to be freed. This results in the
simple test case below, as well as some real world workloads, to
get allocation failures even when the number of ipc namespaces in
actual use is way below the limit.

int main()
{
	int i;

	for (i = 0; i < 100000; i++) {
		if (unshare(CLONE_NEWIPC) < 0)
			error(EXIT_FAILURE, errno, "unshare");
	}
}

Make the allocation of an ipc_namespace wait for pending frees,
so it will succeed.

real	6m19.197s
user	0m0.041s
sys	0m1.019s

Signed-off-by: Rik van Riel <[email protected]>
Reported-by: Chris Mason <[email protected]>
Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
rikvanriel authored and Al Viro committed Jan 28, 2023
1 parent b7bfaa7 commit a80c4ad
Showing 1 changed file with 16 additions and 6 deletions.
22 changes: 16 additions & 6 deletions ipc/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

#include "util.h"

/*
* The work queue is used to avoid the cost of synchronize_rcu in kern_unmount.
*/
static void free_ipc(struct work_struct *unused);
static DECLARE_WORK(free_ipc_work, free_ipc);

static struct ucounts *inc_ipc_namespaces(struct user_namespace *ns)
{
return inc_ucount(ns, current_euid(), UCOUNT_IPC_NAMESPACES);
Expand All @@ -37,9 +43,18 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
int err;

err = -ENOSPC;
again:
ucounts = inc_ipc_namespaces(user_ns);
if (!ucounts)
if (!ucounts) {
/*
* IPC namespaces are freed asynchronously, by free_ipc_work.
* If frees were pending, flush_work will wait, and
* return true. Fail the allocation if no frees are pending.
*/
if (flush_work(&free_ipc_work))
goto again;
goto fail;
}

err = -ENOMEM;
ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL_ACCOUNT);
Expand Down Expand Up @@ -157,11 +172,6 @@ static void free_ipc(struct work_struct *unused)
free_ipc_ns(n);
}

/*
* The work queue is used to avoid the cost of synchronize_rcu in kern_unmount.
*/
static DECLARE_WORK(free_ipc_work, free_ipc);

/*
* put_ipc_ns - drop a reference to an ipc namespace.
* @ns: the namespace to put
Expand Down

0 comments on commit a80c4ad

Please sign in to comment.