Merge tag 'rdma-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[linux-2.6-block.git] / net / sched / cls_u32.c
index 95fdf4e4005190704dcd405e6b9422f8f56ba796..cab9e9b43967a57780140b9229c8cadff088607c 100644 (file)
@@ -463,13 +463,35 @@ static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht)
        return -ENOENT;
 }
 
-static void u32_destroy(struct tcf_proto *tp)
+static bool ht_empty(struct tc_u_hnode *ht)
+{
+       unsigned int h;
+
+       for (h = 0; h <= ht->divisor; h++)
+               if (rcu_access_pointer(ht->ht[h]))
+                       return false;
+
+       return true;
+}
+
+static bool u32_destroy(struct tcf_proto *tp, bool force)
 {
        struct tc_u_common *tp_c = tp->data;
        struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
 
        WARN_ON(root_ht == NULL);
 
+       if (!force) {
+               if (root_ht) {
+                       if (root_ht->refcnt > 1)
+                               return false;
+                       if (root_ht->refcnt == 1) {
+                               if (!ht_empty(root_ht))
+                                       return false;
+                       }
+               }
+       }
+
        if (root_ht && --root_ht->refcnt == 0)
                u32_destroy_hnode(tp, root_ht);
 
@@ -494,6 +516,7 @@ static void u32_destroy(struct tcf_proto *tp)
        }
 
        tp->data = NULL;
+       return true;
 }
 
 static int u32_delete(struct tcf_proto *tp, unsigned long arg)