On Mon, May 28, 2018 at 12:17:28AM +0300, Vlad Buslov wrote:
> Implement function that atomically checks if action exists and either takes
> reference to it, or allocates idr slot for action index to prevent
> concurrent allocations of actions with same index. Use EBUSY error pointer
> to indicate that idr slot is reserved.
> 
> Implement cleanup helper function that removes temporary error pointer from
> idr. (in case of error between idr allocation and insertion of newly
> created action to specified index)
> 
> Refactor all action init functions to insert new action to idr using this
> API.
> 
> Signed-off-by: Vlad Buslov <vla...@mellanox.com>

Reviewed-by: Marcelo Ricardo Leitner <marcelo.leit...@gmail.com>

> ---
> Changes from V1 to V2:
> - Remove unique idr insertion function. Change original idr insert to do
>   the same thing.
> - Refactor action check-alloc code into standalone function.
> 
>  include/net/act_api.h      |  3 ++
>  net/sched/act_api.c        | 92 
> ++++++++++++++++++++++++++++++++++++----------
>  net/sched/act_bpf.c        | 11 ++++--
>  net/sched/act_connmark.c   | 10 +++--
>  net/sched/act_csum.c       | 11 ++++--
>  net/sched/act_gact.c       | 11 ++++--
>  net/sched/act_ife.c        |  6 ++-
>  net/sched/act_ipt.c        | 13 ++++++-
>  net/sched/act_mirred.c     | 16 ++++++--
>  net/sched/act_nat.c        | 11 ++++--
>  net/sched/act_pedit.c      | 15 ++++++--
>  net/sched/act_police.c     |  9 ++++-
>  net/sched/act_sample.c     | 11 ++++--
>  net/sched/act_simple.c     | 11 +++++-
>  net/sched/act_skbedit.c    | 11 +++++-
>  net/sched/act_skbmod.c     | 11 +++++-
>  net/sched/act_tunnel_key.c |  9 ++++-
>  net/sched/act_vlan.c       | 17 ++++++++-
>  18 files changed, 218 insertions(+), 60 deletions(-)
> 
> diff --git a/include/net/act_api.h b/include/net/act_api.h
> index d256e20507b9..cd4547476074 100644
> --- a/include/net/act_api.h
> +++ b/include/net/act_api.h
> @@ -154,6 +154,9 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, 
> struct nlattr *est,
>                  int bind, bool cpustats);
>  void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a);
>  
> +void tcf_idr_cleanup(struct tc_action_net *tn, u32 index);
> +int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
> +                     struct tc_action **a, int bind);
>  int tcf_idr_delete_index(struct tc_action_net *tn, u32 index);
>  int __tcf_idr_release(struct tc_action *a, bool bind, bool strict);
>  
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index eefe8c2fe667..9511502e1cbb 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -303,7 +303,9 @@ static bool __tcf_idr_check(struct tc_action_net *tn, u32 
> index,
>  
>       spin_lock(&idrinfo->lock);
>       p = idr_find(&idrinfo->action_idr, index);
> -     if (p) {
> +     if (IS_ERR(p)) {
> +             p = NULL;
> +     } else if (p) {
>               refcount_inc(&p->tcfa_refcnt);
>               if (bind)
>                       atomic_inc(&p->tcfa_bindcnt);
> @@ -371,7 +373,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, 
> struct nlattr *est,
>  {
>       struct tc_action *p = kzalloc(ops->size, GFP_KERNEL);
>       struct tcf_idrinfo *idrinfo = tn->idrinfo;
> -     struct idr *idr = &idrinfo->action_idr;
>       int err = -ENOMEM;
>  
>       if (unlikely(!p))
> @@ -389,20 +390,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, 
> struct nlattr *est,
>                       goto err2;
>       }
>       spin_lock_init(&p->tcfa_lock);
> -     idr_preload(GFP_KERNEL);
> -     spin_lock(&idrinfo->lock);
> -     /* user doesn't specify an index */
> -     if (!index) {
> -             index = 1;
> -             err = idr_alloc_u32(idr, NULL, &index, UINT_MAX, GFP_ATOMIC);
> -     } else {
> -             err = idr_alloc_u32(idr, NULL, &index, index, GFP_ATOMIC);
> -     }
> -     spin_unlock(&idrinfo->lock);
> -     idr_preload_end();
> -     if (err)
> -             goto err3;
> -
>       p->tcfa_index = index;
>       p->tcfa_tm.install = jiffies;
>       p->tcfa_tm.lastuse = jiffies;
> @@ -412,7 +399,7 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, 
> struct nlattr *est,
>                                       &p->tcfa_rate_est,
>                                       &p->tcfa_lock, NULL, est);
>               if (err)
> -                     goto err4;
> +                     goto err3;
>       }
>  
>       p->idrinfo = idrinfo;
> @@ -420,8 +407,6 @@ int tcf_idr_create(struct tc_action_net *tn, u32 index, 
> struct nlattr *est,
>       INIT_LIST_HEAD(&p->list);
>       *a = p;
>       return 0;
> -err4:
> -     idr_remove(idr, index);
>  err3:
>       free_percpu(p->cpu_qstats);
>  err2:
> @@ -437,11 +422,78 @@ void tcf_idr_insert(struct tc_action_net *tn, struct 
> tc_action *a)
>       struct tcf_idrinfo *idrinfo = tn->idrinfo;
>  
>       spin_lock(&idrinfo->lock);
> -     idr_replace(&idrinfo->action_idr, a, a->tcfa_index);
> +     /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
> +     WARN_ON(!IS_ERR(idr_replace(&idrinfo->action_idr, a, a->tcfa_index)));
>       spin_unlock(&idrinfo->lock);
>  }
>  EXPORT_SYMBOL(tcf_idr_insert);
>  
> +/* Cleanup idr index that was allocated but not initialized. */
> +
> +void tcf_idr_cleanup(struct tc_action_net *tn, u32 index)
> +{
> +     struct tcf_idrinfo *idrinfo = tn->idrinfo;
> +
> +     spin_lock(&idrinfo->lock);
> +     /* Remove ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
> +     WARN_ON(!IS_ERR(idr_remove(&idrinfo->action_idr, index)));
> +     spin_unlock(&idrinfo->lock);
> +}
> +EXPORT_SYMBOL(tcf_idr_cleanup);
> +
> +/* Check if action with specified index exists. If actions is found, 
> increments
> + * its reference and bind counters, and return 1. Otherwise insert temporary
> + * error pointer (to prevent concurrent users from inserting actions with 
> same
> + * index) and return 0.
> + */
> +
> +int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
> +                     struct tc_action **a, int bind)
> +{
> +     struct tcf_idrinfo *idrinfo = tn->idrinfo;
> +     struct tc_action *p;
> +     int ret;
> +
> +again:
> +     spin_lock(&idrinfo->lock);
> +     if (*index) {
> +             p = idr_find(&idrinfo->action_idr, *index);
> +             if (IS_ERR(p)) {
> +                     /* This means that another process allocated
> +                      * index but did not assign the pointer yet.
> +                      */
> +                     spin_unlock(&idrinfo->lock);
> +                     goto again;
> +             }
> +
> +             if (p) {
> +                     refcount_inc(&p->tcfa_refcnt);
> +                     if (bind)
> +                             atomic_inc(&p->tcfa_bindcnt);
> +                     *a = p;
> +                     ret = 1;
> +             } else {
> +                     *a = NULL;
> +                     ret = idr_alloc_u32(&idrinfo->action_idr, NULL, index,
> +                                         *index, GFP_ATOMIC);
> +                     if (!ret)
> +                             idr_replace(&idrinfo->action_idr,
> +                                         ERR_PTR(-EBUSY), *index);
> +             }
> +     } else {
> +             *index = 1;
> +             *a = NULL;
> +             ret = idr_alloc_u32(&idrinfo->action_idr, NULL, index,
> +                                 UINT_MAX, GFP_ATOMIC);
> +             if (!ret)
> +                     idr_replace(&idrinfo->action_idr, ERR_PTR(-EBUSY),
> +                                 *index);
> +     }
> +     spin_unlock(&idrinfo->lock);
> +     return ret;
> +}
> +EXPORT_SYMBOL(tcf_idr_check_alloc);
> +
>  void tcf_idrinfo_destroy(const struct tc_action_ops *ops,
>                        struct tcf_idrinfo *idrinfo)
>  {
> diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c
> index d3f4ac6f2c4b..06f743d8ed41 100644
> --- a/net/sched/act_bpf.c
> +++ b/net/sched/act_bpf.c
> @@ -299,14 +299,17 @@ static int tcf_bpf_init(struct net *net, struct nlattr 
> *nla,
>  
>       parm = nla_data(tb[TCA_ACT_BPF_PARMS]);
>  
> -     if (!tcf_idr_check(tn, parm->index, act, bind)) {
> +     ret = tcf_idr_check_alloc(tn, &parm->index, act, bind);
> +     if (!ret) {
>               ret = tcf_idr_create(tn, parm->index, est, act,
>                                    &act_bpf_ops, bind, true);
> -             if (ret < 0)
> +             if (ret < 0) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>  
>               res = ACT_P_CREATED;
> -     } else {
> +     } else if (ret > 0) {
>               /* Don't override defaults. */
>               if (bind)
>                       return 0;
> @@ -315,6 +318,8 @@ static int tcf_bpf_init(struct net *net, struct nlattr 
> *nla,
>                       tcf_idr_release(*act, bind);
>                       return -EEXIST;
>               }
> +     } else {
> +             return ret;
>       }
>  
>       is_bpf = tb[TCA_ACT_BPF_OPS_LEN] && tb[TCA_ACT_BPF_OPS];
> diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c
> index 701e90244eff..1e31f0e448e2 100644
> --- a/net/sched/act_connmark.c
> +++ b/net/sched/act_connmark.c
> @@ -118,11 +118,14 @@ static int tcf_connmark_init(struct net *net, struct 
> nlattr *nla,
>  
>       parm = nla_data(tb[TCA_CONNMARK_PARMS]);
>  
> -     if (!tcf_idr_check(tn, parm->index, a, bind)) {
> +     ret = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (!ret) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_connmark_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>  
>               ci = to_connmark(*a);
>               ci->tcf_action = parm->action;
> @@ -131,7 +134,7 @@ static int tcf_connmark_init(struct net *net, struct 
> nlattr *nla,
>  
>               tcf_idr_insert(tn, *a);
>               ret = ACT_P_CREATED;
> -     } else {
> +     } else if (ret > 0) {
>               ci = to_connmark(*a);
>               if (bind)
>                       return 0;
> @@ -142,6 +145,7 @@ static int tcf_connmark_init(struct net *net, struct 
> nlattr *nla,
>               /* replacing action and zone */
>               ci->tcf_action = parm->action;
>               ci->zone = parm->zone;
> +             ret = 0;
>       }
>  
>       return ret;
> diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
> index 5dbee136b0a1..bd232d3bd022 100644
> --- a/net/sched/act_csum.c
> +++ b/net/sched/act_csum.c
> @@ -67,19 +67,24 @@ static int tcf_csum_init(struct net *net, struct nlattr 
> *nla,
>               return -EINVAL;
>       parm = nla_data(tb[TCA_CSUM_PARMS]);
>  
> -     if (!tcf_idr_check(tn, parm->index, a, bind)) {
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (!err) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_csum_ops, bind, true);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
> -     } else {
> +     } else if (err > 0) {
>               if (bind)/* dont override defaults */
>                       return 0;
>               if (!ovr) {
>                       tcf_idr_release(*a, bind);
>                       return -EEXIST;
>               }
> +     } else {
> +             return err;
>       }
>  
>       p = to_tcf_csum(*a);
> diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
> index 11c4de3f344e..661b72b9147d 100644
> --- a/net/sched/act_gact.c
> +++ b/net/sched/act_gact.c
> @@ -91,19 +91,24 @@ static int tcf_gact_init(struct net *net, struct nlattr 
> *nla,
>       }
>  #endif
>  
> -     if (!tcf_idr_check(tn, parm->index, a, bind)) {
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (!err) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_gact_ops, bind, true);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
> -     } else {
> +     } else if (err > 0) {
>               if (bind)/* dont override defaults */
>                       return 0;
>               if (!ovr) {
>                       tcf_idr_release(*a, bind);
>                       return -EEXIST;
>               }
> +     } else {
> +             return err;
>       }
>  
>       gact = to_gact(*a);
> diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
> index 3dd3d79c5a4b..5bf0e79796c0 100644
> --- a/net/sched/act_ife.c
> +++ b/net/sched/act_ife.c
> @@ -483,7 +483,10 @@ static int tcf_ife_init(struct net *net, struct nlattr 
> *nla,
>       if (!p)
>               return -ENOMEM;
>  
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind) {
>               kfree(p);
>               return 0;
> @@ -493,6 +496,7 @@ static int tcf_ife_init(struct net *net, struct nlattr 
> *nla,
>               ret = tcf_idr_create(tn, parm->index, est, a, &act_ife_ops,
>                                    bind, true);
>               if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       kfree(p);
>                       return ret;
>               }
> diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
> index 85e85dfba401..0dc787a57798 100644
> --- a/net/sched/act_ipt.c
> +++ b/net/sched/act_ipt.c
> @@ -119,13 +119,18 @@ static int __tcf_ipt_init(struct net *net, unsigned int 
> id, struct nlattr *nla,
>       if (tb[TCA_IPT_INDEX] != NULL)
>               index = nla_get_u32(tb[TCA_IPT_INDEX]);
>  
> -     exists = tcf_idr_check(tn, index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
>       if (tb[TCA_IPT_HOOK] == NULL || tb[TCA_IPT_TARG] == NULL) {
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, index);
>               return -EINVAL;
>       }
>  
> @@ -133,14 +138,18 @@ static int __tcf_ipt_init(struct net *net, unsigned int 
> id, struct nlattr *nla,
>       if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size) {
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, index);
>               return -EINVAL;
>       }
>  
>       if (!exists) {
>               ret = tcf_idr_create(tn, index, est, a, ops, bind,
>                                    false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
>       } else {
>               if (bind)/* dont override defaults */
> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
> index e08aed06d7f8..6afd89a36c69 100644
> --- a/net/sched/act_mirred.c
> +++ b/net/sched/act_mirred.c
> @@ -79,7 +79,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr 
> *nla,
>       struct tcf_mirred *m;
>       struct net_device *dev;
>       bool exists = false;
> -     int ret;
> +     int ret, err;
>  
>       if (!nla) {
>               NL_SET_ERR_MSG_MOD(extack, "Mirred requires attributes to be 
> passed");
> @@ -94,7 +94,10 @@ static int tcf_mirred_init(struct net *net, struct nlattr 
> *nla,
>       }
>       parm = nla_data(tb[TCA_MIRRED_PARMS]);
>  
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
> @@ -107,6 +110,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr 
> *nla,
>       default:
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, parm->index);
>               NL_SET_ERR_MSG_MOD(extack, "Unknown mirred option");
>               return -EINVAL;
>       }
> @@ -115,6 +120,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr 
> *nla,
>               if (dev == NULL) {
>                       if (exists)
>                               tcf_idr_release(*a, bind);
> +                     else
> +                             tcf_idr_cleanup(tn, parm->index);
>                       return -ENODEV;
>               }
>               mac_header_xmit = dev_is_mac_header_xmit(dev);
> @@ -124,13 +131,16 @@ static int tcf_mirred_init(struct net *net, struct 
> nlattr *nla,
>  
>       if (!exists) {
>               if (!dev) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       NL_SET_ERR_MSG_MOD(extack, "Specified device does not 
> exist");
>                       return -EINVAL;
>               }
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_mirred_ops, bind, true);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
>       } else if (!ovr) {
>               tcf_idr_release(*a, bind);
> diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
> index 1f91e8e66c0f..4dd9188a72fd 100644
> --- a/net/sched/act_nat.c
> +++ b/net/sched/act_nat.c
> @@ -57,19 +57,24 @@ static int tcf_nat_init(struct net *net, struct nlattr 
> *nla, struct nlattr *est,
>               return -EINVAL;
>       parm = nla_data(tb[TCA_NAT_PARMS]);
>  
> -     if (!tcf_idr_check(tn, parm->index, a, bind)) {
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (!err) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_nat_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
> -     } else {
> +     } else if (err > 0) {
>               if (bind)
>                       return 0;
>               if (!ovr) {
>                       tcf_idr_release(*a, bind);
>                       return -EEXIST;
>               }
> +     } else {
> +             return err;
>       }
>       p = to_tcf_nat(*a);
>  
> diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
> index fbf283f2ac34..2bd1d3f61488 100644
> --- a/net/sched/act_pedit.c
> +++ b/net/sched/act_pedit.c
> @@ -167,13 +167,18 @@ static int tcf_pedit_init(struct net *net, struct 
> nlattr *nla,
>       if (IS_ERR(keys_ex))
>               return PTR_ERR(keys_ex);
>  
> -     if (!tcf_idr_check(tn, parm->index, a, bind)) {
> -             if (!parm->nkeys)
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (!err) {
> +             if (!parm->nkeys) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return -EINVAL;
> +             }
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_pedit_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               p = to_pedit(*a);
>               keys = kmalloc(ksize, GFP_KERNEL);
>               if (keys == NULL) {
> @@ -182,7 +187,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr 
> *nla,
>                       return -ENOMEM;
>               }
>               ret = ACT_P_CREATED;
> -     } else {
> +     } else if (err > 0) {
>               if (bind)
>                       return 0;
>               if (!ovr) {
> @@ -197,6 +202,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr 
> *nla,
>                               return -ENOMEM;
>                       }
>               }
> +     } else {
> +             return err;
>       }
>  
>       spin_lock_bh(&p->tcf_lock);
> diff --git a/net/sched/act_police.c b/net/sched/act_police.c
> index 99335cca739e..1f3192ea8df7 100644
> --- a/net/sched/act_police.c
> +++ b/net/sched/act_police.c
> @@ -101,15 +101,20 @@ static int tcf_act_police_init(struct net *net, struct 
> nlattr *nla,
>               return -EINVAL;
>  
>       parm = nla_data(tb[TCA_POLICE_TBF]);
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
>       if (!exists) {
>               ret = tcf_idr_create(tn, parm->index, NULL, a,
>                                    &act_police_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
>       } else if (!ovr) {
>               tcf_idr_release(*a, bind);
> diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c
> index a8582e1347db..3079e7be5bde 100644
> --- a/net/sched/act_sample.c
> +++ b/net/sched/act_sample.c
> @@ -46,7 +46,7 @@ static int tcf_sample_init(struct net *net, struct nlattr 
> *nla,
>       struct tc_sample *parm;
>       struct tcf_sample *s;
>       bool exists = false;
> -     int ret;
> +     int ret, err;
>  
>       if (!nla)
>               return -EINVAL;
> @@ -59,15 +59,20 @@ static int tcf_sample_init(struct net *net, struct nlattr 
> *nla,
>  
>       parm = nla_data(tb[TCA_SAMPLE_PARMS]);
>  
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
>       if (!exists) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_sample_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>               ret = ACT_P_CREATED;
>       } else if (!ovr) {
>               tcf_idr_release(*a, bind);
> diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
> index 78fffd329ed9..2cc874f791df 100644
> --- a/net/sched/act_simple.c
> +++ b/net/sched/act_simple.c
> @@ -101,13 +101,18 @@ static int tcf_simp_init(struct net *net, struct nlattr 
> *nla,
>               return -EINVAL;
>  
>       parm = nla_data(tb[TCA_DEF_PARMS]);
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
>       if (tb[TCA_DEF_DATA] == NULL) {
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, parm->index);
>               return -EINVAL;
>       }
>  
> @@ -116,8 +121,10 @@ static int tcf_simp_init(struct net *net, struct nlattr 
> *nla,
>       if (!exists) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_simp_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>  
>               d = to_defact(*a);
>               ret = alloc_defdata(d, defdata);
> diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
> index c0607d1319eb..29a15172a99d 100644
> --- a/net/sched/act_skbedit.c
> +++ b/net/sched/act_skbedit.c
> @@ -117,21 +117,28 @@ static int tcf_skbedit_init(struct net *net, struct 
> nlattr *nla,
>  
>       parm = nla_data(tb[TCA_SKBEDIT_PARMS]);
>  
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
>       if (!flags) {
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, parm->index);
>               return -EINVAL;
>       }
>  
>       if (!exists) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_skbedit_ops, bind, false);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>  
>               d = to_skbedit(*a);
>               ret = ACT_P_CREATED;
> diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c
> index e844381af066..cdc6bacfb190 100644
> --- a/net/sched/act_skbmod.c
> +++ b/net/sched/act_skbmod.c
> @@ -128,21 +128,28 @@ static int tcf_skbmod_init(struct net *net, struct 
> nlattr *nla,
>       if (parm->flags & SKBMOD_F_SWAPMAC)
>               lflags = SKBMOD_F_SWAPMAC;
>  
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
>       if (!lflags) {
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, parm->index);
>               return -EINVAL;
>       }
>  
>       if (!exists) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_skbmod_ops, bind, true);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>  
>               ret = ACT_P_CREATED;
>       } else if (!ovr) {
> diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
> index bd53f39a345b..4679b620af12 100644
> --- a/net/sched/act_tunnel_key.c
> +++ b/net/sched/act_tunnel_key.c
> @@ -99,7 +99,10 @@ static int tunnel_key_init(struct net *net, struct nlattr 
> *nla,
>               return -EINVAL;
>  
>       parm = nla_data(tb[TCA_TUNNEL_KEY_PARMS]);
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
> @@ -162,7 +165,7 @@ static int tunnel_key_init(struct net *net, struct nlattr 
> *nla,
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_tunnel_key_ops, bind, true);
>               if (ret)
> -                     return ret;
> +                     goto err_out;
>  
>               ret = ACT_P_CREATED;
>       } else if (!ovr) {
> @@ -198,6 +201,8 @@ static int tunnel_key_init(struct net *net, struct nlattr 
> *nla,
>  err_out:
>       if (exists)
>               tcf_idr_release(*a, bind);
> +     else
> +             tcf_idr_cleanup(tn, parm->index);
>       return ret;
>  }
>  
> diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
> index 4ac0d565e437..bae9822837d7 100644
> --- a/net/sched/act_vlan.c
> +++ b/net/sched/act_vlan.c
> @@ -134,7 +134,10 @@ static int tcf_vlan_init(struct net *net, struct nlattr 
> *nla,
>       if (!tb[TCA_VLAN_PARMS])
>               return -EINVAL;
>       parm = nla_data(tb[TCA_VLAN_PARMS]);
> -     exists = tcf_idr_check(tn, parm->index, a, bind);
> +     err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
> +     if (err < 0)
> +             return err;
> +     exists = err;
>       if (exists && bind)
>               return 0;
>  
> @@ -146,12 +149,16 @@ static int tcf_vlan_init(struct net *net, struct nlattr 
> *nla,
>               if (!tb[TCA_VLAN_PUSH_VLAN_ID]) {
>                       if (exists)
>                               tcf_idr_release(*a, bind);
> +                     else
> +                             tcf_idr_cleanup(tn, parm->index);
>                       return -EINVAL;
>               }
>               push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
>               if (push_vid >= VLAN_VID_MASK) {
>                       if (exists)
>                               tcf_idr_release(*a, bind);
> +                     else
> +                             tcf_idr_cleanup(tn, parm->index);
>                       return -ERANGE;
>               }
>  
> @@ -162,6 +169,8 @@ static int tcf_vlan_init(struct net *net, struct nlattr 
> *nla,
>                       case htons(ETH_P_8021AD):
>                               break;
>                       default:
> +                             if (!exists)
> +                                     tcf_idr_cleanup(tn, parm->index);
>                               return -EPROTONOSUPPORT;
>                       }
>               } else {
> @@ -174,6 +183,8 @@ static int tcf_vlan_init(struct net *net, struct nlattr 
> *nla,
>       default:
>               if (exists)
>                       tcf_idr_release(*a, bind);
> +             else
> +                     tcf_idr_cleanup(tn, parm->index);
>               return -EINVAL;
>       }
>       action = parm->v_action;
> @@ -181,8 +192,10 @@ static int tcf_vlan_init(struct net *net, struct nlattr 
> *nla,
>       if (!exists) {
>               ret = tcf_idr_create(tn, parm->index, est, a,
>                                    &act_vlan_ops, bind, true);
> -             if (ret)
> +             if (ret) {
> +                     tcf_idr_cleanup(tn, parm->index);
>                       return ret;
> +             }
>  
>               ret = ACT_P_CREATED;
>       } else if (!ovr) {
> -- 
> 2.7.5
> 

Reply via email to