Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: Remove rcu_read_lock() in fib6_get_table().

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>

authored by

Kuniyuki Iwashima and committed by
Jakub Kicinski
f1a8d107 5b1ced44

+10 -12
+10 -12
net/ipv6/ip6_fib.c
··· 281 281 282 282 struct fib6_table *fib6_get_table(struct net *net, u32 id) 283 283 { 284 - struct fib6_table *tb; 285 284 struct hlist_head *head; 286 - unsigned int h; 285 + struct fib6_table *tb; 287 286 288 - if (id == 0) 287 + if (!id) 289 288 id = RT6_TABLE_MAIN; 290 - h = id & (FIB6_TABLE_HASHSZ - 1); 291 - rcu_read_lock(); 292 - head = &net->ipv6.fib_table_hash[h]; 293 - hlist_for_each_entry_rcu(tb, head, tb6_hlist) { 294 - if (tb->tb6_id == id) { 295 - rcu_read_unlock(); 289 + 290 + head = &net->ipv6.fib_table_hash[id & (FIB6_TABLE_HASHSZ - 1)]; 291 + 292 + /* See comment in fib6_link_table(). RCU is not required, 293 + * but rcu_dereference_raw() is used to avoid data-race. 294 + */ 295 + hlist_for_each_entry_rcu(tb, head, tb6_hlist, true) 296 + if (tb->tb6_id == id) 296 297 return tb; 297 - } 298 - } 299 - rcu_read_unlock(); 300 298 301 299 return NULL; 302 300 }