Re: [PATCH v3 10/11] net: sched: atomically check-allocate action

Marcelo Ricardo Leitner
2018-05-28T21:39:01Z

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 <vladbu@mellanox.com>

Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@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
>