@@ -105,6 +105,67 @@ static struct rt6_info *rt6_get_route_info(struct net *net,
105105 const struct in6_addr * gwaddr , int ifindex );
106106#endif
107107
108+ struct uncached_list {
109+ spinlock_t lock ;
110+ struct list_head head ;
111+ };
112+
113+ static DEFINE_PER_CPU_ALIGNED (struct uncached_list , rt6_uncached_list ) ;
114+
115+ static void rt6_uncached_list_add (struct rt6_info * rt )
116+ {
117+ struct uncached_list * ul = raw_cpu_ptr (& rt6_uncached_list );
118+
119+ rt -> dst .flags |= DST_NOCACHE ;
120+ rt -> rt6i_uncached_list = ul ;
121+
122+ spin_lock_bh (& ul -> lock );
123+ list_add_tail (& rt -> rt6i_uncached , & ul -> head );
124+ spin_unlock_bh (& ul -> lock );
125+ }
126+
127+ static void rt6_uncached_list_del (struct rt6_info * rt )
128+ {
129+ if (!list_empty (& rt -> rt6i_uncached )) {
130+ struct uncached_list * ul = rt -> rt6i_uncached_list ;
131+
132+ spin_lock_bh (& ul -> lock );
133+ list_del (& rt -> rt6i_uncached );
134+ spin_unlock_bh (& ul -> lock );
135+ }
136+ }
137+
138+ static void rt6_uncached_list_flush_dev (struct net * net , struct net_device * dev )
139+ {
140+ struct net_device * loopback_dev = net -> loopback_dev ;
141+ int cpu ;
142+
143+ for_each_possible_cpu (cpu ) {
144+ struct uncached_list * ul = per_cpu_ptr (& rt6_uncached_list , cpu );
145+ struct rt6_info * rt ;
146+
147+ spin_lock_bh (& ul -> lock );
148+ list_for_each_entry (rt , & ul -> head , rt6i_uncached ) {
149+ struct inet6_dev * rt_idev = rt -> rt6i_idev ;
150+ struct net_device * rt_dev = rt -> dst .dev ;
151+
152+ if (rt_idev && (rt_idev -> dev == dev || !dev ) &&
153+ rt_idev -> dev != loopback_dev ) {
154+ rt -> rt6i_idev = in6_dev_get (loopback_dev );
155+ in6_dev_put (rt_idev );
156+ }
157+
158+ if (rt_dev && (rt_dev == dev || !dev ) &&
159+ rt_dev != loopback_dev ) {
160+ rt -> dst .dev = loopback_dev ;
161+ dev_hold (rt -> dst .dev );
162+ dev_put (rt_dev );
163+ }
164+ }
165+ spin_unlock_bh (& ul -> lock );
166+ }
167+ }
168+
108169static u32 * ipv6_cow_metrics (struct dst_entry * dst , unsigned long old )
109170{
110171 struct rt6_info * rt = (struct rt6_info * )dst ;
@@ -262,18 +323,22 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
262323
263324 memset (dst + 1 , 0 , sizeof (* rt ) - sizeof (* dst ));
264325 INIT_LIST_HEAD (& rt -> rt6i_siblings );
326+ INIT_LIST_HEAD (& rt -> rt6i_uncached );
265327 }
266328 return rt ;
267329}
268330
269331static void ip6_dst_destroy (struct dst_entry * dst )
270332{
271333 struct rt6_info * rt = (struct rt6_info * )dst ;
272- struct inet6_dev * idev = rt -> rt6i_idev ;
273334 struct dst_entry * from = dst -> from ;
335+ struct inet6_dev * idev ;
274336
275337 dst_destroy_metrics_generic (dst );
276338
339+ rt6_uncached_list_del (rt );
340+
341+ idev = rt -> rt6i_idev ;
277342 if (idev ) {
278343 rt -> rt6i_idev = NULL ;
279344 in6_dev_put (idev );
@@ -920,7 +985,7 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
920985 dst_release (& rt -> dst );
921986
922987 if (uncached_rt )
923- uncached_rt -> dst . flags |= DST_NOCACHE ;
988+ rt6_uncached_list_add ( uncached_rt ) ;
924989 else
925990 uncached_rt = net -> ipv6 .ip6_null_entry ;
926991 dst_hold (& uncached_rt -> dst );
@@ -2367,6 +2432,7 @@ void rt6_ifdown(struct net *net, struct net_device *dev)
23672432
23682433 fib6_clean_all (net , fib6_ifdown , & adn );
23692434 icmp6_clean_all (fib6_ifdown , & adn );
2435+ rt6_uncached_list_flush_dev (net , dev );
23702436}
23712437
23722438struct rt6_mtu_change_arg {
@@ -3263,6 +3329,7 @@ static struct notifier_block ip6_route_dev_notifier = {
32633329int __init ip6_route_init (void )
32643330{
32653331 int ret ;
3332+ int cpu ;
32663333
32673334 ret = - ENOMEM ;
32683335 ip6_dst_ops_template .kmem_cachep =
@@ -3322,6 +3389,13 @@ int __init ip6_route_init(void)
33223389 if (ret )
33233390 goto out_register_late_subsys ;
33243391
3392+ for_each_possible_cpu (cpu ) {
3393+ struct uncached_list * ul = per_cpu_ptr (& rt6_uncached_list , cpu );
3394+
3395+ INIT_LIST_HEAD (& ul -> head );
3396+ spin_lock_init (& ul -> lock );
3397+ }
3398+
33253399out :
33263400 return ret ;
33273401
0 commit comments