net: ipv6: add fib6_nh_release_dsts stub
[ Upstream commit 8837cbbf854246f5f4d565f21e6baa945d37aded ]
We need a way to release a fib6_nh's per-cpu dsts when replacing
nexthops otherwise we can end up with stale per-cpu dsts which hold net
device references, so add a new IPv6 stub called fib6_nh_release_dsts.
It must be used after an RCU grace period, so no new dsts can be created
through a group's nexthop entry.
Similar to fib6_nh_release it shouldn't be used if fib6_nh_init has failed
so it doesn't need a dummy stub when IPv6 is not enabled.
Fixes: 7bf4796dd0
("nexthops: add support for replace")
Signed-off-by: Nikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
dc2f7e9d8d
commit
3c40584595
@@ -491,6 +491,7 @@ int fib6_nh_init(struct net *net, struct fib6_nh *fib6_nh,
|
|||||||
struct fib6_config *cfg, gfp_t gfp_flags,
|
struct fib6_config *cfg, gfp_t gfp_flags,
|
||||||
struct netlink_ext_ack *extack);
|
struct netlink_ext_ack *extack);
|
||||||
void fib6_nh_release(struct fib6_nh *fib6_nh);
|
void fib6_nh_release(struct fib6_nh *fib6_nh);
|
||||||
|
void fib6_nh_release_dsts(struct fib6_nh *fib6_nh);
|
||||||
|
|
||||||
int call_fib6_entry_notifiers(struct net *net,
|
int call_fib6_entry_notifiers(struct net *net,
|
||||||
enum fib_event_type event_type,
|
enum fib_event_type event_type,
|
||||||
|
@@ -47,6 +47,7 @@ struct ipv6_stub {
|
|||||||
struct fib6_config *cfg, gfp_t gfp_flags,
|
struct fib6_config *cfg, gfp_t gfp_flags,
|
||||||
struct netlink_ext_ack *extack);
|
struct netlink_ext_ack *extack);
|
||||||
void (*fib6_nh_release)(struct fib6_nh *fib6_nh);
|
void (*fib6_nh_release)(struct fib6_nh *fib6_nh);
|
||||||
|
void (*fib6_nh_release_dsts)(struct fib6_nh *fib6_nh);
|
||||||
void (*fib6_update_sernum)(struct net *net, struct fib6_info *rt);
|
void (*fib6_update_sernum)(struct net *net, struct fib6_info *rt);
|
||||||
int (*ip6_del_rt)(struct net *net, struct fib6_info *rt, bool skip_notify);
|
int (*ip6_del_rt)(struct net *net, struct fib6_info *rt, bool skip_notify);
|
||||||
void (*fib6_rt_update)(struct net *net, struct fib6_info *rt,
|
void (*fib6_rt_update)(struct net *net, struct fib6_info *rt,
|
||||||
|
@@ -1016,6 +1016,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
|
|||||||
.ip6_mtu_from_fib6 = ip6_mtu_from_fib6,
|
.ip6_mtu_from_fib6 = ip6_mtu_from_fib6,
|
||||||
.fib6_nh_init = fib6_nh_init,
|
.fib6_nh_init = fib6_nh_init,
|
||||||
.fib6_nh_release = fib6_nh_release,
|
.fib6_nh_release = fib6_nh_release,
|
||||||
|
.fib6_nh_release_dsts = fib6_nh_release_dsts,
|
||||||
.fib6_update_sernum = fib6_update_sernum_stub,
|
.fib6_update_sernum = fib6_update_sernum_stub,
|
||||||
.fib6_rt_update = fib6_rt_update,
|
.fib6_rt_update = fib6_rt_update,
|
||||||
.ip6_del_rt = ip6_del_rt,
|
.ip6_del_rt = ip6_del_rt,
|
||||||
|
@@ -3570,6 +3570,25 @@ void fib6_nh_release(struct fib6_nh *fib6_nh)
|
|||||||
fib_nh_common_release(&fib6_nh->nh_common);
|
fib_nh_common_release(&fib6_nh->nh_common);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fib6_nh_release_dsts(struct fib6_nh *fib6_nh)
|
||||||
|
{
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
if (!fib6_nh->rt6i_pcpu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for_each_possible_cpu(cpu) {
|
||||||
|
struct rt6_info *pcpu_rt, **ppcpu_rt;
|
||||||
|
|
||||||
|
ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu);
|
||||||
|
pcpu_rt = xchg(ppcpu_rt, NULL);
|
||||||
|
if (pcpu_rt) {
|
||||||
|
dst_dev_put(&pcpu_rt->dst);
|
||||||
|
dst_release(&pcpu_rt->dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
|
static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
|
||||||
gfp_t gfp_flags,
|
gfp_t gfp_flags,
|
||||||
struct netlink_ext_ack *extack)
|
struct netlink_ext_ack *extack)
|
||||||
|
Reference in New Issue
Block a user