@@ -816,6 +816,9 @@ EXPORT_SYMBOL(tcf_idr_cleanup);
816
816
* its reference and bind counters, and return 1. Otherwise insert temporary
817
817
* error pointer (to prevent concurrent users from inserting actions with same
818
818
* index) and return 0.
819
+ *
820
+ * May return -EAGAIN for binding actions in case of a parallel add/delete on
821
+ * the requested index.
819
822
*/
820
823
821
824
int tcf_idr_check_alloc (struct tc_action_net * tn , u32 * index ,
@@ -824,43 +827,61 @@ int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
824
827
struct tcf_idrinfo * idrinfo = tn -> idrinfo ;
825
828
struct tc_action * p ;
826
829
int ret ;
830
+ u32 max ;
827
831
828
- again :
829
- mutex_lock (& idrinfo -> lock );
830
832
if (* index ) {
833
+ again :
834
+ rcu_read_lock ();
831
835
p = idr_find (& idrinfo -> action_idr , * index );
836
+
832
837
if (IS_ERR (p )) {
833
838
/* This means that another process allocated
834
839
* index but did not assign the pointer yet.
835
840
*/
836
- mutex_unlock ( & idrinfo -> lock );
841
+ rcu_read_unlock ( );
837
842
goto again ;
838
843
}
839
844
840
- if (p ) {
841
- refcount_inc (& p -> tcfa_refcnt );
842
- if (bind )
843
- atomic_inc (& p -> tcfa_bindcnt );
844
- * a = p ;
845
- ret = 1 ;
846
- } else {
847
- * a = NULL ;
848
- ret = idr_alloc_u32 (& idrinfo -> action_idr , NULL , index ,
849
- * index , GFP_KERNEL );
850
- if (!ret )
851
- idr_replace (& idrinfo -> action_idr ,
852
- ERR_PTR (- EBUSY ), * index );
845
+ if (!p ) {
846
+ /* Empty slot, try to allocate it */
847
+ max = * index ;
848
+ rcu_read_unlock ();
849
+ goto new ;
853
850
}
851
+
852
+ if (!refcount_inc_not_zero (& p -> tcfa_refcnt )) {
853
+ /* Action was deleted in parallel */
854
+ rcu_read_unlock ();
855
+ return - EAGAIN ;
856
+ }
857
+
858
+ if (bind )
859
+ atomic_inc (& p -> tcfa_bindcnt );
860
+ * a = p ;
861
+
862
+ rcu_read_unlock ();
863
+
864
+ return 1 ;
854
865
} else {
866
+ /* Find a slot */
855
867
* index = 1 ;
856
- * a = NULL ;
857
- ret = idr_alloc_u32 (& idrinfo -> action_idr , NULL , index ,
858
- UINT_MAX , GFP_KERNEL );
859
- if (!ret )
860
- idr_replace (& idrinfo -> action_idr , ERR_PTR (- EBUSY ),
861
- * index );
868
+ max = UINT_MAX ;
862
869
}
870
+
871
+ new :
872
+ * a = NULL ;
873
+
874
+ mutex_lock (& idrinfo -> lock );
875
+ ret = idr_alloc_u32 (& idrinfo -> action_idr , ERR_PTR (- EBUSY ), index , max ,
876
+ GFP_KERNEL );
863
877
mutex_unlock (& idrinfo -> lock );
878
+
879
+ /* N binds raced for action allocation,
880
+ * retry for all the ones that failed.
881
+ */
882
+ if (ret == - ENOSPC && * index == max )
883
+ ret = - EAGAIN ;
884
+
864
885
return ret ;
865
886
}
866
887
EXPORT_SYMBOL (tcf_idr_check_alloc );
@@ -1283,19 +1304,20 @@ static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = {
1283
1304
[TCA_ACT_HW_STATS ] = NLA_POLICY_BITFIELD32 (TCA_ACT_HW_STATS_ANY ),
1284
1305
};
1285
1306
1286
- void tcf_idr_insert_many (struct tc_action * actions [])
1307
+ void tcf_idr_insert_many (struct tc_action * actions [], int init_res [] )
1287
1308
{
1288
1309
struct tc_action * a ;
1289
1310
int i ;
1290
1311
1291
1312
tcf_act_for_each_action (i , a , actions ) {
1292
1313
struct tcf_idrinfo * idrinfo ;
1293
1314
1315
+ if (init_res [i ] == 0 ) /* Bound */
1316
+ continue ;
1317
+
1294
1318
idrinfo = a -> idrinfo ;
1295
1319
mutex_lock (& idrinfo -> lock );
1296
- /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if
1297
- * it is just created, otherwise this is just a nop.
1298
- */
1320
+ /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
1299
1321
idr_replace (& idrinfo -> action_idr , a , a -> tcfa_index );
1300
1322
mutex_unlock (& idrinfo -> lock );
1301
1323
}
@@ -1495,7 +1517,7 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
1495
1517
/* We have to commit them all together, because if any error happened in
1496
1518
* between, we could not handle the failure gracefully.
1497
1519
*/
1498
- tcf_idr_insert_many (actions );
1520
+ tcf_idr_insert_many (actions , init_res );
1499
1521
1500
1522
* attr_size = tcf_action_full_attrs_size (sz );
1501
1523
err = i - 1 ;
0 commit comments