/* 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 "glusterfs/fd-lk.h" #include "glusterfs/common-utils.h" #include "glusterfs/libglusterfs-messages.h" int32_t _fd_lk_delete_lock(fd_lk_ctx_node_t *lock) { int32_t ret = -1; GF_VALIDATE_OR_GOTO("fd-lk", lock, out); list_del_init(&lock->next); ret = 0; out: return ret; } int32_t _fd_lk_destroy_lock(fd_lk_ctx_node_t *lock) { int32_t ret = -1; GF_VALIDATE_OR_GOTO("fd-lk", lock, out); GF_FREE(lock); ret = 0; out: return ret; } int _fd_lk_destroy_lock_list(fd_lk_ctx_t *lk_ctx) { int ret = -1; fd_lk_ctx_node_t *lk = NULL; fd_lk_ctx_node_t *tmp = NULL; GF_VALIDATE_OR_GOTO("fd-lk", lk_ctx, out); list_for_each_entry_safe(lk, tmp, &lk_ctx->lk_list, next) { _fd_lk_delete_lock(lk); _fd_lk_destroy_lock(lk); } ret = 0; out: return ret; } int fd_lk_ctx_unref(fd_lk_ctx_t *lk_ctx) { int ref = -1; GF_VALIDATE_OR_GOTO("fd-lk", lk_ctx, err); ref = GF_ATOMIC_DEC(lk_ctx->ref); if (ref < 0) GF_ASSERT(!ref); if (ref == 0) _fd_lk_destroy_lock_list(lk_ctx); if (ref == 0) { LOCK_DESTROY(&lk_ctx->lock); GF_FREE(lk_ctx); } return 0; err: return -1; } fd_lk_ctx_t * fd_lk_ctx_ref(fd_lk_ctx_t *lk_ctx) { if (!lk_ctx) { gf_msg_callingfn("fd-lk", GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, "invalid argument"); return NULL; } GF_ATOMIC_INC(lk_ctx->ref); return lk_ctx; } fd_lk_ctx_t * fd_lk_ctx_create() { fd_lk_ctx_t *fd_lk_ctx = NULL; fd_lk_ctx = GF_CALLOC(1, sizeof(fd_lk_ctx_t), gf_common_mt_fd_lk_ctx_t); if (!fd_lk_ctx) goto out; INIT_LIST_HEAD(&fd_lk_ctx->lk_list); LOCK_INIT(&fd_lk_ctx->lock); fd_lk_ctx = fd_lk_ctx_ref(fd_lk_ctx); out: return fd_lk_ctx; } int _fd_lk_insert_lock(fd_lk_ctx_t *lk_ctx, fd_lk_ctx_node_t *lock) { list_add_tail(&lock->next, &lk_ctx->lk_list); return 0; } static off_t _fd_lk_get_lock_len(off_t start, off_t end) { if (end == LLONG_MAX) return 0; else return (end - start + 1); } fd_lk_ctx_node_t * fd_lk_ctx_node_new(int32_t cmd, struct gf_flock *flock) { fd_lk_ctx_node_t *new_lock = NULL; /* TODO: get from mem-pool */ new_lock = GF_CALLOC(1, sizeof(fd_lk_ctx_node_t), gf_common_mt_fd_lk_ctx_node_t); if (!new_lock) goto out; new_lock->cmd = cmd; if (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; memcpy(&new_lock->user_flock, flock, sizeof(struct gf_flock)); } INIT_LIST_HEAD(&new_lock->next); out: return new_lock; } int32_t _fd_lk_delete_unlck_locks(fd_lk_ctx_t *lk_ctx) { int32_t ret = -1; fd_lk_ctx_node_t *tmp = NULL; fd_lk_ctx_node_t *lk = NULL; GF_VALIDATE_OR_GOTO("fd-lk", lk_ctx, out); list_for_each_entry_safe(lk, tmp, &lk_ctx->lk_list, next) { if (lk->fl_type == F_UNLCK) { _fd_lk_delete_lock(lk); _fd_lk_destroy_lock(lk); } } out: return ret; } int fd_lk_overlap(fd_lk_ctx_node_t *l1, fd_lk_ctx_node_t *l2) { if (l1->fl_end >= l2->fl_start && l2->fl_end >= l1->fl_start) return 1; return 0; } fd_lk_ctx_node_t * _fd_lk_add_locks(fd_lk_ctx_node_t *l1, fd_lk_ctx_node_t *l2) { fd_lk_ctx_node_t *sum = NULL; sum = fd_lk_ctx_node_new(0, NULL); if (!sum) goto out; 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 = _fd_lk_get_lock_len(sum->fl_start, sum->fl_end); out: return sum; } /* Subtract two locks */ struct _values { fd_lk_ctx_node_t *locks[3]; }; int32_t _fd_lk_sub_locks(struct _values *v, fd_lk_ctx_node_t *big, fd_lk_ctx_node_t *small) { int32_t ret = -1; if ((big->fl_start == small->fl_start) && (big->fl_end == small->fl_end)) { /* both edges coincide with big */ v->locks[0] = fd_lk_ctx_node_new(small->cmd, NULL); if (!v->locks[0]) goto out; memcpy(v->locks[0], big, sizeof(fd_lk_ctx_node_t)); v->locks[0]->fl_type = small->fl_type; v->locks[0]->user_flock.l_type = small->fl_type; } else if ((small->fl_start > big->fl_start) && (small->fl_end < big->fl_end)) { /* small lock is completely inside big lock, break it down into 3 different locks. */ v->locks[0] = fd_lk_ctx_node_new(big->cmd, NULL); if (!v->locks[0]) goto out; v->locks[1] = fd_lk_ctx_node_new(small->cmd, NULL); if (!v->locks[1]) goto out; v->locks[2] = fd_lk_ctx_node_new(big->cmd, NULL); if (!v->locks[2]) goto out; memcpy(v->locks[0], big, sizeof(fd_lk_ctx_node_t)); v->locks[0]->fl_end = small->fl_start - 1; v->locks[0]->user_flock.l_len = _fd_lk_get_lock_len( v->locks[0]->fl_start, v->locks[0]->fl_end); memcpy(v->locks[1], small, sizeof(fd_lk_ctx_node_t)); memcpy(v->locks[2], big, sizeof(fd_lk_ctx_node_t)); v->locks[2]->fl_start = small->fl_end + 1; v->locks[2]->user_flock.l_len = _fd_lk_get_lock_len( v->locks[2]->fl_start, v->locks[2]->fl_end); } else if (small->fl_start == big->fl_start) { /* One of the ends co-incide, break the locks into two separate parts */ v->locks[0] = fd_lk_ctx_node_new(small->cmd, NULL); if (!v->locks[0]) goto out; v->locks[1] = fd_lk_ctx_node_new(big->cmd, NULL); if (!v->locks[1]) goto out; memcpy(v->locks[0], small, sizeof(fd_lk_ctx_node_t)); memcpy(v->locks[1], big, sizeof(fd_lk_ctx_node_t)); v->locks[1]->fl_start = small->fl_end + 1; v->locks[1]->user_flock.l_start = small->fl_end + 1; } else if (small->fl_end == big->fl_end) { /* One of the ends co-incide, break the locks into two separate parts */ v->locks[0] = fd_lk_ctx_node_new(small->cmd, NULL); if (!v->locks[0]) goto out; v->locks[1] = fd_lk_ctx_node_new(big->cmd, NULL); if (!v->locks[1]) goto out; memcpy(v->locks[0], big, sizeof(fd_lk_ctx_node_t)); v->locks[0]->fl_end = small->fl_start - 1; v->locks[0]->user_flock.l_len = _fd_lk_get_lock_len( v->locks[0]->fl_start, v->locks[0]->fl_end); memcpy(v->locks[1], small, sizeof(fd_lk_ctx_node_t)); } else { /* We should never come to this case */ GF_ASSERT(!"Invalid case"); } ret = 0; out: return ret; } static void _fd_lk_insert_and_merge(fd_lk_ctx_t *lk_ctx, fd_lk_ctx_node_t *lock) { int32_t ret = -1; int32_t i = 0; fd_lk_ctx_node_t *entry = NULL; fd_lk_ctx_node_t *t = NULL; fd_lk_ctx_node_t *sum = NULL; struct _values v = {.locks = {0, 0, 0}}; list_for_each_entry_safe(entry, t, &lk_ctx->lk_list, next) { if (!fd_lk_overlap(entry, lock)) continue; if (entry->fl_type == lock->fl_type) { sum = _fd_lk_add_locks(entry, lock); if (!sum) return; sum->fl_type = entry->fl_type; sum->user_flock.l_type = entry->fl_type; _fd_lk_delete_lock(entry); _fd_lk_destroy_lock(entry); _fd_lk_destroy_lock(lock); _fd_lk_insert_and_merge(lk_ctx, sum); return; } else { sum = _fd_lk_add_locks(entry, lock); sum->fl_type = lock->fl_type; sum->user_flock.l_type = lock->fl_type; ret = _fd_lk_sub_locks(&v, sum, lock); if (ret) return; _fd_lk_delete_lock(entry); _fd_lk_destroy_lock(entry); _fd_lk_delete_lock(lock); _fd_lk_destroy_lock(lock); _fd_lk_destroy_lock(sum); for (i = 0; i < 3; i++) { if (!v.locks[i]) continue; INIT_LIST_HEAD(&v.locks[i]->next); _fd_lk_insert_and_merge(lk_ctx, v.locks[i]); } _fd_lk_delete_unlck_locks(lk_ctx); return; } } /* no conflicts, so just insert */ if (lock->fl_type != F_UNLCK) { _fd_lk_insert_lock(lk_ctx, lock); } else { _fd_lk_destroy_lock(lock); } } static void print_lock_list(fd_lk_ctx_t *lk_ctx) { fd_lk_ctx_node_t *lk = NULL; gf_msg_debug("fd-lk", 0, "lock list:"); list_for_each_entry(lk, &lk_ctx->lk_list, next) gf_msg_debug("fd-lk", 0, "owner = %s, cmd = %s fl_type = %s," " fs_start = %" PRId64 ", fs_end = %" PRId64 ", " "user_flock: l_type = %s, l_start = %" PRId64 ", " "l_len = %" PRId64 ", ", lkowner_utoa(&lk->user_flock.l_owner), get_lk_cmd(lk->cmd), get_lk_type(lk->fl_type), lk->fl_start, lk->fl_end, get_lk_type(lk->user_flock.l_type), lk->user_flock.l_start, lk->user_flock.l_len); } int fd_lk_insert_and_merge(fd_t *fd, int32_t cmd, struct gf_flock *flock) { int32_t ret = -1; fd_lk_ctx_t *lk_ctx = NULL; fd_lk_ctx_node_t *lk = NULL; GF_VALIDATE_OR_GOTO("fd-lk", fd, out); GF_VALIDATE_OR_GOTO("fd-lk", flock, out); lk_ctx = fd_lk_ctx_ref(fd->lk_ctx); lk = fd_lk_ctx_node_new(cmd, flock); gf_msg_debug("fd-lk", 0, "new lock request: owner = %s, fl_type = %s" ", fs_start = %" PRId64 ", fs_end = %" PRId64 ", user_flock:" " l_type = %s, l_start = %" PRId64 ", l_len = %" PRId64, lkowner_utoa(&flock->l_owner), get_lk_type(lk->fl_type), lk->fl_start, lk->fl_end, get_lk_type(lk->user_flock.l_type), lk->user_flock.l_start, lk->user_flock.l_len); LOCK(&lk_ctx->lock); { _fd_lk_insert_and_merge(lk_ctx, lk); print_lock_list(lk_ctx); } UNLOCK(&lk_ctx->lock); fd_lk_ctx_unref(lk_ctx); ret = 0; out: return ret; } gf_boolean_t fd_lk_ctx_empty(fd_lk_ctx_t *lk_ctx) { gf_boolean_t verdict = _gf_true; if (!lk_ctx) return _gf_true; LOCK(&lk_ctx->lock); { verdict = list_empty(&lk_ctx->lk_list); } UNLOCK(&lk_ctx->lock); return verdict; }