diff options
Diffstat (limited to 'xlators/features/upcall/src/upcall-internal.c')
-rw-r--r-- | xlators/features/upcall/src/upcall-internal.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/xlators/features/upcall/src/upcall-internal.c b/xlators/features/upcall/src/upcall-internal.c new file mode 100644 index 00000000000..26473e2a7bd --- /dev/null +++ b/xlators/features/upcall/src/upcall-internal.c @@ -0,0 +1,424 @@ +/* + Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include <unistd.h> +#include <fcntl.h> +#include <limits.h> + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glusterfs.h" +#include "compat.h" +#include "xlator.h" +#include "inode.h" +#include "logging.h" +#include "common-utils.h" + +#include "statedump.h" +#include "syncop.h" + +#include "upcall.h" +#include "upcall-mem-types.h" +#include "glusterfs3-xdr.h" +#include "protocol-common.h" +#include "defaults.h" + +/* + * Allocate and add a new client entry to the given upcall entry + */ +upcall_client_t* +add_upcall_client (call_frame_t *frame, uuid_t gfid, + client_t *client, + upcall_inode_ctx_t *up_inode_ctx) +{ + upcall_client_t *up_client_entry = NULL; + + pthread_mutex_lock (&up_inode_ctx->client_list_lock); + { + up_client_entry = __add_upcall_client (frame, + gfid, + client, + up_inode_ctx); + } + pthread_mutex_unlock (&up_inode_ctx->client_list_lock); + + return up_client_entry; +} + +upcall_client_t* +__add_upcall_client (call_frame_t *frame, uuid_t gfid, + client_t *client, + upcall_inode_ctx_t *up_inode_ctx) +{ + upcall_client_t *up_client_entry = NULL; + + up_client_entry = GF_CALLOC (1, sizeof(*up_client_entry), + gf_upcall_mt_upcall_client_entry_t); + if (!up_client_entry) { + gf_msg ("upcall", GF_LOG_WARNING, 0, + UPCALL_MSG_NO_MEMORY, + "Memory allocation failed"); + return NULL; + } + INIT_LIST_HEAD (&up_client_entry->client_list); + up_client_entry->client_uid = gf_strdup(client->client_uid); + up_client_entry->access_time = time(NULL); + up_client_entry->expire_time_attr = CACHE_INVALIDATION_PERIOD; + + list_add_tail (&up_client_entry->client_list, + &up_inode_ctx->client_list); + + gf_log (THIS->name, GF_LOG_DEBUG, "upcall_entry_t client added - %s", + up_client_entry->client_uid); + + return up_client_entry; +} + +/* + * Given gfid and client->uid, retrieve the corresponding upcall client entry. + * If none found, create a new entry. + */ +upcall_client_t* +__get_upcall_client (call_frame_t *frame, uuid_t gfid, client_t *client, + upcall_inode_ctx_t *up_inode_ctx) +{ + upcall_client_t *up_client_entry = NULL; + upcall_client_t *up_client = NULL; + upcall_client_t *tmp = NULL; + gf_boolean_t found_client = _gf_false; + + list_for_each_entry_safe (up_client_entry, tmp, + &up_inode_ctx->client_list, + client_list) { + if (strcmp(client->client_uid, + up_client_entry->client_uid) == 0) { + /* found client entry. Update the access_time */ + up_client_entry->access_time = time(NULL); + found_client = _gf_true; + gf_log (THIS->name, GF_LOG_DEBUG, + "upcall_entry_t client found - %s", + up_client_entry->client_uid); + break; + } + } + + if (!found_client) { /* create one */ + up_client_entry = __add_upcall_client (frame, gfid, client, + up_inode_ctx); + } + + return up_client_entry; +} + +int +__upcall_inode_ctx_set (inode_t *inode, xlator_t *this) +{ + upcall_inode_ctx_t *inode_ctx = NULL; + int ret = -1; + uint64_t ctx = 0; + + ret = __inode_ctx_get (inode, this, &ctx); + + if (!ret) + goto out; + + inode_ctx = GF_CALLOC (1, sizeof (upcall_inode_ctx_t), + gf_upcall_mt_upcall_inode_ctx_t); + + if (!inode_ctx) { + ret = -ENOMEM; + goto out; + } + + pthread_mutex_init (&inode_ctx->client_list_lock, NULL); + INIT_LIST_HEAD (&inode_ctx->client_list); + + ret = __inode_ctx_set (inode, this, (uint64_t *) inode_ctx); + if (ret) + gf_log (this->name, GF_LOG_DEBUG, + "failed to set inode ctx (%p)", inode); +out: + return ret; +} + +upcall_inode_ctx_t * +__upcall_inode_ctx_get (inode_t *inode, xlator_t *this) +{ + upcall_inode_ctx_t *inode_ctx = NULL; + uint64_t ctx = 0; + int ret = 0; + + ret = __inode_ctx_get (inode, this, &ctx); + + if (ret < 0) { + ret = __upcall_inode_ctx_set (inode, this); + if (ret < 0) + goto out; + + ret = __inode_ctx_get (inode, this, &ctx); + if (ret < 0) + goto out; + } + + inode_ctx = (upcall_inode_ctx_t *)(long) ctx; + +out: + return inode_ctx; +} + +upcall_inode_ctx_t * +upcall_inode_ctx_get (inode_t *inode, xlator_t *this) +{ + upcall_inode_ctx_t *inode_ctx = NULL; + + LOCK (&inode->lock); + { + inode_ctx = __upcall_inode_ctx_get (inode, this); + } + UNLOCK (&inode->lock); + + return inode_ctx; +} + +int +__upcall_cleanup_client_entry (upcall_client_t *up_client) +{ + list_del_init (&up_client->client_list); + + GF_FREE (up_client->client_uid); + GF_FREE (up_client); + + return 0; +} + +/* + * Free Upcall inode_ctx client list + */ +int +__upcall_cleanup_inode_ctx_client_list (upcall_inode_ctx_t *inode_ctx) +{ + upcall_client_t *up_client = NULL; + upcall_client_t *tmp = NULL; + + list_for_each_entry_safe (up_client, tmp, + &inode_ctx->client_list, + client_list) { + __upcall_cleanup_client_entry (up_client); + } + + return 0; +} + +/* + * Free upcall_inode_ctx + */ +int +upcall_cleanup_inode_ctx (xlator_t *this, inode_t *inode) +{ + uint64_t ctx = 0; + upcall_inode_ctx_t *inode_ctx = NULL; + int ret = 0; + + ret = inode_ctx_get (inode, this, &ctx); + + if (ret < 0) { + gf_log (THIS->name, GF_LOG_TRACE, + "Failed to get upcall_inode_ctx (%p)", + inode); + goto out; + } + + /* Invalidate all the upcall cache entries */ + upcall_cache_forget (this, inode, inode_ctx); + + /* Set inode context to NULL */ + ret = __inode_ctx_set (inode, this, NULL); + + if (!ret) { + gf_log (this->name, GF_LOG_WARNING, + "_inode_ctx_set to NULL failed (%p)", + inode); + } + inode_ctx = (upcall_inode_ctx_t *)(long) ctx; + + if (inode_ctx) { + /* do we really need lock? */ + pthread_mutex_lock (&inode_ctx->client_list_lock); + { + if (!list_empty (&inode_ctx->client_list)) { + __upcall_cleanup_inode_ctx_client_list (inode_ctx); + } + } + pthread_mutex_unlock (&inode_ctx->client_list_lock); + + pthread_mutex_destroy (&inode_ctx->client_list_lock); + + GF_FREE (inode_ctx); + } + +out: + return ret; +} + +/* + * Given a gfid, client, first fetch upcall_entry_t based on gfid. + * Later traverse through the client list of that upcall entry. If this client + * is not present in the list, create one client entry with this client info. + * Also check if there are other clients which need to be notified of this + * op. If yes send notify calls to them. + * + * Since sending notifications for cache_invalidation is a best effort, + * any errors during the process are logged and ignored. + */ +void +upcall_cache_invalidate (call_frame_t *frame, xlator_t *this, client_t *client, + inode_t *inode, uint32_t flags) +{ + upcall_client_t *up_client = NULL; + upcall_client_t *up_client_entry = NULL; + upcall_client_t *tmp = NULL; + upcall_inode_ctx_t *up_inode_ctx = NULL; + gf_boolean_t found = _gf_false; + + up_inode_ctx = ((upcall_local_t *)frame->local)->upcall_inode_ctx; + + if (!up_inode_ctx) + up_inode_ctx = upcall_inode_ctx_get (inode, this); + + if (!up_inode_ctx) { + gf_log (this->name, GF_LOG_WARNING, + "upcall_inode_ctx_get failed (%p)", + inode); + return; + } + + pthread_mutex_lock (&up_inode_ctx->client_list_lock); + { + list_for_each_entry_safe (up_client_entry, tmp, + &up_inode_ctx->client_list, + client_list) { + + if (!strcmp(client->client_uid, + up_client_entry->client_uid)) { + up_client_entry->access_time = time(NULL); + found = _gf_true; + } + + /* + * Ignore sending notifications in case of only UP_ATIME + */ + if (!(flags & ~(UP_ATIME))) { + if (found) + break; + else /* we still need to find current client entry*/ + continue; + } + + /* any other client */ + + /* XXX: Send notifications asynchrounously + * instead of in the I/O path - BZ 1200264 + * Also if the file is frequently accessed, set + * expire_time_attr to 0. + */ + upcall_client_cache_invalidate(this, + inode->gfid, + up_client_entry, + flags); + } + + if (!found) { + up_client_entry = __add_upcall_client (frame, + inode->gfid, + client, + up_inode_ctx); + } + } + pthread_mutex_unlock (&up_inode_ctx->client_list_lock); +} + +/* + * If the upcall_client_t has recently accessed the file (i.e, within + * CACHE_INVALIDATION_PERIOD), send a upcall notification. + */ +void +upcall_client_cache_invalidate (xlator_t *this, uuid_t gfid, + upcall_client_t *up_client_entry, + uint32_t flags) +{ + notify_event_data_t n_event_data; + time_t t_expired = time(NULL) - up_client_entry->access_time; + + if (t_expired < CACHE_INVALIDATION_PERIOD) { + /* Send notify call */ + uuid_copy(n_event_data.gfid, gfid); + n_event_data.client_entry = up_client_entry; + n_event_data.event_type = CACHE_INVALIDATION; + n_event_data.invalidate_flags = flags; + + /* Need to send inode flags */ + this->notify (this, GF_EVENT_UPCALL, &n_event_data); + + gf_log (THIS->name, GF_LOG_TRACE, + "Cache invalidation notification sent to %s", + up_client_entry->client_uid); + + } else { + if (t_expired > (2*CACHE_INVALIDATION_PERIOD)) { + /* Cleanup the entry */ + __upcall_cleanup_client_entry (up_client_entry); + } + + gf_log (THIS->name, GF_LOG_TRACE, + "Cache invalidation notification NOT sent to %s", + up_client_entry->client_uid); + } +} + +/* + * This is called during upcall_inode_ctx cleanup incase of 'inode_forget'. + * Send "UP_FORGET" to all the clients so that they invalidate their cache + * entry and do a fresh lookup next time when any I/O comes in. + */ +void +upcall_cache_forget (xlator_t *this, inode_t *inode, upcall_inode_ctx_t *up_inode_ctx) +{ + upcall_client_t *up_client = NULL; + upcall_client_t *up_client_entry = NULL; + upcall_client_t *tmp = NULL; + uint32_t flags = 0; + + if (!up_inode_ctx) { + return; + } + + pthread_mutex_lock (&up_inode_ctx->client_list_lock); + { + list_for_each_entry_safe (up_client_entry, tmp, + &up_inode_ctx->client_list, + client_list) { + flags = UP_FORGET; + + /* Set the access time to time(NULL) + * to send notify */ + up_client_entry->access_time = time(NULL); + + upcall_client_cache_invalidate(this, + inode->gfid, + up_client_entry, + flags); + } + + } + pthread_mutex_unlock (&up_inode_ctx->client_list_lock); +} |