summaryrefslogtreecommitdiffstats
path: root/xlators/features/upcall/src/upcall-internal.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/features/upcall/src/upcall-internal.c')
-rw-r--r--xlators/features/upcall/src/upcall-internal.c424
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);
+}