mirror of https://github.com/torvalds/linux.git
ipv6: Defer fib6_purge_rt() in fib6_add_rt2node() to fib6_add().
The next patch adds per-nexthop spinlock which protects nh->f6i_list. When rt->nh is not NULL, fib6_add_rt2node() will be called under the lock. fib6_add_rt2node() could call fib6_purge_rt() for another route, which could holds another nexthop lock. Then, deadlock could happen between two nexthops. Let's defer fib6_purge_rt() after fib6_add_rt2node(). Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Acked-by: Paolo Abeni <pabeni@redhat.com> Link: https://patch.msgid.link/20250418000443.43734-14-kuniyu@amazon.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
834d97843e
commit
accb46b56b
|
|
@ -198,6 +198,7 @@ struct fib6_info {
|
|||
fib6_destroying:1,
|
||||
unused:4;
|
||||
|
||||
struct list_head purge_link;
|
||||
struct rcu_head rcu;
|
||||
struct nexthop *nh;
|
||||
struct fib6_nh fib6_nh[];
|
||||
|
|
|
|||
|
|
@ -1083,8 +1083,8 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
|
|||
*/
|
||||
|
||||
static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
|
||||
struct nl_info *info,
|
||||
struct netlink_ext_ack *extack)
|
||||
struct nl_info *info, struct netlink_ext_ack *extack,
|
||||
struct list_head *purge_list)
|
||||
{
|
||||
struct fib6_info *leaf = rcu_dereference_protected(fn->leaf,
|
||||
lockdep_is_held(&rt->fib6_table->tb6_lock));
|
||||
|
|
@ -1308,10 +1308,9 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
|
|||
}
|
||||
nsiblings = iter->fib6_nsiblings;
|
||||
iter->fib6_node = NULL;
|
||||
fib6_purge_rt(iter, fn, info->nl_net);
|
||||
list_add(&iter->purge_link, purge_list);
|
||||
if (rcu_access_pointer(fn->rr_ptr) == iter)
|
||||
fn->rr_ptr = NULL;
|
||||
fib6_info_release(iter);
|
||||
|
||||
if (nsiblings) {
|
||||
/* Replacing an ECMP route, remove all siblings */
|
||||
|
|
@ -1324,10 +1323,9 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
|
|||
if (rt6_qualify_for_ecmp(iter)) {
|
||||
*ins = iter->fib6_next;
|
||||
iter->fib6_node = NULL;
|
||||
fib6_purge_rt(iter, fn, info->nl_net);
|
||||
list_add(&iter->purge_link, purge_list);
|
||||
if (rcu_access_pointer(fn->rr_ptr) == iter)
|
||||
fn->rr_ptr = NULL;
|
||||
fib6_info_release(iter);
|
||||
nsiblings--;
|
||||
info->nl_net->ipv6.rt6_stats->fib_rt_entries--;
|
||||
} else {
|
||||
|
|
@ -1397,6 +1395,7 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
|
|||
struct nl_info *info, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct fib6_table *table = rt->fib6_table;
|
||||
LIST_HEAD(purge_list);
|
||||
struct fib6_node *fn;
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
struct fib6_node *pn = NULL;
|
||||
|
|
@ -1499,8 +1498,16 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
|
|||
}
|
||||
#endif
|
||||
|
||||
err = fib6_add_rt2node(fn, rt, info, extack);
|
||||
err = fib6_add_rt2node(fn, rt, info, extack, &purge_list);
|
||||
if (!err) {
|
||||
struct fib6_info *iter, *next;
|
||||
|
||||
list_for_each_entry_safe(iter, next, &purge_list, purge_link) {
|
||||
list_del(&iter->purge_link);
|
||||
fib6_purge_rt(iter, fn, info->nl_net);
|
||||
fib6_info_release(iter);
|
||||
}
|
||||
|
||||
if (rt->nh)
|
||||
list_add(&rt->nh_list, &rt->nh->f6i_list);
|
||||
__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
|
||||
|
|
|
|||
Loading…
Reference in New Issue