diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2025-08-19 17:50:35 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2025-08-19 17:50:36 -0700 |
| commit | 0e041220ea064585d3d667b70e03bad0f31f86bb (patch) | |
| tree | 7de384a66860d4129e9b30df0e7b3e1a8405af84 | |
| parent | 51992f99f068fba966a680a9ac118b815f2fe08e (diff) | |
| parent | b0ac6d3b56a2384db151696cfda2836a8a961b6d (diff) | |
Merge branch 'net-speedup-some-nexthop-handling-when-having-a-lot-of-nexthops'
Christoph Paasch says:
====================
net: Speedup some nexthop handling when having A LOT of nexthops
Configuring a very large number of nexthops is fairly possible within
a reasonable time-frame. But, certain netlink commands can become
extremely slow.
This series addresses some of these, namely dumping and removing
nexthops.
v1: https://lore.kernel.org/20250724-nexthop_dump-v1-1-6b43fffd5bac@openai.com
====================
Link: https://patch.msgid.link/20250816-nexthop_dump-v2-0-491da3462118@openai.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
| -rw-r--r-- | net/ipv4/nexthop.c | 42 |
1 files changed, 39 insertions, 3 deletions
diff --git a/net/ipv4/nexthop.c b/net/ipv4/nexthop.c index 29118c43ebf5..0a20625f5ffb 100644 --- a/net/ipv4/nexthop.c +++ b/net/ipv4/nexthop.c @@ -2087,6 +2087,12 @@ static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh, { struct nh_grp_entry *nhge, *tmp; + /* If there is nothing to do, let's avoid the costly call to + * synchronize_net() + */ + if (list_empty(&nh->grp_list)) + return; + list_for_each_entry_safe(nhge, tmp, &nh->grp_list, nh_list) remove_nh_grp_entry(net, nhge, nlinfo); @@ -3511,12 +3517,42 @@ static int rtm_dump_walk_nexthops(struct sk_buff *skb, int err; s_idx = ctx->idx; - for (node = rb_first(root); node; node = rb_next(node)) { + + /* If this is not the first invocation, ctx->idx will contain the id of + * the last nexthop we processed. Instead of starting from the very + * first element of the red/black tree again and linearly skipping the + * (potentially large) set of nodes with an id smaller than s_idx, walk + * the tree and find the left-most node whose id is >= s_idx. This + * provides an efficient O(log n) starting point for the dump + * continuation. + */ + if (s_idx != 0) { + struct rb_node *tmp = root->rb_node; + + node = NULL; + while (tmp) { + struct nexthop *nh; + + nh = rb_entry(tmp, struct nexthop, rb_node); + if (nh->id < s_idx) { + tmp = tmp->rb_right; + } else { + /* Track current candidate and keep looking on + * the left side to find the left-most + * (smallest id) that is still >= s_idx. + */ + node = tmp; + tmp = tmp->rb_left; + } + } + } else { + node = rb_first(root); + } + + for (; node; node = rb_next(node)) { struct nexthop *nh; nh = rb_entry(node, struct nexthop, rb_node); - if (nh->id < s_idx) - continue; ctx->idx = nh->id; err = nh_cb(skb, cb, nh, data); |
