@@ -303,7 +303,9 @@ static bool __tcf_idr_check(struct tc_action_net *tn, u32 index,
303303
304304 spin_lock (& idrinfo -> lock );
305305 p = idr_find (& idrinfo -> action_idr , index );
306- if (p ) {
306+ if (IS_ERR (p )) {
307+ p = NULL ;
308+ } else if (p ) {
307309 refcount_inc (& p -> tcfa_refcnt );
308310 if (bind )
309311 atomic_inc (& p -> tcfa_bindcnt );
@@ -371,7 +373,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
371373{
372374 struct tc_action * p = kzalloc (ops -> size , GFP_KERNEL );
373375 struct tcf_idrinfo * idrinfo = tn -> idrinfo ;
374- struct idr * idr = & idrinfo -> action_idr ;
375376 int err = - ENOMEM ;
376377
377378 if (unlikely (!p ))
@@ -389,20 +390,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
389390 goto err2 ;
390391 }
391392 spin_lock_init (& p -> tcfa_lock );
392- idr_preload (GFP_KERNEL );
393- spin_lock (& idrinfo -> lock );
394- /* user doesn't specify an index */
395- if (!index ) {
396- index = 1 ;
397- err = idr_alloc_u32 (idr , NULL , & index , UINT_MAX , GFP_ATOMIC );
398- } else {
399- err = idr_alloc_u32 (idr , NULL , & index , index , GFP_ATOMIC );
400- }
401- spin_unlock (& idrinfo -> lock );
402- idr_preload_end ();
403- if (err )
404- goto err3 ;
405-
406393 p -> tcfa_index = index ;
407394 p -> tcfa_tm .install = jiffies ;
408395 p -> tcfa_tm .lastuse = jiffies ;
@@ -412,16 +399,14 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
412399 & p -> tcfa_rate_est ,
413400 & p -> tcfa_lock , NULL , est );
414401 if (err )
415- goto err4 ;
402+ goto err3 ;
416403 }
417404
418405 p -> idrinfo = idrinfo ;
419406 p -> ops = ops ;
420407 INIT_LIST_HEAD (& p -> list );
421408 * a = p ;
422409 return 0 ;
423- err4 :
424- idr_remove (idr , index );
425410err3 :
426411 free_percpu (p -> cpu_qstats );
427412err2 :
@@ -437,11 +422,78 @@ void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a)
437422 struct tcf_idrinfo * idrinfo = tn -> idrinfo ;
438423
439424 spin_lock (& idrinfo -> lock );
440- idr_replace (& idrinfo -> action_idr , a , a -> tcfa_index );
425+ /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
426+ WARN_ON (!IS_ERR (idr_replace (& idrinfo -> action_idr , a , a -> tcfa_index )));
441427 spin_unlock (& idrinfo -> lock );
442428}
443429EXPORT_SYMBOL (tcf_idr_insert );
444430
431+ /* Cleanup idr index that was allocated but not initialized. */
432+
433+ void tcf_idr_cleanup (struct tc_action_net * tn , u32 index )
434+ {
435+ struct tcf_idrinfo * idrinfo = tn -> idrinfo ;
436+
437+ spin_lock (& idrinfo -> lock );
438+ /* Remove ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
439+ WARN_ON (!IS_ERR (idr_remove (& idrinfo -> action_idr , index )));
440+ spin_unlock (& idrinfo -> lock );
441+ }
442+ EXPORT_SYMBOL (tcf_idr_cleanup );
443+
444+ /* Check if action with specified index exists. If actions is found, increments
445+ * its reference and bind counters, and return 1. Otherwise insert temporary
446+ * error pointer (to prevent concurrent users from inserting actions with same
447+ * index) and return 0.
448+ */
449+
450+ int tcf_idr_check_alloc (struct tc_action_net * tn , u32 * index ,
451+ struct tc_action * * a , int bind )
452+ {
453+ struct tcf_idrinfo * idrinfo = tn -> idrinfo ;
454+ struct tc_action * p ;
455+ int ret ;
456+
457+ again :
458+ spin_lock (& idrinfo -> lock );
459+ if (* index ) {
460+ p = idr_find (& idrinfo -> action_idr , * index );
461+ if (IS_ERR (p )) {
462+ /* This means that another process allocated
463+ * index but did not assign the pointer yet.
464+ */
465+ spin_unlock (& idrinfo -> lock );
466+ goto again ;
467+ }
468+
469+ if (p ) {
470+ refcount_inc (& p -> tcfa_refcnt );
471+ if (bind )
472+ atomic_inc (& p -> tcfa_bindcnt );
473+ * a = p ;
474+ ret = 1 ;
475+ } else {
476+ * a = NULL ;
477+ ret = idr_alloc_u32 (& idrinfo -> action_idr , NULL , index ,
478+ * index , GFP_ATOMIC );
479+ if (!ret )
480+ idr_replace (& idrinfo -> action_idr ,
481+ ERR_PTR (- EBUSY ), * index );
482+ }
483+ } else {
484+ * index = 1 ;
485+ * a = NULL ;
486+ ret = idr_alloc_u32 (& idrinfo -> action_idr , NULL , index ,
487+ UINT_MAX , GFP_ATOMIC );
488+ if (!ret )
489+ idr_replace (& idrinfo -> action_idr , ERR_PTR (- EBUSY ),
490+ * index );
491+ }
492+ spin_unlock (& idrinfo -> lock );
493+ return ret ;
494+ }
495+ EXPORT_SYMBOL (tcf_idr_check_alloc );
496+
445497void tcf_idrinfo_destroy (const struct tc_action_ops * ops ,
446498 struct tcf_idrinfo * idrinfo )
447499{
0 commit comments