ipv6: Remove rcu_read_lock() in fib6_get_table().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Fri, 16 May 2025 02:27:17 +0000 (19:27 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 21 May 2025 02:18:23 +0000 (19:18 -0700)
Once allocated, the IPv6 routing table is not freed until
netns is dismantled.

fib6_get_table() uses rcu_read_lock() while iterating
net->ipv6.fib_table_hash[], but it's not needed and
rather confusing.

Because some callers have this pattern,

  table = fib6_get_table();

  rcu_read_lock();
  /* ... use table here ... */
  rcu_read_unlock();

  [ See: addrconf_get_prefix_route(), ip6_route_del(),
         rt6_get_route_info(), rt6_get_dflt_router() ]

and this looks illegal but is actually safe.

Let's remove rcu_read_lock() in fib6_get_table() and pass true
to the last argument of hlist_for_each_entry_rcu() to bypass
the RCU check.

Note that protection is not needed but RCU helper is used to
avoid data-race.

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Link: https://patch.msgid.link/20250516022759.44392-2-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv6/ip6_fib.c

index 1f860340690ca56eb428217e8e0f25d4f702126d..88770ecd2da1f07b099d5cd0a31ba30b2b20d868 100644 (file)
@@ -281,22 +281,20 @@ EXPORT_SYMBOL_GPL(fib6_new_table);
 
 struct fib6_table *fib6_get_table(struct net *net, u32 id)
 {
-       struct fib6_table *tb;
        struct hlist_head *head;
-       unsigned int h;
+       struct fib6_table *tb;
 
-       if (id == 0)
+       if (!id)
                id = RT6_TABLE_MAIN;
-       h = id & (FIB6_TABLE_HASHSZ - 1);
-       rcu_read_lock();
-       head = &net->ipv6.fib_table_hash[h];
-       hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
-               if (tb->tb6_id == id) {
-                       rcu_read_unlock();
+
+       head = &net->ipv6.fib_table_hash[id & (FIB6_TABLE_HASHSZ - 1)];
+
+       /* See comment in fib6_link_table().  RCU is not required,
+        * but rcu_dereference_raw() is used to avoid data-race.
+        */
+       hlist_for_each_entry_rcu(tb, head, tb6_hlist, true)
+               if (tb->tb6_id == id)
                        return tb;
-               }
-       }
-       rcu_read_unlock();
 
        return NULL;
 }