Blame libglusterfs/src/rbthash.c

Packit b2c0d9
/*
Packit b2c0d9
  Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
Packit b2c0d9
  This file is part of GlusterFS.
Packit b2c0d9
Packit b2c0d9
  This file is licensed to you under your choice of the GNU Lesser
Packit b2c0d9
  General Public License, version 3 or any later version (LGPLv3 or
Packit b2c0d9
  later), or the GNU General Public License, version 2 (GPLv2), in all
Packit b2c0d9
  cases as published by the Free Software Foundation.
Packit b2c0d9
*/
Packit b2c0d9
Packit b2c0d9
#include "glusterfs/rbthash.h"
Packit b2c0d9
#include "rb.h"
Packit b2c0d9
#include "glusterfs/locking.h"
Packit b2c0d9
#include "glusterfs/mem-pool.h"
Packit b2c0d9
#include "glusterfs/logging.h"
Packit b2c0d9
#include "glusterfs/libglusterfs-messages.h"
Packit b2c0d9
Packit b2c0d9
#include <pthread.h>
Packit b2c0d9
#include <string.h>
Packit b2c0d9
Packit b2c0d9
int
Packit b2c0d9
rbthash_comparator(void *entry1, void *entry2, void *param)
Packit b2c0d9
{
Packit b2c0d9
    int ret = 0;
Packit b2c0d9
    rbthash_entry_t *e1 = NULL;
Packit b2c0d9
    rbthash_entry_t *e2 = NULL;
Packit b2c0d9
Packit b2c0d9
    if ((!entry1) || (!entry2) || (!param))
Packit b2c0d9
        return -1;
Packit b2c0d9
Packit b2c0d9
    e1 = (rbthash_entry_t *)entry1;
Packit b2c0d9
    e2 = (rbthash_entry_t *)entry2;
Packit b2c0d9
Packit b2c0d9
    if (e1->keylen != e2->keylen) {
Packit b2c0d9
        if (e1->keylen < e2->keylen)
Packit b2c0d9
            ret = -1;
Packit b2c0d9
        else if (e1->keylen > e2->keylen)
Packit b2c0d9
            ret = 1;
Packit b2c0d9
    } else
Packit b2c0d9
        ret = memcmp(e1->key, e2->key, e1->keylen);
Packit b2c0d9
Packit b2c0d9
    return ret;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
int
Packit b2c0d9
__rbthash_init_buckets(rbthash_table_t *tbl, int buckets)
Packit b2c0d9
{
Packit b2c0d9
    int i = 0;
Packit b2c0d9
    int ret = -1;
Packit b2c0d9
Packit b2c0d9
    if (!tbl)
Packit b2c0d9
        return -1;
Packit b2c0d9
Packit b2c0d9
    for (; i < buckets; i++) {
Packit b2c0d9
        LOCK_INIT(&tbl->buckets[i].bucketlock);
Packit b2c0d9
        tbl->buckets[i].bucket = rb_create(
Packit b2c0d9
            (rb_comparison_func *)rbthash_comparator, tbl, NULL);
Packit b2c0d9
        if (!tbl->buckets[i].bucket) {
Packit b2c0d9
            gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RB_TABLE_CREATE_FAILED,
Packit b2c0d9
                   "Failed to "
Packit b2c0d9
                   "create rb table bucket");
Packit b2c0d9
            ret = -1;
Packit b2c0d9
            goto err;
Packit b2c0d9
        }
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    ret = 0;
Packit b2c0d9
err:
Packit b2c0d9
    return ret;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
/*
Packit b2c0d9
 * rbthash_table_init - Initialize a RBT based hash table
Packit b2c0d9
 * @buckets - Number of buckets in the hash table
Packit b2c0d9
 * @hfunc   - hashing function
Packit b2c0d9
 * @dfunc   - destroyer for data in the RBT
Packit b2c0d9
 * @expected_entries - Number of entries expected in RBT. Mutually exclusive
Packit b2c0d9
 * with entrypool.
Packit b2c0d9
 * @entrypool -  Memory pool in lieu of expected_entries.
Packit b2c0d9
 */
Packit b2c0d9
Packit b2c0d9
rbthash_table_t *
Packit b2c0d9
rbthash_table_init(glusterfs_ctx_t *ctx, int buckets, rbt_hasher_t hfunc,
Packit b2c0d9
                   rbt_data_destroyer_t dfunc, unsigned long expected_entries,
Packit b2c0d9
                   struct mem_pool *entrypool)
Packit b2c0d9
{
Packit b2c0d9
    rbthash_table_t *newtab = NULL;
Packit b2c0d9
    int ret = -1;
Packit b2c0d9
Packit b2c0d9
    if (!hfunc) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_HASH_FUNC_ERROR,
Packit b2c0d9
               "Hash function not given");
Packit b2c0d9
        return NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    if (!entrypool && !expected_entries) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY,
Packit b2c0d9
               "Both mem-pool and expected entries not provided");
Packit b2c0d9
        return NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    if (entrypool && expected_entries) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY,
Packit b2c0d9
               "Both mem-pool and expected entries are provided");
