@@ -63,9 +63,16 @@ static void nexthop_free_mpath(struct nexthop *nh)
63
63
int i ;
64
64
65
65
nhg = rcu_dereference_raw (nh -> nh_grp );
66
- for (i = 0 ; i < nhg -> num_nh ; ++ i )
67
- WARN_ON ( nhg -> nh_entries [i ]. nh ) ;
66
+ for (i = 0 ; i < nhg -> num_nh ; ++ i ) {
67
+ struct nh_grp_entry * nhge = & nhg -> nh_entries [i ];
68
68
69
+ WARN_ON (!list_empty (& nhge -> nh_list ));
70
+ nexthop_put (nhge -> nh );
71
+ }
72
+
73
+ WARN_ON (nhg -> spare == nhg );
74
+
75
+ kfree (nhg -> spare );
69
76
kfree (nhg );
70
77
}
71
78
@@ -697,46 +704,53 @@ static void nh_group_rebalance(struct nh_group *nhg)
697
704
static void remove_nh_grp_entry (struct net * net , struct nh_grp_entry * nhge ,
698
705
struct nl_info * nlinfo )
699
706
{
707
+ struct nh_grp_entry * nhges , * new_nhges ;
700
708
struct nexthop * nhp = nhge -> nh_parent ;
701
709
struct nexthop * nh = nhge -> nh ;
702
- struct nh_grp_entry * nhges ;
703
- struct nh_group * nhg ;
704
- bool found = false;
705
- int i ;
710
+ struct nh_group * nhg , * newg ;
711
+ int i , j ;
706
712
707
713
WARN_ON (!nh );
708
714
709
- list_del (& nhge -> nh_list );
710
-
711
715
nhg = rtnl_dereference (nhp -> nh_grp );
712
- nhges = nhg -> nh_entries ;
713
- for (i = 0 ; i < nhg -> num_nh ; ++ i ) {
714
- if (found ) {
715
- nhges [i - 1 ].nh = nhges [i ].nh ;
716
- nhges [i - 1 ].weight = nhges [i ].weight ;
717
- list_del (& nhges [i ].nh_list );
718
- list_add (& nhges [i - 1 ].nh_list , & nhges [i - 1 ].nh -> grp_list );
719
- } else if (nhg -> nh_entries [i ].nh == nh ) {
720
- found = true;
721
- }
722
- }
716
+ newg = nhg -> spare ;
723
717
724
- if (WARN_ON (!found ))
718
+ /* last entry, keep it visible and remove the parent */
719
+ if (nhg -> num_nh == 1 ) {
720
+ remove_nexthop (net , nhp , nlinfo );
725
721
return ;
722
+ }
726
723
727
- nhg -> num_nh -- ;
728
- nhg -> nh_entries [nhg -> num_nh ].nh = NULL ;
724
+ newg -> has_v4 = nhg -> has_v4 ;
725
+ newg -> mpath = nhg -> mpath ;
726
+ newg -> num_nh = nhg -> num_nh ;
729
727
730
- nh_group_rebalance (nhg );
728
+ /* copy old entries to new except the one getting removed */
729
+ nhges = nhg -> nh_entries ;
730
+ new_nhges = newg -> nh_entries ;
731
+ for (i = 0 , j = 0 ; i < nhg -> num_nh ; ++ i ) {
732
+ /* current nexthop getting removed */
733
+ if (nhg -> nh_entries [i ].nh == nh ) {
734
+ newg -> num_nh -- ;
735
+ continue ;
736
+ }
731
737
732
- nexthop_put (nh );
738
+ list_del (& nhges [i ].nh_list );
739
+ new_nhges [j ].nh_parent = nhges [i ].nh_parent ;
740
+ new_nhges [j ].nh = nhges [i ].nh ;
741
+ new_nhges [j ].weight = nhges [i ].weight ;
742
+ list_add (& new_nhges [j ].nh_list , & new_nhges [j ].nh -> grp_list );
743
+ j ++ ;
744
+ }
745
+
746
+ nh_group_rebalance (newg );
747
+ rcu_assign_pointer (nhp -> nh_grp , newg );
748
+
749
+ list_del (& nhge -> nh_list );
750
+ nexthop_put (nhge -> nh );
733
751
734
752
if (nlinfo )
735
753
nexthop_notify (RTM_NEWNEXTHOP , nhp , nlinfo );
736
-
737
- /* if this group has no more entries then remove it */
738
- if (!nhg -> num_nh )
739
- remove_nexthop (net , nhp , nlinfo );
740
754
}
741
755
742
756
static void remove_nexthop_from_groups (struct net * net , struct nexthop * nh ,
@@ -746,6 +760,9 @@ static void remove_nexthop_from_groups(struct net *net, struct nexthop *nh,
746
760
747
761
list_for_each_entry_safe (nhge , tmp , & nh -> grp_list , nh_list )
748
762
remove_nh_grp_entry (net , nhge , nlinfo );
763
+
764
+ /* make sure all see the newly published array before releasing rtnl */
765
+ synchronize_rcu ();
749
766
}
750
767
751
768
static void remove_nexthop_group (struct nexthop * nh , struct nl_info * nlinfo )
@@ -759,10 +776,7 @@ static void remove_nexthop_group(struct nexthop *nh, struct nl_info *nlinfo)
759
776
if (WARN_ON (!nhge -> nh ))
760
777
continue ;
761
778
762
- list_del (& nhge -> nh_list );
763
- nexthop_put (nhge -> nh );
764
- nhge -> nh = NULL ;
765
- nhg -> num_nh -- ;
779
+ list_del_init (& nhge -> nh_list );
766
780
}
767
781
}
768
782
@@ -1085,6 +1099,7 @@ static struct nexthop *nexthop_create_group(struct net *net,
1085
1099
{
1086
1100
struct nlattr * grps_attr = cfg -> nh_grp ;
1087
1101
struct nexthop_grp * entry = nla_data (grps_attr );
1102
+ u16 num_nh = nla_len (grps_attr ) / sizeof (* entry );
1088
1103
struct nh_group * nhg ;
1089
1104
struct nexthop * nh ;
1090
1105
int i ;
@@ -1095,12 +1110,21 @@ static struct nexthop *nexthop_create_group(struct net *net,
1095
1110
1096
1111
nh -> is_group = 1 ;
1097
1112
1098
- nhg = nexthop_grp_alloc (nla_len ( grps_attr ) / sizeof ( * entry ) );
1113
+ nhg = nexthop_grp_alloc (num_nh );
1099
1114
if (!nhg ) {
1100
1115
kfree (nh );
1101
1116
return ERR_PTR (- ENOMEM );
1102
1117
}
1103
1118
1119
+ /* spare group used for removals */
1120
+ nhg -> spare = nexthop_grp_alloc (num_nh );
1121
+ if (!nhg ) {
1122
+ kfree (nhg );
1123
+ kfree (nh );
1124
+ return NULL ;
1125
+ }
1126
+ nhg -> spare -> spare = nhg ;
1127
+
1104
1128
for (i = 0 ; i < nhg -> num_nh ; ++ i ) {
1105
1129
struct nexthop * nhe ;
1106
1130
struct nh_info * nhi ;
@@ -1132,6 +1156,7 @@ static struct nexthop *nexthop_create_group(struct net *net,
1132
1156
for (; i >= 0 ; -- i )
1133
1157
nexthop_put (nhg -> nh_entries [i ].nh );
1134
1158
1159
+ kfree (nhg -> spare );
1135
1160
kfree (nhg );
1136
1161
kfree (nh );
1137
1162
0 commit comments