Skip to content

Commit bbd63f0

Browse files
tracywwnjdavem330
authored andcommitted
ipv6: update fn_sernum after route is inserted to tree
fib6_add() logic currently calls fib6_add_1() to figure out what node should be used for the newly added route and then call fib6_add_rt2node() to insert the route to the node. And during the call of fib6_add_1(), fn_sernum is updated for all nodes that share the same prefix as the new route. This does not have issue in the current code because reader thread will not be able to access the tree while writer thread is inserting new route to it. However, it is not the case once we transition to use RCU. Reader thread could potentially see the new fn_sernum before the new route is inserted. As a result, reader thread's route lookup will return a stale route with the new fn_sernum. In order to solve this issue, we remove all the update of fn_sernum in fib6_add_1(), and instead, introduce a new function that updates fn_sernum for all related nodes and call this functions once the route is successfully inserted to the tree. Also, smp_wmb() is used after a route is successfully inserted into the fib tree and right before the updated of fn->sernum. And smp_rmb() is used right after fn->sernum is accessed in rt6_get_cookie_safe(). This is to guarantee that when the reader thread sees the new fn->sernum, the new route is already inserted in the tree in memory. Signed-off-by: Wei Wang <[email protected]> Signed-off-by: Martin KaFai Lau <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent d3843fe commit bbd63f0

File tree

2 files changed

+23
-18
lines changed

2 files changed

+23
-18
lines changed

include/net/ip6_fib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ static inline bool rt6_get_cookie_safe(const struct rt6_info *rt,
220220

221221
if (fn) {
222222
*cookie = fn->fn_sernum;
223+
/* pairs with smp_wmb() in fib6_update_sernum_upto_root() */
224+
smp_rmb();
223225
status = true;
224226
}
225227

net/ipv6/ip6_fib.c

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
585585
static struct fib6_node *fib6_add_1(struct fib6_node *root,
586586
struct in6_addr *addr, int plen,
587587
int offset, int allow_create,
588-
int replace_required, int sernum,
588+
int replace_required,
589589
struct netlink_ext_ack *extack)
590590
{
591591
struct fib6_node *fn, *in, *ln;
@@ -631,8 +631,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
631631
fn->leaf = NULL;
632632
}
633633

634-
fn->fn_sernum = sernum;
635-
636634
return fn;
637635
}
638636

@@ -641,7 +639,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
641639
*/
642640

643641
/* Try to walk down on tree. */
644-
fn->fn_sernum = sernum;
645642
dir = addr_bit_set(addr, fn->fn_bit);
646643
pn = fn;
647644
fn = dir ? fn->right : fn->left;
@@ -677,7 +674,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
677674
ln->fn_bit = plen;
678675

679676
ln->parent = pn;
680-
ln->fn_sernum = sernum;
681677

682678
if (dir)
683679
pn->right = ln;
@@ -737,8 +733,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
737733
in->leaf = fn->leaf;
738734
atomic_inc(&in->leaf->rt6i_ref);
739735

740-
in->fn_sernum = sernum;
741-
742736
/* update parent pointer */
743737
if (dir)
744738
pn->right = in;
@@ -750,8 +744,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
750744
ln->parent = in;
751745
fn->parent = in;
752746

753-
ln->fn_sernum = sernum;
754-
755747
if (addr_bit_set(addr, bit)) {
756748
in->right = ln;
757749
in->left = fn;
@@ -776,8 +768,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root,
776768

777769
ln->parent = pn;
778770

779-
ln->fn_sernum = sernum;
780-
781771
if (dir)
782772
pn->right = ln;
783773
else
@@ -1079,6 +1069,20 @@ void fib6_force_start_gc(struct net *net)
10791069
jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
10801070
}
10811071

1072+
static void fib6_update_sernum_upto_root(struct rt6_info *rt,
1073+
int sernum)
1074+
{
1075+
struct fib6_node *fn = rcu_dereference_protected(rt->rt6i_node,
1076+
lockdep_is_held(&rt->rt6i_table->tb6_lock));
1077+
1078+
/* paired with smp_rmb() in rt6_get_cookie_safe() */
1079+
smp_wmb();
1080+
while (fn) {
1081+
fn->fn_sernum = sernum;
1082+
fn = fn->parent;
1083+
}
1084+
}
1085+
10821086
/*
10831087
* Add routing information to the routing tree.
10841088
* <destination addr>/<source addr>
@@ -1111,7 +1115,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
11111115

11121116
fn = fib6_add_1(root, &rt->rt6i_dst.addr, rt->rt6i_dst.plen,
11131117
offsetof(struct rt6_info, rt6i_dst), allow_create,
1114-
replace_required, sernum, extack);
1118+
replace_required, extack);
11151119
if (IS_ERR(fn)) {
11161120
err = PTR_ERR(fn);
11171121
fn = NULL;
@@ -1145,15 +1149,13 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
11451149
sfn->leaf = info->nl_net->ipv6.ip6_null_entry;
11461150
atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
11471151
sfn->fn_flags = RTN_ROOT;
1148-
sfn->fn_sernum = sernum;
11491152

11501153
/* Now add the first leaf node to new subtree */
11511154

11521155
sn = fib6_add_1(sfn, &rt->rt6i_src.addr,
11531156
rt->rt6i_src.plen,
11541157
offsetof(struct rt6_info, rt6i_src),
1155-
allow_create, replace_required, sernum,
1156-
extack);
1158+
allow_create, replace_required, extack);
11571159

11581160
if (IS_ERR(sn)) {
11591161
/* If it is failed, discard just allocated
@@ -1172,8 +1174,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
11721174
sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
11731175
rt->rt6i_src.plen,
11741176
offsetof(struct rt6_info, rt6i_src),
1175-
allow_create, replace_required, sernum,
1176-
extack);
1177+
allow_create, replace_required, extack);
11771178

11781179
if (IS_ERR(sn)) {
11791180
err = PTR_ERR(sn);
@@ -1190,8 +1191,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
11901191
#endif
11911192

11921193
err = fib6_add_rt2node(fn, rt, info, mxc);
1193-
if (!err)
1194+
if (!err) {
1195+
fib6_update_sernum_upto_root(rt, sernum);
11941196
fib6_start_gc(info->nl_net, rt);
1197+
}
11951198

11961199
out:
11971200
if (err) {

0 commit comments

Comments
 (0)