diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index feb88ccbbced..736a44ee89b8 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -1823,11 +1823,25 @@ int nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev, int nsim_dev_init(void) { + int err; + nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); - return PTR_ERR_OR_ZERO(nsim_dev_ddir); + if (IS_ERR(nsim_dev_ddir)) + return PTR_ERR(nsim_dev_ddir); + + err = nsim_fib_init(); + if (err) + goto err_debugfs_destroy; + + return 0; + +err_debugfs_destroy: + debugfs_remove_recursive(nsim_dev_ddir); + return err; } void nsim_dev_exit(void) { + nsim_fib_exit(); debugfs_remove_recursive(nsim_dev_ddir); } diff --git a/drivers/net/netdevsim/fib.c b/drivers/net/netdevsim/fib.c index 55bcdefadc9b..948c0ae39f8a 100644 --- a/drivers/net/netdevsim/fib.c +++ b/drivers/net/netdevsim/fib.c @@ -59,6 +59,7 @@ struct nsim_fib_data { spinlock_t fib_event_queue_lock; /* Protects fib event queue list */ struct mutex nh_lock; /* Protects NH HT */ struct dentry *ddir; + struct list_head list; /* Member of nsim_fib_data_list */ bool fail_route_offload; bool fail_res_nexthop_group_replace; bool fail_nexthop_bucket_replace; @@ -1549,6 +1550,49 @@ static void nsim_fib_debugfs_exit(struct nsim_fib_data *data) debugfs_remove_recursive(data->ddir); } +static LIST_HEAD(nsim_fib_data_list); +static DEFINE_MUTEX(nsim_fib_data_list_lock); + +static int nsim_fib_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct nsim_fib_data *data; + + if (event != NETDEV_UNREGISTER) + return NOTIFY_DONE; + + mutex_lock(&nsim_fib_data_list_lock); + list_for_each_entry(data, &nsim_fib_data_list, list) { + if (!net_eq(dev_net(dev), devlink_net(data->devlink))) + continue; + + flush_work(&data->fib_flush_work); + flush_work(&data->fib_event_work); + } + mutex_unlock(&nsim_fib_data_list_lock); + + return NOTIFY_DONE; +} + +/* Run after fib_netdev_notifier and ipv6_dev_notf so that + * FIB_EVENT_ENTRY_DEL events are queued before flushing. + */ +static struct notifier_block nsim_fib_netdev_notifier = { + .notifier_call = nsim_fib_netdevice_event, + .priority = -1, +}; + +int nsim_fib_init(void) +{ + return register_netdevice_notifier(&nsim_fib_netdev_notifier); +} + +void nsim_fib_exit(void) +{ + unregister_netdevice_notifier(&nsim_fib_netdev_notifier); +} + struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, struct netlink_ext_ack *extack) { @@ -1601,6 +1645,10 @@ struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, if (err) goto err_fib_notifier_unregister; + mutex_lock(&nsim_fib_data_list_lock); + list_add_tail(&data->list, &nsim_fib_data_list); + mutex_unlock(&nsim_fib_data_list_lock); + devl_resource_occ_get_register(devlink, NSIM_RESOURCE_IPV4_FIB, nsim_fib_ipv4_resource_occ_get, @@ -1654,6 +1702,9 @@ void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *data) NSIM_RESOURCE_IPV4_FIB_RULES); devl_resource_occ_get_unregister(devlink, NSIM_RESOURCE_IPV4_FIB); + mutex_lock(&nsim_fib_data_list_lock); + list_del(&data->list); + mutex_unlock(&nsim_fib_data_list_lock); nsim_fib_debugfs_exit(data); unregister_fib_notifier(devlink_net(devlink), &data->fib_nb); unregister_nexthop_notifier(devlink_net(devlink), &data->nexthop_nb); diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index fd5599cd3bb9..05035f273eed 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -402,6 +402,8 @@ int nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev, unsigned int nsim_dev_get_vfs(struct nsim_dev *nsim_dev); +int nsim_fib_init(void); +void nsim_fib_exit(void); struct nsim_fib_data *nsim_fib_create(struct devlink *devlink, struct netlink_ext_ack *extack); void nsim_fib_destroy(struct devlink *devlink, struct nsim_fib_data *fib_data); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5ffb3dc73cf4..d58c333aff6a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5407,6 +5407,7 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, struct fib6_info *rt_notif = NULL, *rt_last = NULL; struct nl_info *info = &cfg->fc_nlinfo; struct rt6_nh *nh, *nh_safe; + struct fib6_table *table; struct fib6_config r_cfg; struct rtnexthop *rtnh; LIST_HEAD(rt6_nh_list); @@ -5493,10 +5494,12 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, */ info->skip_notify_kernel = 1; + table = rt->fib6_table; + spin_lock_bh(&table->tb6_lock); + err_nh = NULL; list_for_each_entry(nh, &rt6_nh_list, list) { - err = __ip6_ins_rt(nh->fib6_info, info, extack); - + err = fib6_add(&table->tb6_root, nh->fib6_info, info, extack); if (err) { if (replace && nhn) NL_SET_ERR_MSG_MOD(extack, @@ -5551,11 +5554,12 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, /* success ... tell user about new route */ ip6_route_mpath_notify(rt_notif, rt_last, info, nlflags); + spin_unlock_bh(&table->tb6_lock); goto cleanup; add_errout: /* send notification for routes that were added so that - * the delete notifications sent by ip6_route_del are + * the delete notifications sent by fib6_del() are * coherent */ if (rt_notif) @@ -5565,8 +5569,10 @@ static int ip6_route_multipath_add(struct fib6_config *cfg, list_for_each_entry(nh, &rt6_nh_list, list) { if (err_nh == nh) break; - ip6_route_del(&nh->r_cfg, extack); + fib6_del(nh->fib6_info, &nh->r_cfg.fc_nlinfo, + RT_DEL_REASON_UNSPEC); } + spin_unlock_bh(&table->tb6_lock); cleanup: list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, list) {