summaryrefslogtreecommitdiffstats
path: root/xlators/storage/posix/src/xattr-cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/storage/posix/src/xattr-cache.c')
-rw-r--r--xlators/storage/posix/src/xattr-cache.c521
1 files changed, 521 insertions, 0 deletions
diff --git a/xlators/storage/posix/src/xattr-cache.c b/xlators/storage/posix/src/xattr-cache.c
new file mode 100644
index 000000000..a39c35ae2
--- /dev/null
+++ b/xlators/storage/posix/src/xattr-cache.c
@@ -0,0 +1,521 @@
+/*
+ Copyright (c) 2009 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 "byte-order.h"
+
+#include "xattr-cache.h"
+#include "posix.h"
+#include "compat-errno.h"
+
+static int
+__hgetxattr (xattr_cache_handle_t *handle, xlator_t *this,
+ const char *key, void *value, size_t len)
+{
+ char * real_path = NULL;
+ struct posix_fd * pfd = NULL;
+ uint64_t tmp_pfd = 0;
+ int op_ret = -1;
+ int ret = -1;
+ int _fd = -1;
+
+ if (handle->loc.path) {
+ MAKE_REAL_PATH (real_path, this, handle->loc.path);
+ op_ret = lgetxattr (real_path, key, value, len);
+
+ if (op_ret == -1)
+ op_ret = -errno;
+ } else {
+ ret = fd_ctx_get (handle->fd, this, &tmp_pfd);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "failed to get pfd from fd=%p",
+ handle->fd);
+ op_ret = -EBADFD;
+ goto out;
+ }
+ pfd = (struct posix_fd *)(long)tmp_pfd;
+ _fd = pfd->fd;
+
+ op_ret = fgetxattr (_fd, key, value, len);
+ if (op_ret == -1)
+ op_ret = -errno;
+ }
+
+out:
+ return op_ret;
+}
+
+
+static int
+__hsetxattr (xattr_cache_handle_t *handle, xlator_t *this,
+ const char *key, void *value, size_t len, int flags)
+{
+ char * real_path = NULL;
+ struct posix_fd * pfd = NULL;
+ uint64_t tmp_pfd = 0;
+ int op_ret = -1;
+ int ret = -1;
+ int _fd = -1;
+
+ if (handle->loc.path) {
+ MAKE_REAL_PATH (real_path, this, handle->loc.path);
+
+ op_ret = lsetxattr (real_path, key, value, len, flags);
+ if (op_ret == -1)
+ op_ret = -errno;
+ } else {
+ ret = fd_ctx_get (handle->fd, this, &tmp_pfd);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "failed to get pfd from fd=%p",
+ handle->fd);
+
+ op_ret = -EBADFD;
+ goto out;
+ }
+ pfd = (struct posix_fd *)(long)tmp_pfd;
+
+ _fd = pfd->fd;
+
+ op_ret = fsetxattr (_fd, key, value, len, flags);
+ if (op_ret == -1)
+ op_ret = -errno;
+ }
+
+out:
+ return op_ret;
+}
+
+
+static xattr_cache_entry_t *
+__cache_lookup (xattr_cache_t *cache, inode_t *inode, char *key)
+{
+ int i = 0;
+
+ for (i = 0; i < cache->size; i++) {
+ if ((cache->entries[i]->inode == inode)
+ && (!strcmp (cache->entries[i]->key, key))) {
+ cache->entries[i]->nraccess++;
+ return cache->entries[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static xattr_cache_entry_t *
+__cache_least_used_entry (xattr_cache_t *cache)
+{
+ xattr_cache_entry_t *lue = cache->entries[0];
+ int i;
+
+ for (i = 0; i < cache->size; i++) {
+ if (cache->entries[i]->nraccess < lue->nraccess)
+ lue = cache->entries[i];
+ }
+
+ lue->nraccess++;
+ return lue;
+}
+
+
+static inode_t *
+__inode_for_handle (xattr_cache_handle_t *handle)
+{
+ inode_t *inode = NULL;
+
+ if (handle->loc.path)
+ inode = handle->loc.inode;
+ else if (handle->fd)
+ inode = handle->fd->inode;
+
+ return inode;
+}
+
+
+static void
+__free_handle (xattr_cache_handle_t *handle)
+{
+ if (handle->loc.path)
+ loc_wipe (&handle->loc);
+
+ FREE (handle);
+}
+
+
+static xattr_cache_handle_t *
+__copy_handle (xattr_cache_handle_t *handle)
+{
+ xattr_cache_handle_t *hnew = calloc (1, sizeof (xattr_cache_handle_t));
+
+ if (handle->loc.path)
+ loc_copy (&hnew->loc, &handle->loc);
+ else
+ hnew->fd = handle->fd;
+
+ return hnew;
+}
+
+
+static int
+__cache_populate_entry (xattr_cache_entry_t *entry, xlator_t *this,
+ xattr_cache_handle_t *handle, char *key, size_t len)
+{
+ int op_ret = -1;
+
+ entry->array = calloc (1, len);
+ if (!entry->array) {
+ op_ret = -ENOMEM;
+ goto out;
+ }
+
+ op_ret = __hgetxattr (handle, this, key, entry->array, len);
+
+ entry->key = strdup (key);
+ entry->inode = __inode_for_handle (handle);
+ entry->handle = __copy_handle (handle);
+ entry->len = len;
+ entry->nraccess = 1;
+
+out:
+ return op_ret;
+}
+
+
+static int
+__cache_flush_entry (xattr_cache_entry_t *entry, xlator_t *this)
+{
+ int ret = -1;
+
+ if (entry->dirty) {
+ ret = __hsetxattr (entry->handle, this,
+ entry->key, entry->array, entry->len, 0);
+ }
+
+ entry->len = 0;
+ entry->nraccess = 0;
+ entry->dirty = 0;
+ entry->inode = NULL;
+
+ if (entry->key) {
+ FREE (entry->key);
+ entry->key = NULL;
+ }
+
+ if (entry->array) {
+ FREE (entry->array);
+ entry->array = NULL;
+ }
+
+ if (entry->handle) {
+ __free_handle (entry->handle);
+ entry->handle = NULL;
+ }
+
+ return 0;
+}
+
+
+static void
+__print_array (char *str, xlator_t *this, int32_t *array, size_t len)
+{
+ char *ptr = NULL;
+ char *buf = NULL;
+
+ int i, count = -1;
+
+ count = len / sizeof (int32_t);
+
+ /* 10 digits per entry + 1 space + '[' and ']' */
+ buf = malloc (count * 11 + 8);
+
+ ptr = buf;
+ ptr += sprintf (ptr, "[ ");
+ for (i = 0; i < count; i++)
+ ptr += sprintf (ptr, "%d ", ntoh32 (array[i]));
+ ptr += sprintf (ptr, "]");
+
+ gf_log (this->name, GF_LOG_DEBUG,
+ "%s%s", str, buf);
+
+ FREE (buf);
+}
+
+
+int
+posix_xattr_cache_read (xlator_t *this, xattr_cache_handle_t *handle,
+ char *key, int32_t *array, size_t len)
+{
+ xattr_cache_entry_t *entry = NULL;
+ xattr_cache_entry_t *purgee = NULL;
+
+ xattr_cache_t *cache = NULL;
+ inode_t *inode = NULL;
+
+ int op_ret = -1;
+
+ inode = __inode_for_handle (handle);
+
+ if (!inode) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "handle has no inode!");
+ goto out;
+ }
+
+ cache = ((struct posix_private *) (this->private))->xattr_cache;
+
+ pthread_mutex_lock (&cache->lock);
+ {
+ entry = __cache_lookup (cache, inode, key);
+
+ if (entry) {
+ if (handle->loc.path)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "cache hit for %s", handle->loc.path);
+ else if (handle->fd)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "cache hit for fd=%p", handle->fd);
+ }
+
+ if (!entry) {
+ purgee = __cache_least_used_entry (cache);
+
+ if (purgee->handle && purgee->handle->loc.path)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "flushing and purging entry for %s",
+ purgee->handle->loc.path);
+ else if (purgee->handle && purgee->handle->fd)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "flushing and purging entry for fd=%p",
+ purgee->handle->fd);
+ __cache_flush_entry (purgee, this);
+
+ if (handle->loc.path)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "populating entry for %s",
+ handle->loc.path);
+ else if (handle->fd)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "populating entry for fd=%p",
+ handle->fd);
+ __cache_populate_entry (purgee, this, handle, key, len);
+
+ entry = purgee;
+ }
+
+ memcpy (array, entry->array, len);
+
+ __print_array ("read array: ", this, array, len);
+ }
+ pthread_mutex_unlock (&cache->lock);
+
+ op_ret = 0;
+out:
+ return op_ret;
+}
+
+
+int posix_xattr_cache_write (xlator_t *this, xattr_cache_handle_t *handle,
+ char *key, int32_t *array, size_t len)
+{
+ xattr_cache_t * cache = NULL;
+ xattr_cache_entry_t * entry = NULL;
+
+ inode_t *inode = NULL;
+
+ int op_ret = -1;
+
+ inode = __inode_for_handle (handle);
+
+ if (!inode) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "handle has no inode!");
+ goto out;
+ }
+
+ cache = ((struct posix_private *) (this->private))->xattr_cache;
+
+ pthread_mutex_lock (&cache->lock);
+ {
+ entry = __cache_lookup (cache, inode, key);
+
+ if (entry) {
+ entry->dirty = 1;
+ memcpy (entry->array, array, len);
+ } else {
+ /*
+ * This case shouldn't usually happen, since the
+ * entry should have been brought into the cache
+ * by the previous read (xattrop always does a read &
+ * write).
+ *
+ * If we've reached here, it means things are happening
+ * very quickly and the entry was flushed after read
+ * but before this write. In that case, let's just
+ * write this to disk
+ */
+
+ op_ret = __hsetxattr (handle, this, key, array,
+ len, 0);
+ }
+
+ __print_array ("wrote array: ", this, array, len);
+ }
+ pthread_mutex_unlock (&cache->lock);
+
+ op_ret = 0;
+out:
+ return op_ret;
+}
+
+
+int posix_xattr_cache_flush (xlator_t *this, xattr_cache_handle_t *handle)
+{
+ xattr_cache_t *cache = NULL;
+ xattr_cache_entry_t *entry = NULL;
+
+ int i;
+ inode_t *inode = NULL;
+
+ int op_ret = -1;
+
+ inode = __inode_for_handle (handle);
+ if (!inode) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "handle has no inode!");
+ op_ret = -EINVAL;
+ goto out;
+ }
+
+ cache = ((struct posix_private *) (this->private))->xattr_cache;
+
+ pthread_mutex_lock (&cache->lock);
+ {
+ for (i = 0; i < cache->size; i++) {
+ entry = cache->entries[i];
+
+ if (entry->inode == inode) {
+ if (entry->handle->loc.path)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "force flushing entry for %s",
+ entry->handle->loc.path);
+
+ else if (cache->entries[i]->handle->fd)
+ gf_log (this->name, GF_LOG_DEBUG,
+ "force flushing entry for fd=%p",
+ entry->handle->fd);
+
+ __cache_flush_entry (entry, this);
+ }
+ }
+ }
+ pthread_mutex_unlock (&cache->lock);
+
+ op_ret = 0;
+out:
+ return op_ret;
+}
+
+
+int
+posix_xattr_cache_flush_all (xlator_t *this)
+{
+ xattr_cache_t *cache = NULL;
+ xattr_cache_entry_t *entry = NULL;
+
+ int i;
+ int op_ret = 0;
+
+ cache = ((struct posix_private *) (this->private))->xattr_cache;
+
+ pthread_mutex_lock (&cache->lock);
+ {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "flushing entire xattr cache: ");
+
+ for (i = 0; i < cache->size; i++) {
+ entry = cache->entries[i];
+
+ if (!entry || !entry->handle)
+ continue;
+
+ if (entry->handle->loc.path)
+ gf_log (this->name, GF_LOG_DEBUG,
+ " force flushing entry for %s",
+ entry->handle->loc.path);
+
+ else if (cache->entries[i]->handle->fd)
+ gf_log (this->name, GF_LOG_DEBUG,
+ " force flushing entry for fd=%p",
+ entry->handle->fd);
+
+ __cache_flush_entry (entry, this);
+ }
+ }
+ pthread_mutex_unlock (&cache->lock);
+
+ return op_ret;
+}
+
+
+xattr_cache_t *
+posix_xattr_cache_init (size_t size)
+{
+ int i = 0;
+ xattr_cache_t * cache = NULL;
+ int op_ret = -1;
+
+ cache = CALLOC (1, sizeof (xattr_cache_t));
+ if (!cache) {
+ goto out;
+ }
+
+ cache->entries = CALLOC (size, sizeof (xattr_cache_entry_t *));
+ if (!cache->entries)
+ goto out;
+
+ cache->size = size;
+
+ for (i = 0; i < size; i++) {
+ cache->entries[i] = calloc (1, sizeof (xattr_cache_entry_t));
+ if (!cache->entries[i])
+ goto out;
+ }
+
+ pthread_mutex_init (&cache->lock, NULL);
+
+ op_ret = 0;
+out:
+ if (op_ret == -1) {
+ if (cache) {
+ if (cache->entries) {
+ for (i = 0; i < size; i++)
+ if (cache->entries[i])
+ FREE (cache->entries[i]);
+
+ FREE (cache->entries);
+ }
+
+ FREE (cache);
+ }
+ }
+
+ return cache;
+}