+ unsigned long i;
+ struct rcu_ht_node **prev, *node, *inext;
+ int cnt = 0;
+
+ for (i = 0; i < ht->size.lookup; i++) {
+ rcu_read_lock();
+ prev = &ht->tbl[i];
+ /*
+ * Cut the head. After that, we own the first element.
+ */
+ node = rcu_xchg_pointer(prev, NULL);
+ if (!node) {
+ rcu_read_unlock();
+ continue;
+ }
+ /*
+ * We manage a list shared with concurrent writers and readers.
+ * Note that a concurrent add may or may not be deleted by us,
+ * depending if it arrives before or after the head is cut.
+ * "node" points to our first node. Remove first elements
+ * iteratively.
+ */
+ for (;;) {
+ inext = NULL;
+ prev = &node->next;
+ if (prev)
+ inext = rcu_xchg_pointer(prev, NULL);
+ /*
+ * "node" is the first element of the list we have cut.
+ * We therefore own it, no concurrent writer may delete
+ * it. There can only be concurrent lookups. Concurrent
+ * add can only be done on a bucket head, but we've cut
+ * it already. inext is also owned by us, because we
+ * have exchanged it for "NULL". It will therefore be
+ * safe to use it after a G.P.
+ */
+ rcu_read_unlock();
+ if (node->data)
+ call_rcu(ht->free_fct, node->data);
+ call_rcu(free, node);
+ cnt++;
+ if (likely(!inext))
+ break;
+ rcu_read_lock();
+ node = inext;
+ }
+ }
+ return cnt;
+}
+
+/*
+ * Should only be called when no more concurrent readers nor writers can
+ * possibly access the table.
+ */
+int ht_destroy(struct rcu_ht *ht)
+{
+ int ret;
+
+ ret = ht_delete_all(ht);
+ free(ht->tbl);
+ free(ht);
+ return ret;
+}
+
+/*
+ * Expects keys <= than pointer size to be encoded in the pointer itself.
+ */
+uint32_t ht_jhash(void *key, uint32_t length, uint32_t initval)
+{
+ uint32_t ret;
+ void *vkey;
+
+ if (length <= sizeof(void *))
+ vkey = &key;
+ else
+ vkey = key;
+ ret = jhash(vkey, length, initval);
+ return ret;