glusterfs
454 строки · 10.1 Кб
1/*
2Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
3This file is part of GlusterFS.
4
5This file is licensed to you under your choice of the GNU Lesser
6General Public License, version 3 or any later version (LGPLv3 or
7later), or the GNU General Public License, version 2 (GPLv2), in all
8cases as published by the Free Software Foundation.
9*/
10
11#include "glusterfs/rbthash.h"12#include "rb.h"13#include "glusterfs/locking.h"14#include "glusterfs/mem-pool.h"15#include "glusterfs/libglusterfs-messages.h"16
17#include <pthread.h>18#include <string.h>19
20int
21rbthash_comparator(void *entry1, void *entry2, void *param)22{
23int ret = 0;24rbthash_entry_t *e1 = NULL;25rbthash_entry_t *e2 = NULL;26
27if ((!entry1) || (!entry2) || (!param))28return -1;29
30e1 = (rbthash_entry_t *)entry1;31e2 = (rbthash_entry_t *)entry2;32
33if (e1->keylen != e2->keylen) {34if (e1->keylen < e2->keylen)35ret = -1;36else if (e1->keylen > e2->keylen)37ret = 1;38} else39ret = memcmp(e1->key, e2->key, e1->keylen);40
41return ret;42}
43
44int
45__rbthash_init_buckets(rbthash_table_t *tbl, int buckets)46{
47int i = 0;48int ret = -1;49
50if (!tbl)51return -1;52
53for (; i < buckets; i++) {54LOCK_INIT(&tbl->buckets[i].bucketlock);55tbl->buckets[i].bucket = rb_create(56(rb_comparison_func *)rbthash_comparator, tbl, NULL);57if (!tbl->buckets[i].bucket) {58gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RB_TABLE_CREATE_FAILED,59NULL);60ret = -1;61goto err;62}63}64
65ret = 0;66err:67return ret;68}
69
70/*
71* rbthash_table_init - Initialize a RBT based hash table
72* @buckets - Number of buckets in the hash table
73* @hfunc - hashing function
74* @dfunc - destroyer for data in the RBT
75* @expected_entries - Number of entries expected in RBT. Mutually exclusive
76* with entrypool.
77* @entrypool - Memory pool in lieu of expected_entries.
78*/
79
80rbthash_table_t *81rbthash_table_init(glusterfs_ctx_t *ctx, int buckets, rbt_hasher_t hfunc,82rbt_data_destroyer_t dfunc, unsigned long expected_entries,83struct mem_pool *entrypool)84{
85rbthash_table_t *newtab = NULL;86int ret = -1;87
88if (!hfunc) {89gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_HASH_FUNC_ERROR, NULL);90return NULL;91}92
93if (!entrypool && !expected_entries) {94gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_ENTRIES_NOT_PROVIDED, NULL);95return NULL;96}97
98if (entrypool && expected_entries) {99gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_ENTRIES_PROVIDED, NULL);100return NULL;101}102
103newtab = GF_CALLOC(1, sizeof(*newtab), gf_common_mt_rbthash_table_t);104if (!newtab)105return NULL;106
107newtab->buckets = GF_CALLOC(buckets, sizeof(struct rbthash_bucket),108gf_common_mt_rbthash_bucket);109if (!newtab->buckets) {110goto free_newtab;111}112
113if (expected_entries) {114newtab->entrypool = mem_pool_new_ctx(ctx, rbthash_entry_t,115expected_entries);116if (!newtab->entrypool) {117goto free_buckets;118}119newtab->pool_alloced = _gf_true;120} else {121newtab->entrypool = entrypool;122}123
124LOCK_INIT(&newtab->tablelock);125INIT_LIST_HEAD(&newtab->list);126newtab->numbuckets = buckets;127ret = __rbthash_init_buckets(newtab, buckets);128
129if (ret == -1) {130gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INIT_BUCKET_FAILED,131NULL);132if (newtab->pool_alloced)133mem_pool_destroy(newtab->entrypool);134} else {135gf_msg_trace(GF_RBTHASH, 0,136"Inited hash table: buckets:"137" %d",138buckets);139}140
141newtab->hashfunc = hfunc;142newtab->dfunc = dfunc;143
144free_buckets:145if (ret == -1)146GF_FREE(newtab->buckets);147
148free_newtab:149if (ret == -1) {150GF_FREE(newtab);151newtab = NULL;152}153
154return newtab;155}
156
157rbthash_entry_t *158rbthash_init_entry(rbthash_table_t *tbl, void *data, void *key, int keylen)159{
160int ret = -1;161rbthash_entry_t *entry = NULL;162
163if ((!tbl) || (!data) || (!key))164return NULL;165
166entry = mem_get(tbl->entrypool);167if (!entry) {168gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_ENTRY_FAILED,169NULL);170goto ret;171}172
173entry->data = data;174entry->key = GF_MALLOC(keylen, gf_common_mt_char);175if (!entry->key) {176goto free_entry;177}178
179INIT_LIST_HEAD(&entry->list);180memcpy(entry->key, key, keylen);181entry->keylen = keylen;182entry->keyhash = tbl->hashfunc(entry->key, entry->keylen);183gf_msg_trace(GF_RBTHASH, 0, "HASH: %u", entry->keyhash);184
185ret = 0;186free_entry:187if (ret == -1) {188mem_put(entry);189entry = NULL;190}191
192ret:193return entry;194}
195
196void
197rbthash_deinit_entry(rbthash_table_t *tbl, rbthash_entry_t *entry)198{
199if (!entry)200return;201
202GF_FREE(entry->key);203
204if (tbl) {205if ((entry->data) && (tbl->dfunc))206tbl->dfunc(entry->data);207
208LOCK(&tbl->tablelock);209{210list_del_init(&entry->list);211}212UNLOCK(&tbl->tablelock);213
214mem_put(entry);215}216
217return;218}
219
220static struct rbthash_bucket *221rbthash_entry_bucket(rbthash_table_t *tbl, rbthash_entry_t *entry)222{
223int nbucket = 0;224
225nbucket = (entry->keyhash % tbl->numbuckets);226gf_msg_trace(GF_RBTHASH, 0, "BUCKET: %d", nbucket);227return &tbl->buckets[nbucket];228}
229
230int
231rbthash_insert_entry(rbthash_table_t *tbl, rbthash_entry_t *entry)232{
233struct rbthash_bucket *bucket = NULL;234int ret = -1;235
236if ((!tbl) || (!entry))237return -1;238
239bucket = rbthash_entry_bucket(tbl, entry);240if (!bucket) {241gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_BUCKET_FAILED,242NULL);243goto err;244}245
246ret = 0;247LOCK(&bucket->bucketlock);248{249if (!rb_probe(bucket->bucket, (void *)entry)) {250UNLOCK(&bucket->bucketlock);251gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INSERT_FAILED,252NULL);253ret = -1;254goto err;255}256}257UNLOCK(&bucket->bucketlock);258
259err:260return ret;261}
262
263int
264rbthash_insert(rbthash_table_t *tbl, void *data, void *key, int keylen)265{
266rbthash_entry_t *entry = NULL;267int ret = -1;268
269if ((!tbl) || (!data) || (!key))270return -1;271
272entry = rbthash_init_entry(tbl, data, key, keylen);273if (!entry) {274gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INIT_ENTRY_FAILED,275NULL);276goto err;277}278
279ret = rbthash_insert_entry(tbl, entry);280
281if (ret == -1) {282gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_INSERT_FAILED,283NULL);284rbthash_deinit_entry(tbl, entry);285goto err;286}287
288LOCK(&tbl->tablelock);289{290list_add_tail(&entry->list, &tbl->list);291}292UNLOCK(&tbl->tablelock);293
294err:295return ret;296}
297
298static struct rbthash_bucket *299rbthash_key_bucket(rbthash_table_t *tbl, void *key, int keylen)300{
301uint32_t keyhash = 0;302int nbucket = 0;303
304if ((!tbl) || (!key))305return NULL;306
307keyhash = tbl->hashfunc(key, keylen);308gf_msg_trace(GF_RBTHASH, 0, "HASH: %u", keyhash);309nbucket = (keyhash % tbl->numbuckets);310gf_msg_trace(GF_RBTHASH, 0, "BUCKET: %u", nbucket);311
312return &tbl->buckets[nbucket];313}
314
315void *316rbthash_get(rbthash_table_t *tbl, void *key, int keylen)317{
318struct rbthash_bucket *bucket = NULL;319rbthash_entry_t *entry = NULL;320rbthash_entry_t searchentry = {3210,322};323
324if ((!tbl) || (!key))325return NULL;326
327bucket = rbthash_key_bucket(tbl, key, keylen);328if (!bucket) {329gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_BUCKET_FAILED,330NULL);331return NULL;332}333
334searchentry.key = key;335searchentry.keylen = keylen;336LOCK(&bucket->bucketlock);337{338entry = rb_find(bucket->bucket, &searchentry);339}340UNLOCK(&bucket->bucketlock);341
342if (!entry)343return NULL;344
345return entry->data;346}
347
348void *349rbthash_remove(rbthash_table_t *tbl, void *key, int keylen)350{
351struct rbthash_bucket *bucket = NULL;352rbthash_entry_t *entry = NULL;353rbthash_entry_t searchentry = {3540,355};356void *dataref = NULL;357
358if ((!tbl) || (!key))359return NULL;360
361bucket = rbthash_key_bucket(tbl, key, keylen);362if (!bucket) {363gf_smsg(GF_RBTHASH, GF_LOG_ERROR, 0, LG_MSG_RBTHASH_GET_BUCKET_FAILED,364NULL);365return NULL;366}367
368searchentry.key = key;369searchentry.keylen = keylen;370
371LOCK(&bucket->bucketlock);372{373entry = rb_delete(bucket->bucket, &searchentry);374}375UNLOCK(&bucket->bucketlock);376
377if (!entry)378return NULL;379
380GF_FREE(entry->key);381dataref = entry->data;382
383LOCK(&tbl->tablelock);384{385list_del_init(&entry->list);386}387UNLOCK(&tbl->tablelock);388
389mem_put(entry);390
391return dataref;392}
393
394void
395rbthash_entry_deiniter(void *entry, void *rbparam)396{
397if (!entry)398return;399
400rbthash_deinit_entry(rbparam, entry);401}
402
403void
404rbthash_table_destroy_buckets(rbthash_table_t *tbl)405{
406int x = 0;407if (!tbl)408return;409
410for (; x < tbl->numbuckets; x++) {411LOCK_DESTROY(&tbl->buckets[x].bucketlock);412rb_destroy(tbl->buckets[x].bucket, rbthash_entry_deiniter);413}414
415return;416}
417
418void
419rbthash_table_destroy(rbthash_table_t *tbl)420{
421if (!tbl)422return;423
424rbthash_table_destroy_buckets(tbl);425if (tbl->pool_alloced)426mem_pool_destroy(tbl->entrypool);427
428LOCK_DESTROY(&tbl->tablelock);429GF_FREE(tbl->buckets);430GF_FREE(tbl);431}
432
433void
434rbthash_table_traverse(rbthash_table_t *tbl, rbt_traverse_t traverse,435void *mydata)436{
437rbthash_entry_t *entry = NULL;438
439if ((tbl == NULL) || (traverse == NULL)) {440goto out;441}442
443LOCK(&tbl->tablelock);444{445list_for_each_entry(entry, &tbl->list, list)446{447traverse(entry->data, mydata);448}449}450UNLOCK(&tbl->tablelock);451
452out:453return;454}
455