[PATCH libnl v2 1/5] hashtable: convert hashtable bucket list to a circular doubly linked list
David Ahern
dsa at cumulusnetworks.com
Sat Jun 18 15:30:05 PDT 2016
From: Roopa Prabhu <roopa at cumulusnetworks.com>
This patch converts hashtable bucket list to a circular doubly linked list
for O(1) enqueue/dequeue. This helps support:
- a netlink object append that causes enqueue at tail and
- support for non-exclusive (ie create only flag) netlink objects
causes an enqueue at head
Signed-off-by: Roopa Prabhu <roopa at cumulusnetworks.com>
---
include/netlink/hashtable.h | 2 +-
lib/hashtable.c | 111 ++++++++++++++++++++++++++------------------
2 files changed, 68 insertions(+), 45 deletions(-)
diff --git a/include/netlink/hashtable.h b/include/netlink/hashtable.h
index d9e6ee4..70d8c0f 100644
--- a/include/netlink/hashtable.h
+++ b/include/netlink/hashtable.h
@@ -20,7 +20,7 @@ typedef struct nl_hash_node {
uint32_t key;
uint32_t key_size;
struct nl_object * obj;
- struct nl_hash_node * next;
+ struct nl_list_head list;
} nl_hash_node_t;
typedef struct nl_hash_table {
diff --git a/lib/hashtable.c b/lib/hashtable.c
index 8b15925..75e9fda 100644
--- a/lib/hashtable.c
+++ b/lib/hashtable.c
@@ -14,6 +14,19 @@
#include <netlink/hash.h>
#include <netlink/hashtable.h>
+static nl_hash_node_t *nl_hash_table_list_head_alloc()
+{
+ nl_hash_node_t *head;
+
+ head = calloc(1, sizeof(*head));
+ if (!head)
+ return NULL;
+
+ nl_init_list_head(&head->list);
+
+ return head;
+}
+
/**
* @ingroup core_types
* @defgroup hashtable Hashtable
@@ -58,15 +71,15 @@ void nl_hash_table_free(nl_hash_table_t *ht)
int i;
for(i = 0; i < ht->size; i++) {
- nl_hash_node_t *node = ht->nodes[i];
- nl_hash_node_t *saved_node;
-
- while (node) {
- saved_node = node;
- node = node->next;
- nl_object_put(saved_node->obj);
- free(saved_node);
- }
+ nl_hash_node_t *node, *head = ht->nodes[i], *tmp;
+
+ if (!head)
+ continue;
+
+ nl_list_for_each_entry_safe(node, tmp, &head->list, list) {
+ nl_list_del(&node->list);
+ free(node);
+ }
}
free(ht->nodes);
@@ -86,16 +99,17 @@ void nl_hash_table_free(nl_hash_table_t *ht)
struct nl_object* nl_hash_table_lookup(nl_hash_table_t *ht,
struct nl_object *obj)
{
- nl_hash_node_t *node;
+ nl_hash_node_t *node, *head;
uint32_t key_hash;
nl_object_keygen(obj, &key_hash, ht->size);
- node = ht->nodes[key_hash];
+ head = ht->nodes[key_hash];
+ if (!head)
+ return NULL;
- while (node) {
- if (nl_object_identical(node->obj, obj))
+ nl_list_for_each_entry(node, &head->list, list) {
+ if (nl_object_identical(node->obj, obj))
return node->obj;
- node = node->next;
}
return NULL;
@@ -116,18 +130,27 @@ struct nl_object* nl_hash_table_lookup(nl_hash_table_t *ht,
*/
int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj)
{
- nl_hash_node_t *node;
+ nl_hash_node_t *node, *head;
uint32_t key_hash;
nl_object_keygen(obj, &key_hash, ht->size);
- node = ht->nodes[key_hash];
-
- while (node) {
- if (nl_object_identical(node->obj, obj)) {
- NL_DBG(2, "Warning: Add to hashtable found duplicate...\n");
- return -NLE_EXIST;
- }
- node = node->next;
+ head = ht->nodes[key_hash];
+ if (head != NULL) {
+ nl_list_for_each_entry(node, &head->list, list) {
+ if (nl_object_identical(node->obj, obj)) {
+ NL_DBG(2, "Warning: Add to hashtable found "
+ "duplicate...\n");
+ return -NLE_EXIST;
+ }
+ }
+ } else {
+ /* Initialize head (This can also be done in hash table alloc
+ * function. Its done here so that we only allocate nodes
+ * when needed)
+ */
+ ht->nodes[key_hash] = nl_hash_table_list_head_alloc();
+ if (!ht->nodes[key_hash])
+ return -NLE_NOMEM;
}
NL_DBG (5, "adding cache entry of obj %p in table %p, with hash 0x%x\n",
@@ -140,8 +163,13 @@ int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj)
node->obj = obj;
node->key = key_hash;
node->key_size = sizeof(uint32_t);
- node->next = ht->nodes[key_hash];
- ht->nodes[key_hash] = node;
+ nl_init_list_head(&node->list);
+
+ if ((obj->ce_msgflags & NLM_F_APPEND) ||
+ (obj->ce_flags & NL_OBJ_DUMP))
+ nl_list_add_tail(&node->list, &ht->nodes[key_hash]->list);
+ else
+ nl_list_add_head(&node->list, &ht->nodes[key_hash]->list);
return 0;
}
@@ -160,30 +188,25 @@ int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj)
*/
int nl_hash_table_del(nl_hash_table_t *ht, struct nl_object *obj)
{
- nl_hash_node_t *node, *prev;
+ nl_hash_node_t *node, *head;
uint32_t key_hash;
nl_object_keygen(obj, &key_hash, ht->size);
- prev = node = ht->nodes[key_hash];
+ head = ht->nodes[key_hash];
+ if (!head)
+ return -NLE_OBJ_NOTFOUND;
- while (node) {
+ nl_list_for_each_entry(node, &head->list, list) {
if (nl_object_identical(node->obj, obj)) {
- nl_object_put(obj);
-
- NL_DBG (5, "deleting cache entry of obj %p in table %p, with"
- " hash 0x%x\n", obj, ht, key_hash);
-
- if (node == ht->nodes[key_hash])
- ht->nodes[key_hash] = node->next;
- else
- prev->next = node->next;
-
- free(node);
-
- return 0;
- }
- prev = node;
- node = node->next;
+ nl_object_put(obj);
+ nl_list_del(&node->list);
+ free(node);
+ if (nl_list_empty(&head->list)) {
+ free(ht->nodes[key_hash]);
+ ht->nodes[key_hash] = NULL;
+ }
+ return 0;
+ }
}
return -NLE_OBJ_NOTFOUND;
--
2.1.4
More information about the libnl
mailing list