diff options
Diffstat (limited to 'xlators/protocol/client/src/client-lk.c')
| -rw-r--r-- | xlators/protocol/client/src/client-lk.c | 515 |
1 files changed, 515 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..795839734c5 --- /dev/null +++ b/xlators/protocol/client/src/client-lk.c @@ -0,0 +1,515 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include <glusterfs/common-utils.h> +#include <glusterfs/xlator.h> +#include "client.h" +#include <glusterfs/lkowner.h> +#include "client-messages.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_smsg( + this->name, GF_LOG_INFO, 0, PC_MSG_CLIENT_LOCK_INFO, "fd=%p", lock->fd, + "fl_type=%s", lock->fl_type == F_WRLCK ? "Write-Lock" : "Read-Lock", + "lk-owner=%s", lkowner_utoa(&lock->owner), "l_start=%" PRId64, + lock->user_flock.l_start, "l_len=%" PRId64, lock->user_flock.l_len, + "start=%" PRId64, lock->fl_start, "end=%" PRId64, lock->fl_end, NULL); +} + +static int +dump_client_locks_fd(clnt_fd_ctx_t *fdctx) +{ + client_posix_lock_t *lock = NULL; + int count = 0; + + list_for_each_entry(lock, &fdctx->lock_list, list) + { + __dump_client_lock(lock); + count++; + } + + return count; +} + +int +dump_client_locks(inode_t *inode) +{ + fd_t *fd = NULL; + xlator_t *this = NULL; + clnt_fd_ctx_t *fdctx = NULL; + clnt_conf_t *conf = 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_spin_lock(&conf->fd_lock); + fdctx = this_fd_get_ctx(fd, this); + if (fdctx) + locks_fd_count = dump_client_locks_fd(fdctx); + pthread_spin_unlock(&conf->fd_lock); + + 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_MALLOC(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_MALLOC(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_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); + v.locks[1] = GF_MALLOC(sizeof(client_posix_lock_t), + gf_client_mt_clnt_lock_t); + GF_ASSERT(v.locks[1]); + memcpy(v.locks[1], small, sizeof(client_posix_lock_t)); + v.locks[2] = GF_MALLOC(sizeof(client_posix_lock_t), + gf_client_mt_clnt_lock_t); + GF_ASSERT(v.locks[2]); + 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_MALLOC(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_start = small->fl_end + 1; + v.locks[0]->user_flock.l_start = small->fl_end + 1; + v.locks[1] = GF_MALLOC(sizeof(client_posix_lock_t), + gf_client_mt_clnt_lock_t); + GF_ASSERT(v.locks[1]); + memcpy(v.locks[1], small, sizeof(client_posix_lock_t)); + } else if (small->fl_end == big->fl_end) { + v.locks[0] = GF_MALLOC(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_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); + + v.locks[1] = GF_MALLOC(sizeof(client_posix_lock_t), + gf_client_mt_clnt_lock_t); + GF_ASSERT(v.locks[1]); + memcpy(v.locks[1], small, sizeof(client_posix_lock_t)); + } else { + /* LOG-TODO : decide what more info is required here*/ + gf_smsg("client-protocol", GF_LOG_CRITICAL, 0, PC_MSG_LOCK_ERROR, NULL); + } + + 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) +{ + __insert_and_merge(fdctx, lock); +} + +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; + clnt_conf_t *conf = NULL; + + struct list_head delete_list; + int ret = 0; + int count = 0; + + INIT_LIST_HEAD(&delete_list); + this = THIS; + conf = this->private; + + pthread_spin_lock(&conf->fd_lock); + + fdctx = this_fd_get_ctx(fd, this); + if (!fdctx) { + pthread_spin_unlock(&conf->fd_lock); + + gf_smsg(this->name, GF_LOG_WARNING, EINVAL, PC_MSG_FD_CTX_INVALID, + NULL); + ret = -1; + goto out; + } + + 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_spin_unlock(&conf->fd_lock); + + if (!list_empty(&delete_list)) { + 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_msg_trace(this->name, 0, "Number of locks cleared=%d", count); + +out: + 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_spin_lock(&conf->fd_lock); + + fdctx = this_fd_get_ctx(fd, this); + if (!fdctx) { + pthread_spin_unlock(&conf->fd_lock); + + gf_smsg(this->name, GF_LOG_WARNING, 0, PC_MSG_FD_GET_FAIL, NULL); + ret = -EBADFD; + goto out; + } + + lock = new_client_lock(flock, owner, cmd, fd); + if (!lock) { + pthread_spin_unlock(&conf->fd_lock); + + ret = -ENOMEM; + goto out; + } + + client_setlk(fdctx, lock); + + pthread_spin_unlock(&conf->fd_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_smsg(THIS->name, GF_LOG_WARNING, 0, PC_MSG_DICT_SET_FAIL, "lock=%s", + CLIENT_DUMP_LOCKS, NULL); + 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; +} |
