Commit 447cd7a0 authored by Kirill Tkhai's avatar Kirill Tkhai Committed by David S. Miller
Browse files

net: Allow pernet_operations to be executed in parallel



This adds new pernet_operations::async flag to indicate operations,
which ->init(), ->exit() and ->exit_batch() methods are allowed
to be executed in parallel with the methods of any other pernet_operations.

When there are only asynchronous pernet_operations in the system,
net_mutex won't be taken for a net construction and destruction.

Also, remove BUG_ON(mutex_is_locked()) from net_assign_generic()
without replacing with the equivalent net_sem check, as there is
one more lockdep assert below.

v3: Add comment near net_mutex.

Suggested-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarKirill Tkhai <ktkhai@virtuozzo.com>
Acked-by: default avatarAndrei Vagin <avagin@virtuozzo.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bcab1ddd
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -313,6 +313,12 @@ struct pernet_operations {
	void (*exit_batch)(struct list_head *net_exit_list);
	unsigned int *id;
	size_t size;
	/*
	 * Indicates above methods are allowed to be executed in parallel
	 * with methods of any other pernet_operations, i.e. they are not
	 * need synchronization via net_mutex.
	 */
	bool async;
};

/*
+20 −10
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@

static LIST_HEAD(pernet_list);
static struct list_head *first_device = &pernet_list;
/* Used only if there are !async pernet_operations registered */
DEFINE_MUTEX(net_mutex);

LIST_HEAD(net_namespace_list);
@@ -41,8 +42,9 @@ struct net init_net = {
EXPORT_SYMBOL(init_net);

static bool init_net_initialized;
static unsigned nr_sync_pernet_ops;
/*
 * net_sem: protects: pernet_list, net_generic_ids,
 * net_sem: protects: pernet_list, net_generic_ids, nr_sync_pernet_ops,
 * init_net_initialized and first_device pointer.
 */
DECLARE_RWSEM(net_sem);
@@ -70,11 +72,10 @@ static int net_assign_generic(struct net *net, unsigned int id, void *data)
{
	struct net_generic *ng, *old_ng;

	BUG_ON(!mutex_is_locked(&net_mutex));
	BUG_ON(id < MIN_PERNET_OPS_ID);

	old_ng = rcu_dereference_protected(net->gen,
					   lockdep_is_held(&net_mutex));
					   lockdep_is_held(&net_sem));
	if (old_ng->s.len > id) {
		old_ng->ptr[id] = data;
		return 0;
@@ -426,10 +427,13 @@ struct net *copy_net_ns(unsigned long flags,
	rv = down_read_killable(&net_sem);
	if (rv < 0)
		goto put_userns;
	if (nr_sync_pernet_ops) {
		rv = mutex_lock_killable(&net_mutex);
		if (rv < 0)
			goto up_read;
	}
	rv = setup_net(net, user_ns);
	if (nr_sync_pernet_ops)
		mutex_unlock(&net_mutex);
up_read:
	up_read(&net_sem);
@@ -487,6 +491,7 @@ static void cleanup_net(struct work_struct *work)
	spin_unlock_irq(&cleanup_list_lock);

	down_read(&net_sem);
	if (nr_sync_pernet_ops)
		mutex_lock(&net_mutex);

	/* Don't let anyone else find us. */
@@ -522,6 +527,7 @@ static void cleanup_net(struct work_struct *work)
	list_for_each_entry_reverse(ops, &pernet_list, list)
		ops_exit_list(ops, &net_exit_list);

	if (nr_sync_pernet_ops)
		mutex_unlock(&net_mutex);

	/* Free the net generic variables */
@@ -994,6 +1000,9 @@ static int register_pernet_operations(struct list_head *list,
		rcu_barrier();
		if (ops->id)
			ida_remove(&net_generic_ids, *ops->id);
	} else if (!ops->async) {
		pr_info_once("Pernet operations %ps are sync.\n", ops);
		nr_sync_pernet_ops++;
	}

	return error;
@@ -1001,7 +1010,8 @@ static int register_pernet_operations(struct list_head *list,

static void unregister_pernet_operations(struct pernet_operations *ops)
{
	
	if (!ops->async)
		BUG_ON(nr_sync_pernet_ops-- == 0);
	__unregister_pernet_operations(ops);
	rcu_barrier();
	if (ops->id)