summaryrefslogtreecommitdiffstats
path: root/xlators/storage/bdb/src/bctx.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/storage/bdb/src/bctx.c')
-rw-r--r--xlators/storage/bdb/src/bctx.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/xlators/storage/bdb/src/bctx.c b/xlators/storage/bdb/src/bctx.c
new file mode 100644
index 00000000000..2bfa3ea8762
--- /dev/null
+++ b/xlators/storage/bdb/src/bctx.c
@@ -0,0 +1,394 @@
+/*
+ Copyright (c) 2008 Z RESEARCH, Inc. <http://www.zresearch.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <list.h>
+#include <bdb.h>
+#include <libgen.h> /* for dirname */
+
+static void
+__destroy_bctx (bctx_t *bctx)
+{
+ if (bctx->directory)
+ FREE (bctx->directory);
+
+ if (bctx->db_path)
+ FREE (bctx->db_path);
+
+ FREE (bctx);
+}
+
+static void
+__unhash_bctx (bctx_t *bctx)
+{
+ list_del_init (&bctx->b_hash);
+}
+
+static int32_t
+bctx_table_prune (bctx_table_t *table)
+{
+ int32_t ret = 0;
+ struct list_head purge = {0,};
+ struct list_head *next = NULL;
+ bctx_t *entry = NULL;
+ bctx_t *del = NULL, *tmp = NULL;
+
+ if (!table)
+ return 0;
+
+ INIT_LIST_HEAD (&purge);
+
+ LOCK (&table->lock);
+ {
+ if ((table->lru_limit) &&
+ (table->lru_size > table->lru_limit)) {
+ while (table->lru_size > table->lru_limit) {
+ next = table->b_lru.next;
+ entry = list_entry (next, bctx_t, list);
+
+ list_move_tail (next, &table->purge);
+ __unhash_bctx (entry);
+
+ table->lru_size--;
+ ret++;
+ }
+ }
+ list_move_tail (&purge, &table->purge);
+ list_del_init (&table->purge);
+ }
+ UNLOCK (&table->lock);
+
+ {
+ list_for_each_entry_safe (del, tmp, &purge, list) {
+ list_del_init (&del->list);
+ if (del->dbp) {
+ ret = del->dbp->close (del->dbp, 0);
+ if (ret != 0) {
+ gf_log (table->this->name, GF_LOG_ERROR,
+ "failed to close db on path (%s): %s",
+ del->directory, db_strerror (ret));
+ } else {
+ gf_log (table->this->name, GF_LOG_WARNING,
+ "close db for path %s; table->lru_count = %d",
+ del->directory, table->lru_size);
+ }
+ }
+ __destroy_bctx (del);
+ }
+ }
+
+ return ret;
+}
+
+
+/* struct bdb_ctx related */
+static inline uint32_t
+bdb_key_hash (char *key, uint32_t hash_size)
+{
+ uint32_t hash = 0;
+
+ hash = *key;
+
+ if (hash) {
+ for (key += 1; *key != '\0'; key++) {
+ hash = (hash << 5) - hash + *key;
+ }
+ }
+
+ return (hash + *key) % hash_size;
+}
+
+static void
+__hash_bctx (bctx_t *bctx)
+{
+ bctx_table_t *table = NULL;
+ char *key = NULL;
+
+ table = bctx->table;
+
+ MAKE_KEY_FROM_PATH (key, bctx->directory);
+ bctx->key_hash = bdb_key_hash (key, table->hash_size);
+
+ list_del_init (&bctx->b_hash);
+ list_add (&bctx->b_hash, &table->b_hash[bctx->key_hash]);
+}
+
+static inline bctx_t *
+__bctx_passivate (bctx_t *bctx)
+{
+ if (bctx->dbp) {
+ list_move_tail (&bctx->list, &(bctx->table->b_lru));
+ bctx->table->lru_size++;
+ } else {
+ list_move_tail (&bctx->list, &bctx->table->purge);
+ __unhash_bctx (bctx);
+ }
+ return bctx;
+}
+
+static inline bctx_t *
+__bctx_activate (bctx_t *bctx)
+{
+ list_move (&bctx->list, &bctx->table->active);
+ bctx->table->lru_size--;
+
+ return bctx;
+}
+
+static bctx_t *
+__bdb_ctx_unref (bctx_t *bctx)
+{
+ assert (bctx->ref);
+
+ --bctx->ref;
+
+ if (!bctx->ref)
+ bctx = __bctx_passivate (bctx);
+
+ return bctx;
+}
+
+
+bctx_t *
+bctx_unref (bctx_t *bctx)
+{
+ bctx_table_t *table = NULL;
+
+ if (!bctx && !bctx->table)
+ return NULL;
+
+ table = bctx->table;
+
+ LOCK (&table->lock);
+ {
+ bctx = __bdb_ctx_unref (bctx);
+ }
+ UNLOCK (&table->lock);
+
+ bctx_table_prune (table);
+
+ return bctx;
+}
+
+/*
+ * NOTE: __bdb_ctx_ref() is called only after holding table->lock and bctx->lock, in that order
+ */
+static inline bctx_t *
+__bctx_ref (bctx_t *bctx)
+{
+ if (!bctx->ref)
+ __bctx_activate (bctx);
+
+ bctx->ref++;
+
+ return bctx;
+}
+
+bctx_t *
+bctx_ref (bctx_t *bctx)
+{
+ LOCK (&(bctx->table->lock));
+ {
+ __bctx_ref (bctx);
+ }
+ UNLOCK (&(bctx->table->lock));
+
+ return bctx;
+}
+
+
+#define BDB_THIS(table) (table->this)
+
+static inline bctx_t *
+__create_bctx (bctx_table_t *table,
+ const char *path)
+{
+ bctx_t *bctx = NULL;
+ char *db_path = NULL;
+
+ bctx = CALLOC (1, sizeof (*bctx));
+ GF_VALIDATE_OR_GOTO ("bctx", bctx, out);
+
+ bctx->table = table;
+ bctx->directory = strdup (path);
+ GF_VALIDATE_OR_GOTO ("bctx", bctx->directory, out);
+
+ MAKE_REAL_PATH_TO_STORAGE_DB (db_path, BDB_THIS (table), path);
+
+ bctx->db_path = strdup (db_path);
+ GF_VALIDATE_OR_GOTO ("bctx", bctx->directory, out);
+
+ INIT_LIST_HEAD (&bctx->c_list);
+ INIT_LIST_HEAD (&bctx->list);
+ INIT_LIST_HEAD (&bctx->b_hash);
+
+ LOCK_INIT (&bctx->lock);
+
+ __hash_bctx (bctx);
+
+ list_add (&bctx->list, &table->b_lru);
+ table->lru_size++;
+
+out:
+ return bctx;
+}
+
+/* bctx_lookup - lookup bctx_t for the directory @directory. (see description of bctx_t in bdb.h)
+ *
+ * @table: bctx_table_t for this instance of bdb.
+ * @directory: directory for which bctx_t is being looked up.
+ */
+bctx_t *
+bctx_lookup (bctx_table_t *table,
+ const char *directory)
+{
+ char *key = NULL;
+ uint32_t key_hash = 0;
+ bctx_t *trav = NULL, *bctx = NULL, *tmp = NULL;
+ int32_t need_break = 0;
+
+ GF_VALIDATE_OR_GOTO ("bctx", table, out);
+ GF_VALIDATE_OR_GOTO ("bctx", directory, out);
+
+ MAKE_KEY_FROM_PATH (key, directory);
+ key_hash = bdb_key_hash (key, table->hash_size);
+
+ LOCK (&table->lock);
+ {
+ if (!list_empty (&table->b_hash[key_hash])) {
+ list_for_each_entry_safe (trav, tmp, &table->b_hash[key_hash], b_hash) {
+ LOCK(&trav->lock);
+ if (!strcmp(trav->directory, directory)) {
+ bctx = __bctx_ref (trav);
+ need_break = 1;
+ }
+ UNLOCK(&trav->lock);
+ if (need_break)
+ break;
+ }
+ }
+
+ if (!bctx) {
+ bctx = __create_bctx (table, directory);
+ bctx = __bctx_ref (bctx);
+ }
+ }
+ UNLOCK (&table->lock);
+out:
+ return bctx;
+}
+
+
+bctx_t *
+bctx_parent (bctx_table_t *table,
+ const char *path)
+{
+ char *pathname = NULL, *directory = NULL;
+ bctx_t *bctx = NULL;
+
+ GF_VALIDATE_OR_GOTO ("bctx", table, out);
+ GF_VALIDATE_OR_GOTO ("bctx", path, out);
+
+ pathname = strdup (path);
+ GF_VALIDATE_OR_GOTO ("bctx", pathname, out);
+ directory = dirname (pathname);
+
+ bctx = bctx_lookup (table, directory);
+ GF_VALIDATE_OR_GOTO ("bctx", bctx, out);
+
+out:
+ if (pathname)
+ free (pathname);
+ return bctx;
+}
+
+inline int32_t
+bdb_db_rename (bctx_table_t *table,
+ const char *oldpath,
+ const char *newpath)
+{
+ DB_ENV *dbenv = NULL;
+ int32_t ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("bctx", table, out);
+ GF_VALIDATE_OR_GOTO ("bctx", oldpath, out);
+ GF_VALIDATE_OR_GOTO ("bctx", newpath, out);
+
+ dbenv = table->dbenv;
+ GF_VALIDATE_OR_GOTO ("bctx", dbenv, out);
+
+ LOCK (&table->lock);
+ {
+ ret = dbenv->dbrename (dbenv, NULL, oldpath, NULL, newpath, 0);
+
+ if (ret != 0) {
+ gf_log ("bctx",
+ GF_LOG_ERROR,
+ "failed to rename %s to %s: %s",
+ oldpath, newpath, db_strerror (ret));
+ } else {
+ gf_log ("bctx",
+ GF_LOG_DEBUG,
+ "successfully renamed %s to %s: %s",
+ oldpath, newpath, db_strerror (ret));
+ }
+ }
+ UNLOCK (&table->lock);
+
+out:
+ return ret;
+}
+
+bctx_t *
+bctx_rename (bctx_t *bctx,
+ const char *db_newpath)
+{
+ bctx_table_t *table = NULL;
+ int32_t ret = -1;
+
+ table = bctx->table;
+
+ LOCK (&table->lock);
+ {
+ __unhash_bctx (bctx);
+ list_del_init (&bctx->list);
+ if (bctx->dbp) {
+ ret = bctx->dbp->close (bctx->dbp, 0);
+ if (ret != 0) {
+ gf_log ("bdb-ll",
+ GF_LOG_ERROR,
+ "failed to close db for directory %s (%s)",
+ bctx->directory, db_strerror (ret));
+ }
+ bctx->dbp = NULL;
+ }
+ }
+ UNLOCK (&table->lock);
+
+ ret = bdb_db_rename (table, bctx->db_path, db_newpath);
+
+ if (ret != 0) {
+ gf_log ("bctx",
+ GF_LOG_ERROR,
+ "bdb_db_rename failed for directory %s",
+ bctx->directory);
+ bctx = NULL;
+ }
+
+ return bctx;
+}