Skip to content

Commit fd4f101

Browse files
Eric Dumazetkuba-moo
authored andcommitted
net: add exit_batch_rtnl() method
Many (struct pernet_operations)->exit_batch() methods have to acquire rtnl. In presence of rtnl mutex pressure, this makes cleanup_net() very slow. This patch adds a new exit_batch_rtnl() method to reduce number of rtnl acquisitions from cleanup_net(). exit_batch_rtnl() handlers are called while rtnl is locked, and devices to be killed can be queued in a list provided as their second argument. A single unregister_netdevice_many() is called right before rtnl is released. exit_batch_rtnl() handlers are called before ->exit() and ->exit_batch() handlers. Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: Antoine Tenart <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent a1e55f5 commit fd4f101

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

include/net/net_namespace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,9 @@ struct pernet_operations {
448448
void (*pre_exit)(struct net *net);
449449
void (*exit)(struct net *net);
450450
void (*exit_batch)(struct list_head *net_exit_list);
451+
/* Following method is called with RTNL held. */
452+
void (*exit_batch_rtnl)(struct list_head *net_exit_list,
453+
struct list_head *dev_kill_list);
451454
unsigned int *id;
452455
size_t size;
453456
};

net/core/net_namespace.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,9 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
318318
{
319319
/* Must be called with pernet_ops_rwsem held */
320320
const struct pernet_operations *ops, *saved_ops;
321-
int error = 0;
322321
LIST_HEAD(net_exit_list);
322+
LIST_HEAD(dev_kill_list);
323+
int error = 0;
323324

324325
refcount_set(&net->ns.count, 1);
325326
ref_tracker_dir_init(&net->refcnt_tracker, 128, "net refcnt");
@@ -357,6 +358,15 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
357358

358359
synchronize_rcu();
359360

361+
ops = saved_ops;
362+
rtnl_lock();
363+
list_for_each_entry_continue_reverse(ops, &pernet_list, list) {
364+
if (ops->exit_batch_rtnl)
365+
ops->exit_batch_rtnl(&net_exit_list, &dev_kill_list);
366+
}
367+
unregister_netdevice_many(&dev_kill_list);
368+
rtnl_unlock();
369+
360370
ops = saved_ops;
361371
list_for_each_entry_continue_reverse(ops, &pernet_list, list)
362372
ops_exit_list(ops, &net_exit_list);
@@ -573,6 +583,7 @@ static void cleanup_net(struct work_struct *work)
573583
struct net *net, *tmp, *last;
574584
struct llist_node *net_kill_list;
575585
LIST_HEAD(net_exit_list);
586+
LIST_HEAD(dev_kill_list);
576587

577588
/* Atomically snapshot the list of namespaces to cleanup */
578589
net_kill_list = llist_del_all(&cleanup_list);
@@ -613,6 +624,14 @@ static void cleanup_net(struct work_struct *work)
613624
*/
614625
synchronize_rcu();
615626

627+
rtnl_lock();
628+
list_for_each_entry_reverse(ops, &pernet_list, list) {
629+
if (ops->exit_batch_rtnl)
630+
ops->exit_batch_rtnl(&net_exit_list, &dev_kill_list);
631+
}
632+
unregister_netdevice_many(&dev_kill_list);
633+
rtnl_unlock();
634+
616635
/* Run all of the network namespace exit methods */
617636
list_for_each_entry_reverse(ops, &pernet_list, list)
618637
ops_exit_list(ops, &net_exit_list);
@@ -1193,7 +1212,17 @@ static void free_exit_list(struct pernet_operations *ops, struct list_head *net_
11931212
{
11941213
ops_pre_exit_list(ops, net_exit_list);
11951214
synchronize_rcu();
1215+
1216+
if (ops->exit_batch_rtnl) {
1217+
LIST_HEAD(dev_kill_list);
1218+
1219+
rtnl_lock();
1220+
ops->exit_batch_rtnl(net_exit_list, &dev_kill_list);
1221+
unregister_netdevice_many(&dev_kill_list);
1222+
rtnl_unlock();
1223+
}
11961224
ops_exit_list(ops, net_exit_list);
1225+
11971226
ops_free_list(ops, net_exit_list);
11981227
}
11991228

0 commit comments

Comments
 (0)