]> www.infradead.org Git - users/hch/configfs.git/commitdiff
net: sched: atomically check-allocate action
authorVlad Buslov <vladbu@mellanox.com>
Thu, 5 Jul 2018 14:24:32 +0000 (17:24 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sun, 8 Jul 2018 03:42:29 +0000 (12:42 +0900)
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.

Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Vlad Buslov <vladbu@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
18 files changed:
include/net/act_api.h
net/sched/act_api.c
net/sched/act_bpf.c
net/sched/act_connmark.c
net/sched/act_csum.c
net/sched/act_gact.c
net/sched/act_ife.c
net/sched/act_ipt.c
net/sched/act_mirred.c
net/sched/act_nat.c
net/sched/act_pedit.c
net/sched/act_police.c
net/sched/act_sample.c
net/sched/act_simple.c
net/sched/act_skbedit.c
net/sched/act_skbmod.c
net/sched/act_tunnel_key.c
net/sched/act_vlan.c

index b9ed2b8256a5612505afc563b1a4526e83f7251f..8090de2edab752552ae02a21fbd9dfe911d1ec05 100644 (file)
@@ -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);
 
index eefe8c2fe667a3f78f6843c22b3115b85fe1db67..9511502e1cbbd30dc31af9e038c3e1d68e32fefa 100644 (file)
@@ -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)
 {
index d3f4ac6f2c4ba3fd0021b42d19975f86a0f94b45..06f743d8ed4130bc3cc53996ecb9313546230066 100644 (file)
@@ -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];
index 701e90244eff3eeeb7588ce1402fffb354544b1e..1e31f0e448e2c57f6ac6458d94efd196841bb2b0 100644 (file)
@@ -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;
index 5dbee136b0a1457af3f89323b332c8c0a81a18b6..bd232d3bd022d516ad1ff228a69aaea7c194d752 100644 (file)
@@ -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);
index 11c4de3f344ebab587a09e74827a355d2a9d59cf..661b72b9147d52d320f094b91b7392488c1f25c2 100644 (file)
@@ -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);
index acea3feae76216142ad3ac9b8c113060f8327765..a3eef00cd711235aaba69d6cac9d015fa94f089d 100644 (file)
@@ -484,7 +484,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;
@@ -494,6 +497,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;
                }
index 85e85dfba401d6f163bf314a993adddc6c1111db..0dc787a57798292be40ba1f66c16d2affd31a046 100644 (file)
@@ -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 */
index e08aed06d7f8e1a7ec92030660a8eea8da1e01ca..6afd89a36c69032668bf2f287da9493076c4e5ea 100644 (file)
@@ -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);
index 1f91e8e66c0f548ed3178f25eff65766743ca666..4dd9188a72fddd9ebb7ccf87950d1068b61837af 100644 (file)
@@ -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);
 
index 3a0e2f762f4e282b6b0614c5bec70b8c19625619..cc8ffcd1ddb5841e3cc795391bea6758d20fa530 100644 (file)
@@ -173,16 +173,20 @@ 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)) {
+       err = tcf_idr_check_alloc(tn, &parm->index, a, bind);
+       if (!err) {
                if (!parm->nkeys) {
+                       tcf_idr_cleanup(tn, parm->index);
                        NL_SET_ERR_MSG_MOD(extack, "Pedit requires keys to be passed");
                        ret = -EINVAL;
                        goto out_free;
                }
                ret = tcf_idr_create(tn, parm->index, est, a,
                                     &act_pedit_ops, bind, false);
-               if (ret)
+               if (ret) {
+                       tcf_idr_cleanup(tn, parm->index);
                        goto out_free;
+               }
                p = to_pedit(*a);
                keys = kmalloc(ksize, GFP_KERNEL);
                if (!keys) {
@@ -191,7 +195,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
                        goto out_free;
                }
                ret = ACT_P_CREATED;
-       } else {
+       } else if (err > 0) {
                if (bind)
                        goto out_free;
                if (!ovr) {
@@ -207,6 +211,8 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla,
                                goto out_free;
                        }
                }
+       } else {
+               return err;
        }
 
        spin_lock_bh(&p->tcf_lock);
index 99335cca739e419366ba01332038ea0da3604e52..1f3192ea8df7a5f4de28c297cac3337cfccfe601 100644 (file)
@@ -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);
index a8582e1347dbc10066a702be73821e9840252a32..3079e7be5bdef54de97692e27fa65299e69406be 100644 (file)
@@ -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);
index 2da47c682a30c87be0b1ab7440323371777d9d69..aa51152e00668e76cccc08e3592769f1f7874915 100644 (file)
@@ -100,21 +100,28 @@ 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;
        }
 
        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, tb[TCA_DEF_DATA]);
index 4616a2c1821fc8b2a0b13a4787c76ccf2d88c01a..86521a74ecdd30f72de83ccac62c7c9e50175307 100644 (file)
@@ -152,21 +152,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;
index e844381af0661437427badf437b394c00eee8c3b..cdc6bacfb19078a8be10d846ea5b356c47f849ed 100644 (file)
@@ -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) {
index ab5bf5c13f87a33adcea0b346fd02ad22bda6c2c..3ec585d587629e282e9be08bb493c70c79dcbaa5 100644 (file)
@@ -237,7 +237,10 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
        }
 
        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;
 
@@ -325,7 +328,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
                                     &act_tunnel_key_ops, bind, true);
                if (ret) {
                        NL_SET_ERR_MSG(extack, "Cannot create TC IDR");
-                       return ret;
+                       goto err_out;
                }
 
                ret = ACT_P_CREATED;
@@ -364,6 +367,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;
 }
 
index 9b600faaccbb56d553ccff70e8a3aebff7d5be24..ad37f308175ad0ab574356552a5c9d9328d5b2a2 100644 (file)
@@ -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;
                }
 
@@ -164,6 +171,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 -EPROTONOSUPPORT;
                        }
                } else {
@@ -176,6 +185,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;
@@ -183,8 +194,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) {