Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/sched: acp_api: no longer acquire RTNL in tc_action_net_exit()

tc_action_net_exit() got an rtnl exclusion in commit
a159d3c4b829 ("net_sched: acquire RTNL in tc_action_net_exit()")

Since then, commit 16af6067392c ("net: sched: implement reference
counted action release") made this RTNL exclusion obsolete for
most cases.

Only tcf_action_offload_del() might still require it.

Move the rtnl locking into tcf_idrinfo_destroy() when
an offload action is found.

Most netns do not have actions, yet deleting them is adding a lot
of pressure on RTNL, which is for many the most contended mutex
in the kernel.

We are moving to a per-netns 'rtnl', so tc_action_net_exit()
will not be able to grab 'rtnl' a single time for a batch of netns.

Before the patch:

perf probe -a rtnl_lock

perf record -e probe:rtnl_lock -a /bin/bash -c 'unshare -n "/bin/true"; sleep 1'
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.305 MB perf.data (25 samples) ]

After the patch:

perf record -e probe:rtnl_lock -a /bin/bash -c 'unshare -n "/bin/true"; sleep 1'
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.304 MB perf.data (9 samples) ]

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Vlad Buslov <vladbu@nvidia.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Cc: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Link: https://patch.msgid.link/20250702071230.1892674-1-edumazet@google.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Eric Dumazet and committed by
Paolo Abeni
84a7d679 d23647fd

+8 -3
-2
include/net/act_api.h
··· 170 170 { 171 171 struct net *net; 172 172 173 - rtnl_lock(); 174 173 list_for_each_entry(net, net_list, exit_list) { 175 174 struct tc_action_net *tn = net_generic(net, id); 176 175 177 176 tcf_idrinfo_destroy(tn->ops, tn->idrinfo); 178 177 kfree(tn->idrinfo); 179 178 } 180 - rtnl_unlock(); 181 179 } 182 180 183 181 int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb,
+8 -1
net/sched/act_api.c
··· 933 933 struct tcf_idrinfo *idrinfo) 934 934 { 935 935 struct idr *idr = &idrinfo->action_idr; 936 + bool mutex_taken = false; 936 937 struct tc_action *p; 937 - int ret; 938 938 unsigned long id = 1; 939 939 unsigned long tmp; 940 + int ret; 940 941 941 942 idr_for_each_entry_ul(idr, p, tmp, id) { 943 + if (tc_act_in_hw(p) && !mutex_taken) { 944 + rtnl_lock(); 945 + mutex_taken = true; 946 + } 942 947 ret = __tcf_idr_release(p, false, true); 943 948 if (ret == ACT_P_DELETED) 944 949 module_put(ops->owner); 945 950 else if (ret < 0) 946 951 return; 947 952 } 953 + if (mutex_taken) 954 + rtnl_unlock(); 948 955 idr_destroy(&idrinfo->action_idr); 949 956 } 950 957 EXPORT_SYMBOL(tcf_idrinfo_destroy);