@@ -5346,7 +5346,8 @@ static void ip6_route_mpath_notify(struct fib6_info *rt,
5346
5346
*/
5347
5347
rcu_read_lock ();
5348
5348
5349
- if ((nlflags & NLM_F_APPEND ) && rt_last && rt_last -> fib6_nsiblings ) {
5349
+ if ((nlflags & NLM_F_APPEND ) && rt_last &&
5350
+ READ_ONCE (rt_last -> fib6_nsiblings )) {
5350
5351
rt = list_first_or_null_rcu (& rt_last -> fib6_siblings ,
5351
5352
struct fib6_info ,
5352
5353
fib6_siblings );
@@ -5670,32 +5671,34 @@ static int rt6_nh_nlmsg_size(struct fib6_nh *nh, void *arg)
5670
5671
5671
5672
static size_t rt6_nlmsg_size (struct fib6_info * f6i )
5672
5673
{
5674
+ struct fib6_info * sibling ;
5675
+ struct fib6_nh * nh ;
5673
5676
int nexthop_len ;
5674
5677
5675
5678
if (f6i -> nh ) {
5676
5679
nexthop_len = nla_total_size (4 ); /* RTA_NH_ID */
5677
5680
nexthop_for_each_fib6_nh (f6i -> nh , rt6_nh_nlmsg_size ,
5678
5681
& nexthop_len );
5679
- } else {
5680
- struct fib6_nh * nh = f6i -> fib6_nh ;
5681
- struct fib6_info * sibling ;
5682
-
5683
- nexthop_len = 0 ;
5684
- if (f6i -> fib6_nsiblings ) {
5685
- rt6_nh_nlmsg_size (nh , & nexthop_len );
5686
-
5687
- rcu_read_lock ();
5682
+ goto common ;
5683
+ }
5688
5684
5689
- list_for_each_entry_rcu (sibling , & f6i -> fib6_siblings ,
5690
- fib6_siblings ) {
5691
- rt6_nh_nlmsg_size (sibling -> fib6_nh , & nexthop_len );
5692
- }
5685
+ rcu_read_lock ();
5686
+ retry :
5687
+ nh = f6i -> fib6_nh ;
5688
+ nexthop_len = 0 ;
5689
+ if (READ_ONCE (f6i -> fib6_nsiblings )) {
5690
+ rt6_nh_nlmsg_size (nh , & nexthop_len );
5693
5691
5694
- rcu_read_unlock ();
5692
+ list_for_each_entry_rcu (sibling , & f6i -> fib6_siblings ,
5693
+ fib6_siblings ) {
5694
+ rt6_nh_nlmsg_size (sibling -> fib6_nh , & nexthop_len );
5695
+ if (!READ_ONCE (f6i -> fib6_nsiblings ))
5696
+ goto retry ;
5695
5697
}
5696
- nexthop_len += lwtunnel_get_encap_size (nh -> fib_nh_lws );
5697
5698
}
5698
-
5699
+ rcu_read_unlock ();
5700
+ nexthop_len += lwtunnel_get_encap_size (nh -> fib_nh_lws );
5701
+ common :
5699
5702
return NLMSG_ALIGN (sizeof (struct rtmsg ))
5700
5703
+ nla_total_size (16 ) /* RTA_SRC */
5701
5704
+ nla_total_size (16 ) /* RTA_DST */
@@ -5854,7 +5857,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
5854
5857
if (dst -> lwtstate &&
5855
5858
lwtunnel_fill_encap (skb , dst -> lwtstate , RTA_ENCAP , RTA_ENCAP_TYPE ) < 0 )
5856
5859
goto nla_put_failure ;
5857
- } else if (rt -> fib6_nsiblings ) {
5860
+ } else if (READ_ONCE ( rt -> fib6_nsiblings ) ) {
5858
5861
struct fib6_info * sibling ;
5859
5862
struct nlattr * mp ;
5860
5863
@@ -5956,16 +5959,21 @@ static bool fib6_info_uses_dev(const struct fib6_info *f6i,
5956
5959
if (f6i -> fib6_nh -> fib_nh_dev == dev )
5957
5960
return true;
5958
5961
5959
- if (f6i -> fib6_nsiblings ) {
5960
- struct fib6_info * sibling , * next_sibling ;
5962
+ if (READ_ONCE ( f6i -> fib6_nsiblings ) ) {
5963
+ const struct fib6_info * sibling ;
5961
5964
5962
- list_for_each_entry_safe (sibling , next_sibling ,
5963
- & f6i -> fib6_siblings , fib6_siblings ) {
5964
- if (sibling -> fib6_nh -> fib_nh_dev == dev )
5965
+ rcu_read_lock ();
5966
+ list_for_each_entry_rcu (sibling , & f6i -> fib6_siblings ,
5967
+ fib6_siblings ) {
5968
+ if (sibling -> fib6_nh -> fib_nh_dev == dev ) {
5969
+ rcu_read_unlock ();
5965
5970
return true;
5971
+ }
5972
+ if (!READ_ONCE (f6i -> fib6_nsiblings ))
5973
+ break ;
5966
5974
}
5975
+ rcu_read_unlock ();
5967
5976
}
5968
-
5969
5977
return false;
5970
5978
}
5971
5979
@@ -6321,26 +6329,31 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
6321
6329
void inet6_rt_notify (int event , struct fib6_info * rt , struct nl_info * info ,
6322
6330
unsigned int nlm_flags )
6323
6331
{
6324
- struct sk_buff * skb ;
6325
6332
struct net * net = info -> nl_net ;
6333
+ struct sk_buff * skb ;
6334
+ size_t sz ;
6326
6335
u32 seq ;
6327
6336
int err ;
6328
6337
6329
6338
err = - ENOBUFS ;
6330
6339
seq = info -> nlh ? info -> nlh -> nlmsg_seq : 0 ;
6331
6340
6332
6341
rcu_read_lock ();
6333
-
6334
- skb = nlmsg_new (rt6_nlmsg_size (rt ), GFP_ATOMIC );
6342
+ sz = rt6_nlmsg_size (rt );
6343
+ retry :
6344
+ skb = nlmsg_new (sz , GFP_ATOMIC );
6335
6345
if (!skb )
6336
6346
goto errout ;
6337
6347
6338
6348
err = rt6_fill_node (net , skb , rt , NULL , NULL , NULL , 0 ,
6339
6349
event , info -> portid , seq , nlm_flags );
6340
6350
if (err < 0 ) {
6341
- /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
6342
- WARN_ON (err == - EMSGSIZE );
6343
6351
kfree_skb (skb );
6352
+ /* -EMSGSIZE implies needed space grew under us. */
6353
+ if (err == - EMSGSIZE ) {
6354
+ sz = max (rt6_nlmsg_size (rt ), sz << 1 );
6355
+ goto retry ;
6356
+ }
6344
6357
goto errout ;
6345
6358
}
6346
6359
0 commit comments