diff options
Diffstat (limited to 'xlators/features/locks/src/clear.c')
| -rw-r--r-- | xlators/features/locks/src/clear.c | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/xlators/features/locks/src/clear.c b/xlators/features/locks/src/clear.c new file mode 100644 index 00000000000..ab1eac68a53 --- /dev/null +++ b/xlators/features/locks/src/clear.c @@ -0,0 +1,460 @@ +/* + Copyright (c) 2006-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 <unistd.h> +#include <fcntl.h> +#include <limits.h> +#include <pthread.h> + +#include <glusterfs/glusterfs.h> +#include <glusterfs/compat.h> +#include <glusterfs/xlator.h> +#include <glusterfs/logging.h> +#include <glusterfs/common-utils.h> + +#include "locks.h" +#include "common.h" +#include <glusterfs/statedump.h> +#include "clear.h" + +const char *clrlk_type_names[CLRLK_TYPE_MAX] = { + [CLRLK_INODE] = "inode", + [CLRLK_ENTRY] = "entry", + [CLRLK_POSIX] = "posix", +}; + +int +clrlk_get_kind(char *kind) +{ + char *clrlk_kinds[CLRLK_KIND_MAX] = {"dummy", "blocked", "granted", "all"}; + int ret_kind = CLRLK_KIND_MAX; + int i = 0; + + for (i = CLRLK_BLOCKED; i < CLRLK_KIND_MAX; i++) { + if (!strcmp(clrlk_kinds[i], kind)) { + ret_kind = i; + break; + } + } + + return ret_kind; +} + +int +clrlk_get_type(char *type) +{ + char *clrlk_types[CLRLK_TYPE_MAX] = {"inode", "entry", "posix"}; + int ret_type = CLRLK_TYPE_MAX; + int i = 0; + + for (i = CLRLK_INODE; i < CLRLK_TYPE_MAX; i++) { + if (!strcmp(clrlk_types[i], type)) { + ret_type = i; + break; + } + } + + return ret_type; +} + +int +clrlk_get_lock_range(char *range_str, struct gf_flock *ulock, + gf_boolean_t *chk_range) +{ + int ret = -1; + + if (!chk_range) + goto out; + + if (!range_str) { + ret = 0; + *chk_range = _gf_false; + goto out; + } + + if (sscanf(range_str, + "%hd,%" PRId64 "-" + "%" PRId64, + &ulock->l_whence, &ulock->l_start, &ulock->l_len) != 3) { + goto out; + } + + ret = 0; + *chk_range = _gf_true; +out: + return ret; +} + +int +clrlk_parse_args(const char *cmd, clrlk_args *args) +{ + char *opts = NULL; + char *cur = NULL; + char *tok = NULL; + char *sptr = NULL; + char *free_ptr = NULL; + char kw[KW_MAX] = { + [KW_TYPE] = 't', + [KW_KIND] = 'k', + }; + int ret = -1; + int i = 0; + + GF_ASSERT(cmd); + free_ptr = opts = GF_CALLOC(1, strlen(cmd), gf_common_mt_char); + if (!opts) + goto out; + + if (sscanf(cmd, GF_XATTR_CLRLK_CMD ".%s", opts) < 1) { + ret = -1; + goto out; + } + + /*clr_lk_prefix.ttype.kkind.args, args - type specific*/ + cur = opts; + for (i = 0; i < KW_MAX && (tok = strtok_r(cur, ".", &sptr)); + cur = NULL, i++) { + if (tok[0] != kw[i]) { + ret = -1; + goto out; + } + if (i == KW_TYPE) + args->type = clrlk_get_type(tok + 1); + if (i == KW_KIND) + args->kind = clrlk_get_kind(tok + 1); + } + + if ((args->type == CLRLK_TYPE_MAX) || (args->kind == CLRLK_KIND_MAX)) + goto out; + + /*optional args, neither range nor basename can 'legally' contain + * "/" in them*/ + tok = strtok_r(NULL, "/", &sptr); + if (tok) + args->opts = gf_strdup(tok); + + ret = 0; +out: + GF_FREE(free_ptr); + return ret; +} + +int +clrlk_clear_posixlk(xlator_t *this, pl_inode_t *pl_inode, clrlk_args *args, + int *blkd, int *granted, int *op_errno) +{ + posix_lock_t *plock = NULL; + posix_lock_t *tmp = NULL; + struct gf_flock ulock = { + 0, + }; + int ret = -1; + int bcount = 0; + int gcount = 0; + gf_boolean_t chk_range = _gf_false; + + if (clrlk_get_lock_range(args->opts, &ulock, &chk_range)) { + *op_errno = EINVAL; + goto out; + } + + pthread_mutex_lock(&pl_inode->mutex); + { + list_for_each_entry_safe(plock, tmp, &pl_inode->ext_list, list) + { + if ((plock->blocked && !(args->kind & CLRLK_BLOCKED)) || + (!plock->blocked && !(args->kind & CLRLK_GRANTED))) + continue; + + if (chk_range && (plock->user_flock.l_whence != ulock.l_whence || + plock->user_flock.l_start != ulock.l_start || + plock->user_flock.l_len != ulock.l_len)) + continue; + + list_del_init(&plock->list); + if (plock->blocked) { + bcount++; + pl_trace_out(this, plock->frame, NULL, NULL, F_SETLKW, + &plock->user_flock, -1, EINTR, NULL); + + STACK_UNWIND_STRICT(lk, plock->frame, -1, EINTR, + &plock->user_flock, NULL); + + } else { + gcount++; + } + __destroy_lock(plock); + } + } + pthread_mutex_unlock(&pl_inode->mutex); + grant_blocked_locks(this, pl_inode); + ret = 0; +out: + *blkd = bcount; + *granted = gcount; + return ret; +} + +/* Returns 0 on success and -1 on failure */ +int +clrlk_clear_inodelk(xlator_t *this, pl_inode_t *pl_inode, pl_dom_list_t *dom, + clrlk_args *args, int *blkd, int *granted, int *op_errno) +{ + posix_locks_private_t *priv; + pl_inode_lock_t *ilock = NULL; + pl_inode_lock_t *tmp = NULL; + struct gf_flock ulock = { + 0, + }; + int ret = -1; + int bcount = 0; + int gcount = 0; + gf_boolean_t chk_range = _gf_false; + struct list_head *pcontend = NULL; + struct list_head released; + struct list_head contend; + struct timespec now = {}; + + INIT_LIST_HEAD(&released); + + priv = this->private; + if (priv->notify_contention) { + pcontend = &contend; + INIT_LIST_HEAD(pcontend); + timespec_now(&now); + } + + if (clrlk_get_lock_range(args->opts, &ulock, &chk_range)) { + *op_errno = EINVAL; + goto out; + } + + if (args->kind & CLRLK_BLOCKED) + goto blkd; + + if (args->kind & CLRLK_GRANTED) + goto granted; + +blkd: + pthread_mutex_lock(&pl_inode->mutex); + { + list_for_each_entry_safe(ilock, tmp, &dom->blocked_inodelks, + blocked_locks) + { + if (chk_range && (ilock->user_flock.l_whence != ulock.l_whence || + ilock->user_flock.l_start != ulock.l_start || + ilock->user_flock.l_len != ulock.l_len)) + continue; + + bcount++; + list_del_init(&ilock->client_list); + list_del_init(&ilock->blocked_locks); + list_add(&ilock->blocked_locks, &released); + } + } + pthread_mutex_unlock(&pl_inode->mutex); + + if (!list_empty(&released)) { + list_for_each_entry_safe(ilock, tmp, &released, blocked_locks) + { + list_del_init(&ilock->blocked_locks); + pl_trace_out(this, ilock->frame, NULL, NULL, F_SETLKW, + &ilock->user_flock, -1, EAGAIN, ilock->volume); + STACK_UNWIND_STRICT(inodelk, ilock->frame, -1, EAGAIN, NULL); + // No need to take lock as the locks are only in one list + __pl_inodelk_unref(ilock); + } + } + + if (!(args->kind & CLRLK_GRANTED)) { + ret = 0; + goto out; + } + +granted: + pthread_mutex_lock(&pl_inode->mutex); + { + list_for_each_entry_safe(ilock, tmp, &dom->inodelk_list, list) + { + if (chk_range && (ilock->user_flock.l_whence != ulock.l_whence || + ilock->user_flock.l_start != ulock.l_start || + ilock->user_flock.l_len != ulock.l_len)) + continue; + + gcount++; + list_del_init(&ilock->client_list); + list_del_init(&ilock->list); + list_add(&ilock->list, &released); + } + } + pthread_mutex_unlock(&pl_inode->mutex); + + list_for_each_entry_safe(ilock, tmp, &released, list) + { + list_del_init(&ilock->list); + // No need to take lock as the locks are only in one list + __pl_inodelk_unref(ilock); + } + + ret = 0; +out: + grant_blocked_inode_locks(this, pl_inode, dom, &now, pcontend); + if (pcontend != NULL) { + inodelk_contention_notify(this, pcontend); + } + *blkd = bcount; + *granted = gcount; + return ret; +} + +/* Returns 0 on success and -1 on failure */ +int +clrlk_clear_entrylk(xlator_t *this, pl_inode_t *pl_inode, pl_dom_list_t *dom, + clrlk_args *args, int *blkd, int *granted, int *op_errno) +{ + posix_locks_private_t *priv; + pl_entry_lock_t *elock = NULL; + pl_entry_lock_t *tmp = NULL; + int bcount = 0; + int gcount = 0; + int ret = -1; + struct list_head *pcontend = NULL; + struct list_head removed; + struct list_head released; + struct list_head contend; + struct timespec now; + + INIT_LIST_HEAD(&released); + + priv = this->private; + if (priv->notify_contention) { + pcontend = &contend; + INIT_LIST_HEAD(pcontend); + timespec_now(&now); + } + + if (args->kind & CLRLK_BLOCKED) + goto blkd; + + if (args->kind & CLRLK_GRANTED) + goto granted; + +blkd: + pthread_mutex_lock(&pl_inode->mutex); + { + list_for_each_entry_safe(elock, tmp, &dom->blocked_entrylks, + blocked_locks) + { + if (args->opts) { + if (!elock->basename || strcmp(elock->basename, args->opts)) + continue; + } + + bcount++; + + list_del_init(&elock->client_list); + list_del_init(&elock->blocked_locks); + list_add_tail(&elock->blocked_locks, &released); + } + } + pthread_mutex_unlock(&pl_inode->mutex); + + if (!list_empty(&released)) { + list_for_each_entry_safe(elock, tmp, &released, blocked_locks) + { + list_del_init(&elock->blocked_locks); + entrylk_trace_out(this, elock->frame, elock->volume, NULL, NULL, + elock->basename, ENTRYLK_LOCK, elock->type, -1, + EAGAIN); + STACK_UNWIND_STRICT(entrylk, elock->frame, -1, EAGAIN, NULL); + + __pl_entrylk_unref(elock); + } + } + + if (!(args->kind & CLRLK_GRANTED)) { + ret = 0; + goto out; + } + +granted: + INIT_LIST_HEAD(&removed); + pthread_mutex_lock(&pl_inode->mutex); + { + list_for_each_entry_safe(elock, tmp, &dom->entrylk_list, domain_list) + { + if (args->opts) { + if (!elock->basename || strcmp(elock->basename, args->opts)) + continue; + } + + gcount++; + list_del_init(&elock->client_list); + list_del_init(&elock->domain_list); + list_add_tail(&elock->domain_list, &removed); + + __pl_entrylk_unref(elock); + } + } + pthread_mutex_unlock(&pl_inode->mutex); + + grant_blocked_entry_locks(this, pl_inode, dom, &now, pcontend); + if (pcontend != NULL) { + entrylk_contention_notify(this, pcontend); + } + + ret = 0; +out: + *blkd = bcount; + *granted = gcount; + return ret; +} + +int +clrlk_clear_lks_in_all_domains(xlator_t *this, pl_inode_t *pl_inode, + clrlk_args *args, int *blkd, int *granted, + int *op_errno) +{ + pl_dom_list_t *dom = NULL; + int ret = -1; + int tmp_bcount = 0; + int tmp_gcount = 0; + + if (list_empty(&pl_inode->dom_list)) { + ret = 0; + goto out; + } + + list_for_each_entry(dom, &pl_inode->dom_list, inode_list) + { + tmp_bcount = tmp_gcount = 0; + + switch (args->type) { + case CLRLK_INODE: + ret = clrlk_clear_inodelk(this, pl_inode, dom, args, + &tmp_bcount, &tmp_gcount, op_errno); + if (ret) + goto out; + break; + case CLRLK_ENTRY: + ret = clrlk_clear_entrylk(this, pl_inode, dom, args, + &tmp_bcount, &tmp_gcount, op_errno); + if (ret) + goto out; + break; + } + + *blkd += tmp_bcount; + *granted += tmp_gcount; + } + + ret = 0; +out: + return ret; +} |
