aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r--net/ipv6/route.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 74b9b6fd4168..047c47224dba 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1274,18 +1274,29 @@ static DEFINE_SPINLOCK(rt6_exception_lock);
1274static void rt6_remove_exception(struct rt6_exception_bucket *bucket, 1274static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
1275 struct rt6_exception *rt6_ex) 1275 struct rt6_exception *rt6_ex)
1276{ 1276{
1277 struct fib6_info *from;
1277 struct net *net; 1278 struct net *net;
1278 1279
1279 if (!bucket || !rt6_ex) 1280 if (!bucket || !rt6_ex)
1280 return; 1281 return;
1281 1282
1282 net = dev_net(rt6_ex->rt6i->dst.dev); 1283 net = dev_net(rt6_ex->rt6i->dst.dev);
1284 net->ipv6.rt6_stats->fib_rt_cache--;
1285
1286 /* purge completely the exception to allow releasing the held resources:
1287 * some [sk] cache may keep the dst around for unlimited time
1288 */
1289 from = rcu_dereference_protected(rt6_ex->rt6i->from,
1290 lockdep_is_held(&rt6_exception_lock));
1291 rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
1292 fib6_info_release(from);
1293 dst_dev_put(&rt6_ex->rt6i->dst);
1294
1283 hlist_del_rcu(&rt6_ex->hlist); 1295 hlist_del_rcu(&rt6_ex->hlist);
1284 dst_release(&rt6_ex->rt6i->dst); 1296 dst_release(&rt6_ex->rt6i->dst);
1285 kfree_rcu(rt6_ex, rcu); 1297 kfree_rcu(rt6_ex, rcu);
1286 WARN_ON_ONCE(!bucket->depth); 1298 WARN_ON_ONCE(!bucket->depth);
1287 bucket->depth--; 1299 bucket->depth--;
1288 net->ipv6.rt6_stats->fib_rt_cache--;
1289} 1300}
1290 1301
1291/* Remove oldest rt6_ex in bucket and free the memory 1302/* Remove oldest rt6_ex in bucket and free the memory