|
|
@@ -1282,18 +1282,29 @@ static DEFINE_SPINLOCK(rt6_exception_lock);
|
|
|
static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
|
|
|
struct rt6_exception *rt6_ex)
|
|
|
{
|
|
|
+ struct fib6_info *from;
|
|
|
struct net *net;
|
|
|
|
|
|
if (!bucket || !rt6_ex)
|
|
|
return;
|
|
|
|
|
|
net = dev_net(rt6_ex->rt6i->dst.dev);
|
|
|
+ net->ipv6.rt6_stats->fib_rt_cache--;
|
|
|
+
|
|
|
+ /* purge completely the exception to allow releasing the held resources:
|
|
|
+ * some [sk] cache may keep the dst around for unlimited time
|
|
|
+ */
|
|
|
+ from = rcu_dereference_protected(rt6_ex->rt6i->from,
|
|
|
+ lockdep_is_held(&rt6_exception_lock));
|
|
|
+ rcu_assign_pointer(rt6_ex->rt6i->from, NULL);
|
|
|
+ fib6_info_release(from);
|
|
|
+ dst_dev_put(&rt6_ex->rt6i->dst);
|
|
|
+
|
|
|
hlist_del_rcu(&rt6_ex->hlist);
|
|
|
dst_release(&rt6_ex->rt6i->dst);
|
|
|
kfree_rcu(rt6_ex, rcu);
|
|
|
WARN_ON_ONCE(!bucket->depth);
|
|
|
bucket->depth--;
|
|
|
- net->ipv6.rt6_stats->fib_rt_cache--;
|
|
|
}
|
|
|
|
|
|
/* Remove oldest rt6_ex in bucket and free the memory
|