/* Copyright (c) 2008-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include "common-utils.h" #include "xlator.h" #include "client.h" #include "lkowner.h" static void __insert_and_merge (clnt_fd_ctx_t *fdctx, 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_INFO, "{fd=%p}" "{%s lk-owner:%s %"PRId64" - %"PRId64"}" "{start=%"PRId64" end=%"PRId64"}", lock->fd, lock->fl_type == F_WRLCK ? "Write-Lock" : "Read-Lock", lkowner_utoa (&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 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) { GF_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 { /* LOG-TODO : decide what more info is required here*/ gf_log ("client-protocol", GF_LOG_CRITICAL, "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 (is_same_lkowner (&conf->owner, &lock->owner)) { 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, gf_lkowner_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_WARNING, "fdctx not valid"); ret = -1; goto out; } pthread_mutex_lock (&fdctx->mutex); { list_for_each_entry_safe (lock, tmp, &fdctx->lock_list, list) { if (!is_same_lkowner (&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_TRACE, "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_TRACE, "Number of locks cleared=%d", count); return ret; } 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 if (cmd == F_GETLK_FD) *gf_cmd = GF_LK_GETLK_FD; else ret = -1; return ret; } static client_posix_lock_t * new_client_lock (struct gf_flock *flock, gf_lkowner_t *owner, int32_t cmd, fd_t *fd) { client_posix_lock_t *new_lock = NULL; new_lock = GF_CALLOC (1, sizeof (*new_lock), gf_client_mt_clnt_lock_t); if (!new_lock) { goto out; } INIT_LIST_HEAD (&new_lock->list); new_lock->fd = fd; memcpy (&new_lock->user_flock, flock, sizeof (struct gf_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 gf_flock *flock, gf_lkowner_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_WARNING, "failed to get fd context. sending EBADFD"); ret = -EBADFD; goto out; } lock = new_client_lock (flock, owner, cmd, fd); if (!lock) { ret = -ENOMEM; goto out; } client_setlk (fdctx, lock); out: return ret; } int32_t client_dump_locks (char *name, inode_t *inode, dict_t *dict) { int ret = 0; dict_t *new_dict = NULL; char dict_string[256]; GF_ASSERT (dict); new_dict = dict; ret = dump_client_locks (inode); snprintf (dict_string, 256, "%d locks dumped in log file", ret); ret = dict_set_dynstr(new_dict, CLIENT_DUMP_LOCKS, dict_string); if (ret) { gf_log (THIS->name, GF_LOG_WARNING, "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; }