Linux kernel -stable discussions
 help / color / mirror / Atom feed
* [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