* [PATCH net 1/1] ipv4: fib: avoid quadratic table ID lookup [not found] <cover.1787982246.git.zihanx@nebusec.ai> @ 2026-08-29 6:24 ` Zihan Xi 2026-08-31 11:06 ` Ido Schimmel 0 siblings, 1 reply; 3+ messages in thread From: Zihan Xi @ 2026-08-29 6:24 UTC (permalink / raw) To: netdev Cc: linux-kernel, David Ahern, Ido Schimmel, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Patrick McHardy, Zihan Xi, stable, Vega fib_empty_table() probes every table ID from 1 until it finds a free one. Since IPv4 tables are stored in a 256-bucket hash table, a dense set of IDs makes the probes repeatedly walk growing hash chains while RTNL is held. Count the existing tables once and use a bitmap for the bounded range that can contain the first free ID. This keeps table-ID selection linear in the number of tables instead of quadratic, without changing the lowest-free-ID behavior. Fixes: 1af5a8c4a11c ("[IPV4]: Increase number of possible routing tables to 2^32") Cc: stable@vger.kernel.org Reported-by: Vega <vega@nebusec.ai> Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi <zihanx@nebusec.ai> --- net/ipv4/fib_rules.c | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index e068a5bac..55751b0d1 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -16,6 +16,7 @@ #include <linux/types.h> #include <linux/kernel.h> +#include <linux/bitmap.h> #include <linux/netdevice.h> #include <linux/netlink.h> #include <linux/inetdevice.h> @@ -216,16 +217,45 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule, static struct fib_table *fib_empty_table(struct net *net) { - u32 id = 1; + unsigned int h, count = 0; + unsigned long *table_ids; + struct fib_table *table; + u32 id, max_id; - while (1) { - if (!fib_get_table(net, id)) - return fib_new_table(net, id); + /* The first unused ID is no greater than the number of tables + 1. */ + rcu_read_lock(); + for (h = 0; h < FIB_TABLE_HASHSZ; h++) { + hlist_for_each_entry_rcu(table, + &net->ipv4.fib_table_hash[h], + tb_hlist) { + count++; + } + } + rcu_read_unlock(); + + if (count == RT_TABLE_MAX) + return NULL; + + max_id = count + 1; + table_ids = bitmap_zalloc(max_id, GFP_KERNEL); + if (!table_ids) + return NULL; - if (id++ == RT_TABLE_MAX) - break; + rcu_read_lock(); + for (h = 0; h < FIB_TABLE_HASHSZ; h++) { + hlist_for_each_entry_rcu(table, + &net->ipv4.fib_table_hash[h], + tb_hlist) { + if (table->tb_id <= max_id) + __set_bit(table->tb_id - 1, table_ids); + } } - return NULL; + rcu_read_unlock(); + + id = find_first_zero_bit(table_ids, max_id) + 1; + bitmap_free(table_ids); + + return fib_new_table(net, id); } static int fib4_nl2rule_dscp(const struct nlattr *nla, struct fib4_rule *rule4, -- 2.55.0.windows.3 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net 1/1] ipv4: fib: avoid quadratic table ID lookup 2026-08-29 6:24 ` [PATCH net 1/1] ipv4: fib: avoid quadratic table ID lookup Zihan Xi @ 2026-08-31 11:06 ` Ido Schimmel 2026-08-31 12:15 ` zihan xi 0 siblings, 1 reply; 3+ messages in thread From: Ido Schimmel @ 2026-08-31 11:06 UTC (permalink / raw) To: Zihan Xi Cc: netdev, linux-kernel, David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Patrick McHardy, stable, Vega On Sat, Aug 29, 2026 at 06:24:57AM +0000, Zihan Xi wrote: > fib_empty_table() probes every table ID from 1 until it finds a free > one. Since IPv4 tables are stored in a 256-bucket hash table, a dense > set of IDs makes the probes repeatedly walk growing hash chains while > RTNL is held. > > Count the existing tables once and use a bitmap for the bounded range > that can contain the first free ID. This keeps table-ID selection > linear in the number of tables instead of quadratic, without changing > the lowest-free-ID behavior. TBH, I wasn't even aware of this "table 0" functionality and I'm quite certain it's unused nowadays: 1. IPv4 specific: # ip -6 rule add from 2001:db8:1::1 table 0 Error: Invalid table. 2. Not documented in ip-rule man page: " table TABLEID the routing table identifier to lookup if the rule selector matches. It is also possible to use lookup instead of table. " 3. No kernel selftests despite ip-rule having good coverage. 4. Quirky. Requires dumping the rules or listening to netlink notifications to discover the allocated table ID. So, for now, I suggest bounding the maximum table ID that can be automatically allocated. Something like [1]. I will add a deprecation warning in net-next. FTR, I did consider storing the tables in something like xarray, but: 1. We would still need to keep the hash table given the analysis Jakub shared in commit 759ab1edb56c ("net: store netdevs in an xarray"). Xarray only starts being worthwhile at around 1k tables and most deployments never reach this number. 2. It requires adding per-netns xarray for a functionality that is unlikely to be used today. [1] diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 4edb0dca7be8..2e8629df34bb 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -214,6 +214,8 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule, return 1; } +#define FIB_MAX_AUTO_TABLE_ID 4096 + static struct fib_table *fib_empty_table(struct net *net) { u32 id = 1; @@ -222,7 +224,7 @@ static struct fib_table *fib_empty_table(struct net *net) if (!fib_get_table(net, id)) return fib_new_table(net, id); - if (id++ == RT_TABLE_MAX) + if (id++ == FIB_MAX_AUTO_TABLE_ID) break; } return NULL; ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net 1/1] ipv4: fib: avoid quadratic table ID lookup 2026-08-31 11:06 ` Ido Schimmel @ 2026-08-31 12:15 ` zihan xi 0 siblings, 0 replies; 3+ messages in thread From: zihan xi @ 2026-08-31 12:15 UTC (permalink / raw) To: Ido Schimmel Cc: netdev, linux-kernel, David Ahern, David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, Patrick McHardy, stable, Vega On Mon, Aug 31, 2026 at 7:07 PM Ido Schimmel <idosch@nvidia.com> wrote: > > On Sat, Aug 29, 2026 at 06:24:57AM +0000, Zihan Xi wrote: > > fib_empty_table() probes every table ID from 1 until it finds a free > > one. Since IPv4 tables are stored in a 256-bucket hash table, a dense > > set of IDs makes the probes repeatedly walk growing hash chains while > > RTNL is held. > > > > Count the existing tables once and use a bitmap for the bounded range > > that can contain the first free ID. This keeps table-ID selection > > linear in the number of tables instead of quadratic, without changing > > the lowest-free-ID behavior. > > TBH, I wasn't even aware of this "table 0" functionality and I'm quite > certain it's unused nowadays: > > 1. IPv4 specific: > > # ip -6 rule add from 2001:db8:1::1 table 0 > Error: Invalid table. > > 2. Not documented in ip-rule man page: > > " > table TABLEID > the routing table identifier to lookup if the rule selector > matches. It is also possible to use lookup instead of table. > " > > 3. No kernel selftests despite ip-rule having good coverage. > > 4. Quirky. Requires dumping the rules or listening to netlink > notifications to discover the allocated table ID. > > So, for now, I suggest bounding the maximum table ID that can be > automatically allocated. Something like [1]. I will add a deprecation > warning in net-next. > Thanks for the review and the suggested patch. I wasn't aware how unused this automatic table-0 assignment is either. I'll drop the bitmap rewrite and send a v2 that just caps the automatically allocated ID at 4096 as you proposed. > FTR, I did consider storing the tables in something like xarray, but: > > 1. We would still need to keep the hash table given the analysis Jakub > shared in commit 759ab1edb56c ("net: store netdevs in an xarray"). > Xarray only starts being worthwhile at around 1k tables and most > deployments never reach this number. > > 2. It requires adding per-netns xarray for a functionality that is > unlikely to be used today. > Agreed, bounding the auto-allocation is the simpler fix here. Thanks, Zihan > [1] > diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c > index 4edb0dca7be8..2e8629df34bb 100644 > --- a/net/ipv4/fib_rules.c > +++ b/net/ipv4/fib_rules.c > @@ -214,6 +214,8 @@ INDIRECT_CALLABLE_SCOPE int fib4_rule_match(struct fib_rule *rule, > return 1; > } > > +#define FIB_MAX_AUTO_TABLE_ID 4096 > + > static struct fib_table *fib_empty_table(struct net *net) > { > u32 id = 1; > @@ -222,7 +224,7 @@ static struct fib_table *fib_empty_table(struct net *net) > if (!fib_get_table(net, id)) > return fib_new_table(net, id); > > - if (id++ == RT_TABLE_MAX) > + if (id++ == FIB_MAX_AUTO_TABLE_ID) > break; > } > return NULL; > ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-31 12:16 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <cover.1787982246.git.zihanx@nebusec.ai>
2026-08-29 6:24 ` [PATCH net 1/1] ipv4: fib: avoid quadratic table ID lookup Zihan Xi
2026-08-31 11:06 ` Ido Schimmel
2026-08-31 12:15 ` zihan xi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox