net: fib_rules: Only hold RTNL for the first IPv4 RTM_NEWRULE.

Now, RTM_DELRULE no longer needs RTNL, and the only RTNL dependant
in RTM_NEWRULE is fib_unmerge(), which is called for the first
IPv4 rule.

Let's add fib_rules_ops.need_rtnl() and hold RTNL only for the
first IPv4 rule.

Tested:
The script below creates 1K rules in parallel in 4K netns, and
it got 20x/30x faster for IPv4/IPv6.

  #!/bin/bash
  N=4096
  F=rules.txt

  for i in $(seq $N); do ip netns add ns-$i; done
  printf 'rule add from all table %d\n' {1..1024} > $F

  for v in 4 6; do
  	echo "=== IPv${v} ==="
  	time { for i in $(seq $N); do nsenter \
  	--net=/var/run/netns/ns-$i ip -$v -batch $F & done; wait; }
  done

  for i in $(seq $N); do ip netns del ns-$i; done
  rm -f $F

Without this series:

  # ./test.sh
  === IPv4 ===

  real	0m22.752s
  user	0m7.834s
  sys	92m46.721s
  === IPv6 ===

  real	0m35.181s
  user	0m8.635s
  sys	142m30.479s

With this series:

  # ./test.sh
  === IPv4 ===

  real	0m0.918s
  user	0m5.675s
  sys	2m7.024s
  === IPv6 ===

  real	0m1.214s
  user	0m7.917s
  sys	4m19.489s

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260629181226.1929658-10-kuniyu@google.com
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Kuniyuki Iwashima 2026-06-29 18:11:01 +00:00 committed by Paolo Abeni
parent 34ea249938
commit eef9bddc33
3 changed files with 13 additions and 9 deletions

View File

@ -93,6 +93,7 @@ struct fib_rules_ops {
/* Called after modifications to the rules set, must flush
* the route cache if one exists. */
void (*flush_cache)(struct fib_rules_ops *ops);
bool (*need_rtnl)(struct net *net);
int nlgroup;
struct list_head rules_list;

View File

@ -881,6 +881,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
struct nlattr *tb[FRA_MAX + 1];
bool user_priority = false;
struct fib_rule_hdr *frh;
bool unlock_rtnl = false;
frh = nlmsg_payload(nlh, sizeof(*frh));
if (!frh) {
@ -906,8 +907,10 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
if (!rtnl_held)
if (!rtnl_held && ops->need_rtnl && ops->need_rtnl(net)) {
unlock_rtnl = true;
rtnl_net_lock(net);
}
mutex_lock(&ops->lock);
err = fib_nl2rule_locked(rule, ops, tb, extack);
@ -978,7 +981,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
fib_rule_get(rule);
mutex_unlock(&ops->lock);
if (!rtnl_held)
if (unlock_rtnl)
rtnl_net_unlock(net);
notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
@ -989,7 +992,7 @@ int fib_newrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
errout_free:
mutex_unlock(&ops->lock);
if (!rtnl_held)
if (unlock_rtnl)
rtnl_net_unlock(net);
kfree(rule);
errout:
@ -1038,8 +1041,6 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
if (err)
goto errout;
if (!rtnl_held)
rtnl_net_lock(net);
mutex_lock(&ops->lock);
err = fib_nl2rule_locked(nlrule, ops, tb, extack);
@ -1096,8 +1097,6 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, NULL);
mutex_unlock(&ops->lock);
if (!rtnl_held)
rtnl_net_unlock(net);
notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid);
fib_rule_put(rule);
@ -1108,8 +1107,6 @@ int fib_delrule(struct net *net, struct sk_buff *skb, struct nlmsghdr *nlh,
errout_free:
mutex_unlock(&ops->lock);
if (!rtnl_held)
rtnl_net_unlock(net);
kfree(nlrule);
errout:
rules_ops_put(ops);

View File

@ -460,6 +460,11 @@ static void fib4_rule_flush_cache(struct fib_rules_ops *ops)
rt_cache_flush(ops->fro_net);
}
static bool fib4_rule_need_rtnl(struct net *net)
{
return !net->ipv4.fib_has_custom_rules;
}
static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
.family = AF_INET,
.rule_size = sizeof(struct fib4_rule),
@ -473,6 +478,7 @@ static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {
.fill = fib4_rule_fill,
.nlmsg_payload = fib4_rule_nlmsg_payload,
.flush_cache = fib4_rule_flush_cache,
.need_rtnl = fib4_rule_need_rtnl,
.nlgroup = RTNLGRP_IPV4_RULE,
.owner = THIS_MODULE,
};