summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac4
-rw-r--r--glusterfs.spec.in1
-rw-r--r--libglusterfs/src/glfs-message-id.h7
-rw-r--r--libglusterfs/src/mem-types.h3
-rw-r--r--xlators/features/Makefile.am2
-rw-r--r--xlators/features/leases/Makefile.am3
-rw-r--r--xlators/features/leases/src/Makefile.am17
-rw-r--r--xlators/features/leases/src/leases-internal.c1333
-rw-r--r--xlators/features/leases/src/leases-mem-types.h27
-rw-r--r--xlators/features/leases/src/leases-messages.h129
-rw-r--r--xlators/features/leases/src/leases.c1134
-rw-r--r--xlators/features/leases/src/leases.h239
12 files changed, 2895 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac
index 21707737873..369cd2a3e9a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-dnl Copyright (c) 2006-2012 Red Hat, Inc. <http://www.redhat.com>
+dnl Copyright (c) 2006-2016 Red Hat, Inc. <http://www.redhat.com>
dnl This file is part of GlusterFS.
dnl
dnl This file is licensed to you under your choice of the GNU Lesser
@@ -171,6 +171,8 @@ AC_CONFIG_FILES([Makefile
xlators/features/bit-rot/src/Makefile
xlators/features/bit-rot/src/stub/Makefile
xlators/features/bit-rot/src/bitd/Makefile
+ xlators/features/leases/Makefile
+ xlators/features/leases/src/Makefile
xlators/playground/Makefile
xlators/playground/template/Makefile
xlators/playground/template/src/Makefile
diff --git a/glusterfs.spec.in b/glusterfs.spec.in
index 6332ef2d8f1..e0d1da143d4 100644
--- a/glusterfs.spec.in
+++ b/glusterfs.spec.in
@@ -1093,6 +1093,7 @@ exit 0
%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/quota*
%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/trash.so
%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/upcall.so
+%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/features/leases.so
%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/mgmt*
%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/nfs*
%{_libdir}/glusterfs/%{version}%{?prereltag}/xlator/protocol/server*
diff --git a/libglusterfs/src/glfs-message-id.h b/libglusterfs/src/glfs-message-id.h
index d772dd37983..de7c494d326 100644
--- a/libglusterfs/src/glfs-message-id.h
+++ b/libglusterfs/src/glfs-message-id.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2015 Red Hat, Inc. <http://www.redhat.com>
+ Copyright (c) 2015-2016 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
@@ -174,6 +174,11 @@ GLFS_MSGID_COMP_SYMLINK_CACHE_END
#define GLFS_MSGID_COMP_DC GLFS_MSGID_COMP_PL_END
#define GLFS_MSGID_COMP_DC_END (GLFS_MSGID_COMP_PL_END+\
GLFS_MSGID_SEGMENT)
+
+#define GLFS_MSGID_COMP_LEASES GLFS_MSGID_COMP_DC_END
+#define GLFS_MSGID_COMP_LEASES_END (GLFS_MSGID_COMP_LEASES +\
+ GLFS_MSGID_SEGMENT)
+
/* --- new segments for messages goes above this line --- */
#endif /* !_GLFS_MESSAGE_ID_H_ */
diff --git a/libglusterfs/src/mem-types.h b/libglusterfs/src/mem-types.h
index e185a49ec21..09d096b83f5 100644
--- a/libglusterfs/src/mem-types.h
+++ b/libglusterfs/src/mem-types.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
+ Copyright (c) 2008-2016 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
@@ -164,6 +164,7 @@ enum gf_common_mem_types_ {
/*used for compound fops*/
gf_mt_compound_req_t,
gf_mt_compound_rsp_t,
+ gf_common_mt_tw_timer_list,
gf_common_mt_end
};
#endif
diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am
index 3543ce7a465..c63eb75a7c3 100644
--- a/xlators/features/Makefile.am
+++ b/xlators/features/Makefile.am
@@ -1,6 +1,6 @@
SUBDIRS = locks quota read-only mac-compat quiesce marker index barrier \
arbiter protect compress changelog changetimerecorder ganesha \
gfid-access $(GLUPY_SUBDIR) upcall snapview-client snapview-server \
- trash shard bit-rot
+ trash shard bit-rot leases
CLEANFILES =
diff --git a/xlators/features/leases/Makefile.am b/xlators/features/leases/Makefile.am
new file mode 100644
index 00000000000..a985f42a877
--- /dev/null
+++ b/xlators/features/leases/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES =
diff --git a/xlators/features/leases/src/Makefile.am b/xlators/features/leases/src/Makefile.am
new file mode 100644
index 00000000000..343f5c82425
--- /dev/null
+++ b/xlators/features/leases/src/Makefile.am
@@ -0,0 +1,17 @@
+xlator_LTLIBRARIES = leases.la
+xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features
+
+leases_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS)
+
+leases_la_SOURCES = leases.c leases-internal.c
+
+leases_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la
+
+noinst_HEADERS = leases.h leases-mem-types.h leases-messages.h
+
+AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \
+ -I$(CONTRIBDIR)/timer-wheel
+
+AM_CFLAGS = -Wall $(GF_CFLAGS)
+
+CLEANFILES =
diff --git a/xlators/features/leases/src/leases-internal.c b/xlators/features/leases/src/leases-internal.c
new file mode 100644
index 00000000000..8ed0abcae82
--- /dev/null
+++ b/xlators/features/leases/src/leases-internal.c
@@ -0,0 +1,1333 @@
+/*
+ Copyright (c) 2015-2016 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.
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "leases.h"
+
+
+/* Mutex locks used in this xlator and their order of acquisition:
+ * Check lease conflict:
+ * lease_ctx lock
+ * add_timer => internal timer locks
+ * lease_ctx unlock
+ *
+ * Add/remove lease:
+ * lease_ctx lock
+ * add_timer => internal timer locks
+ * OR
+ * priv lock => Adding/removing to/from the cleanup client list
+ * priv unlock
+ * lease_ctx unlock
+ *
+ * Timer thread:
+ * Timer internal lock
+ * priv lock => By timer handler
+ * priv unlock
+ * Timer internal unlock
+ *
+ * Expired recall cleanup thread:
+ * priv lock
+ * priv condwait
+ * priv unlock
+ * lease_ctx lock
+ * priv lock
+ * priv unlock
+ * lease_ctx unlock
+ */
+
+/*
+ * Check if lease_lk is enabled
+ * Return Value:
+ * _gf_true - lease lock option enabled
+ * _gf_false - lease lock option disabled
+ */
+gf_boolean_t
+is_leases_enabled (xlator_t *this)
+{
+ leases_private_t *priv = NULL;
+ gf_boolean_t is_enabled = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+
+ if (this->private) {
+ priv = (leases_private_t *)this->private;
+ is_enabled = priv->leases_enabled;
+ }
+out:
+ return is_enabled;
+}
+
+
+/*
+ * Get the recall_leaselk_timeout
+ * Return Value:
+ * timeout value(in seconds) set as an option to this xlator.
+ * -1 error case
+ */
+int32_t
+get_recall_lease_timeout (xlator_t *this)
+{
+ leases_private_t *priv = NULL;
+ int32_t timeout = -1;
+
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+
+ if (this->private) {
+ priv = (leases_private_t *)this->private;
+ timeout = priv->recall_lease_timeout;
+ }
+out:
+ return timeout;
+}
+
+
+static void
+__dump_leases_info (xlator_t *this, lease_inode_ctx_t *lease_ctx)
+{
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+
+ gf_msg_debug (this->name, 0, "Lease held on this inode, lease_type: %d,"
+ " lease_cnt:%"PRIu64", RD lease:%d, RW lease:%d, "
+ "openfd cnt:%"PRIu64, lease_ctx->lease_type,
+ lease_ctx->lease_cnt,
+ lease_ctx->lease_type_cnt[GF_RD_LEASE],
+ lease_ctx->lease_type_cnt[GF_RW_LEASE],
+ lease_ctx->openfd_cnt);
+
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+ gf_msg_debug (this->name, 0, "Leases held by client: %s, lease "
+ "ID:%s, RD lease:%d, RW lease:%d, lease_type: %d, "
+ "lease_cnt:%"PRIu64, lease_entry->client_uid,
+ lease_entry->lease_id,
+ lease_entry->lease_type_cnt[GF_RD_LEASE],
+ lease_entry->lease_type_cnt[GF_RW_LEASE],
+ lease_entry->lease_type, lease_entry->lease_cnt);
+ }
+out:
+ return;
+}
+
+
+static int
+__lease_ctx_set (inode_t *inode, xlator_t *this)
+{
+ lease_inode_ctx_t *inode_ctx = NULL;
+ int ret = -1;
+ uint64_t ctx = 0;
+
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+
+ ret = __inode_ctx_get (inode, this, &ctx);
+ if (!ret) {
+ gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_INVAL_INODE_CTX,
+ "inode_ctx_get failed");
+ goto out;
+ }
+
+ inode_ctx = GF_CALLOC (1, sizeof (*inode_ctx),
+ gf_leases_mt_lease_inode_ctx_t);
+ GF_CHECK_ALLOC (inode_ctx, ret, out);
+
+ pthread_mutex_init (&inode_ctx->lock, NULL);
+ INIT_LIST_HEAD (&inode_ctx->lease_id_list);
+ INIT_LIST_HEAD (&inode_ctx->blocked_list);
+
+ inode_ctx->lease_cnt = 0;
+
+ ret = __inode_ctx_set (inode, this, (uint64_t *) inode_ctx);
+ if (ret) {
+ GF_FREE (inode_ctx);
+ gf_msg (this->name, GF_LOG_INFO, 0, LEASE_MSG_INVAL_INODE_CTX,
+ "failed to set inode ctx (%p)", inode);
+ }
+out:
+ return ret;
+}
+
+
+static lease_inode_ctx_t *
+__lease_ctx_get (inode_t *inode, xlator_t *this)
+{
+ lease_inode_ctx_t *inode_ctx = NULL;
+ uint64_t ctx = 0;
+ int ret = 0;
+
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+
+ ret = __inode_ctx_get (inode, this, &ctx);
+ if (ret < 0) {
+ ret = __lease_ctx_set (inode, this);
+ if (ret < 0)
+ goto out;
+
+ ret = __inode_ctx_get (inode, this, &ctx);
+ if (ret < 0) {
+ gf_msg (this->name, GF_LOG_WARNING, 0, LEASE_MSG_INVAL_INODE_CTX,
+ "failed to get inode ctx (%p)", inode);
+ goto out;
+ }
+ }
+
+ inode_ctx = (lease_inode_ctx_t *)(long) ctx;
+out:
+ return inode_ctx;
+}
+
+
+lease_inode_ctx_t *
+lease_ctx_get (inode_t *inode, xlator_t *this)
+{
+ lease_inode_ctx_t *inode_ctx = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+
+ LOCK (&inode->lock);
+ {
+ inode_ctx = __lease_ctx_get (inode, this);
+ }
+ UNLOCK (&inode->lock);
+out:
+ return inode_ctx;
+}
+
+
+static lease_id_entry_t *
+new_lease_id_entry (call_frame_t *frame, const char *lease_id)
+{
+ lease_id_entry_t *lease_entry = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", frame, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_id, out);
+
+ lease_entry = GF_CALLOC (1, sizeof (*lease_entry),
+ gf_leases_mt_lease_id_entry_t);
+ if (!lease_entry) {
+ gf_msg (frame->this->name, GF_LOG_ERROR, ENOMEM, LEASE_MSG_NO_MEM,
+ "Memory allocation for lease_entry failed");
+ return NULL;
+ }
+
+ INIT_LIST_HEAD (&lease_entry->lease_id_list);
+ lease_entry->lease_type = NONE;
+ lease_entry->lease_cnt = 0;
+ lease_entry->recall_time =
+ get_recall_lease_timeout (frame->this);
+ lease_entry->client_uid = gf_strdup (frame->root->client->client_uid);
+ if (!lease_entry->client_uid) {
+ gf_msg (frame->this->name, GF_LOG_ERROR, ENOMEM, LEASE_MSG_NO_MEM,
+ "Memory allocation for client_uid failed");
+ GF_FREE (lease_entry);
+ lease_entry = NULL;
+ goto out;
+ }
+
+ memcpy (lease_entry->lease_id, lease_id, LEASE_ID_SIZE);
+out:
+ return lease_entry;
+}
+
+
+static void
+__destroy_lease_id_entry (lease_id_entry_t *lease_entry)
+{
+ GF_VALIDATE_OR_GOTO ("leases", lease_entry, out);
+
+ list_del_init (&lease_entry->lease_id_list);
+ GF_FREE (lease_entry->client_uid);
+ GF_FREE (lease_entry);
+out:
+ return;
+}
+
+
+static inline gf_boolean_t
+__is_same_lease_id (const char *k1, const char *k2)
+{
+ if (memcmp(k1, k2, LEASE_ID_SIZE) == 0)
+ return _gf_true;
+
+ return _gf_false;
+}
+
+
+/* Checks if there are any leases, other than the leases taken
+ * by the given lease_id
+ */
+static gf_boolean_t
+__another_lease_found (lease_inode_ctx_t *lease_ctx, const char *lease_id)
+{
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+ gf_boolean_t found_lease = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("leases", lease_id, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+
+ if (!__is_same_lease_id (lease_id, lease_entry->lease_id)) {
+ if (lease_entry->lease_cnt > 0) {
+ found_lease = _gf_true;
+ break;
+ }
+ }
+ }
+out:
+ return found_lease;
+}
+
+
+/* Returns the lease_id_entry for a given lease_id and a given inode.
+ * Return values:
+ * NULL - If no client entry found
+ * lease_id_entry_t* - a pointer to the client entry if found
+ */
+static lease_id_entry_t *
+__get_lease_id_entry (lease_inode_ctx_t *lease_ctx, const char *lease_id)
+{
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+ lease_id_entry_t *found = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", lease_id, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+
+ if (__is_same_lease_id (lease_id, lease_entry->lease_id)) {
+ found = lease_entry;
+ gf_msg_debug ("leases", 0, "lease ID entry found "
+ "Client UID:%s, lease id:%s",
+ lease_entry->client_uid,
+ leaseid_utoa (lease_entry->lease_id));
+ break;
+ }
+ }
+out:
+ return found;
+}
+
+
+/* Returns the lease_id_entry for a given lease_id and a given inode,
+ * if none found creates one.
+ * Return values:
+ * lease_id_entry_t* - a pointer to the client entry
+ */
+static lease_id_entry_t *
+__get_or_new_lease_entry (call_frame_t *frame, const char *lease_id,
+ lease_inode_ctx_t *lease_ctx)
+{
+ lease_id_entry_t *lease_entry = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", frame, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_id, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+
+ lease_entry = __get_lease_id_entry (lease_ctx, lease_id);
+ if (!lease_entry) { /* create one */
+ lease_entry = new_lease_id_entry (frame, lease_id);
+ if (!lease_entry)
+ goto out;
+
+ list_add_tail (&lease_entry->lease_id_list,
+ &lease_ctx->lease_id_list);
+
+ gf_msg_debug (frame->this->name, 0, "lease ID entry added,"
+ " Client UID:%s, lease id:%s",
+ lease_entry->client_uid,
+ leaseid_utoa (lease_entry->lease_id));
+ }
+out:
+ return lease_entry;
+}
+
+
+static lease_inode_t *
+new_lease_inode (inode_t *inode)
+{
+ lease_inode_t *l_inode = NULL;
+
+ l_inode = GF_CALLOC (1, sizeof (*l_inode), gf_leases_mt_lease_inode_t);
+ if (!l_inode)
+ goto out;
+
+ INIT_LIST_HEAD (&l_inode->list);
+ l_inode->inode = inode_ref (inode);
+out:
+ return l_inode;
+}
+
+
+static void
+__destroy_lease_inode (lease_inode_t *l_inode)
+{
+ list_del_init (&l_inode->list);
+ inode_unref (l_inode->inode);
+ GF_FREE (l_inode);
+}
+
+
+static lease_client_t *
+new_lease_client (const char *client_uid)
+{
+ lease_client_t *clnt = NULL;
+
+ clnt = GF_CALLOC (1, sizeof (*clnt), gf_leases_mt_lease_client_t);
+ if (!clnt)
+ goto out;
+
+ INIT_LIST_HEAD (&clnt->client_list);
+ INIT_LIST_HEAD (&clnt->inode_list);
+ clnt->client_uid = gf_strdup (client_uid);
+out:
+ return clnt;
+}
+
+
+static void
+__destroy_lease_client (lease_client_t *clnt)
+{
+ list_del_init (&clnt->inode_list);
+ list_del_init (&clnt->client_list);
+ GF_FREE (clnt);
+
+ return;
+}
+
+
+static lease_client_t *
+__get_lease_client (xlator_t *this, leases_private_t *priv,
+ const char *client_uid)
+{
+ lease_client_t *clnt = NULL;
+ lease_client_t *tmp = NULL;
+ lease_client_t *found = NULL;
+
+ list_for_each_entry_safe (clnt, tmp, &priv->client_list, client_list) {
+ if ((strcmp (clnt->client_uid, client_uid) == 0)) {
+ found = clnt;
+ gf_msg_debug (this->name, 0, "Client:%s already found "
+ "in the cleanup list", client_uid);
+ break;
+ }
+ }
+ return found;
+}
+
+
+static lease_client_t *
+__get_or_new_lease_client (xlator_t *this, leases_private_t *priv,
+ const char *client_uid)
+{
+ lease_client_t *found = NULL;
+
+ found = __get_lease_client (this, priv, client_uid);
+ if (!found) {
+ found = new_lease_client (client_uid);
+ if (!found)
+ goto out;
+ list_add_tail (&found->client_list, &priv->client_list);
+ gf_msg_debug (this->name, 0, "Adding a new client:%s entry "
+ "to the cleanup list", client_uid);
+ }
+out:
+ return found;
+}
+
+
+static int
+add_inode_to_client_list (xlator_t *this, inode_t *inode, const char *client_uid)
+{
+ int ret = 0;
+ leases_private_t *priv = NULL;
+ lease_client_t *clnt = NULL;
+ lease_inode_t *lease_inode = NULL;
+
+ priv = this->private;
+ pthread_mutex_lock (&priv->mutex);
+ {
+ clnt = __get_or_new_lease_client (this, priv, client_uid);
+ GF_CHECK_ALLOC (clnt, ret, out);
+
+ lease_inode = new_lease_inode (inode);
+ GF_CHECK_ALLOC (lease_inode, ret, out);
+
+ list_add_tail (&clnt->inode_list, &lease_inode->list);
+ gf_msg_debug (this->name, 0,
+ "Added a new inode:%p to the client(%s) "
+ "cleanup list, gfid(%s)", inode, client_uid,
+ uuid_utoa (inode->gfid));
+ }
+ pthread_mutex_unlock (&priv->mutex);
+out:
+ return ret;
+}
+
+
+/* Add lease entry to the corresponding client entry.
+ * Return values:
+ * 0 Success
+ * -1 Failure
+ */
+static int
+__add_lease (call_frame_t *frame, inode_t *inode, lease_inode_ctx_t *lease_ctx,
+ const char *client_uid, struct gf_lease *lease)
+{
+ lease_id_entry_t *lease_entry = NULL;
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("leases", frame, out);
+ GF_VALIDATE_OR_GOTO ("leases", client_uid, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease, out);
+
+ gf_msg_trace (frame->this->name, 0,
+ "Granting lease lock to client %s with lease id %s"
+ " on gfid(%s)", client_uid, leaseid_utoa (lease->lease_id),
+ uuid_utoa (inode->gfid));
+
+ lease_entry = __get_or_new_lease_entry (frame, lease->lease_id, lease_ctx);
+ if (!lease_entry) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ lease_entry->lease_type_cnt[lease->lease_type]++;
+ lease_entry->lease_cnt++;
+ lease_entry->lease_type |= lease->lease_type;
+ /* If this is the first lease taken by the client on the file, then
+ * add this inode/file to the client disconnect cleanup list
+ */
+ if (lease_entry->lease_cnt == 1) {
+ add_inode_to_client_list (frame->this, inode, client_uid);
+ }
+
+ lease_ctx->lease_cnt++;
+ lease_ctx->lease_type_cnt[lease->lease_type]++;
+ lease_ctx->lease_type |= lease->lease_type;
+
+ /* Take a ref for the first lock taken on this inode. Corresponding
+ * unref when all the leases are unlocked or during DISCONNECT
+ * Ref is required because the inode on which lease is acquired should
+ * not be deleted when lru cleanup kicks in*/
+ if (lease_ctx->lease_cnt == 1) {
+ lease_ctx->inode = inode_ref (inode);
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+
+static gf_boolean_t
+__is_clnt_lease_none (const char *client_uid, lease_inode_ctx_t *lease_ctx)
+{
+ gf_boolean_t lease_none = _gf_true;
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+ if ((strcmp (client_uid, lease_entry->client_uid) == 0)
+ && (lease_entry->lease_cnt != 0)) {
+ lease_none = _gf_false;
+ break;
+ }
+ }
+
+ return lease_none;
+}
+
+static int
+__remove_inode_from_clnt_list (xlator_t *this, lease_client_t *clnt,
+ inode_t *inode)
+{
+ int ret = -1;
+ lease_inode_t *l_inode = NULL;
+ lease_inode_t *tmp1 = NULL;
+
+ list_for_each_entry_safe (l_inode, tmp1,
+ &clnt->inode_list,
+ list) {
+ if (l_inode->inode == inode) {
+ __destroy_lease_inode (l_inode);
+ gf_msg_debug (this->name, 0,
+ "Removed the inode from the client cleanup list");
+ ret = 0;
+ }
+ }
+ /* TODO: Remove the client entry from the cleanup list */
+
+ return ret;
+}
+
+
+static int
+remove_from_clnt_list (xlator_t *this, const char *client_uid, inode_t *inode)
+{
+ leases_private_t *priv = NULL;
+ int ret = -1;
+ lease_client_t *clnt = NULL;
+
+ priv = this->private;
+ if (!priv)
+ goto out;
+
+ pthread_mutex_lock (&priv->mutex);
+ {
+ clnt = __get_lease_client (this, priv, client_uid);
+ if (!clnt) {
+ gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_CLNT_NOTFOUND,
+ "There is no client entry found in the cleanup list");
+ pthread_mutex_unlock (&priv->mutex);
+ goto out;
+ }
+ ret = __remove_inode_from_clnt_list (this, clnt, inode);
+ if (ret) {
+ gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_INODE_NOTFOUND,
+ "There is no inode entry found in the cleanup list");
+ }
+ }
+ pthread_mutex_unlock (&priv->mutex);
+out:
+ return ret;
+}
+
+
+/* Remove lease entry in the corresponding client entry.
+ */
+static int
+__remove_lease (xlator_t *this, inode_t *inode, lease_inode_ctx_t *lease_ctx,
+ const char *client_uid, struct gf_lease *lease)
+{
+ lease_id_entry_t *lease_entry = NULL;
+ int ret = 0;
+ int32_t lease_type = 0;
+ leases_private_t *priv = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease, out);
+
+ priv = this->private;
+
+ gf_msg_trace (this->name, 0, "Removing lease entry for client: %s, "
+ "lease type:%d, lease id:%s", client_uid, lease->lease_type,
+ leaseid_utoa (lease->lease_id));
+
+ lease_entry = __get_lease_id_entry (lease_ctx, lease->lease_id);
+ if (!lease_entry) {
+ gf_msg (this->name, GF_LOG_INFO, 0, LEASE_MSG_INVAL_UNLK_LEASE,
+ "Got unlock lease request from client:%s, but has no "
+ "corresponding lock", client_uid);
+ ret = -EINVAL;
+ errno = EINVAL;
+ goto out;
+ }
+
+ lease_type = lease->lease_type;
+ lease_entry->lease_type_cnt[lease_type]--;
+ lease_entry->lease_cnt--;
+
+ lease_ctx->lease_type_cnt[lease_type]--;
+ lease_ctx->lease_cnt--;
+
+ if (lease_entry->lease_type_cnt[lease_type] == 0)
+ lease_entry->lease_type = lease_entry->lease_type & (~lease_type);
+
+ if (lease_ctx->lease_type_cnt[lease_type] == 0)
+ lease_ctx->lease_type = lease_ctx->lease_type & (~lease_type);
+
+ if (lease_entry->lease_cnt == 0) {
+ if (__is_clnt_lease_none (client_uid, lease_ctx)) {
+ gf_msg_debug (this->name, 0, "Client(%s) has no leases"
+ " on gfid (%s), hence removing the inode"
+ " from the client cleanup list",
+ client_uid, uuid_utoa (inode->gfid));
+ remove_from_clnt_list (this, client_uid, lease_ctx->inode);
+ }
+ __destroy_lease_id_entry (lease_entry);
+ }
+
+ if (lease_ctx->lease_cnt == 0 && lease_ctx->timer) {
+ ret = gf_tw_del_timer (priv->timer_wheel, lease_ctx->timer);
+ lease_ctx->recall_in_progress = _gf_false;
+ }
+out:
+ return ret;
+}
+
+
+static gf_boolean_t
+__is_lease_grantable (xlator_t *this, lease_inode_ctx_t *lease_ctx,
+ struct gf_lease *lease, inode_t *inode)
+{
+ uint32_t fd_count = 0;
+ int32_t flags = 0;
+ fd_t *iter_fd = NULL;
+ gf_boolean_t grant = _gf_false;
+ int ret = 0;
+ lease_fd_ctx_t *fd_ctx = NULL;
+ uint64_t ctx = 0;
+
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease, out);
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+
+ if (lease_ctx->recall_in_progress) {
+ gf_msg_debug (this->name, 0, "Recall in progress, hence "
+ "failing the lease request");
+ grant = _gf_false;
+ goto out;
+ }
+
+ LOCK (&inode->lock);
+ {
+ list_for_each_entry (iter_fd, &inode->fd_list, inode_list) {
+ ret = fd_ctx_get (iter_fd, this, &ctx);
+ if (ret < 0) {
+ grant = _gf_false;
+ UNLOCK (&inode->lock);
+ gf_msg (this->name, GF_LOG_ERROR, 0,
+ LEASE_MSG_INVAL_FD_CTX,
+ "Unable to get fd ctx");
+ goto out;
+ }
+ fd_ctx = (lease_fd_ctx_t *)(long) ctx;
+
+ /* Check for open fd conflict, note that open fds from
+ * the same lease id is not checked for conflict, as it is
+ * lease id based lease.
+ */
+ if (!__is_same_lease_id (fd_ctx->lease_id, lease->lease_id)) {
+ fd_count++;
+ flags |= iter_fd->flags;
+ }
+ }
+ }
+ UNLOCK (&inode->lock);
+
+ gf_msg_debug (this->name, 0, "open fd count:%d flags:%d",
+ fd_count, flags);
+
+ __dump_leases_info (this, lease_ctx);
+
+ switch (lease->lease_type) {
+ case GF_RD_LEASE:
+ /* check open fd conflict */
+ if ((fd_count > 0) && ((flags & O_WRONLY) || (flags & O_RDWR))) {
+ grant = _gf_false;
+ break;
+ }
+
+ /* check for conflict with existing leases */
+ if (lease_ctx->lease_type == NONE || lease_ctx->lease_type == GF_RD_LEASE)
+ grant = _gf_true;
+ else
+ grant = _gf_false;
+ break;
+
+ case GF_RW_LEASE:
+ /* check open fd conflict; conflict if there are any fds open
+ * other than the client on which the lease is requested. */
+ if (fd_count > 0) {
+ grant = _gf_false;
+ break;
+ }
+
+ /* check existing lease conflict */
+ if (lease_ctx->lease_type == NONE ||
+ !(__another_lease_found (lease_ctx, lease->lease_id)))
+ grant = _gf_true;
+ else
+ grant = _gf_false;
+ break;
+
+ default:
+ gf_msg (this->name, GF_LOG_ERROR, EINVAL, LEASE_MSG_INVAL_LEASE_TYPE,
+ "Invalid lease type specified");
+ break;
+ }
+out:
+ return grant;
+}
+
+
+static void
+do_blocked_fops (xlator_t *this, lease_inode_ctx_t *lease_ctx)
+{
+ struct list_head wind_list;
+ fop_stub_t *blk_fop = NULL;
+ fop_stub_t *tmp = NULL;
+
+ INIT_LIST_HEAD (&wind_list);
+
+ pthread_mutex_lock (&lease_ctx->lock);
+ {
+ list_for_each_entry_safe (blk_fop, tmp,
+ &lease_ctx->blocked_list, list) {
+ list_del_init (&blk_fop->list);
+ list_add_tail (&blk_fop->list, &wind_list);
+ }
+ }
+ pthread_mutex_unlock (&lease_ctx->lock);
+
+ gf_msg_trace (this->name, 0, "Executing the blocked stubs on gfid(%s)",
+ uuid_utoa (lease_ctx->inode->gfid));
+
+ list_for_each_entry_safe (blk_fop, tmp, &wind_list, list) {
+ list_del_init (&blk_fop->list);
+ gf_msg_trace (this->name, 0, "Executing fop:%d", blk_fop->stub->fop);
+ call_resume (blk_fop->stub);
+ GF_FREE (blk_fop);
+ }
+
+ pthread_mutex_lock (&lease_ctx->lock);
+ {
+ lease_ctx->lease_type = NONE;
+ inode_unref (lease_ctx->inode);
+ lease_ctx->inode = NULL;
+ }
+ pthread_mutex_unlock (&lease_ctx->lock);
+
+ return;
+}
+
+
+void
+recall_lease_timer_handler (struct gf_tw_timer_list *timer,
+ void *data, unsigned long calltime)
+{
+ inode_t *inode = NULL;
+ lease_inode_t *lease_inode = NULL;
+ leases_private_t *priv = NULL;
+
+ priv = THIS->private;
+ inode = (inode_t *)data;
+ pthread_mutex_lock (&priv->mutex);
+ {
+ lease_inode = new_lease_inode (inode);
+ if (!lease_inode) {
+ errno = ENOMEM;
+ goto out;
+ }
+ list_add_tail (&lease_inode->list, &priv->recall_list);
+ pthread_cond_broadcast (&priv->cond);
+ }
+out:
+ pthread_mutex_unlock (&priv->mutex);
+
+ GF_FREE (timer);
+}
+
+
+static void
+__recall_lease (xlator_t *this, lease_inode_ctx_t *lease_ctx)
+{
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+ struct gf_upcall up_req = {0,};
+ struct gf_upcall_recall_lease recall_req = {0,};
+ int notify_ret = -1;
+ struct gf_tw_timer_list *timer = NULL;
+ leases_private_t *priv = NULL;
+
+ if (lease_ctx->recall_in_progress) {
+ gf_msg_debug (this->name, 0, "Lease recall is already in "
+ "progress, hence not sending another recall");
+ goto out;
+ }
+
+ priv = this->private;
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+ gf_uuid_copy (up_req.gfid, lease_ctx->inode->gfid);
+ up_req.client_uid = lease_entry->client_uid;
+ up_req.event_type = GF_UPCALL_RECALL_LEASE;
+ up_req.data = &recall_req;
+
+ notify_ret = this->notify (this, GF_EVENT_UPCALL, &up_req);
+ if (notify_ret < 0) {
+ gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_RECALL_FAIL,
+ "Recall notification to client: %s failed",
+ lease_entry->client_uid);
+ /* Do not return from here, continue registering the timer,
+ this is required mostly o keep replicas in sync*/
+ } else {
+ gf_msg_debug (this->name, 0, "Recall lease (all)"
+ "notification sent to client %s",
+ lease_entry->client_uid);
+ }
+
+ lease_ctx->recall_in_progress = _gf_true;
+ lease_entry->recall_time = time (NULL);
+ }
+ timer = GF_CALLOC (1, sizeof (*timer),
+ gf_common_mt_tw_timer_list);
+ if (!timer) {
+ goto out;
+ }
+ INIT_LIST_HEAD (&timer->entry);
+ timer->data = inode_ref (lease_ctx->inode);
+ timer->expires = get_recall_lease_timeout (this);
+ timer->function = recall_lease_timer_handler;
+ lease_ctx->timer = timer;
+ gf_tw_add_timer (priv->timer_wheel, timer);
+ gf_msg_trace (this->name, 0, "Registering timer " "%p, after "
+ "sending recall", timer);
+out:
+ return;
+}
+
+
+/* ret = 0; STACK_UNWIND Success
+ * ret = -1; STACK_UNWIND failure
+ */
+int
+process_lease_req (call_frame_t *frame, xlator_t *this,
+ inode_t *inode, struct gf_lease *lease)
+{
+ int ret = 0;
+ char *client_uid = NULL;
+ lease_inode_ctx_t *lease_ctx = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", frame, out);
+ GF_VALIDATE_OR_GOTO ("leases", this, out);
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease, out);
+
+ client_uid = frame->root->client->client_uid;
+
+ if (!is_valid_lease_id (lease->lease_id)) {
+ gf_msg (this->name, GF_LOG_ERROR, EINVAL,
+ LEASE_MSG_INVAL_LEASE_ID, "Invalid lease id, from"
+ "client:%s", client_uid);
+ ret = -EINVAL;
+ errno = EINVAL;
+ goto out;
+ }
+
+ lease_ctx = lease_ctx_get (inode, this);
+ if (!lease_ctx) {
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM,
+ LEASE_MSG_NO_MEM, "Unable to create/get inode ctx, "
+ "inode:%p", inode);
+ ret = -ENOMEM;
+ errno = ENOMEM;
+ goto out;
+ }
+
+ gf_msg_debug (this->name, 0, "Lease request from client: %s, "
+ "lease type:%d, lease cmd:%d, lease ID:%s, gfid:%s",
+ client_uid, lease->lease_type, lease->cmd,
+ leaseid_utoa (lease->lease_id), uuid_utoa (inode->gfid));
+
+ pthread_mutex_lock (&lease_ctx->lock);
+ {
+ switch (lease->cmd) {
+ case GF_GET_LEASE:
+ lease->lease_type = lease_ctx->lease_type;
+ gf_msg_debug (this->name, 0, "Get lease, existing lease"
+ "type: %d", lease_ctx->lease_type);
+ /*TODO:Should it consider lease id or client_uid?*/
+ break;
+
+ case GF_SET_LEASE:
+ if (__is_lease_grantable (this, lease_ctx, lease, inode)) {
+ __add_lease (frame, inode, lease_ctx,
+ client_uid, lease);
+ ret = 0;
+ } else {
+ gf_msg_debug (this->name, GF_LOG_DEBUG,
+ "Not granting the conflicting lease"
+ " request from %s on gfid(%s)",
+ client_uid, uuid_utoa (inode->gfid));
+ __recall_lease (this, lease_ctx);
+ ret = -1;
+ }
+ break;
+ case GF_UNLK_LEASE:
+ ret = __remove_lease (this, inode, lease_ctx,
+ client_uid, lease);
+ if ((ret == 0) && (lease_ctx->lease_cnt == 0)) {
+ pthread_mutex_unlock (&lease_ctx->lock);
+ goto unblock;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ }
+ pthread_mutex_unlock (&lease_ctx->lock);
+
+ return ret;
+
+unblock:
+ do_blocked_fops (this, lease_ctx);
+out:
+ return ret;
+}
+
+
+/* ret = 1 conflict
+ * ret = 0 no conflict
+ */
+gf_boolean_t
+__check_lease_conflict (call_frame_t *frame, lease_inode_ctx_t *lease_ctx,
+ const char *lease_id, gf_boolean_t is_write)
+{
+ gf_lease_types_t lease_type = {0,};
+ gf_boolean_t conflicts = _gf_false;
+ lease_id_entry_t *lease_entry = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", frame, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_ctx, out);
+ GF_VALIDATE_OR_GOTO ("leases", lease_id, out);
+
+ lease_type = lease_ctx->lease_type;
+
+ /* If the fop is rename or unlink conflict the lease even if its
+ * from the same client??
+ */
+ if ((frame->op == GF_FOP_RENAME) || (frame->op == GF_FOP_UNLINK)) {
+ conflicts = _gf_true;
+ goto recall;
+ }
+
+ /* TODO: If lease_id is not sent, fall back to client uid conflict check?
+ * Or set conflicts = true if lease_id is 0 when there is an existing
+ * lease */
+ switch (lease_type) {
+ case (GF_RW_LEASE | GF_RD_LEASE):
+ case GF_RW_LEASE:
+ lease_entry = __get_lease_id_entry (lease_ctx, lease_id);
+ if (lease_entry && (lease_entry->lease_type & GF_RW_LEASE))
+ conflicts = _gf_false;
+ else
+ conflicts = _gf_true;
+ break;
+ case GF_RD_LEASE:
+ if (is_write && __another_lease_found(lease_ctx, lease_id))
+ conflicts = _gf_true;
+ else
+ conflicts = _gf_false;
+ break;
+ default:
+ break;
+ }
+
+recall:
+ /* If there is a conflict found and recall is not already sent to all
+ * the clients, then send recall to each of the client holding lease.
+ */
+ if (conflicts)
+ __recall_lease (frame->this, lease_ctx);
+out:
+ return conflicts;
+}
+
+
+/* Return values:
+ * -1 : error, unwind the fop
+ * WIND_FOP: No conflict, wind the fop
+ * BLOCK_FOP: Found a conflicting lease, block the fop
+ */
+int
+check_lease_conflict (call_frame_t *frame, inode_t *inode,
+ const char *lease_id, uint32_t fop_flags)
+{
+ lease_inode_ctx_t *lease_ctx = NULL;
+ gf_boolean_t is_blocking_fop = _gf_false;
+ gf_boolean_t is_write_fop = _gf_false;
+ gf_boolean_t conflicts = _gf_false;
+ int ret = -1;
+
+ lease_ctx = lease_ctx_get (inode, frame->this);
+ if (!lease_ctx) {
+ gf_msg (frame->this->name, GF_LOG_WARNING, ENOMEM,
+ LEASE_MSG_NO_MEM,
+ "Unable to create/get inode ctx");
+ ret = -1;
+ errno = ENOMEM;
+ goto out;
+ }
+
+ is_blocking_fop = fop_flags & BLOCKING_FOP;
+ is_write_fop = fop_flags & DATA_MODIFY_FOP;
+
+ pthread_mutex_lock (&lease_ctx->lock);
+ {
+ if (lease_ctx->lease_type == NONE) {
+ gf_msg_debug (frame->this->name, 0,
+ "No leases found continuing with the"
+ " fop:%s", gf_fop_list[frame->op]);
+ ret = WIND_FOP;
+ goto unlock;
+ }
+ conflicts = __check_lease_conflict (frame, lease_ctx,
+ lease_id, is_write_fop);
+ if (conflicts) {
+ if (is_blocking_fop) {
+ gf_msg_debug (frame->this->name, 0, "Fop: %s "
+ "conflicting existing "
+ "lease: %d, blocking the"
+ "fop", gf_fop_list[frame->op],
+ lease_ctx->lease_type);
+ ret = BLOCK_FOP;
+ } else {
+ gf_msg_debug (frame->this->name, 0, "Fop: %s "
+ "conflicting existing "
+ "lease: %d, sending "
+ "EAGAIN", gf_fop_list[frame->op],
+ lease_ctx->lease_type);
+ errno = EAGAIN;
+ ret = -1;
+ }
+ }
+ }
+unlock:
+ pthread_mutex_unlock (&lease_ctx->lock);
+out:
+ return ret;
+}
+
+
+static int
+remove_clnt_leases (const char *client_uid, inode_t *inode, xlator_t *this)
+{
+ lease_inode_ctx_t *lease_ctx = NULL;
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+ int ret = 0;
+ int i = 0;
+
+ lease_ctx = lease_ctx_get (inode, this);
+ if (!lease_ctx) {
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM,
+ LEASE_MSG_INVAL_INODE_CTX,
+ "Unable to create/get inode ctx");
+ ret = -1;
+ errno = ENOMEM;
+ goto out;
+ }
+
+ pthread_mutex_lock (&lease_ctx->lock);
+ {
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+ if (strcmp (client_uid, lease_entry->client_uid) == 0) {
+ for (i = 0; i < GF_LEASE_MAX_TYPE; i++) {
+ lease_ctx->lease_type_cnt[i] -= lease_entry->lease_type_cnt[i];
+ }
+ lease_ctx->lease_cnt -= lease_entry->lease_cnt;
+ __destroy_lease_id_entry (lease_entry);
+ if (lease_ctx->lease_cnt == 0) {
+ pthread_mutex_unlock (&lease_ctx->lock);
+ goto unblock;
+ }
+ }
+ }
+ }
+ pthread_mutex_unlock (&lease_ctx->lock);
+out:
+ return ret;
+
+unblock:
+ do_blocked_fops (this, lease_ctx);
+ return ret;
+}
+
+
+int
+cleanup_client_leases (xlator_t *this, const char *client_uid)
+{
+ lease_client_t *clnt = NULL;
+ lease_client_t *tmp = NULL;
+ struct list_head cleanup_list = {0, };
+ lease_inode_t *l_inode = NULL;
+ lease_inode_t *tmp1 = NULL;
+ leases_private_t *priv = NULL;
+ int ret = 0;
+
+ priv = this->private;
+ if (!priv) {
+ ret = -1;
+ errno = EINVAL;
+ goto out;
+ }
+
+ INIT_LIST_HEAD (&cleanup_list);
+ pthread_mutex_lock (&priv->mutex);
+ {
+ list_for_each_entry_safe (clnt, tmp, &priv->client_list, client_list) {
+ if ((strcmp (clnt->client_uid, client_uid) == 0)) {
+ list_for_each_entry_safe (l_inode, tmp1,
+ &clnt->inode_list, list) {
+ list_del_init (&l_inode->list);
+ list_add_tail (&l_inode->list, &cleanup_list);
+ }
+ break;
+ }
+ __destroy_lease_client (clnt);
+ }
+ }
+ pthread_mutex_unlock (&priv->mutex);
+
+ l_inode = tmp1 = NULL;
+ list_for_each_entry_safe (l_inode, tmp1, &cleanup_list, list) {
+ remove_clnt_leases (client_uid, l_inode->inode, this);
+ }
+out:
+ return ret;
+}
+
+
+static void
+__remove_all_leases (xlator_t *this, lease_inode_ctx_t *lease_ctx)
+{
+ int i = 0;
+ lease_id_entry_t *lease_entry = NULL;
+ lease_id_entry_t *tmp = NULL;
+
+ __dump_leases_info (this, lease_ctx);
+
+ list_for_each_entry_safe (lease_entry, tmp,
+ &lease_ctx->lease_id_list,
+ lease_id_list) {
+ lease_entry->lease_cnt = 0;
+ remove_from_clnt_list (this, lease_entry->client_uid, lease_ctx->inode);
+ __destroy_lease_id_entry (lease_entry);
+ }
+ INIT_LIST_HEAD (&lease_ctx->lease_id_list);
+ for (i = 0; i <= GF_LEASE_MAX_TYPE; i++)
+ lease_ctx->lease_type_cnt[i] = 0;
+ lease_ctx->lease_type = 0;
+ lease_ctx->lease_cnt = 0;
+ lease_ctx->recall_in_progress = _gf_false;
+ inode_unref (lease_ctx->inode);
+ lease_ctx->timer = NULL;
+
+ /* TODO:
+ * - Mark the corresponding fd bad. Could be done on client side
+ * as a result of recall
+ * - Free the lease_ctx
+ */
+ return;
+}
+
+
+static int
+remove_all_leases (xlator_t *this, inode_t *inode)
+{
+ lease_inode_ctx_t *lease_ctx = NULL;
+ int ret = 0;
+
+ GF_VALIDATE_OR_GOTO ("leases", inode, out);
+
+ lease_ctx = lease_ctx_get (inode, this);
+ if (!lease_ctx) {
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM,
+ LEASE_MSG_INVAL_INODE_CTX,
+ "Unable to create/get inode ctx");
+ ret = -1;
+ errno = ENOMEM;
+ goto out;
+ }
+
+ pthread_mutex_lock (&lease_ctx->lock);
+ {
+ __remove_all_leases (this, lease_ctx);
+ }
+ pthread_mutex_unlock (&lease_ctx->lock);
+
+ do_blocked_fops (this, lease_ctx);
+out:
+ return ret;
+}
+
+
+void *
+expired_recall_cleanup (void *data)
+{
+ struct timespec sleep_till = {0, };
+ struct list_head recall_cleanup_list;
+ lease_inode_t *recall_entry = NULL;
+ lease_inode_t *tmp = NULL;
+ leases_private_t *priv = NULL;
+ xlator_t *this = NULL;
+
+ GF_VALIDATE_OR_GOTO ("leases", data, out);
+
+ this = data;
+ priv = this->private;
+
+ gf_msg_debug (this->name, 0, "Started the expired_recall_cleanup thread");
+
+ while (1) {
+ pthread_mutex_lock (&priv->mutex);
+ {
+ if (priv->fini) {
+ pthread_mutex_unlock (&priv->mutex);
+ goto out;
+ }
+ INIT_LIST_HEAD (&recall_cleanup_list);
+ if (list_empty (&priv->recall_list)) {
+ sleep_till.tv_sec = time (NULL) + 600;
+ pthread_cond_timedwait (&priv->cond, &priv->mutex,
+ &sleep_till);
+ }
+ if (!list_empty (&priv->recall_list)) {
+ gf_msg_debug (this->name, 0, "Found expired recalls");
+ list_for_each_entry_safe (recall_entry, tmp,
+ &priv->recall_list, list) {
+ list_del_init (&recall_entry->list);
+ list_add_tail (&recall_entry->list, &recall_cleanup_list);
+ }
+ }
+ }
+ pthread_mutex_unlock (&priv->mutex);
+
+ recall_entry = tmp = NULL;
+ list_for_each_entry_safe (recall_entry, tmp, &recall_cleanup_list, list) {
+ gf_msg_debug (this->name, 0, "Recall lease was sent on"
+ " inode:%p, recall timer has expired"
+ " and clients haven't unlocked the lease"
+ " hence cleaning up leases on the inode",
+ recall_entry->inode);
+ remove_all_leases (this, recall_entry->inode);
+ list_del_init (&recall_entry->list);
+ }
+ }
+
+out:
+ return NULL;
+}
diff --git a/xlators/features/leases/src/leases-mem-types.h b/xlators/features/leases/src/leases-mem-types.h
new file mode 100644
index 00000000000..055da45be5d
--- /dev/null
+++ b/xlators/features/leases/src/leases-mem-types.h
@@ -0,0 +1,27 @@
+/*
+ Copyright (c) 2015-2016 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.
+*/
+
+#ifndef __LEASES_MEM_TYPES_H__
+#define __LEASES_MEM_TYPES_H__
+
+#include "mem-types.h"
+
+enum gf_leases_mem_types_ {
+ gf_leases_mt_conf_t = gf_common_mt_end + 1,
+ gf_leases_mt_private_t,
+ gf_leases_mt_lease_client_t,
+ gf_leases_mt_lease_inode_t,
+ gf_leases_mt_fd_ctx_t,
+ gf_leases_mt_lease_inode_ctx_t,
+ gf_leases_mt_lease_id_entry_t,
+ gf_leases_mt_fop_stub_t,
+ gf_leases_mt_end
+};
+#endif
diff --git a/xlators/features/leases/src/leases-messages.h b/xlators/features/leases/src/leases-messages.h
new file mode 100644
index 00000000000..62df4395a59
--- /dev/null
+++ b/xlators/features/leases/src/leases-messages.h
@@ -0,0 +1,129 @@
+/*
+ Copyright (c) 2015-2016 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.
+ */
+
+#ifndef _LEASES_MESSAGES_H_
+#define _LEASES_MESSAGES_H_
+
+#include "glfs-message-id.h"
+
+/* NOTE: Rules for message additions
+ * 1) Each instance of a message is _better_ left with a unique message ID, even
+ * if the message format is the same. Reasoning is that, if the message
+ * format needs to change in one instance, the other instances are not
+ * impacted or the new change does not change the ID of the instance being
+ * modified.
+ * 2) Addition of a message,
+ * - Should increment the GLFS_NUM_MESSAGES
+ * - Append to the list of messages defined, towards the end
+ * - Retain macro naming as glfs_msg_X (for redability across developers)
+ * NOTE: Rules for message format modifications
+ * 3) Check across the code if the message ID macro in question is reused
+ * anywhere. If reused then then the modifications should ensure correctness
+ * everywhere, or needs a new message ID as (1) above was not adhered to. If
+ * not used anywhere, proceed with the required modification.
+ * NOTE: Rules for message deletion
+ * 4) Check (3) and if used anywhere else, then cannot be deleted. If not used
+ * anywhere, then can be deleted, but will leave a hole by design, as
+ * addition rules specify modification to the end of the list and not filling
+ * holes.
+ */
+
+#define LEASES_COMP_BASE GLFS_MSGID_COMP_LEASES
+#define GLFS_NUM_MESSAGES 11
+#define GLFS_MSGID_END (LEASES_COMP_BASE + GLFS_NUM_MESSAGES + 1)
+
+#define glfs_msg_start_x LEASES_COMP_BASE, "Invalid: Start of messages"
+/*------------*/
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_NO_MEM (LEASES_COMP_BASE + 1)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_RECALL_FAIL (LEASES_COMP_BASE + 2)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_INVAL_LEASE_ID (LEASES_COMP_BASE + 3)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_INVAL_UNLK_LEASE (LEASES_COMP_BASE + 4)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_INVAL_INODE_CTX (LEASES_COMP_BASE + 5)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_NOT_ENABLED (LEASES_COMP_BASE + 6)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_NO_TIMER_WHEEL (LEASES_COMP_BASE + 7)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_CLNT_NOTFOUND (LEASES_COMP_BASE + 8)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_INODE_NOTFOUND (LEASES_COMP_BASE + 9)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_INVAL_FD_CTX (LEASES_COMP_BASE + 10)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define LEASE_MSG_INVAL_LEASE_TYPE (LEASES_COMP_BASE + 11)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ */
+#define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages"
+
+#endif /* !_LEASES_MESSAGES_H_ */
diff --git a/xlators/features/leases/src/leases.c b/xlators/features/leases/src/leases.c
new file mode 100644
index 00000000000..8783a5ce9c2
--- /dev/null
+++ b/xlators/features/leases/src/leases.c
@@ -0,0 +1,1134 @@
+/*
+ Copyright (c) 2015-2016 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.
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "leases.h"
+
+int32_t
+leases_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd, xdata);
+
+ return 0;
+}
+
+
+int32_t
+leases_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ fd_t *fd, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = 0;
+ int ret = 0;
+ lease_fd_ctx_t *fd_ctx = NULL;
+ char *lease_id = NULL;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ fd_ctx = GF_CALLOC (1, sizeof (*fd_ctx), gf_leases_mt_fd_ctx_t);
+
+ fd_ctx->client_uid = gf_strdup (frame->root->client->client_uid);
+ if (!fd_ctx->client_uid) {
+ op_errno = ENOMEM;
+ goto err;
+ }
+
+ GET_FLAGS (frame->op, flags);
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ if (lease_id != NULL)
+ memcpy (fd_ctx->lease_id, lease_id, LEASE_ID_SIZE);
+ else
+ memset (fd_ctx->lease_id, 0, LEASE_ID_SIZE);
+
+ ret = fd_ctx_set (fd, this, (uint64_t)fd_ctx);
+ if (ret) {
+ op_errno = ENOMEM;
+ goto err;
+ }
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, open, frame, this,
+ loc, flags, fd, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_open_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->open,
+ loc, flags, fd, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (open, frame, -1, op_errno, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *prebuf,
+ struct iatt *postbuf, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (writev, frame, op_ret, op_errno,
+ prebuf, postbuf, xdata);
+
+ return 0;
+}
+
+
+int32_t
+leases_writev (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iovec *vector, int count, off_t off, uint32_t flags,
+ struct iobref *iobref, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, writev, frame, this, fd, vector, count,
+ off, flags, iobref, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_writev_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->writev,
+ fd, vector, count, off, flags, iobref, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (writev, frame, -1, op_errno, NULL, NULL, NULL);
+ return 0;
+}
+
+
+int32_t
+leases_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno,
+ struct iovec *vector, int count, struct iatt *stbuf,
+ struct iobref *iobref, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (readv, frame, op_ret, op_errno, vector,
+ count, stbuf, iobref, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_readv (call_frame_t *frame, xlator_t *this,
+ fd_t *fd, size_t size, off_t offset,
+ uint32_t flags, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, readv, frame, this,
+ fd, size, offset, flags, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_readv_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->readv,
+ fd, size, offset, flags, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (readv, frame, -1, op_errno, NULL, 0,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_lk_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct gf_flock *lock,
+ dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (lk, frame, op_ret, op_errno, lock, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_lk (call_frame_t *frame, xlator_t *this,
+ fd_t *fd, int32_t cmd, struct gf_flock *flock, dict_t *xdata)
+{
+ int32_t op_errno = 0;
+ uint32_t fop_flags = 0;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS_LK (cmd, flock->l_type, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, lk, frame, this,
+ fd, cmd, flock, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_lk_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->lk,
+ fd, cmd, flock, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (lk, frame, -1, op_errno, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_lease (call_frame_t *frame, xlator_t *this,
+ loc_t *loc, struct gf_lease *lease, dict_t *xdata)
+{
+ int32_t op_errno = 0;
+ int ret = 0;
+ struct gf_lease nullease = {0, };
+ int32_t op_ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ ret = process_lease_req (frame, this, loc->inode, lease);
+ if (ret < 0) {
+ op_errno = -ret;
+ op_ret = -1;
+ }
+ goto unwind;
+
+out:
+ gf_msg (this->name, GF_LOG_ERROR, EINVAL, LEASE_MSG_NOT_ENABLED,
+ "\"features/leases\" translator is not enabled. "
+ "You need to enable it for proper functioning of your "
+ "application");
+ op_errno = ENOSYS;
+ op_ret = -1;
+
+unwind:
+ STACK_UNWIND_STRICT (lease, frame, op_ret, op_errno,
+ (op_errno == ENOSYS) ? &nullease : lease, xdata);
+ return 0;
+}
+
+int32_t
+leases_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *prebuf,
+ struct iatt *postbuf, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (truncate, frame, op_ret, op_errno,
+ prebuf, postbuf, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset,
+ dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, 0);
+
+ ret = check_lease_conflict (frame, loc->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (loc->inode, truncate, frame, this,
+ loc, offset, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_truncate_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate,
+ loc, offset, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (truncate, frame, -1, op_errno, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *statpre,
+ struct iatt *statpost, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno,
+ statpre, statpost, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ struct iatt *stbuf, int32_t valid, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, 0);
+
+ ret = check_lease_conflict (frame, loc->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (loc->inode, setattr, frame, this,
+ loc, stbuf, valid, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_setattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->setattr,
+ loc, stbuf, valid, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *stbuf,
+ struct iatt *preoldparent, struct iatt *postoldparent,
+ struct iatt *prenewparent, struct iatt *postnewparent,
+ dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (rename, frame, op_ret, op_errno,
+ stbuf, preoldparent, postoldparent,
+ prenewparent, postnewparent, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_rename (call_frame_t *frame, xlator_t *this,
+ loc_t *oldloc, loc_t *newloc, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ /* should the lease be also checked for newloc */
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, 0);
+
+ ret = check_lease_conflict (frame, oldloc->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (oldloc->inode, rename, frame, this,
+ oldloc, newloc, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_rename_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename,
+ oldloc, newloc, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (rename, frame, -1, op_errno, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, struct iatt *preparent,
+ struct iatt *postparent, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (unlink, frame, op_ret, op_errno,
+ preparent, postparent, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag,
+ dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, 0);
+
+ ret = check_lease_conflict (frame, loc->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (loc->inode, unlink, frame, this,
+ loc, xflag, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_unlink_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink,
+ loc, xflag, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (unlink, frame, -1, op_errno, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, inode_t *inode, struct iatt *stbuf,
+ struct iatt *preparent, struct iatt *postparent, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (link, frame, op_ret, op_errno,
+ inode, stbuf, preparent, postparent, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc,
+ loc_t *newloc, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, 0);
+
+ ret = check_lease_conflict (frame, oldloc->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (oldloc->inode, link, frame, this,
+ oldloc, newloc, xdata);
+ return 0;
+out:
+ STACK_WIND (frame, leases_link_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->link,
+ oldloc, newloc, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL,
+ NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int op_ret, int op_errno, fd_t *fd, inode_t *inode,
+ struct iatt *stbuf, struct iatt *preparent,
+ struct iatt *postparent, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd,
+ inode, stbuf, preparent, postparent, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_create (call_frame_t *frame, xlator_t *this,
+ loc_t *loc, int32_t flags, mode_t mode,
+ mode_t umask, fd_t *fd, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, create, frame, this,
+ loc, flags, mode, umask, fd, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_create_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->create,
+ loc, flags, mode, umask, fd, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (create, frame, -1, op_errno, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *prebuf,
+ struct iatt *postbuf,
+ dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (fsync, frame, op_ret, op_errno, prebuf, postbuf,
+ xdata);
+ return 0;
+}
+
+int32_t
+leases_fsync (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ int32_t flags, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, fsync, frame, this,
+ fd, flags, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_fsync_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->fsync, fd, flags, xdata);
+ return 0;
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (fsync, frame, -1, op_errno, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_ftruncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *prebuf,
+ struct iatt *postbuf,
+ dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (ftruncate, frame, op_ret, op_errno, prebuf,
+ postbuf, xdata);
+ return 0;
+}
+
+int32_t
+leases_ftruncate (call_frame_t *frame, xlator_t *this,
+ fd_t *fd, off_t offset, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, 0); /* TODO:fd->flags?*/
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, ftruncate, frame, this,
+ fd, offset, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_ftruncate_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->ftruncate,
+ fd, offset, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (ftruncate, frame, -1, op_errno, NULL,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *statpre,
+ struct iatt *statpost, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno,
+ statpre, statpost, xdata);
+ return 0;
+}
+
+int32_t
+leases_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iatt *stbuf, int32_t valid, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, fsetattr, frame, this,
+ fd, stbuf, valid, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_fsetattr_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetattr,
+ fd, stbuf, valid, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (fsetattr, frame, -1, op_errno, NULL,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_fallocate_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *pre,
+ struct iatt *post, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (fallocate, frame, op_ret, op_errno, pre,
+ post, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_fallocate (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ int32_t mode, off_t offset, size_t len, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, fallocate, frame, this,
+ fd, mode, offset, len, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_fallocate_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->fallocate,
+ fd, mode, offset, len, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (fallocate, frame, -1, op_errno, NULL,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_discard_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *pre,
+ struct iatt *post, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (discard, frame, op_ret, op_errno, pre,
+ post, xdata);
+
+ return 0;
+}
+
+int32_t
+leases_discard (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ off_t offset, size_t len, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, discard, frame, this,
+ fd, offset, len, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_discard_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->discard,
+ fd, offset, len, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (discard, frame, -1, op_errno, NULL,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+leases_zerofill_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, struct iatt *pre,
+ struct iatt *post, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (zerofill, frame, op_ret, op_errno, pre,
+ post, xdata);
+
+ return 0;
+}
+
+int
+leases_zerofill (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ off_t offset, off_t len, dict_t *xdata)
+{
+ uint32_t fop_flags = 0;
+ int32_t op_errno = -1;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, zerofill, frame, this,
+ fd, offset, len, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_zerofill_cbk,
+ FIRST_CHILD(this), FIRST_CHILD(this)->fops->zerofill,
+ fd, offset, len, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (zerofill, frame, -1, op_errno, NULL,
+ NULL, NULL);
+ return 0;
+}
+
+int
+leases_flush_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, dict_t *xdata)
+{
+ STACK_UNWIND_STRICT (flush, frame, op_ret, op_errno, xdata);
+
+ return 0;
+}
+
+int
+leases_flush (call_frame_t *frame, xlator_t *this,
+ fd_t *fd, dict_t *xdata)
+{
+ int32_t op_errno = -1;
+ uint32_t fop_flags = 0;
+ char *lease_id = NULL;
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ GET_LEASE_ID (xdata, lease_id, frame->root->client->client_uid);
+ GET_FLAGS (frame->op, fd->flags);
+
+ ret = check_lease_conflict (frame, fd->inode, lease_id, fop_flags);
+ if (ret < 0)
+ goto err;
+ else if (ret == BLOCK_FOP)
+ goto block;
+ else if (ret == WIND_FOP)
+ goto out;
+
+block:
+ LEASE_BLOCK_FOP (fd->inode, flush, frame, this,
+ fd, xdata);
+ return 0;
+
+out:
+ STACK_WIND (frame, leases_flush_cbk, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->flush, fd, xdata);
+ return 0;
+
+err:
+ op_errno = (op_errno == -1) ? errno : op_errno;
+ STACK_UNWIND_STRICT (create, frame, -1, op_errno, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+mem_acct_init (xlator_t *this)
+{
+ int ret = -1;
+
+ if (!this)
+ return ret;
+
+ ret = xlator_mem_acct_init (this, gf_leases_mt_end + 1);
+
+ if (ret != 0) {
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM, LEASE_MSG_NO_MEM,
+ "mem account init failed");
+ return ret;
+ }
+
+ return ret;
+}
+
+int
+reconfigure (xlator_t *this, dict_t *options)
+{
+ leases_private_t *priv = NULL;
+ int ret = -1;
+
+ priv = this->private;
+ GF_ASSERT (priv);
+
+ GF_OPTION_RECONF ("leases", priv->leases_enabled,
+ options, bool, out);
+ GF_OPTION_RECONF ("lease-lock-recall-timeout",
+ priv->recall_lease_timeout,
+ options, int32, out);
+
+ ret = 0;
+out:
+ return ret;
+}
+
+int
+init (xlator_t *this)
+{
+ int ret = -1;
+ leases_private_t *priv = NULL;
+
+ priv = GF_CALLOC (1, sizeof (*priv),
+ gf_leases_mt_private_t);
+ if (!priv) {
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM, LEASE_MSG_NO_MEM,
+ "Leases init failed");
+ goto out;
+ }
+
+ GF_OPTION_INIT ("leases", priv->leases_enabled,
+ bool, out);
+ GF_OPTION_INIT ("lease-lock-recall-timeout",
+ priv->recall_lease_timeout, int32, out);
+ pthread_mutex_init (&priv->mutex, NULL);
+ INIT_LIST_HEAD (&priv->client_list);
+ INIT_LIST_HEAD (&priv->recall_list);
+
+ priv->timer_wheel = glusterfs_global_timer_wheel (this);
+ if (!priv->timer_wheel) {
+ gf_msg (this->name, GF_LOG_ERROR, 0, LEASE_MSG_NO_TIMER_WHEEL,
+ "Initing the global timer wheel");
+ ret = glusterfs_global_timer_wheel_init (this->ctx);
+ if (ret) {
+ gf_msg (this->name, GF_LOG_INFO, 0, LEASE_MSG_NO_TIMER_WHEEL,
+ "Initing the global timer wheel failed");
+ goto out;
+ }
+ }
+
+ pthread_create (&priv->recall_thr, NULL, expired_recall_cleanup, this);
+
+ this->private = priv;
+ ret = 0;
+
+out:
+ if (ret) {
+ GF_FREE (priv);
+ }
+
+ return ret;
+}
+
+int
+fini (xlator_t *this)
+{
+ leases_private_t *priv = NULL;
+
+ priv = this->private;
+ if (!priv) {
+ return 0;
+ }
+ this->private = NULL;
+
+ priv->fini = _gf_false;
+ pthread_join (priv->recall_thr, NULL);
+
+ GF_FREE (priv);
+
+ return 0;
+}
+
+static int
+leases_forget (xlator_t *this, inode_t *inode)
+{
+ /* TODO:leases_cleanup_inode_ctx (this, inode); */
+ return 0;
+}
+
+static int
+leases_release (xlator_t *this, fd_t *fd)
+{
+ /* TODO:cleanup fd_ctx */
+ return 0;
+}
+
+static int
+leases_clnt_disconnect_cbk (xlator_t *this, client_t *client)
+{
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ ret = cleanup_client_leases (this, client->client_uid);
+out:
+ return ret;
+}
+
+int
+notify (xlator_t *this, int32_t event, void *data, ...)
+{
+ int ret = 0;
+
+ EXIT_IF_LEASES_OFF (this, out);
+
+ ret = default_notify (this, event, data);
+out:
+ return ret;
+}
+
+struct xlator_fops fops = {
+ /* Metadata modifying fops */
+ .fsetattr = leases_fsetattr,
+ .setattr = leases_setattr,
+
+ /* File Data reading fops */
+ .open = leases_open,
+ .readv = leases_readv,
+
+ /* File Data modifying fops */
+ .truncate = leases_truncate,
+ .ftruncate = leases_ftruncate,
+ .writev = leases_writev,
+ .zerofill = leases_zerofill,
+ .fallocate = leases_fallocate,
+ .discard = leases_discard,
+ .lk = leases_lk,
+ .fsync = leases_fsync,
+ .flush = leases_flush,
+ .lease = leases_lease,
+
+ /* Directory Data modifying fops */
+ .create = leases_create,
+ .rename = leases_rename,
+ .unlink = leases_unlink,
+ .link = leases_link,
+
+#ifdef NOT_SUPPORTED
+ /* internal lk fops */
+ .inodelk = leases_inodelk,
+ .finodelk = leases_finodelk,
+ .entrylk = leases_entrylk,
+ .fentrylk = leases_fentrylk,
+
+ /* Internal special fops*/
+ .xattrop = leases_xattrop,
+ .fxattrop = leases_fxattrop,
+#endif
+};
+
+struct xlator_cbks cbks = {
+ .forget = leases_forget,
+ .release = leases_release,
+ .client_disconnect = leases_clnt_disconnect_cbk,
+};
+
+struct volume_options options[] = {
+ { .key = {"leases"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .default_value = "off",
+ .description = "When \"on\", enables leases support"
+ },
+ { .key = {"lease-lock-recall-timeout"},
+ .type = GF_OPTION_TYPE_INT,
+ .default_value = RECALL_LEASE_LK_TIMEOUT,
+ .description = "After 'timeout' seconds since the recall_lease"
+ " request has been sent to the client, the lease lock"
+ " will be forcefully purged by the server."
+ },
+ { .key = {NULL} },
+};
diff --git a/xlators/features/leases/src/leases.h b/xlators/features/leases/src/leases.h
new file mode 100644
index 00000000000..16143691075
--- /dev/null
+++ b/xlators/features/leases/src/leases.h
@@ -0,0 +1,239 @@
+/*
+ Copyright (c) 2015-2016 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.
+*/
+
+#ifndef _LEASES_H
+#define _LEASES_H
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+#include "common-utils.h"
+#include "glusterfs.h"
+#include "xlator.h"
+#include "inode.h"
+#include "call-stub.h"
+#include "logging.h"
+#include "client_t.h"
+#include "lkowner.h"
+#include "locking.h"
+#include "upcall-utils.h"
+#include "tw.h"
+#include "timer-wheel.h"
+#include "leases-mem-types.h"
+#include "leases-messages.h"
+
+/* The time period for which a client lease lock will be stored after its been
+ * recalled for the first time. */
+#define RECALL_LEASE_LK_TIMEOUT "60"
+
+#define DATA_MODIFY_FOP 0x0001
+#define BLOCKING_FOP 0x0002
+
+#define BLOCK_FOP 0x0001
+#define WIND_FOP 0x0002
+
+#define EXIT_IF_LEASES_OFF(this, label) do { \
+ if (!is_leases_enabled(this)) \
+ goto label; \
+} while (0)
+
+#define GET_LEASE_ID(xdata, lease_id, client_uid) do { \
+ int ret_val = -1; \
+ ret_val = dict_get_bin (xdata, "lease-id", (void **)&lease_id); \
+ if (ret_val) { \
+ ret_val = 0; \
+ gf_msg_debug ("leases", 0, "Lease id is not set for client:%s", client_uid); \
+ } \
+} while (0)
+
+#define GET_FLAGS(fop, fd_flags) \
+do { \
+ if (fd_flags & (O_WRONLY | O_RDWR) && fop == GF_FOP_OPEN) \
+ fop_flags = DATA_MODIFY_FOP; \
+ \
+ if (fop == GF_FOP_UNLINK || fop == GF_FOP_RENAME || \
+ fop == GF_FOP_TRUNCATE || fop == GF_FOP_FTRUNCATE || \
+ fop == GF_FOP_FLUSH || fop == GF_FOP_FSYNC || \
+ fop == GF_FOP_WRITE || fop == GF_FOP_FALLOCATE || \
+ fop == GF_FOP_DISCARD || fop == GF_FOP_ZEROFILL || \
+ fop == GF_FOP_SETATTR || fop == GF_FOP_FSETATTR || \
+ fop == GF_FOP_LINK) \
+ fop_flags = DATA_MODIFY_FOP; \
+ \
+ if (fd_flags & (O_NONBLOCK | O_NDELAY)) \
+ fop_flags |= BLOCKING_FOP; \
+ \
+} while (0) \
+
+
+#define GET_FLAGS_LK(cmd, l_type, fd_flags) \
+do { \
+ /* TODO: handle F_RESLK_LCK and other glusterfs_lk_recovery_cmds_t */ \
+ if ((cmd == F_SETLKW || cmd == F_SETLKW64 || \
+ cmd == F_SETLK || cmd == F_SETLK64) && \
+ l_type == F_WRLCK) \
+ fop_flags = DATA_MODIFY_FOP; \
+ \
+ if (fd_flags & (O_NONBLOCK | O_NDELAY) && \
+ (cmd == F_SETLKW || cmd == F_SETLKW64)) \
+ fop_flags |= BLOCKING_FOP; \
+ \
+} while (0) \
+
+#define LEASE_BLOCK_FOP(inode, fop_name, frame, this, params ...) \
+do { \
+ call_stub_t *__stub = NULL; \
+ fop_stub_t *blk_fop = NULL; \
+ lease_inode_ctx_t *lease_ctx = NULL; \
+ int __ret = 0; \
+ \
+ __stub = fop_##fop_name##_stub (frame, default_##fop_name##_resume, \
+ params); \
+ if (!__stub) { \
+ ret = -ENOMEM; \
+ goto __out; \
+ } \
+ \
+ blk_fop = GF_CALLOC (1, sizeof (*blk_fop), \
+ gf_leases_mt_fop_stub_t); \
+ if (blk_fop) { \
+ ret = -ENOMEM; \
+ goto __out; \
+ } \
+ \
+ lease_ctx = lease_ctx_get (inode, this); \
+ if (!lease_ctx) { \
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM, \
+ LEASE_MSG_NO_MEM, \
+ "Unable to create/get inode ctx"); \
+ op_errno = ENOMEM; \
+ goto __out; \
+ } \
+ \
+ pthread_mutex_lock (&lease_ctx->lock); \
+ { \
+ /*TODO: If the lease is unlocked btw check lease conflict and \
+ * by now, then this fop shouldn't be add to the blocked fop \
+ * list, can use generation number for the same?*/ \
+ list_add_tail (&blk_fop->list, &lease_ctx->blocked_list); \
+ } \
+ pthread_mutex_unlock (&lease_ctx->lock); \
+ \
+__out: \
+ if (ret < 0) { \
+ gf_msg (this->name, GF_LOG_WARNING, ENOMEM, LEASE_MSG_NO_MEM, \
+ "Unable to create stub for blocking the fop:%s (%s)", \
+ gf_fop_list[frame->op], strerror(ENOMEM)); \
+ default_##fop_name##_failure_cbk (frame, -__ret); \
+ if (__stub != NULL) { \
+ call_stub_destroy (__stub); \
+ } \
+ GF_FREE (blk_fop); \
+ goto err; \
+ } \
+} while (0) \
+
+struct _leases_private {
+ gf_boolean_t leases_enabled;
+ int32_t recall_lease_timeout;
+ struct list_head client_list;
+ struct list_head recall_list;
+ struct tvec_base *timer_wheel; /* timer wheel where the recall request
+ is qued and waits for unlock/expiry */
+ gf_boolean_t fini;
+ pthread_t recall_thr;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+typedef struct _leases_private leases_private_t;
+
+struct _lease_client {
+ char *client_uid;
+ struct list_head client_list;
+ struct list_head inode_list;
+};
+typedef struct _lease_client lease_client_t;
+
+struct _lease_inode {
+ inode_t *inode;
+ struct list_head list; /* This can be part of both inode_list and recall_list */
+};
+typedef struct _lease_inode lease_inode_t;
+
+struct _lease_fd_ctx {
+ char *client_uid;
+ char lease_id[LEASE_ID_SIZE];
+};
+typedef struct _lease_fd_ctx lease_fd_ctx_t;
+
+struct _lease_inode_ctx {
+ struct list_head lease_id_list; /* clients that have taken leases */
+ int lease_type_cnt[GF_LEASE_MAX_TYPE+1];
+ int lease_type; /* Types of leases acquired */
+ uint64_t lease_cnt; /* Total number of leases on this inode */
+ uint64_t openfd_cnt; /* number of fds open */
+ gf_boolean_t recall_in_progress; /* if lease recall is sent on this inode */
+ struct list_head blocked_list; /* List of fops blocked until the
+ lease recall is complete */
+ inode_t *inode; /* this represents the inode on which the
+ lock was taken, required mainly during
+ disconnect cleanup */
+ struct gf_tw_timer_list *timer;
+ pthread_mutex_t lock;
+};
+typedef struct _lease_inode_ctx lease_inode_ctx_t;
+
+struct _lease_id_entry {
+ struct list_head lease_id_list;
+ char lease_id[LEASE_ID_SIZE];
+ char *client_uid; /* uid of the client that has
+ taken the lease */
+ int lease_type_cnt[GF_LEASE_MAX_TYPE+1]; /* count of each lease type */
+ int lease_type; /* Union of all the leases taken
+ under the given lease id */
+ uint64_t lease_cnt; /* Number of leases taken under the
+ given lease id */
+ time_t recall_time; /* time @ which recall was sent */
+};
+typedef struct _lease_id_entry lease_id_entry_t;
+
+/* Required? as stub itself will have list */
+struct __fop_stub {
+ struct list_head list;
+ call_stub_t *stub;
+};
+typedef struct __fop_stub fop_stub_t;
+
+gf_boolean_t
+is_leases_enabled (xlator_t *this);
+
+int32_t
+get_recall_lease_timeout (xlator_t *this);
+
+lease_inode_ctx_t *
+lease_ctx_get (inode_t *inode, xlator_t *this);
+
+int
+process_lease_req (call_frame_t *frame, xlator_t *this,
+ inode_t *inode, struct gf_lease *lease);
+
+int
+check_lease_conflict (call_frame_t *frame, inode_t *inode,
+ const char *lease_id, uint32_t fop_flags);
+
+int
+cleanup_client_leases (xlator_t *this, const char *client_uid);
+
+void *
+expired_recall_cleanup (void *data);
+
+#endif /* _LEASES_H */