summaryrefslogtreecommitdiffstats
path: root/xlators/protocol/client/src/client-lk.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/protocol/client/src/client-lk.c')
-rw-r--r--xlators/protocol/client/src/client-lk.c949
1 files changed, 949 insertions, 0 deletions
diff --git a/xlators/protocol/client/src/client-lk.c b/xlators/protocol/client/src/client-lk.c
new file mode 100644
index 00000000000..4ad7fc2b1ec
--- /dev/null
+++ b/xlators/protocol/client/src/client-lk.c
@@ -0,0 +1,949 @@
+/*
+ Copyright (c) 2008-2009 Gluster, Inc. <http://www.gluster.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include "common-utils.h"
+#include "xlator.h"
+#include "client.h"
+
+static void
+__insert_and_merge (clnt_fd_ctx_t *fdctx, client_posix_lock_t *lock);
+
+static int
+client_send_recovery_lock (call_frame_t *frame, xlator_t *this,
+ client_posix_lock_t *lock);
+static void
+__dump_client_lock (client_posix_lock_t *lock)
+{
+ xlator_t *this = NULL;
+
+ this = THIS;
+
+ gf_log (this->name, GF_LOG_TRACE,
+ "{fd=%lld}"
+ "{%s lk-owner:%"PRIu64" %"PRId64" - %"PRId64"}"
+ "{start=%"PRId64" end=%"PRId64"}",
+ (unsigned long long)lock->fd,
+ lock->fl_type == F_WRLCK ? "Write-Lock" : "Read-Lock",
+ lock->owner,
+ lock->user_flock.l_start,
+ lock->user_flock.l_len,
+ lock->fl_start,
+ lock->fl_end);
+}
+
+static int
+dump_client_locks_fd (clnt_fd_ctx_t *fdctx)
+{
+ client_posix_lock_t *lock = NULL;
+ int count = 0;
+
+ pthread_mutex_lock (&fdctx->mutex);
+ {
+ list_for_each_entry (lock, &fdctx->lock_list, list) {
+ __dump_client_lock (lock);
+ count++;
+ }
+ }
+ pthread_mutex_unlock (&fdctx->mutex);
+
+ return count;
+
+}
+
+int
+dump_client_locks (inode_t *inode)
+{
+ fd_t *fd = NULL;
+ clnt_conf_t *conf = NULL;
+ xlator_t *this = NULL;
+ clnt_fd_ctx_t *fdctx = NULL;
+
+ int total_count = 0;
+ int locks_fd_count = 0;
+
+ this = THIS;
+ conf = this->private;
+
+ LOCK (&inode->lock);
+ {
+ list_for_each_entry (fd, &inode->fd_list, inode_list) {
+ locks_fd_count = 0;
+
+ pthread_mutex_lock (&conf->lock);
+ {
+ fdctx = this_fd_get_ctx (fd, this);
+ }
+ pthread_mutex_unlock (&conf->lock);
+
+ if (fdctx)
+ locks_fd_count = dump_client_locks_fd (fdctx);
+
+ total_count += locks_fd_count;
+ }
+
+ }
+ UNLOCK (&inode->lock);
+
+ return total_count;
+
+}
+
+static off_t
+__get_lock_length (off_t start, off_t end)
+{
+ if (end == LLONG_MAX)
+ return 0;
+ else
+ return (end - start + 1);
+}
+
+/* Add two locks */
+static client_posix_lock_t *
+add_locks (client_posix_lock_t *l1, client_posix_lock_t *l2)
+{
+ client_posix_lock_t *sum = NULL;
+
+ sum = GF_CALLOC (1, sizeof (*sum), gf_client_mt_clnt_lock_t);
+ if (!sum)
+ return NULL;
+
+ sum->fl_start = min (l1->fl_start, l2->fl_start);
+ sum->fl_end = max (l1->fl_end, l2->fl_end);
+
+ sum->user_flock.l_start = sum->fl_start;
+ sum->user_flock.l_len = __get_lock_length (sum->fl_start,
+ sum->fl_end);
+
+ return sum;
+}
+
+/* Return true if the locks have the same owner */
+static int
+same_owner (client_posix_lock_t *l1, client_posix_lock_t *l2)
+{
+ return ((l1->owner == l2->owner));
+}
+
+/* Return true if the locks overlap, false otherwise */
+static int
+locks_overlap (client_posix_lock_t *l1, client_posix_lock_t *l2)
+{
+ /*
+ Note:
+ FUSE always gives us absolute offsets, so no need to worry
+ about SEEK_CUR or SEEK_END
+ */
+
+ return ((l1->fl_end >= l2->fl_start) &&
+ (l2->fl_end >= l1->fl_start));
+}
+
+static void
+__delete_client_lock (client_posix_lock_t *lock)
+{
+ list_del_init (&lock->list);
+}
+
+/* Destroy a posix_lock */
+static void
+__destroy_client_lock (client_posix_lock_t *lock)
+{
+ free (lock);
+}
+
+/* Subtract two locks */
+struct _values {
+ client_posix_lock_t *locks[3];
+};
+
+/* {big} must always be contained inside {small} */
+static struct _values
+subtract_locks (client_posix_lock_t *big, client_posix_lock_t *small)
+{
+ struct _values v = { .locks = {0, 0, 0} };
+
+ if ((big->fl_start == small->fl_start) &&
+ (big->fl_end == small->fl_end)) {
+ /* both edges coincide with big */
+ v.locks[0] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t );
+ GF_ASSERT (v.locks[0]);
+ memcpy (v.locks[0], big, sizeof (client_posix_lock_t));
+ v.locks[0]->fl_type = small->fl_type;
+ }
+ else if ((small->fl_start > big->fl_start) &&
+ (small->fl_end < big->fl_end)) {
+ /* both edges lie inside big */
+ v.locks[0] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[0]);
+ v.locks[1] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[1]);
+ v.locks[2] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[2]);
+
+ memcpy (v.locks[0], big, sizeof (client_posix_lock_t));
+ v.locks[0]->fl_end = small->fl_start - 1;
+ v.locks[0]->user_flock.l_len = __get_lock_length (v.locks[0]->fl_start,
+ v.locks[0]->fl_end);
+
+ memcpy (v.locks[1], small, sizeof (client_posix_lock_t));
+ memcpy (v.locks[2], big, sizeof (client_posix_lock_t));
+ v.locks[2]->fl_start = small->fl_end + 1;
+ v.locks[2]->user_flock.l_start = small->fl_end + 1;
+ }
+ /* one edge coincides with big */
+ else if (small->fl_start == big->fl_start) {
+ v.locks[0] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[0]);
+ v.locks[1] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[1]);
+
+ memcpy (v.locks[0], big, sizeof (client_posix_lock_t));
+ v.locks[0]->fl_start = small->fl_end + 1;
+ v.locks[0]->user_flock.l_start = small->fl_end + 1;
+
+ memcpy (v.locks[1], small, sizeof (client_posix_lock_t));
+ }
+ else if (small->fl_end == big->fl_end) {
+ v.locks[0] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[0]);
+ v.locks[1] = GF_CALLOC (1, sizeof (client_posix_lock_t),
+ gf_client_mt_clnt_lock_t);
+ GF_ASSERT (v.locks[1]);
+
+ memcpy (v.locks[0], big, sizeof (client_posix_lock_t));
+ v.locks[0]->fl_end = small->fl_start - 1;
+ v.locks[0]->user_flock.l_len = __get_lock_length (v.locks[0]->fl_start,
+ v.locks[0]->fl_end);
+
+ memcpy (v.locks[1], small, sizeof (client_posix_lock_t));
+ }
+ else {
+ gf_log ("client-protocol", GF_LOG_ERROR,
+ "Unexpected case in subtract_locks. Please send "
+ "a bug report to gluster-devel@nongnu.org");
+ }
+
+ return v;
+}
+
+static void
+__delete_unlck_locks (clnt_fd_ctx_t *fdctx)
+{
+ client_posix_lock_t *l = NULL;
+ client_posix_lock_t *tmp = NULL;
+
+ list_for_each_entry_safe (l, tmp, &fdctx->lock_list, list) {
+ if (l->fl_type == F_UNLCK) {
+ __delete_client_lock (l);
+ __destroy_client_lock (l);
+ }
+ }
+}
+
+static void
+__insert_lock (clnt_fd_ctx_t *fdctx, client_posix_lock_t *lock)
+{
+ list_add_tail (&lock->list, &fdctx->lock_list);
+
+ return;
+}
+
+static void
+__insert_and_merge (clnt_fd_ctx_t *fdctx, client_posix_lock_t *lock)
+{
+ client_posix_lock_t *conf = NULL;
+ client_posix_lock_t *t = NULL;
+ client_posix_lock_t *sum = NULL;
+ int i = 0;
+ struct _values v = { .locks = {0, 0, 0} };
+
+ list_for_each_entry_safe (conf, t, &fdctx->lock_list, list) {
+ if (!locks_overlap (conf, lock))
+ continue;
+
+ if (same_owner (conf, lock)) {
+ if (conf->fl_type == lock->fl_type) {
+ sum = add_locks (lock, conf);
+
+ sum->fd = lock->fd;
+
+ __delete_client_lock (conf);
+ __destroy_client_lock (conf);
+
+ __destroy_client_lock (lock);
+ __insert_and_merge (fdctx, sum);
+
+ return;
+ } else {
+ sum = add_locks (lock, conf);
+
+ sum->fd = conf->fd;
+ sum->owner = conf->owner;
+
+ v = subtract_locks (sum, lock);
+
+ __delete_client_lock (conf);
+ __destroy_client_lock (conf);
+
+ __delete_client_lock (lock);
+ __destroy_client_lock (lock);
+
+ __destroy_client_lock (sum);
+
+ for (i = 0; i < 3; i++) {
+ if (!v.locks[i])
+ continue;
+
+ INIT_LIST_HEAD (&v.locks[i]->list);
+ __insert_and_merge (fdctx,
+ v.locks[i]);
+ }
+
+ __delete_unlck_locks (fdctx);
+ return;
+ }
+ }
+
+ if (lock->fl_type == F_UNLCK) {
+ continue;
+ }
+
+ if ((conf->fl_type == F_RDLCK) && (lock->fl_type == F_RDLCK)) {
+ __insert_lock (fdctx, lock);
+ return;
+ }
+ }
+
+ /* no conflicts, so just insert */
+ if (lock->fl_type != F_UNLCK) {
+ __insert_lock (fdctx, lock);
+ } else {
+ __destroy_client_lock (lock);
+ }
+}
+
+static void
+client_setlk (clnt_fd_ctx_t *fdctx, client_posix_lock_t *lock)
+{
+ pthread_mutex_lock (&fdctx->mutex);
+ {
+ __insert_and_merge (fdctx, lock);
+ }
+ pthread_mutex_unlock (&fdctx->mutex);
+
+ return;
+}
+
+static void
+destroy_client_lock (client_posix_lock_t *lock)
+{
+ GF_FREE (lock);
+}
+
+int32_t
+delete_granted_locks_owner (fd_t *fd, uint64_t owner)
+{
+ clnt_fd_ctx_t *fdctx = NULL;
+ client_posix_lock_t *lock = NULL;
+ client_posix_lock_t *tmp = NULL;
+ xlator_t *this = NULL;
+
+ struct list_head delete_list;
+ int ret = 0;
+ int count = 0;
+
+ INIT_LIST_HEAD (&delete_list);
+ this = THIS;
+ fdctx = this_fd_get_ctx (fd, this);
+ if (!fdctx) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "fdctx not valid");
+ ret = -1;
+ goto out;
+ }
+
+ pthread_mutex_lock (&fdctx->mutex);
+ {
+ list_for_each_entry_safe (lock, tmp, &fdctx->lock_list, list) {
+ if (lock->owner == owner) {
+ list_del_init (&lock->list);
+ list_add_tail (&lock->list, &delete_list);
+ count++;
+ }
+ }
+ }
+ pthread_mutex_unlock (&fdctx->mutex);
+
+ list_for_each_entry_safe (lock, tmp, &delete_list, list) {
+ list_del_init (&lock->list);
+ destroy_client_lock (lock);
+ }
+
+/* FIXME: Need to actually print the locks instead of count */
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Number of locks cleared=%d", count);
+
+out:
+ return ret;
+}
+
+int32_t
+delete_granted_locks_fd (clnt_fd_ctx_t *fdctx)
+{
+ client_posix_lock_t *lock = NULL;
+ client_posix_lock_t *tmp = NULL;
+ xlator_t *this = NULL;
+
+ struct list_head delete_list;
+ int ret = 0;
+ int count = 0;
+
+ INIT_LIST_HEAD (&delete_list);
+ this = THIS;
+
+ pthread_mutex_lock (&fdctx->mutex);
+ {
+ list_splice_init (&fdctx->lock_list, &delete_list);
+ }
+ pthread_mutex_unlock (&fdctx->mutex);
+
+ list_for_each_entry_safe (lock, tmp, &delete_list, list) {
+ list_del_init (&lock->list);
+ count++;
+ destroy_client_lock (lock);
+ }
+
+ /* FIXME: Need to actually print the locks instead of count */
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Number of locks cleared=%d", count);
+
+ return ret;
+}
+
+static void
+client_mark_bad_fd (fd_t *fd, clnt_fd_ctx_t *fdctx)
+{
+ xlator_t *this = NULL;
+
+ this = THIS;
+ if (fdctx)
+ fdctx->remote_fd = -1;
+ this_fd_set_ctx (fd, this, NULL, fdctx);
+}
+
+int32_t
+client_cmd_to_gf_cmd (int32_t cmd, int32_t *gf_cmd)
+{
+ int ret = 0;
+
+ if (cmd == F_GETLK || cmd == F_GETLK64)
+ *gf_cmd = GF_LK_GETLK;
+ else if (cmd == F_SETLK || cmd == F_SETLK64)
+ *gf_cmd = GF_LK_SETLK;
+ else if (cmd == F_SETLKW || cmd == F_SETLKW64)
+ *gf_cmd = GF_LK_SETLKW;
+ else if (cmd == F_RESLK_LCK)
+ *gf_cmd = GF_LK_RESLK_LCK;
+ else if (cmd == F_RESLK_LCKW)
+ *gf_cmd = GF_LK_RESLK_LCKW;
+ else if (cmd == F_RESLK_UNLCK)
+ *gf_cmd = GF_LK_RESLK_UNLCK;
+ else
+ ret = -1;
+
+ return ret;
+
+}
+
+static client_posix_lock_t *
+new_client_lock (struct flock *flock, uint64_t owner,
+ int32_t cmd, fd_t *fd)
+{
+ client_posix_lock_t *new_lock = NULL;
+ xlator_t *this = NULL;
+
+
+ this = THIS;
+ new_lock = GF_CALLOC (1, sizeof (*new_lock),
+ gf_client_mt_clnt_lock_t);
+ if (!new_lock) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Out of memory");
+ goto out;
+ }
+
+ INIT_LIST_HEAD (&new_lock->list);
+ new_lock->fd = fd;
+ memcpy (&new_lock->user_flock, flock, sizeof (struct flock));
+
+ new_lock->fl_type = flock->l_type;
+ new_lock->fl_start = flock->l_start;
+
+ if (flock->l_len == 0)
+ new_lock->fl_end = LLONG_MAX;
+ else
+ new_lock->fl_end = flock->l_start + flock->l_len - 1;
+
+ new_lock->owner = owner;
+ new_lock->cmd = cmd; /* Not really useful */
+
+
+out:
+ return new_lock;
+}
+
+void
+client_save_number_fds (clnt_conf_t *conf, int count)
+{
+ LOCK (&conf->rec_lock);
+ {
+ conf->reopen_fd_count = count;
+ }
+ UNLOCK (&conf->rec_lock);
+}
+
+int
+client_add_lock_for_recovery (fd_t *fd, struct flock *flock, uint64_t owner,
+ int32_t cmd)
+{
+ clnt_fd_ctx_t *fdctx = NULL;
+ xlator_t *this = NULL;
+ client_posix_lock_t *lock = NULL;
+ clnt_conf_t *conf = NULL;
+
+ int ret = 0;
+
+ this = THIS;
+ conf = this->private;
+
+ pthread_mutex_lock (&conf->lock);
+ {
+ fdctx = this_fd_get_ctx (fd, this);
+ }
+ pthread_mutex_unlock (&conf->lock);
+
+ if (!fdctx) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "failed to get fd context. Marking as bad fd.");
+ ret = -EBADFD;
+ goto out;
+ }
+
+ lock = new_client_lock (flock, owner, cmd, fd);
+ if (!lock) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Out of memory");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ client_setlk (fdctx, lock);
+
+out:
+ return ret;
+
+}
+
+static int
+construct_reserve_unlock (struct flock *lock, call_frame_t *frame,
+ client_posix_lock_t *client_lock)
+{
+ GF_ASSERT (lock);
+ GF_ASSERT (frame);
+ GF_ASSERT (frame->root->lk_owner);
+
+ lock->l_type = F_UNLCK;
+ lock->l_start = 0;
+ lock->l_whence = SEEK_SET;
+ lock->l_len = 0; /* Whole file */
+ lock->l_pid = (uint64_t)frame->root;
+
+ frame->root->lk_owner = client_lock->owner;
+
+ return 0;
+}
+
+static int
+construct_reserve_lock (client_posix_lock_t *client_lock, call_frame_t *frame,
+ struct flock *lock)
+{
+ GF_ASSERT (client_lock);
+
+ memcpy (lock, &(client_lock->user_flock), sizeof (struct flock));
+
+ frame->root->lk_owner = client_lock->owner;
+
+ return 0;
+}
+
+uint64_t
+decrement_reopen_fd_count (xlator_t *this, clnt_conf_t *conf)
+{
+ uint64_t fd_count = 0;
+
+ LOCK (&conf->rec_lock);
+ {
+ fd_count = --(conf->reopen_fd_count);
+ }
+ UNLOCK (&conf->rec_lock);
+
+ if (fd_count == 0) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "All locks healed on the last fd - notifying CHILDUP");
+ client_notify_parents_child_up (this);
+ }
+
+ return fd_count;
+}
+
+int32_t
+client_remove_reserve_lock_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct flock *lock)
+{
+ clnt_local_t *local = NULL;
+ clnt_conf_t *conf = NULL;
+
+ uint64_t fd_count = 0;
+
+ local = frame->local;
+ conf = this->private;
+
+ if (op_ret < 0) {
+ /* TODO: critical error describing recovery command
+ and blanket on ops on fd */
+ gf_log (this->name, GF_LOG_CRITICAL,
+ "Lock recovery failed with error msg=%s",
+ strerror(op_errno));
+ goto cleanup;
+ }
+
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Removing reserve lock was successful.");
+
+cleanup:
+ frame->local = NULL;
+ client_mark_bad_fd (local->client_lock->fd,
+ local->fdctx);
+ destroy_client_lock (local->client_lock);
+ client_local_wipe (local);
+ STACK_DESTROY (frame->root);
+ fd_count = decrement_reopen_fd_count (this, conf);
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Need to attempt lock recovery on %lld open fds",
+ (unsigned long long) fd_count);
+ return 0;
+}
+
+static void
+client_remove_reserve_lock (xlator_t *this, call_frame_t *frame,
+ client_posix_lock_t *lock)
+{
+ struct flock unlock;
+ clnt_local_t *local = NULL;
+
+ local = frame->local;
+ construct_reserve_unlock (&unlock, frame, lock);
+
+ STACK_WIND (frame, client_remove_reserve_lock_cbk,
+ this, this->fops->lk,
+ lock->fd, F_RESLK_UNLCK, &unlock);
+}
+
+static client_posix_lock_t *
+get_next_recovery_lock (xlator_t *this, clnt_local_t *local)
+{
+ client_posix_lock_t *lock = NULL;
+
+ pthread_mutex_lock (&local->mutex);
+ {
+ if (list_empty (&local->lock_list)) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "lock-list empty");
+ goto unlock;
+ }
+
+ lock = list_entry ((local->lock_list).next, typeof (*lock), list);
+ list_del_init (&lock->list);
+ }
+unlock:
+ pthread_mutex_unlock (&local->mutex);
+
+ return lock;
+
+}
+
+int32_t
+client_reserve_lock_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct flock *lock)
+{
+
+ clnt_local_t *local = NULL;
+ clnt_conf_t *conf = NULL;
+
+ uint64_t fd_count = 0;
+
+ local = frame->local;
+ conf = this->private;
+
+ /* Got the reserve lock. Check if lock is grantable and proceed
+ with the real lk call */
+
+ if (op_ret >= 0) {
+ /* Lock is grantable if flock reflects a successful getlk() call*/
+ if (lock->l_type == F_UNLCK && lock->l_pid) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Got the reservelk, but the lock is not grantable. ");
+ client_remove_reserve_lock (this, frame, local->client_lock);
+ goto out;
+ }
+
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Reserve Lock succeeded");
+ client_send_recovery_lock (frame, this, local->client_lock);
+ goto out;
+ }
+
+ /* Somebody else has a reserve lk. Lock conflict detected.
+ Mark fd as bad */
+
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Reservelk OP failed. Aborting lock recovery and marking bad fd");
+
+ client_mark_bad_fd (local->client_lock->fd,
+ local->fdctx);
+ destroy_client_lock (local->client_lock);
+ frame->local = NULL;
+ client_local_wipe (local);
+ STACK_DESTROY (frame->root);
+ fd_count = decrement_reopen_fd_count (this, conf);
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Need to attempt lock recovery on %lld open fds",
+ (unsigned long long) fd_count);
+
+out:
+ return 0;
+}
+
+int32_t
+client_recovery_lock_cbk (call_frame_t *frame,
+ void *cookie,
+ xlator_t *this,
+ int32_t op_ret,
+ int32_t op_errno,
+ struct flock *lock)
+{
+ clnt_local_t *local = NULL;
+ clnt_fd_ctx_t *fdctx = NULL;
+ clnt_conf_t *conf = NULL;
+ client_posix_lock_t *next_lock = NULL;
+
+ struct flock reserve_flock;
+ uint64_t fd_count = 0;
+
+ local = frame->local;
+ conf = this->private;
+
+ if (op_ret < 0) {
+ /* TODO: critical error describing recovery command
+ and blanket on ops on fd */
+ gf_log (this->name, GF_LOG_CRITICAL,
+ "Lock recovery failed with error msg=%s",
+ strerror(op_errno));
+
+ client_mark_bad_fd (local->client_lock->fd,
+ local->fdctx);
+ goto cleanup;
+
+ /* Lock recovered. Continue with reserve lock for next lock */
+ } else {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "lock recovered successfully - Continuing with next lock.");
+
+ next_lock = get_next_recovery_lock (this, local);
+ if (!next_lock) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "All locks recovered on fd");
+ goto cleanup;
+ }
+
+ construct_reserve_lock (next_lock, frame, &reserve_flock);
+ local->fdctx = fdctx;
+ local->client_lock = next_lock;
+
+ STACK_WIND (frame, client_reserve_lock_cbk,
+ this, this->fops->lk,
+ next_lock->fd, F_RESLK_LCK, &reserve_flock);
+ goto out;
+
+ }
+
+cleanup:
+ frame->local = NULL;
+ client_local_wipe (local);
+ if (local->client_lock)
+ destroy_client_lock (local->client_lock);
+ STACK_DESTROY (frame->root);
+ fd_count = decrement_reopen_fd_count (this, conf);
+ gf_log (this->name, GF_LOG_DEBUG,
+ "Need to attempt lock recovery on %lld open fds",
+ (unsigned long long) fd_count);
+
+out:
+ return 0;
+}
+
+static int
+client_send_recovery_lock (call_frame_t *frame, xlator_t *this,
+ client_posix_lock_t *lock)
+{
+
+ frame->root->lk_owner = lock->owner;
+
+ /* Send all locks as F_SETLK to prevent the frame
+ from blocking if there is a conflict */
+
+ STACK_WIND (frame, client_recovery_lock_cbk,
+ this, this->fops->lk,
+ lock->fd, F_SETLK,
+ &(lock->user_flock));
+
+ return 0;
+}
+
+static int
+client_lockrec_init (clnt_fd_ctx_t *fdctx, clnt_local_t *local)
+{
+
+ INIT_LIST_HEAD (&local->lock_list);
+ pthread_mutex_init (&local->mutex, NULL);
+
+ pthread_mutex_lock (&fdctx->mutex);
+ {
+ list_splice_init (&fdctx->lock_list, &local->lock_list);
+ }
+ pthread_mutex_unlock (&fdctx->mutex);
+
+ return 0;
+}
+
+int
+client_attempt_lock_recovery (xlator_t *this, clnt_fd_ctx_t *fdctx)
+{
+ call_frame_t *frame = NULL;
+ clnt_local_t *local = NULL;
+ client_posix_lock_t *lock = NULL;
+
+ struct flock reserve_flock;
+ int ret = 0;
+
+ local = GF_CALLOC (1, sizeof (*local), gf_client_mt_clnt_local_t);
+ if (!local) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Out of memory");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ client_lockrec_init (fdctx, local);
+
+ lock = get_next_recovery_lock (this, local);
+ if (!lock) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "No locks on fd");
+ ret = -1;
+ goto out;
+ }
+
+ frame = create_frame (this, this->ctx->pool);
+ if (!frame) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ construct_reserve_lock (lock, frame, &reserve_flock);
+
+ local->fdctx = fdctx;
+ local->client_lock = lock;
+
+ STACK_WIND (frame, client_reserve_lock_cbk,
+ this, this->fops->lk,
+ lock->fd, F_RESLK_LCK, &reserve_flock);
+
+out:
+ return ret;
+
+
+}
+
+int32_t
+client_dump_locks (char *name, inode_t *inode,
+ dict_t *dict)
+{
+ int ret = 0;
+ char dict_string[256];
+
+ ret = dump_client_locks (inode);
+ snprintf (dict_string, 256, "%d locks dumped in log file", ret);
+
+ dict = dict_new ();
+ if (!dict) {
+ gf_log (THIS->name, GF_LOG_DEBUG,
+ "Out of memory");
+ ret = -1;
+ goto out;
+ }
+
+ ret = dict_set_str (dict, "trusted.glusterfs.clientlk-dump", dict_string);
+ if (ret) {
+ gf_log (THIS->name, GF_LOG_DEBUG,
+ "Could not set dict with %s", CLIENT_DUMP_LOCKS);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int32_t
+is_client_dump_locks_cmd (char *name)
+{
+ int ret = 0;
+
+ if (strcmp (name, CLIENT_DUMP_LOCKS) == 0)
+ ret = 1;
+
+ return ret;
+}