@@ -165,11 +165,18 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev)
165
165
}
166
166
}
167
167
168
+ static u32 * rt6_pcpu_cow_metrics (struct rt6_info * rt )
169
+ {
170
+ return dst_metrics_write_ptr (rt -> dst .from );
171
+ }
172
+
168
173
static u32 * ipv6_cow_metrics (struct dst_entry * dst , unsigned long old )
169
174
{
170
175
struct rt6_info * rt = (struct rt6_info * )dst ;
171
176
172
- if (rt -> rt6i_flags & RTF_CACHE )
177
+ if (rt -> rt6i_flags & RTF_PCPU )
178
+ return rt6_pcpu_cow_metrics (rt );
179
+ else if (rt -> rt6i_flags & RTF_CACHE )
173
180
return NULL ;
174
181
else
175
182
return dst_cow_metrics_generic (dst , old );
@@ -309,10 +316,10 @@ static const struct rt6_info ip6_blk_hole_entry_template = {
309
316
#endif
310
317
311
318
/* allocate dst with ip6_dst_ops */
312
- static inline struct rt6_info * ip6_dst_alloc (struct net * net ,
313
- struct net_device * dev ,
314
- int flags ,
315
- struct fib6_table * table )
319
+ static struct rt6_info * __ip6_dst_alloc (struct net * net ,
320
+ struct net_device * dev ,
321
+ int flags ,
322
+ struct fib6_table * table )
316
323
{
317
324
struct rt6_info * rt = dst_alloc (& net -> ipv6 .ip6_dst_ops , dev ,
318
325
0 , DST_OBSOLETE_FORCE_CHK , flags );
@@ -327,6 +334,34 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
327
334
return rt ;
328
335
}
329
336
337
+ static struct rt6_info * ip6_dst_alloc (struct net * net ,
338
+ struct net_device * dev ,
339
+ int flags ,
340
+ struct fib6_table * table )
341
+ {
342
+ struct rt6_info * rt = __ip6_dst_alloc (net , dev , flags , table );
343
+
344
+ if (rt ) {
345
+ rt -> rt6i_pcpu = alloc_percpu_gfp (struct rt6_info * , GFP_ATOMIC );
346
+ if (rt -> rt6i_pcpu ) {
347
+ int cpu ;
348
+
349
+ for_each_possible_cpu (cpu ) {
350
+ struct rt6_info * * p ;
351
+
352
+ p = per_cpu_ptr (rt -> rt6i_pcpu , cpu );
353
+ /* no one shares rt */
354
+ * p = NULL ;
355
+ }
356
+ } else {
357
+ dst_destroy ((struct dst_entry * )rt );
358
+ return NULL ;
359
+ }
360
+ }
361
+
362
+ return rt ;
363
+ }
364
+
330
365
static void ip6_dst_destroy (struct dst_entry * dst )
331
366
{
332
367
struct rt6_info * rt = (struct rt6_info * )dst ;
@@ -335,6 +370,9 @@ static void ip6_dst_destroy(struct dst_entry *dst)
335
370
336
371
dst_destroy_metrics_generic (dst );
337
372
373
+ if (rt -> rt6i_pcpu )
374
+ free_percpu (rt -> rt6i_pcpu );
375
+
338
376
rt6_uncached_list_del (rt );
339
377
340
378
idev = rt -> rt6i_idev ;
@@ -912,11 +950,11 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
912
950
* Clone the route.
913
951
*/
914
952
915
- if (ort -> rt6i_flags & RTF_CACHE )
953
+ if (ort -> rt6i_flags & ( RTF_CACHE | RTF_PCPU ) )
916
954
ort = (struct rt6_info * )ort -> dst .from ;
917
955
918
- rt = ip6_dst_alloc (dev_net (ort -> dst .dev ), ort -> dst .dev ,
919
- 0 , ort -> rt6i_table );
956
+ rt = __ip6_dst_alloc (dev_net (ort -> dst .dev ), ort -> dst .dev ,
957
+ 0 , ort -> rt6i_table );
920
958
921
959
if (!rt )
922
960
return NULL ;
@@ -943,6 +981,54 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort,
943
981
return rt ;
944
982
}
945
983
984
+ static struct rt6_info * ip6_rt_pcpu_alloc (struct rt6_info * rt )
985
+ {
986
+ struct rt6_info * pcpu_rt ;
987
+
988
+ pcpu_rt = __ip6_dst_alloc (dev_net (rt -> dst .dev ),
989
+ rt -> dst .dev , rt -> dst .flags ,
990
+ rt -> rt6i_table );
991
+
992
+ if (!pcpu_rt )
993
+ return NULL ;
994
+ ip6_rt_copy_init (pcpu_rt , rt );
995
+ pcpu_rt -> rt6i_protocol = rt -> rt6i_protocol ;
996
+ pcpu_rt -> rt6i_flags |= RTF_PCPU ;
997
+ return pcpu_rt ;
998
+ }
999
+
1000
+ /* It should be called with read_lock_bh(&tb6_lock) acquired */
1001
+ static struct rt6_info * rt6_get_pcpu_route (struct rt6_info * rt )
1002
+ {
1003
+ struct rt6_info * pcpu_rt , * prev , * * p ;
1004
+
1005
+ p = this_cpu_ptr (rt -> rt6i_pcpu );
1006
+ pcpu_rt = * p ;
1007
+
1008
+ if (pcpu_rt )
1009
+ goto done ;
1010
+
1011
+ pcpu_rt = ip6_rt_pcpu_alloc (rt );
1012
+ if (!pcpu_rt ) {
1013
+ struct net * net = dev_net (rt -> dst .dev );
1014
+
1015
+ pcpu_rt = net -> ipv6 .ip6_null_entry ;
1016
+ goto done ;
1017
+ }
1018
+
1019
+ prev = cmpxchg (p , NULL , pcpu_rt );
1020
+ if (prev ) {
1021
+ /* If someone did it before us, return prev instead */
1022
+ dst_destroy (& pcpu_rt -> dst );
1023
+ pcpu_rt = prev ;
1024
+ }
1025
+
1026
+ done :
1027
+ dst_hold (& pcpu_rt -> dst );
1028
+ rt6_dst_from_metrics_check (pcpu_rt );
1029
+ return pcpu_rt ;
1030
+ }
1031
+
946
1032
static struct rt6_info * ip6_pol_route (struct net * net , struct fib6_table * table , int oif ,
947
1033
struct flowi6 * fl6 , int flags )
948
1034
{
@@ -975,11 +1061,13 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
975
1061
}
976
1062
}
977
1063
978
- dst_use (& rt -> dst , jiffies );
979
- read_unlock_bh (& table -> tb6_lock );
980
1064
981
1065
if (rt == net -> ipv6 .ip6_null_entry || (rt -> rt6i_flags & RTF_CACHE )) {
982
- goto done ;
1066
+ dst_use (& rt -> dst , jiffies );
1067
+ read_unlock_bh (& table -> tb6_lock );
1068
+
1069
+ rt6_dst_from_metrics_check (rt );
1070
+ return rt ;
983
1071
} else if (unlikely ((fl6 -> flowi6_flags & FLOWI_FLAG_KNOWN_NH ) &&
984
1072
!(rt -> rt6i_flags & RTF_GATEWAY ))) {
985
1073
/* Create a RTF_CACHE clone which will not be
@@ -990,20 +1078,32 @@ static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
990
1078
991
1079
struct rt6_info * uncached_rt ;
992
1080
1081
+ dst_use (& rt -> dst , jiffies );
1082
+ read_unlock_bh (& table -> tb6_lock );
1083
+
993
1084
uncached_rt = ip6_rt_cache_alloc (rt , & fl6 -> daddr , NULL );
994
1085
dst_release (& rt -> dst );
995
1086
996
1087
if (uncached_rt )
997
1088
rt6_uncached_list_add (uncached_rt );
998
1089
else
999
1090
uncached_rt = net -> ipv6 .ip6_null_entry ;
1091
+
1000
1092
dst_hold (& uncached_rt -> dst );
1001
1093
return uncached_rt ;
1002
- }
1003
1094
1004
- done :
1005
- rt6_dst_from_metrics_check (rt );
1006
- return rt ;
1095
+ } else {
1096
+ /* Get a percpu copy */
1097
+
1098
+ struct rt6_info * pcpu_rt ;
1099
+
1100
+ rt -> dst .lastuse = jiffies ;
1101
+ rt -> dst .__use ++ ;
1102
+ pcpu_rt = rt6_get_pcpu_route (rt );
1103
+ read_unlock_bh (& table -> tb6_lock );
1104
+
1105
+ return pcpu_rt ;
1106
+ }
1007
1107
}
1008
1108
1009
1109
static struct rt6_info * ip6_pol_route_input (struct net * net , struct fib6_table * table ,
@@ -1147,7 +1247,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
1147
1247
1148
1248
rt6_dst_from_metrics_check (rt );
1149
1249
1150
- if (unlikely (dst -> flags & DST_NOCACHE ))
1250
+ if (( rt -> rt6i_flags & RTF_PCPU ) || unlikely (dst -> flags & DST_NOCACHE ))
1151
1251
return rt6_dst_from_check (rt , cookie );
1152
1252
else
1153
1253
return rt6_check (rt , cookie );
0 commit comments