Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[sfrench/cifs-2.6.git] / net / sched / cls_u32.c
index bd55ed783cb176b8709523bd4d26d5e88c25870b..6c7601a530e35489d38de939264a854209399676 100644 (file)
@@ -316,19 +316,13 @@ static void *u32_get(struct tcf_proto *tp, u32 handle)
        return u32_lookup_key(ht, handle);
 }
 
+/* Protected by rtnl lock */
 static u32 gen_new_htid(struct tc_u_common *tp_c, struct tc_u_hnode *ptr)
 {
-       unsigned long idr_index;
-       int err;
-
-       /* This is only used inside rtnl lock it is safe to increment
-        * without read _copy_ update semantics
-        */
-       err = idr_alloc_ext(&tp_c->handle_idr, ptr, &idr_index,
-                           1, 0x7FF, GFP_KERNEL);
-       if (err)
+       int id = idr_alloc_cyclic(&tp_c->handle_idr, ptr, 1, 0x7FF, GFP_KERNEL);
+       if (id < 0)
                return 0;
-       return (u32)(idr_index | 0x800) << 20;
+       return (id | 0x800U) << 20;
 }
 
 static struct hlist_head *tc_u_common_hash;
@@ -398,10 +392,12 @@ static int u32_init(struct tcf_proto *tp)
 static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n,
                           bool free_pf)
 {
+       struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
+
        tcf_exts_destroy(&n->exts);
        tcf_exts_put_net(&n->exts);
-       if (n->ht_down)
-               n->ht_down->refcnt--;
+       if (ht && --ht->refcnt == 0)
+               kfree(ht);
 #ifdef CONFIG_CLS_U32_PERF
        if (free_pf)
                free_percpu(n->pf);
@@ -659,16 +655,15 @@ static void u32_destroy(struct tcf_proto *tp, struct netlink_ext_ack *extack)
 
                hlist_del(&tp_c->hnode);
 
-               for (ht = rtnl_dereference(tp_c->hlist);
-                    ht;
-                    ht = rtnl_dereference(ht->next)) {
-                       ht->refcnt--;
-                       u32_clear_hnode(tp, ht, extack);
-               }
-
                while ((ht = rtnl_dereference(tp_c->hlist)) != NULL) {
+                       u32_clear_hnode(tp, ht, extack);
                        RCU_INIT_POINTER(tp_c->hlist, ht->next);
-                       kfree_rcu(ht, rcu);
+
+                       /* u32_destroy_key() will later free ht for us, if it's
+                        * still referenced by some knode
+                        */
+                       if (--ht->refcnt == 0)
+                               kfree_rcu(ht, rcu);
                }
 
                idr_destroy(&tp_c->handle_idr);
@@ -746,19 +741,17 @@ ret:
 
 static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
 {
-       unsigned long idr_index;
-       u32 start = htid | 0x800;
+       u32 index = htid | 0x800;
        u32 max = htid | 0xFFF;
-       u32 min = htid;
 
-       if (idr_alloc_ext(&ht->handle_idr, NULL, &idr_index,
-                         start, max + 1, GFP_KERNEL)) {
-               if (idr_alloc_ext(&ht->handle_idr, NULL, &idr_index,
-                                 min + 1, max + 1, GFP_KERNEL))
-                       return max;
+       if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
+               index = htid + 1;
+               if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
+                                GFP_KERNEL))
+                       index = max;
        }
 
-       return (u32)idr_index;
+       return index;
 }
 
 static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
@@ -848,7 +841,7 @@ static void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c,
                if (pins->handle == n->handle)
                        break;
 
-       idr_replace_ext(&ht->handle_idr, n, n->handle);
+       idr_replace(&ht->handle_idr, n, n->handle);
        RCU_INIT_POINTER(n->next, pins->next);
        rcu_assign_pointer(*ins, n);
 }
@@ -954,7 +947,8 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
                        return -EINVAL;
                }
 
-               if (n->flags != flags) {
+               if ((n->flags ^ flags) &
+                   ~(TCA_CLS_FLAGS_IN_HW | TCA_CLS_FLAGS_NOT_IN_HW)) {
                        NL_SET_ERR_MSG_MOD(extack, "Key node flags do not match passed flags");
                        return -EINVAL;
                }
@@ -1009,8 +1003,8 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
                                return -ENOMEM;
                        }
                } else {
-                       err = idr_alloc_ext(&tp_c->handle_idr, ht, NULL,
-                                           handle, handle + 1, GFP_KERNEL);
+                       err = idr_alloc_u32(&tp_c->handle_idr, ht, &handle,
+                                           handle, GFP_KERNEL);
                        if (err) {
                                kfree(ht);
                                return err;
@@ -1066,8 +1060,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
                        return -EINVAL;
                }
                handle = htid | TC_U32_NODE(handle);
-               err = idr_alloc_ext(&ht->handle_idr, NULL, NULL,
-                                   handle, handle + 1,
+               err = idr_alloc_u32(&ht->handle_idr, NULL, &handle, handle,
                                    GFP_KERNEL);
                if (err)
                        return err;