Packit b2c0d9
        return NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    newtab = GF_CALLOC(1, sizeof(*newtab), gf_common_mt_rbthash_table_t);
Packit b2c0d9
    if (!newtab)
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    newtab->buckets = GF_CALLOC(buckets, sizeof(struct rbthash_bucket),
Packit b2c0d9
                                gf_common_mt_rbthash_bucket);
Packit b2c0d9
    if (!newtab->buckets) {
Packit b2c0d9
        goto free_newtab;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    if (expected_entries) {
Packit b2c0d9
        newtab->entrypool = mem_pool_new_ctx(ctx, rbthash_entry_t,
Packit b2c0d9
                                             expected_entries);
Packit b2c0d9
        if (!newtab->entrypool) {
Packit b2c0d9
            goto free_buckets;
Packit b2c0d9
        }
Packit b2c0d9
        newtab->pool_alloced = _gf_true;
Packit b2c0d9
    } else {
Packit b2c0d9
        newtab->entrypool = entrypool;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    LOCK_INIT(&newtab->tablelock);
Packit b2c0d9
    INIT_LIST_HEAD(&newtab->list);
Packit b2c0d9
    newtab->numbuckets = buckets;
Packit b2c0d9
    ret = __rbthash_init_buckets(newtab, buckets);
Packit b2c0d9
Packit b2c0d9
    if (ret == -1) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INIT_BUCKET_FAILED,
Packit b2c0d9
               "Failed to init buckets");
Packit b2c0d9
        if (newtab->pool_alloced)
Packit b2c0d9
            mem_pool_destroy(newtab->entrypool);
Packit b2c0d9
    } else {
Packit b2c0d9
        gf_msg_trace(GF_RBTHASH, 0,
Packit b2c0d9
                     "Inited hash table: buckets:"
Packit b2c0d9
                     " %d",
Packit b2c0d9
                     buckets);
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    newtab->hashfunc = hfunc;
Packit b2c0d9
    newtab->dfunc = dfunc;
Packit b2c0d9
Packit b2c0d9
free_buckets:
Packit b2c0d9
    if (ret == -1)
Packit b2c0d9
        GF_FREE(newtab->buckets);
Packit b2c0d9
Packit b2c0d9
free_newtab:
Packit b2c0d9
    if (ret == -1) {
Packit b2c0d9
        GF_FREE(newtab);
Packit b2c0d9
        newtab = NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    return newtab;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
rbthash_entry_t *
Packit b2c0d9
rbthash_init_entry(rbthash_table_t *tbl, void *data, void *key, int keylen)
Packit b2c0d9
{
Packit b2c0d9
    int ret = -1;
Packit b2c0d9
    rbthash_entry_t *entry = NULL;
Packit b2c0d9
Packit b2c0d9
    if ((!tbl) || (!data) || (!key))
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    entry = mem_get(tbl->entrypool);
Packit b2c0d9
    if (!entry) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_ENTRY_FAILED,
Packit b2c0d9
               "Failed to get entry from mem-pool");
Packit b2c0d9
        goto ret;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    entry->data = data;
Packit b2c0d9
    entry->key = GF_MALLOC(keylen, gf_common_mt_char);
Packit b2c0d9
    if (!entry->key) {
Packit b2c0d9
        goto free_entry;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    INIT_LIST_HEAD(&entry->list);
Packit b2c0d9
    memcpy(entry->key, key, keylen);
Packit b2c0d9
    entry->keylen = keylen;
Packit b2c0d9
    entry->keyhash = tbl->hashfunc(entry->key, entry->keylen);
Packit b2c0d9
    gf_msg_trace(GF_RBTHASH, 0, "HASH: %u", entry->keyhash);
Packit b2c0d9
Packit b2c0d9
    ret = 0;
Packit b2c0d9
free_entry:
Packit b2c0d9
    if (ret == -1) {
Packit b2c0d9
        mem_put(entry);
Packit b2c0d9
        entry = NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
ret:
Packit b2c0d9
    return entry;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void
Packit b2c0d9
rbthash_deinit_entry(rbthash_table_t *tbl, rbthash_entry_t *entry)
Packit b2c0d9
{
Packit b2c0d9
    if (!entry)
Packit b2c0d9
        return;
Packit b2c0d9
Packit b2c0d9
    GF_FREE(entry->key);
Packit b2c0d9
Packit b2c0d9
    if (tbl) {
Packit b2c0d9
        if ((entry->data) && (tbl->dfunc))
Packit b2c0d9
            tbl->dfunc(entry->data);
Packit b2c0d9
Packit b2c0d9
        LOCK(&tbl->tablelock);
Packit b2c0d9
        {
Packit b2c0d9
            list_del_init(&entry->list);
Packit b2c0d9
        }
Packit b2c0d9
        UNLOCK(&tbl->tablelock);
Packit b2c0d9
Packit b2c0d9
        mem_put(entry);
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    return;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
static struct rbthash_bucket *
Packit b2c0d9
rbthash_entry_bucket(rbthash_table_t *tbl, rbthash_entry_t *entry)
Packit b2c0d9
{
Packit b2c0d9
    int nbucket = 0;
Packit b2c0d9
Packit b2c0d9
    nbucket = (entry->keyhash % tbl->numbuckets);
Packit b2c0d9
    gf_msg_trace(GF_RBTHASH, 0, "BUCKET: %d", nbucket);
Packit b2c0d9
    return &tbl->buckets[nbucket];
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
int
Packit b2c0d9
rbthash_insert_entry(rbthash_table_t *tbl, rbthash_entry_t *entry)
Packit b2c0d9
{
Packit b2c0d9
    struct rbthash_bucket *bucket = NULL;
Packit b2c0d9
    int ret = -1;
Packit b2c0d9
Packit b2c0d9
    if ((!tbl) || (!entry))
Packit b2c0d9
        return -1;
Packit b2c0d9
Packit b2c0d9
    bucket = rbthash_entry_bucket(tbl, entry);
Packit b2c0d9
    if (!bucket) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_BUCKET_FAILED,
Packit b2c0d9
               "Failed to get bucket");
Packit b2c0d9
        goto err;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    ret = 0;
Packit b2c0d9
    LOCK(&bucket->bucketlock);
Packit b2c0d9
    {
Packit b2c0d9
        if (!rb_probe(bucket->bucket, (void *)entry)) {
Packit b2c0d9
            UNLOCK(&bucket->bucketlock);
Packit b2c0d9
            gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INSERT_FAILED,
Packit b2c0d9
                   "Failed to insert entry");
Packit b2c0d9
            ret = -1;
Packit b2c0d9
            goto err;
Packit b2c0d9
        }
Packit b2c0d9
    }
Packit b2c0d9
    UNLOCK(&bucket->bucketlock);
Packit b2c0d9
Packit b2c0d9
err:
Packit b2c0d9
    return ret;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
int
Packit b2c0d9
rbthash_insert(rbthash_table_t *tbl, void *data, void *key, int keylen)
Packit b2c0d9
{
Packit b2c0d9
    rbthash_entry_t *entry = NULL;
Packit b2c0d9
    int ret = -1;
Packit b2c0d9
Packit b2c0d9
    if ((!tbl) || (!data) || (!key))
Packit b2c0d9
        return -1;
Packit b2c0d9
Packit b2c0d9
    entry = rbthash_init_entry(tbl, data, key, keylen);
Packit b2c0d9
    if (!entry) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INIT_ENTRY_FAILED,
Packit b2c0d9
               "Failed to init entry");
Packit b2c0d9
        goto err;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    ret = rbthash_insert_entry(tbl, entry);
Packit b2c0d9
Packit b2c0d9
    if (ret == -1) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INSERT_FAILED,
Packit b2c0d9
               "Failed to insert entry");
Packit b2c0d9
        rbthash_deinit_entry(tbl, entry);
Packit b2c0d9
        goto err;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    LOCK(&tbl->tablelock);
Packit b2c0d9
    {
Packit b2c0d9
        list_add_tail(&entry->list, &tbl->list);
Packit b2c0d9
    }
Packit b2c0d9
    UNLOCK(&tbl->tablelock);
Packit b2c0d9
Packit b2c0d9
err:
Packit b2c0d9
    return ret;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
static struct rbthash_bucket *
Packit b2c0d9
rbthash_key_bucket(rbthash_table_t *tbl, void *key, int keylen)
Packit b2c0d9
{
Packit b2c0d9
    uint32_t keyhash = 0;
Packit b2c0d9
    int nbucket = 0;
Packit b2c0d9
Packit b2c0d9
    if ((!tbl) || (!key))
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    keyhash = tbl->hashfunc(key, keylen);
Packit b2c0d9
    gf_msg_trace(GF_RBTHASH, 0, "HASH: %u", keyhash);
Packit b2c0d9
    nbucket = (keyhash % tbl->numbuckets);
Packit b2c0d9
    gf_msg_trace(GF_RBTHASH, 0, "BUCKET: %u", nbucket);
Packit b2c0d9
Packit b2c0d9
    return &tbl->buckets[nbucket];
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void *
Packit b2c0d9
rbthash_get(rbthash_table_t *tbl, void *key, int keylen)
Packit b2c0d9
{
Packit b2c0d9
    struct rbthash_bucket *bucket = NULL;
Packit b2c0d9
    rbthash_entry_t *entry = NULL;
Packit b2c0d9
    rbthash_entry_t searchentry = {
Packit b2c0d9
        0,
Packit b2c0d9
    };
Packit b2c0d9
Packit b2c0d9
    if ((!tbl) || (!key))
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    bucket = rbthash_key_bucket(tbl, key, keylen);
Packit b2c0d9
    if (!bucket) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_GET_BUCKET_FAILED,
Packit b2c0d9
               "Failed to get bucket");
Packit b2c0d9
        return NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    searchentry.key = key;
Packit b2c0d9
    searchentry.keylen = keylen;
Packit b2c0d9
    LOCK(&bucket->bucketlock);
Packit b2c0d9
    {
Packit b2c0d9
        entry = rb_find(bucket->bucket, &searchentry);
Packit b2c0d9
    }
Packit b2c0d9
    UNLOCK(&bucket->bucketlock);
Packit b2c0d9
Packit b2c0d9
    if (!entry)
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    return entry->data;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void *
Packit b2c0d9
rbthash_remove(rbthash_table_t *tbl, void *key, int keylen)
Packit b2c0d9
{
Packit b2c0d9
    struct rbthash_bucket *bucket = NULL;
Packit b2c0d9
    rbthash_entry_t *entry = NULL;
Packit b2c0d9
    rbthash_entry_t searchentry = {
Packit b2c0d9
        0,
Packit b2c0d9
    };
Packit b2c0d9
    void *dataref = NULL;
Packit b2c0d9
Packit b2c0d9
    if ((!tbl) || (!key))
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    bucket = rbthash_key_bucket(tbl, key, keylen);
Packit b2c0d9
    if (!bucket) {
Packit b2c0d9
        gf_msg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_BUCKET_FAILED,
Packit b2c0d9
               "Failed to get bucket");
Packit b2c0d9
        return NULL;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    searchentry.key = key;
Packit b2c0d9
    searchentry.keylen = keylen;
Packit b2c0d9
Packit b2c0d9
    LOCK(&bucket->bucketlock);
Packit b2c0d9
    {
Packit b2c0d9
        entry = rb_delete(bucket->bucket, &searchentry);
Packit b2c0d9
    }
Packit b2c0d9
    UNLOCK(&bucket->bucketlock);
Packit b2c0d9
Packit b2c0d9
    if (!entry)
Packit b2c0d9
        return NULL;
Packit b2c0d9
Packit b2c0d9
    GF_FREE(entry->key);
Packit b2c0d9
    dataref = entry->data;
Packit b2c0d9
Packit b2c0d9
    LOCK(&tbl->tablelock);
Packit b2c0d9
    {
Packit b2c0d9
        list_del_init(&entry->list);
Packit b2c0d9
    }
Packit b2c0d9
    UNLOCK(&tbl->tablelock);
Packit b2c0d9
Packit b2c0d9
    mem_put(entry);
Packit b2c0d9
Packit b2c0d9
    return dataref;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void
Packit b2c0d9
rbthash_entry_deiniter(void *entry, void *rbparam)
Packit b2c0d9
{
Packit b2c0d9
    if (!entry)
Packit b2c0d9
        return;
Packit b2c0d9
Packit b2c0d9
    rbthash_deinit_entry(rbparam, entry);
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void
Packit b2c0d9
rbthash_table_destroy_buckets(rbthash_table_t *tbl)
Packit b2c0d9
{
Packit b2c0d9
    int x = 0;
Packit b2c0d9
    if (!tbl)
Packit b2c0d9
        return;
Packit b2c0d9
Packit b2c0d9
    for (; x < tbl->numbuckets; x++) {
Packit b2c0d9
        LOCK_DESTROY(&tbl->buckets[x].bucketlock);
Packit b2c0d9
        rb_destroy(tbl->buckets[x].bucket, rbthash_entry_deiniter);
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    return;
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void
Packit b2c0d9
rbthash_table_destroy(rbthash_table_t *tbl)
Packit b2c0d9
{
Packit b2c0d9
    if (!tbl)
Packit b2c0d9
        return;
Packit b2c0d9
Packit b2c0d9
    rbthash_table_destroy_buckets(tbl);
Packit b2c0d9
    if (tbl->pool_alloced)
Packit b2c0d9
        mem_pool_destroy(tbl->entrypool);
Packit b2c0d9
Packit b2c0d9
    GF_FREE(tbl->buckets);
Packit b2c0d9
    GF_FREE(tbl);
Packit b2c0d9
}
Packit b2c0d9
Packit b2c0d9
void
Packit b2c0d9
rbthash_table_traverse(rbthash_table_t *tbl, rbt_traverse_t traverse,
Packit b2c0d9
                       void *mydata)
Packit b2c0d9
{
Packit b2c0d9
    rbthash_entry_t *entry = NULL;
Packit b2c0d9
Packit b2c0d9
    if ((tbl == NULL) || (traverse == NULL)) {
Packit b2c0d9
        goto out;
Packit b2c0d9
    }
Packit b2c0d9
Packit b2c0d9
    LOCK(&tbl->tablelock);
Packit b2c0d9
    {
Packit b2c0d9
        list_for_each_entry(entry, &tbl->list, list)
Packit b2c0d9
        {
Packit b2c0d9
            traverse(entry->data, mydata);
Packit b2c0d9
        }
Packit b2c0d9
    }
Packit b2c0d9
    UNLOCK(&tbl->tablelock);
Packit b2c0d9
Packit b2c0d9
out:
Packit b2c0d9
    return;
Packit b2c0d9
}