+void ht_add(struct rcu_ht *ht, struct rcu_ht_node *node)
+{
+ struct rcu_table *t;
+ unsigned long hash;
+
+ hash = ht->hash_fct(node->key, node->key_len, ht->hash_seed);
+ node->p.reverse_hash = bit_reverse_ulong((unsigned long) hash);
+
+ t = rcu_dereference(ht->t);
+ (void) _ht_add(ht, t, node, 0, 0);
+}
+
+struct rcu_ht_node *ht_add_unique(struct rcu_ht *ht, struct rcu_ht_node *node)
+{
+ struct rcu_table *t;
+ unsigned long hash;
+
+ hash = ht->hash_fct(node->key, node->key_len, ht->hash_seed);
+ node->p.reverse_hash = bit_reverse_ulong((unsigned long) hash);
+
+ t = rcu_dereference(ht->t);
+ return _ht_add(ht, t, node, 1, 0);
+}
+
+int ht_remove(struct rcu_ht *ht, struct rcu_ht_node *node)
+{
+ struct rcu_table *t;
+
+ t = rcu_dereference(ht->t);
+ return _ht_remove(ht, t, node);
+}
+
+static
+int ht_delete_dummy(struct rcu_ht *ht)
+{
+ struct rcu_table *t;
+ struct rcu_ht_node *node;
+ struct _rcu_ht_node *lookup;
+ unsigned long order, i;
+
+ t = ht->t;
+ /* Check that the table is empty */
+ lookup = &t->tbl[0][0];
+ node = (struct rcu_ht_node *) lookup;
+ do {
+ node = clear_flag(node)->p.next;
+ if (!is_dummy(node))
+ return -EPERM;
+ assert(!is_removed(node));
+ } while (clear_flag(node));
+ /* Internal sanity check: all nodes left should be dummy */
+ for (order = 0; order < get_count_order_ulong(t->size) + 1; order++) {
+ unsigned long len;
+
+ len = !order ? 1 : 1UL << (order - 1);
+ for (i = 0; i < len; i++) {
+ dbg_printf("rculfhash: delete order %lu i %lu hash %lu\n",
+ order, i,
+ bit_reverse_ulong(t->tbl[order][i].reverse_hash));
+ assert(is_dummy(t->tbl[order][i].next));
+ }
+ free(t->tbl[order]);
+ }
+ return 0;