You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1254 lines
43 KiB
1254 lines
43 KiB
3 years ago
|
From 3f6ff474db3934f43d9963dfe4dda7d201211e75 Mon Sep 17 00:00:00 2001
|
||
|
From: Xavi Hernandez <xhernandez@redhat.com>
|
||
|
Date: Fri, 12 Jun 2020 00:06:36 +0200
|
||
|
Subject: [PATCH 455/456] locks: prevent deletion of locked entries
|
||
|
|
||
|
To keep consistency inside transactions started by locking an entry or
|
||
|
an inode, this change delays the removal of entries that are currently
|
||
|
locked by one or more clients. Once all locks are released, the removal
|
||
|
is processed.
|
||
|
|
||
|
It has also been improved the detection of stale inodes in the locking
|
||
|
code of EC.
|
||
|
|
||
|
>Upstream patch - https://review.gluster.org/#/c/glusterfs/+/20025/
|
||
|
>Fixes: #990
|
||
|
|
||
|
Change-Id: Ic8ba23d9480f80c7f74e7a310bf8a15922320fd5
|
||
|
BUG: 1812789
|
||
|
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
|
||
|
Reviewed-on: https://code.engineering.redhat.com/gerrit/206442
|
||
|
Tested-by: RHGS Build Bot <nigelb@redhat.com>
|
||
|
---
|
||
|
xlators/cluster/ec/src/ec-locks.c | 69 ++++++--
|
||
|
xlators/features/locks/src/common.c | 316 ++++++++++++++++++++++++++++++++++-
|
||
|
xlators/features/locks/src/common.h | 43 +++++
|
||
|
xlators/features/locks/src/entrylk.c | 19 +--
|
||
|
xlators/features/locks/src/inodelk.c | 150 ++++++++++-------
|
||
|
xlators/features/locks/src/locks.h | 23 ++-
|
||
|
xlators/features/locks/src/posix.c | 183 ++++++++++++++++++--
|
||
|
7 files changed, 689 insertions(+), 114 deletions(-)
|
||
|
|
||
|
diff --git a/xlators/cluster/ec/src/ec-locks.c b/xlators/cluster/ec/src/ec-locks.c
|
||
|
index ffcac07..db86296 100644
|
||
|
--- a/xlators/cluster/ec/src/ec-locks.c
|
||
|
+++ b/xlators/cluster/ec/src/ec-locks.c
|
||
|
@@ -28,9 +28,36 @@ ec_lock_check(ec_fop_data_t *fop, uintptr_t *mask)
|
||
|
ec_t *ec = fop->xl->private;
|
||
|
ec_cbk_data_t *ans = NULL;
|
||
|
ec_cbk_data_t *cbk = NULL;
|
||
|
- uintptr_t locked = 0, notlocked = 0;
|
||
|
+ uintptr_t locked = 0;
|
||
|
+ int32_t good = 0;
|
||
|
+ int32_t eagain = 0;
|
||
|
+ int32_t estale = 0;
|
||
|
int32_t error = -1;
|
||
|
|
||
|
+ /* There are some errors that we'll handle in an special way while trying
|
||
|
+ * to acquire a lock.
|
||
|
+ *
|
||
|
+ * EAGAIN: If it's found during a parallel non-blocking lock request, we
|
||
|
+ * consider that there's contention on the inode, so we consider
|
||
|
+ * the acquisition a failure and try again with a sequential
|
||
|
+ * blocking lock request. This will ensure that we get a lock on
|
||
|
+ * as many bricks as possible (ignoring EAGAIN here would cause
|
||
|
+ * unnecessary triggers of self-healing).
|
||
|
+ *
|
||
|
+ * If it's found during a sequential blocking lock request, it's
|
||
|
+ * considered an error. Lock will only succeed if there are
|
||
|
+ * enough other bricks locked.
|
||
|
+ *
|
||
|
+ * ESTALE: This can appear during parallel or sequential lock request if
|
||
|
+ * the inode has just been unlinked. We consider this error is
|
||
|
+ * not recoverable, but we also don't consider it as fatal. So,
|
||
|
+ * if it happens during parallel lock, we won't attempt a
|
||
|
+ * sequential one unless there are EAGAIN errors on other
|
||
|
+ * bricks (and are enough to form a quorum), but if we reach
|
||
|
+ * quorum counting the ESTALE bricks, we consider the whole
|
||
|
+ * result of the operation is ESTALE instead of EIO.
|
||
|
+ */
|
||
|
+
|
||
|
list_for_each_entry(ans, &fop->cbk_list, list)
|
||
|
{
|
||
|
if (ans->op_ret >= 0) {
|
||
|
@@ -38,24 +65,23 @@ ec_lock_check(ec_fop_data_t *fop, uintptr_t *mask)
|
||
|
error = EIO;
|
||
|
}
|
||
|
locked |= ans->mask;
|
||
|
+ good = ans->count;
|
||
|
cbk = ans;
|
||
|
- } else {
|
||
|
- if (ans->op_errno == EAGAIN) {
|
||
|
- switch (fop->uint32) {
|
||
|
- case EC_LOCK_MODE_NONE:
|
||
|
- case EC_LOCK_MODE_ALL:
|
||
|
- /* Goal is to treat non-blocking lock as failure
|
||
|
- * even if there is a single EAGAIN*/
|
||
|
- notlocked |= ans->mask;
|
||
|
- break;
|
||
|
- }
|
||
|
- }
|
||
|
+ } else if (ans->op_errno == ESTALE) {
|
||
|
+ estale += ans->count;
|
||
|
+ } else if ((ans->op_errno == EAGAIN) &&
|
||
|
+ (fop->uint32 != EC_LOCK_MODE_INC)) {
|
||
|
+ eagain += ans->count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (error == -1) {
|
||
|
- if (gf_bits_count(locked | notlocked) >= ec->fragments) {
|
||
|
- if (notlocked == 0) {
|
||
|
+ /* If we have enough quorum with succeeded and EAGAIN answers, we
|
||
|
+ * ignore for now any ESTALE answer. If there are EAGAIN answers,
|
||
|
+ * we retry with a sequential blocking lock request if needed.
|
||
|
+ * Otherwise we succeed. */
|
||
|
+ if ((good + eagain) >= ec->fragments) {
|
||
|
+ if (eagain == 0) {
|
||
|
if (fop->answer == NULL) {
|
||
|
fop->answer = cbk;
|
||
|
}
|
||
|
@@ -68,21 +94,28 @@ ec_lock_check(ec_fop_data_t *fop, uintptr_t *mask)
|
||
|
case EC_LOCK_MODE_NONE:
|
||
|
error = EAGAIN;
|
||
|
break;
|
||
|
-
|
||
|
case EC_LOCK_MODE_ALL:
|
||
|
fop->uint32 = EC_LOCK_MODE_INC;
|
||
|
break;
|
||
|
-
|
||
|
default:
|
||
|
+ /* This shouldn't happen because eagain cannot be > 0
|
||
|
+ * when fop->uint32 is EC_LOCK_MODE_INC. */
|
||
|
error = EIO;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
- if (fop->answer && fop->answer->op_ret < 0)
|
||
|
+ /* We have been unable to find enough candidates that will be able
|
||
|
+ * to take the lock. If we have quorum on some answer, we return
|
||
|
+ * it. Otherwise we check if ESTALE answers allow us to reach
|
||
|
+ * quorum. If so, we return ESTALE. */
|
||
|
+ if (fop->answer && fop->answer->op_ret < 0) {
|
||
|
error = fop->answer->op_errno;
|
||
|
- else
|
||
|
+ } else if ((good + eagain + estale) >= ec->fragments) {
|
||
|
+ error = ESTALE;
|
||
|
+ } else {
|
||
|
error = EIO;
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
diff --git a/xlators/features/locks/src/common.c b/xlators/features/locks/src/common.c
|
||
|
index 1406e70..0c52853 100644
|
||
|
--- a/xlators/features/locks/src/common.c
|
||
|
+++ b/xlators/features/locks/src/common.c
|
||
|
@@ -462,11 +462,16 @@ pl_inode_get(xlator_t *this, inode_t *inode, pl_local_t *local)
|
||
|
INIT_LIST_HEAD(&pl_inode->blocked_calls);
|
||
|
INIT_LIST_HEAD(&pl_inode->metalk_list);
|
||
|
INIT_LIST_HEAD(&pl_inode->queued_locks);
|
||
|
+ INIT_LIST_HEAD(&pl_inode->waiting);
|
||
|
gf_uuid_copy(pl_inode->gfid, inode->gfid);
|
||
|
|
||
|
pl_inode->check_mlock_info = _gf_true;
|
||
|
pl_inode->mlock_enforced = _gf_false;
|
||
|
|
||
|
+ /* -2 means never looked up. -1 means something went wrong and link
|
||
|
+ * tracking is disabled. */
|
||
|
+ pl_inode->links = -2;
|
||
|
+
|
||
|
ret = __inode_ctx_put(inode, this, (uint64_t)(long)(pl_inode));
|
||
|
if (ret) {
|
||
|
pthread_mutex_destroy(&pl_inode->mutex);
|
||
|
@@ -1276,4 +1281,313 @@ pl_local_init(call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd)
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
-}
|
||
|
\ No newline at end of file
|
||
|
+}
|
||
|
+
|
||
|
+gf_boolean_t
|
||
|
+pl_is_lk_owner_valid(gf_lkowner_t *owner, client_t *client)
|
||
|
+{
|
||
|
+ if (client && (client->opversion < GD_OP_VERSION_7_0)) {
|
||
|
+ return _gf_true;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (is_lk_owner_null(owner)) {
|
||
|
+ return _gf_false;
|
||
|
+ }
|
||
|
+ return _gf_true;
|
||
|
+}
|
||
|
+
|
||
|
+static int32_t
|
||
|
+pl_inode_from_loc(loc_t *loc, inode_t **pinode)
|
||
|
+{
|
||
|
+ inode_t *inode = NULL;
|
||
|
+ int32_t error = 0;
|
||
|
+
|
||
|
+ if (loc->inode != NULL) {
|
||
|
+ inode = inode_ref(loc->inode);
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (loc->parent == NULL) {
|
||
|
+ error = EINVAL;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!gf_uuid_is_null(loc->gfid)) {
|
||
|
+ inode = inode_find(loc->parent->table, loc->gfid);
|
||
|
+ if (inode != NULL) {
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (loc->name == NULL) {
|
||
|
+ error = EINVAL;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ inode = inode_grep(loc->parent->table, loc->parent, loc->name);
|
||
|
+ if (inode == NULL) {
|
||
|
+ /* We haven't found any inode. This means that the file doesn't exist
|
||
|
+ * or that even if it exists, we don't have any knowledge about it, so
|
||
|
+ * we don't have locks on it either, which is fine for our purposes. */
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+done:
|
||
|
+ *pinode = inode;
|
||
|
+
|
||
|
+ return error;
|
||
|
+}
|
||
|
+
|
||
|
+static gf_boolean_t
|
||
|
+pl_inode_has_owners(xlator_t *xl, client_t *client, pl_inode_t *pl_inode,
|
||
|
+ struct timespec *now, struct list_head *contend)
|
||
|
+{
|
||
|
+ pl_dom_list_t *dom;
|
||
|
+ pl_inode_lock_t *lock;
|
||
|
+ gf_boolean_t has_owners = _gf_false;
|
||
|
+
|
||
|
+ list_for_each_entry(dom, &pl_inode->dom_list, inode_list)
|
||
|
+ {
|
||
|
+ list_for_each_entry(lock, &dom->inodelk_list, list)
|
||
|
+ {
|
||
|
+ /* If the lock belongs to the same client, we assume it's related
|
||
|
+ * to the same operation, so we allow the removal to continue. */
|
||
|
+ if (lock->client == client) {
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ /* If the lock belongs to an internal process, we don't block the
|
||
|
+ * removal. */
|
||
|
+ if (lock->client_pid < 0) {
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ if (contend == NULL) {
|
||
|
+ return _gf_true;
|
||
|
+ }
|
||
|
+ has_owners = _gf_true;
|
||
|
+ inodelk_contention_notify_check(xl, lock, now, contend);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return has_owners;
|
||
|
+}
|
||
|
+
|
||
|
+int32_t
|
||
|
+pl_inode_remove_prepare(xlator_t *xl, call_frame_t *frame, loc_t *loc,
|
||
|
+ pl_inode_t **ppl_inode, struct list_head *contend)
|
||
|
+{
|
||
|
+ struct timespec now;
|
||
|
+ inode_t *inode;
|
||
|
+ pl_inode_t *pl_inode;
|
||
|
+ int32_t error;
|
||
|
+
|
||
|
+ pl_inode = NULL;
|
||
|
+
|
||
|
+ error = pl_inode_from_loc(loc, &inode);
|
||
|
+ if ((error != 0) || (inode == NULL)) {
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ pl_inode = pl_inode_get(xl, inode, NULL);
|
||
|
+ if (pl_inode == NULL) {
|
||
|
+ inode_unref(inode);
|
||
|
+ error = ENOMEM;
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* pl_inode_from_loc() already increments ref count for inode, so
|
||
|
+ * we only assign here our reference. */
|
||
|
+ pl_inode->inode = inode;
|
||
|
+
|
||
|
+ timespec_now(&now);
|
||
|
+
|
||
|
+ pthread_mutex_lock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ if (pl_inode->removed) {
|
||
|
+ error = ESTALE;
|
||
|
+ goto unlock;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (pl_inode_has_owners(xl, frame->root->client, pl_inode, &now, contend)) {
|
||
|
+ error = -1;
|
||
|
+ /* We skip the unlock here because the caller must create a stub when
|
||
|
+ * we return -1 and do a call to pl_inode_remove_complete(), which
|
||
|
+ * assumes the lock is still acquired and will release it once
|
||
|
+ * everything else is prepared. */
|
||
|
+ goto done;
|
||
|
+ }
|
||
|
+
|
||
|
+ pl_inode->is_locked = _gf_true;
|
||
|
+ pl_inode->remove_running++;
|
||
|
+
|
||
|
+unlock:
|
||
|
+ pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
+
|
||
|
+done:
|
||
|
+ *ppl_inode = pl_inode;
|
||
|
+
|
||
|
+ return error;
|
||
|
+}
|
||
|
+
|
||
|
+int32_t
|
||
|
+pl_inode_remove_complete(xlator_t *xl, pl_inode_t *pl_inode, call_stub_t *stub,
|
||
|
+ struct list_head *contend)
|
||
|
+{
|
||
|
+ pl_inode_lock_t *lock;
|
||
|
+ int32_t error = -1;
|
||
|
+
|
||
|
+ if (stub != NULL) {
|
||
|
+ list_add_tail(&stub->list, &pl_inode->waiting);
|
||
|
+ pl_inode->is_locked = _gf_true;
|
||
|
+ } else {
|
||
|
+ error = ENOMEM;
|
||
|
+
|
||
|
+ while (!list_empty(contend)) {
|
||
|
+ lock = list_first_entry(contend, pl_inode_lock_t, list);
|
||
|
+ list_del_init(&lock->list);
|
||
|
+ __pl_inodelk_unref(lock);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ if (error < 0) {
|
||
|
+ inodelk_contention_notify(xl, contend);
|
||
|
+ }
|
||
|
+
|
||
|
+ inode_unref(pl_inode->inode);
|
||
|
+
|
||
|
+ return error;
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+pl_inode_remove_wake(struct list_head *list)
|
||
|
+{
|
||
|
+ call_stub_t *stub;
|
||
|
+
|
||
|
+ while (!list_empty(list)) {
|
||
|
+ stub = list_first_entry(list, call_stub_t, list);
|
||
|
+ list_del_init(&stub->list);
|
||
|
+
|
||
|
+ call_resume(stub);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+pl_inode_remove_cbk(xlator_t *xl, pl_inode_t *pl_inode, int32_t error)
|
||
|
+{
|
||
|
+ struct list_head contend, granted;
|
||
|
+ struct timespec now;
|
||
|
+ pl_dom_list_t *dom;
|
||
|
+
|
||
|
+ if (pl_inode == NULL) {
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ INIT_LIST_HEAD(&contend);
|
||
|
+ INIT_LIST_HEAD(&granted);
|
||
|
+ timespec_now(&now);
|
||
|
+
|
||
|
+ pthread_mutex_lock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ if (error == 0) {
|
||
|
+ if (pl_inode->links >= 0) {
|
||
|
+ pl_inode->links--;
|
||
|
+ }
|
||
|
+ if (pl_inode->links == 0) {
|
||
|
+ pl_inode->removed = _gf_true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ pl_inode->remove_running--;
|
||
|
+
|
||
|
+ if ((pl_inode->remove_running == 0) && list_empty(&pl_inode->waiting)) {
|
||
|
+ pl_inode->is_locked = _gf_false;
|
||
|
+
|
||
|
+ list_for_each_entry(dom, &pl_inode->dom_list, inode_list)
|
||
|
+ {
|
||
|
+ __grant_blocked_inode_locks(xl, pl_inode, &granted, dom, &now,
|
||
|
+ &contend);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ unwind_granted_inodes(xl, pl_inode, &granted);
|
||
|
+
|
||
|
+ inodelk_contention_notify(xl, &contend);
|
||
|
+
|
||
|
+ inode_unref(pl_inode->inode);
|
||
|
+}
|
||
|
+
|
||
|
+void
|
||
|
+pl_inode_remove_unlocked(xlator_t *xl, pl_inode_t *pl_inode,
|
||
|
+ struct list_head *list)
|
||
|
+{
|
||
|
+ call_stub_t *stub, *tmp;
|
||
|
+
|
||
|
+ if (!pl_inode->is_locked) {
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ list_for_each_entry_safe(stub, tmp, &pl_inode->waiting, list)
|
||
|
+ {
|
||
|
+ if (!pl_inode_has_owners(xl, stub->frame->root->client, pl_inode, NULL,
|
||
|
+ NULL)) {
|
||
|
+ list_move_tail(&stub->list, list);
|
||
|
+ }
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+/* This function determines if an inodelk attempt can be done now or it needs
|
||
|
+ * to wait.
|
||
|
+ *
|
||
|
+ * Possible return values:
|
||
|
+ * < 0: An error occurred. Currently only -ESTALE can be returned if the
|
||
|
+ * inode has been deleted previously by unlink/rmdir/rename
|
||
|
+ * = 0: The lock can be attempted.
|
||
|
+ * > 0: The lock needs to wait because a conflicting remove operation is
|
||
|
+ * ongoing.
|
||
|
+ */
|
||
|
+int32_t
|
||
|
+pl_inode_remove_inodelk(pl_inode_t *pl_inode, pl_inode_lock_t *lock)
|
||
|
+{
|
||
|
+ pl_dom_list_t *dom;
|
||
|
+ pl_inode_lock_t *ilock;
|
||
|
+
|
||
|
+ /* If the inode has been deleted, we won't allow any lock. */
|
||
|
+ if (pl_inode->removed) {
|
||
|
+ return -ESTALE;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* We only synchronize with locks made for regular operations coming from
|
||
|
+ * the user. Locks done for internal purposes are hard to control and could
|
||
|
+ * lead to long delays or deadlocks quite easily. */
|
||
|
+ if (lock->client_pid < 0) {
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ if (!pl_inode->is_locked) {
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ if (pl_inode->remove_running > 0) {
|
||
|
+ return 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ list_for_each_entry(dom, &pl_inode->dom_list, inode_list)
|
||
|
+ {
|
||
|
+ list_for_each_entry(ilock, &dom->inodelk_list, list)
|
||
|
+ {
|
||
|
+ /* If a lock from the same client is already granted, we allow this
|
||
|
+ * one to continue. This is necessary to prevent deadlocks when
|
||
|
+ * multiple locks are taken for the same operation.
|
||
|
+ *
|
||
|
+ * On the other side it's unlikely that the same client sends
|
||
|
+ * completely unrelated locks for the same inode.
|
||
|
+ */
|
||
|
+ if (ilock->client == lock->client) {
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 1;
|
||
|
+}
|
||
|
diff --git a/xlators/features/locks/src/common.h b/xlators/features/locks/src/common.h
|
||
|
index ea86b96..6c81ac3 100644
|
||
|
--- a/xlators/features/locks/src/common.h
|
||
|
+++ b/xlators/features/locks/src/common.h
|
||
|
@@ -105,6 +105,15 @@ void
|
||
|
__pl_inodelk_unref(pl_inode_lock_t *lock);
|
||
|
|
||
|
void
|
||
|
+__grant_blocked_inode_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
+ struct list_head *granted, pl_dom_list_t *dom,
|
||
|
+ struct timespec *now, struct list_head *contend);
|
||
|
+
|
||
|
+void
|
||
|
+unwind_granted_inodes(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
+ struct list_head *granted);
|
||
|
+
|
||
|
+void
|
||
|
grant_blocked_entry_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
pl_dom_list_t *dom, struct timespec *now,
|
||
|
struct list_head *contend);
|
||
|
@@ -204,6 +213,16 @@ pl_metalock_is_active(pl_inode_t *pl_inode);
|
||
|
void
|
||
|
__pl_queue_lock(pl_inode_t *pl_inode, posix_lock_t *reqlock);
|
||
|
|
||
|
+void
|
||
|
+inodelk_contention_notify_check(xlator_t *xl, pl_inode_lock_t *lock,
|
||
|
+ struct timespec *now,
|
||
|
+ struct list_head *contend);
|
||
|
+
|
||
|
+void
|
||
|
+entrylk_contention_notify_check(xlator_t *xl, pl_entry_lock_t *lock,
|
||
|
+ struct timespec *now,
|
||
|
+ struct list_head *contend);
|
||
|
+
|
||
|
gf_boolean_t
|
||
|
pl_does_monkey_want_stuck_lock();
|
||
|
|
||
|
@@ -216,4 +235,28 @@ pl_clean_local(pl_local_t *local);
|
||
|
int
|
||
|
pl_local_init(call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd);
|
||
|
|
||
|
+gf_boolean_t
|
||
|
+pl_is_lk_owner_valid(gf_lkowner_t *owner, client_t *client);
|
||
|
+
|
||
|
+int32_t
|
||
|
+pl_inode_remove_prepare(xlator_t *xl, call_frame_t *frame, loc_t *loc,
|
||
|
+ pl_inode_t **ppl_inode, struct list_head *contend);
|
||
|
+
|
||
|
+int32_t
|
||
|
+pl_inode_remove_complete(xlator_t *xl, pl_inode_t *pl_inode, call_stub_t *stub,
|
||
|
+ struct list_head *contend);
|
||
|
+
|
||
|
+void
|
||
|
+pl_inode_remove_wake(struct list_head *list);
|
||
|
+
|
||
|
+void
|
||
|
+pl_inode_remove_cbk(xlator_t *xl, pl_inode_t *pl_inode, int32_t error);
|
||
|
+
|
||
|
+void
|
||
|
+pl_inode_remove_unlocked(xlator_t *xl, pl_inode_t *pl_inode,
|
||
|
+ struct list_head *list);
|
||
|
+
|
||
|
+int32_t
|
||
|
+pl_inode_remove_inodelk(pl_inode_t *pl_inode, pl_inode_lock_t *lock);
|
||
|
+
|
||
|
#endif /* __COMMON_H__ */
|
||
|
diff --git a/xlators/features/locks/src/entrylk.c b/xlators/features/locks/src/entrylk.c
|
||
|
index 93c649c..b97836f 100644
|
||
|
--- a/xlators/features/locks/src/entrylk.c
|
||
|
+++ b/xlators/features/locks/src/entrylk.c
|
||
|
@@ -197,9 +197,9 @@ out:
|
||
|
return revoke_lock;
|
||
|
}
|
||
|
|
||
|
-static gf_boolean_t
|
||
|
-__entrylk_needs_contention_notify(xlator_t *this, pl_entry_lock_t *lock,
|
||
|
- struct timespec *now)
|
||
|
+void
|
||
|
+entrylk_contention_notify_check(xlator_t *this, pl_entry_lock_t *lock,
|
||
|
+ struct timespec *now, struct list_head *contend)
|
||
|
{
|
||
|
posix_locks_private_t *priv;
|
||
|
int64_t elapsed;
|
||
|
@@ -209,7 +209,7 @@ __entrylk_needs_contention_notify(xlator_t *this, pl_entry_lock_t *lock,
|
||
|
/* If this lock is in a list, it means that we are about to send a
|
||
|
* notification for it, so no need to do anything else. */
|
||
|
if (!list_empty(&lock->contend)) {
|
||
|
- return _gf_false;
|
||
|
+ return;
|
||
|
}
|
||
|
|
||
|
elapsed = now->tv_sec;
|
||
|
@@ -218,7 +218,7 @@ __entrylk_needs_contention_notify(xlator_t *this, pl_entry_lock_t *lock,
|
||
|
elapsed--;
|
||
|
}
|
||
|
if (elapsed < priv->notify_contention_delay) {
|
||
|
- return _gf_false;
|
||
|
+ return;
|
||
|
}
|
||
|
|
||
|
/* All contention notifications will be sent outside of the locked
|
||
|
@@ -231,7 +231,7 @@ __entrylk_needs_contention_notify(xlator_t *this, pl_entry_lock_t *lock,
|
||
|
|
||
|
lock->contention_time = *now;
|
||
|
|
||
|
- return _gf_true;
|
||
|
+ list_add_tail(&lock->contend, contend);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
@@ -325,9 +325,7 @@ __entrylk_grantable(xlator_t *this, pl_dom_list_t *dom, pl_entry_lock_t *lock,
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
- if (__entrylk_needs_contention_notify(this, tmp, now)) {
|
||
|
- list_add_tail(&tmp->contend, contend);
|
||
|
- }
|
||
|
+ entrylk_contention_notify_check(this, tmp, now, contend);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -690,10 +688,9 @@ __grant_blocked_entry_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
bl_ret = __lock_entrylk(bl->this, pl_inode, bl, 0, dom, now, contend);
|
||
|
|
||
|
if (bl_ret == 0) {
|
||
|
- list_add(&bl->blocked_locks, granted);
|
||
|
+ list_add_tail(&bl->blocked_locks, granted);
|
||
|
}
|
||
|
}
|
||
|
- return;
|
||
|
}
|
||
|
|
||
|
/* Grants locks if possible which are blocked on a lock */
|
||
|
diff --git a/xlators/features/locks/src/inodelk.c b/xlators/features/locks/src/inodelk.c
|
||
|
index 24dee49..1a07243 100644
|
||
|
--- a/xlators/features/locks/src/inodelk.c
|
||
|
+++ b/xlators/features/locks/src/inodelk.c
|
||
|
@@ -231,9 +231,9 @@ out:
|
||
|
return revoke_lock;
|
||
|
}
|
||
|
|
||
|
-static gf_boolean_t
|
||
|
-__inodelk_needs_contention_notify(xlator_t *this, pl_inode_lock_t *lock,
|
||
|
- struct timespec *now)
|
||
|
+void
|
||
|
+inodelk_contention_notify_check(xlator_t *this, pl_inode_lock_t *lock,
|
||
|
+ struct timespec *now, struct list_head *contend)
|
||
|
{
|
||
|
posix_locks_private_t *priv;
|
||
|
int64_t elapsed;
|
||
|
@@ -243,7 +243,7 @@ __inodelk_needs_contention_notify(xlator_t *this, pl_inode_lock_t *lock,
|
||
|
/* If this lock is in a list, it means that we are about to send a
|
||
|
* notification for it, so no need to do anything else. */
|
||
|
if (!list_empty(&lock->contend)) {
|
||
|
- return _gf_false;
|
||
|
+ return;
|
||
|
}
|
||
|
|
||
|
elapsed = now->tv_sec;
|
||
|
@@ -252,7 +252,7 @@ __inodelk_needs_contention_notify(xlator_t *this, pl_inode_lock_t *lock,
|
||
|
elapsed--;
|
||
|
}
|
||
|
if (elapsed < priv->notify_contention_delay) {
|
||
|
- return _gf_false;
|
||
|
+ return;
|
||
|
}
|
||
|
|
||
|
/* All contention notifications will be sent outside of the locked
|
||
|
@@ -265,7 +265,7 @@ __inodelk_needs_contention_notify(xlator_t *this, pl_inode_lock_t *lock,
|
||
|
|
||
|
lock->contention_time = *now;
|
||
|
|
||
|
- return _gf_true;
|
||
|
+ list_add_tail(&lock->contend, contend);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
@@ -353,9 +353,7 @@ __inodelk_grantable(xlator_t *this, pl_dom_list_t *dom, pl_inode_lock_t *lock,
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
- if (__inodelk_needs_contention_notify(this, l, now)) {
|
||
|
- list_add_tail(&l->contend, contend);
|
||
|
- }
|
||
|
+ inodelk_contention_notify_check(this, l, now, contend);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -435,12 +433,17 @@ __lock_inodelk(xlator_t *this, pl_inode_t *pl_inode, pl_inode_lock_t *lock,
|
||
|
struct list_head *contend)
|
||
|
{
|
||
|
pl_inode_lock_t *conf = NULL;
|
||
|
- int ret = -EINVAL;
|
||
|
+ int ret;
|
||
|
|
||
|
- conf = __inodelk_grantable(this, dom, lock, now, contend);
|
||
|
- if (conf) {
|
||
|
- ret = __lock_blocked_add(this, dom, lock, can_block);
|
||
|
- goto out;
|
||
|
+ ret = pl_inode_remove_inodelk(pl_inode, lock);
|
||
|
+ if (ret < 0) {
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ if (ret == 0) {
|
||
|
+ conf = __inodelk_grantable(this, dom, lock, now, contend);
|
||
|
+ }
|
||
|
+ if ((ret > 0) || (conf != NULL)) {
|
||
|
+ return __lock_blocked_add(this, dom, lock, can_block);
|
||
|
}
|
||
|
|
||
|
/* To prevent blocked locks starvation, check if there are any blocked
|
||
|
@@ -462,17 +465,13 @@ __lock_inodelk(xlator_t *this, pl_inode_t *pl_inode, pl_inode_lock_t *lock,
|
||
|
"starvation");
|
||
|
}
|
||
|
|
||
|
- ret = __lock_blocked_add(this, dom, lock, can_block);
|
||
|
- goto out;
|
||
|
+ return __lock_blocked_add(this, dom, lock, can_block);
|
||
|
}
|
||
|
__pl_inodelk_ref(lock);
|
||
|
gettimeofday(&lock->granted_time, NULL);
|
||
|
list_add(&lock->list, &dom->inodelk_list);
|
||
|
|
||
|
- ret = 0;
|
||
|
-
|
||
|
-out:
|
||
|
- return ret;
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
/* Return true if the two inodelks have exactly same lock boundaries */
|
||
|
@@ -529,12 +528,11 @@ out:
|
||
|
return conf;
|
||
|
}
|
||
|
|
||
|
-static void
|
||
|
+void
|
||
|
__grant_blocked_inode_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
struct list_head *granted, pl_dom_list_t *dom,
|
||
|
struct timespec *now, struct list_head *contend)
|
||
|
{
|
||
|
- int bl_ret = 0;
|
||
|
pl_inode_lock_t *bl = NULL;
|
||
|
pl_inode_lock_t *tmp = NULL;
|
||
|
|
||
|
@@ -547,52 +545,48 @@ __grant_blocked_inode_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
{
|
||
|
list_del_init(&bl->blocked_locks);
|
||
|
|
||
|
- bl_ret = __lock_inodelk(this, pl_inode, bl, 1, dom, now, contend);
|
||
|
+ bl->status = __lock_inodelk(this, pl_inode, bl, 1, dom, now, contend);
|
||
|
|
||
|
- if (bl_ret == 0) {
|
||
|
- list_add(&bl->blocked_locks, granted);
|
||
|
+ if (bl->status != -EAGAIN) {
|
||
|
+ list_add_tail(&bl->blocked_locks, granted);
|
||
|
}
|
||
|
}
|
||
|
- return;
|
||
|
}
|
||
|
|
||
|
-/* Grant all inodelks blocked on a lock */
|
||
|
void
|
||
|
-grant_blocked_inode_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
- pl_dom_list_t *dom, struct timespec *now,
|
||
|
- struct list_head *contend)
|
||
|
+unwind_granted_inodes(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
+ struct list_head *granted)
|
||
|
{
|
||
|
- struct list_head granted;
|
||
|
pl_inode_lock_t *lock;
|
||
|
pl_inode_lock_t *tmp;
|
||
|
+ int32_t op_ret;
|
||
|
+ int32_t op_errno;
|
||
|
|
||
|
- INIT_LIST_HEAD(&granted);
|
||
|
-
|
||
|
- pthread_mutex_lock(&pl_inode->mutex);
|
||
|
- {
|
||
|
- __grant_blocked_inode_locks(this, pl_inode, &granted, dom, now,
|
||
|
- contend);
|
||
|
- }
|
||
|
- pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
-
|
||
|
- list_for_each_entry_safe(lock, tmp, &granted, blocked_locks)
|
||
|
+ list_for_each_entry_safe(lock, tmp, granted, blocked_locks)
|
||
|
{
|
||
|
- gf_log(this->name, GF_LOG_TRACE,
|
||
|
- "%s (pid=%d) (lk-owner=%s) %" PRId64 " - %" PRId64 " => Granted",
|
||
|
- lock->fl_type == F_UNLCK ? "Unlock" : "Lock", lock->client_pid,
|
||
|
- lkowner_utoa(&lock->owner), lock->user_flock.l_start,
|
||
|
- lock->user_flock.l_len);
|
||
|
-
|
||
|
+ if (lock->status == 0) {
|
||
|
+ op_ret = 0;
|
||
|
+ op_errno = 0;
|
||
|
+ gf_log(this->name, GF_LOG_TRACE,
|
||
|
+ "%s (pid=%d) (lk-owner=%s) %" PRId64 " - %" PRId64
|
||
|
+ " => Granted",
|
||
|
+ lock->fl_type == F_UNLCK ? "Unlock" : "Lock",
|
||
|
+ lock->client_pid, lkowner_utoa(&lock->owner),
|
||
|
+ lock->user_flock.l_start, lock->user_flock.l_len);
|
||
|
+ } else {
|
||
|
+ op_ret = -1;
|
||
|
+ op_errno = -lock->status;
|
||
|
+ }
|
||
|
pl_trace_out(this, lock->frame, NULL, NULL, F_SETLKW, &lock->user_flock,
|
||
|
- 0, 0, lock->volume);
|
||
|
+ op_ret, op_errno, lock->volume);
|
||
|
|
||
|
- STACK_UNWIND_STRICT(inodelk, lock->frame, 0, 0, NULL);
|
||
|
+ STACK_UNWIND_STRICT(inodelk, lock->frame, op_ret, op_errno, NULL);
|
||
|
lock->frame = NULL;
|
||
|
}
|
||
|
|
||
|
pthread_mutex_lock(&pl_inode->mutex);
|
||
|
{
|
||
|
- list_for_each_entry_safe(lock, tmp, &granted, blocked_locks)
|
||
|
+ list_for_each_entry_safe(lock, tmp, granted, blocked_locks)
|
||
|
{
|
||
|
list_del_init(&lock->blocked_locks);
|
||
|
__pl_inodelk_unref(lock);
|
||
|
@@ -601,6 +595,26 @@ grant_blocked_inode_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
}
|
||
|
|
||
|
+/* Grant all inodelks blocked on a lock */
|
||
|
+void
|
||
|
+grant_blocked_inode_locks(xlator_t *this, pl_inode_t *pl_inode,
|
||
|
+ pl_dom_list_t *dom, struct timespec *now,
|
||
|
+ struct list_head *contend)
|
||
|
+{
|
||
|
+ struct list_head granted;
|
||
|
+
|
||
|
+ INIT_LIST_HEAD(&granted);
|
||
|
+
|
||
|
+ pthread_mutex_lock(&pl_inode->mutex);
|
||
|
+ {
|
||
|
+ __grant_blocked_inode_locks(this, pl_inode, &granted, dom, now,
|
||
|
+ contend);
|
||
|
+ }
|
||
|
+ pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ unwind_granted_inodes(this, pl_inode, &granted);
|
||
|
+}
|
||
|
+
|
||
|
static void
|
||
|
pl_inodelk_log_cleanup(pl_inode_lock_t *lock)
|
||
|
{
|
||
|
@@ -662,7 +676,7 @@ pl_inodelk_client_cleanup(xlator_t *this, pl_ctx_t *ctx)
|
||
|
* and blocked lists, then this means that a parallel
|
||
|
* unlock on another inodelk (L2 say) may have 'granted'
|
||
|
* L1 and added it to 'granted' list in
|
||
|
- * __grant_blocked_node_locks() (although using the
|
||
|
+ * __grant_blocked_inode_locks() (although using the
|
||
|
* 'blocked_locks' member). In that case, the cleanup
|
||
|
* codepath must try and grant other overlapping
|
||
|
* blocked inodelks from other clients, now that L1 is
|
||
|
@@ -747,6 +761,7 @@ pl_inode_setlk(xlator_t *this, pl_ctx_t *ctx, pl_inode_t *pl_inode,
|
||
|
gf_boolean_t need_inode_unref = _gf_false;
|
||
|
struct list_head *pcontend = NULL;
|
||
|
struct list_head contend;
|
||
|
+ struct list_head wake;
|
||
|
struct timespec now = {};
|
||
|
short fl_type;
|
||
|
|
||
|
@@ -798,6 +813,8 @@ pl_inode_setlk(xlator_t *this, pl_ctx_t *ctx, pl_inode_t *pl_inode,
|
||
|
timespec_now(&now);
|
||
|
}
|
||
|
|
||
|
+ INIT_LIST_HEAD(&wake);
|
||
|
+
|
||
|
if (ctx)
|
||
|
pthread_mutex_lock(&ctx->lock);
|
||
|
pthread_mutex_lock(&pl_inode->mutex);
|
||
|
@@ -820,18 +837,17 @@ pl_inode_setlk(xlator_t *this, pl_ctx_t *ctx, pl_inode_t *pl_inode,
|
||
|
lock->fl_type == F_UNLCK ? "Unlock" : "Lock",
|
||
|
lock->client_pid, lkowner_utoa(&lock->owner),
|
||
|
lock->user_flock.l_start, lock->user_flock.l_len);
|
||
|
- if (can_block)
|
||
|
+ if (can_block) {
|
||
|
unref = _gf_false;
|
||
|
- /* For all but the case where a non-blocking
|
||
|
- * lock attempt fails, the extra ref taken at
|
||
|
- * the start of this function must be negated.
|
||
|
- */
|
||
|
- else
|
||
|
- need_inode_unref = _gf_true;
|
||
|
+ }
|
||
|
}
|
||
|
-
|
||
|
- if (ctx && (!ret || can_block))
|
||
|
+ /* For all but the case where a non-blocking lock attempt fails
|
||
|
+ * with -EAGAIN, the extra ref taken at the start of this function
|
||
|
+ * must be negated. */
|
||
|
+ need_inode_unref = (ret != 0) && ((ret != -EAGAIN) || !can_block);
|
||
|
+ if (ctx && !need_inode_unref) {
|
||
|
list_add_tail(&lock->client_list, &ctx->inodelk_lockers);
|
||
|
+ }
|
||
|
} else {
|
||
|
/* Irrespective of whether unlock succeeds or not,
|
||
|
* the extra inode ref that was done at the start of
|
||
|
@@ -849,6 +865,8 @@ pl_inode_setlk(xlator_t *this, pl_ctx_t *ctx, pl_inode_t *pl_inode,
|
||
|
list_del_init(&retlock->client_list);
|
||
|
__pl_inodelk_unref(retlock);
|
||
|
|
||
|
+ pl_inode_remove_unlocked(this, pl_inode, &wake);
|
||
|
+
|
||
|
ret = 0;
|
||
|
}
|
||
|
out:
|
||
|
@@ -859,6 +877,8 @@ pl_inode_setlk(xlator_t *this, pl_ctx_t *ctx, pl_inode_t *pl_inode,
|
||
|
if (ctx)
|
||
|
pthread_mutex_unlock(&ctx->lock);
|
||
|
|
||
|
+ pl_inode_remove_wake(&wake);
|
||
|
+
|
||
|
/* The following (extra) unref corresponds to the ref that
|
||
|
* was done at the time the lock was granted.
|
||
|
*/
|
||
|
@@ -1033,10 +1053,14 @@ pl_common_inodelk(call_frame_t *frame, xlator_t *this, const char *volume,
|
||
|
inode);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
- if ((can_block) && (F_UNLCK != lock_type)) {
|
||
|
- goto out;
|
||
|
+ if (ret == -EAGAIN) {
|
||
|
+ if (can_block && (F_UNLCK != lock_type)) {
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ gf_log(this->name, GF_LOG_TRACE, "returning EAGAIN");
|
||
|
+ } else {
|
||
|
+ gf_log(this->name, GF_LOG_TRACE, "returning %d", ret);
|
||
|
}
|
||
|
- gf_log(this->name, GF_LOG_TRACE, "returning EAGAIN");
|
||
|
op_errno = -ret;
|
||
|
goto unwind;
|
||
|
}
|
||
|
diff --git a/xlators/features/locks/src/locks.h b/xlators/features/locks/src/locks.h
|
||
|
index aa267de..6666feb 100644
|
||
|
--- a/xlators/features/locks/src/locks.h
|
||
|
+++ b/xlators/features/locks/src/locks.h
|
||
|
@@ -102,6 +102,9 @@ struct __pl_inode_lock {
|
||
|
|
||
|
struct list_head client_list; /* list of all locks from a client */
|
||
|
short fl_type;
|
||
|
+
|
||
|
+ int32_t status; /* Error code when we try to grant a lock in blocked
|
||
|
+ state */
|
||
|
};
|
||
|
typedef struct __pl_inode_lock pl_inode_lock_t;
|
||
|
|
||
|
@@ -164,13 +167,14 @@ struct __pl_inode {
|
||
|
struct list_head rw_list; /* list of waiting r/w requests */
|
||
|
struct list_head reservelk_list; /* list of reservelks */
|
||
|
struct list_head blocked_reservelks; /* list of blocked reservelks */
|
||
|
- struct list_head
|
||
|
- blocked_calls; /* List of blocked lock calls while a reserve is held*/
|
||
|
- struct list_head metalk_list; /* Meta lock list */
|
||
|
- /* This is to store the incoming lock
|
||
|
- requests while meta lock is enabled */
|
||
|
- struct list_head queued_locks;
|
||
|
- int mandatory; /* if mandatory locking is enabled */
|
||
|
+ struct list_head blocked_calls; /* List of blocked lock calls while a
|
||
|
+ reserve is held*/
|
||
|
+ struct list_head metalk_list; /* Meta lock list */
|
||
|
+ struct list_head queued_locks; /* This is to store the incoming lock
|
||
|
+ requests while meta lock is enabled */
|
||
|
+ struct list_head waiting; /* List of pending fops waiting to unlink/rmdir
|
||
|
+ the inode. */
|
||
|
+ int mandatory; /* if mandatory locking is enabled */
|
||
|
|
||
|
inode_t *refkeeper; /* hold refs on an inode while locks are
|
||
|
held to prevent pruning */
|
||
|
@@ -197,6 +201,11 @@ struct __pl_inode {
|
||
|
*/
|
||
|
int fop_wind_count;
|
||
|
pthread_cond_t check_fop_wind_count;
|
||
|
+
|
||
|
+ int32_t links; /* Number of hard links the inode has. */
|
||
|
+ uint32_t remove_running; /* Number of remove operations running. */
|
||
|
+ gf_boolean_t is_locked; /* Regular locks will be blocked. */
|
||
|
+ gf_boolean_t removed; /* The inode has been deleted. */
|
||
|
};
|
||
|
typedef struct __pl_inode pl_inode_t;
|
||
|
|
||
|
diff --git a/xlators/features/locks/src/posix.c b/xlators/features/locks/src/posix.c
|
||
|
index 7887b82..5ae0125 100644
|
||
|
--- a/xlators/features/locks/src/posix.c
|
||
|
+++ b/xlators/features/locks/src/posix.c
|
||
|
@@ -147,6 +147,29 @@ fetch_pathinfo(xlator_t *, inode_t *, int32_t *, char **);
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
+#define PL_INODE_REMOVE(_fop, _frame, _xl, _loc1, _loc2, _cont, _cbk, \
|
||
|
+ _args...) \
|
||
|
+ ({ \
|
||
|
+ struct list_head contend; \
|
||
|
+ pl_inode_t *__pl_inode; \
|
||
|
+ call_stub_t *__stub; \
|
||
|
+ int32_t __error; \
|
||
|
+ INIT_LIST_HEAD(&contend); \
|
||
|
+ __error = pl_inode_remove_prepare(_xl, _frame, _loc2 ? _loc2 : _loc1, \
|
||
|
+ &__pl_inode, &contend); \
|
||
|
+ if (__error < 0) { \
|
||
|
+ __stub = fop_##_fop##_stub(_frame, _cont, ##_args); \
|
||
|
+ __error = pl_inode_remove_complete(_xl, __pl_inode, __stub, \
|
||
|
+ &contend); \
|
||
|
+ } else if (__error == 0) { \
|
||
|
+ PL_LOCAL_GET_REQUESTS(_frame, _xl, xdata, ((fd_t *)NULL), _loc1, \
|
||
|
+ _loc2); \
|
||
|
+ STACK_WIND_COOKIE(_frame, _cbk, __pl_inode, FIRST_CHILD(_xl), \
|
||
|
+ FIRST_CHILD(_xl)->fops->_fop, ##_args); \
|
||
|
+ } \
|
||
|
+ __error; \
|
||
|
+ })
|
||
|
+
|
||
|
gf_boolean_t
|
||
|
pl_has_xdata_requests(dict_t *xdata)
|
||
|
{
|
||
|
@@ -2969,11 +2992,85 @@ out:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
+static int32_t
|
||
|
+pl_request_link_count(dict_t **pxdata)
|
||
|
+{
|
||
|
+ dict_t *xdata;
|
||
|
+
|
||
|
+ xdata = *pxdata;
|
||
|
+ if (xdata == NULL) {
|
||
|
+ xdata = dict_new();
|
||
|
+ if (xdata == NULL) {
|
||
|
+ return ENOMEM;
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ dict_ref(xdata);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (dict_set_uint32(xdata, GET_LINK_COUNT, 0) != 0) {
|
||
|
+ dict_unref(xdata);
|
||
|
+ return ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ *pxdata = xdata;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int32_t
|
||
|
+pl_check_link_count(dict_t *xdata)
|
||
|
+{
|
||
|
+ int32_t count;
|
||
|
+
|
||
|
+ /* In case we are unable to read the link count from xdata, we take a
|
||
|
+ * conservative approach and return -2, which will prevent the inode from
|
||
|
+ * being considered deleted. In fact it will cause link tracking for this
|
||
|
+ * inode to be disabled completely to avoid races. */
|
||
|
+
|
||
|
+ if (xdata == NULL) {
|
||
|
+ return -2;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (dict_get_int32(xdata, GET_LINK_COUNT, &count) != 0) {
|
||
|
+ return -2;
|
||
|
+ }
|
||
|
+
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
int32_t
|
||
|
pl_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
|
||
|
int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata,
|
||
|
struct iatt *postparent)
|
||
|
{
|
||
|
+ pl_inode_t *pl_inode;
|
||
|
+
|
||
|
+ if (op_ret >= 0) {
|
||
|
+ pl_inode = pl_inode_get(this, inode, NULL);
|
||
|
+ if (pl_inode == NULL) {
|
||
|
+ PL_STACK_UNWIND(lookup, xdata, frame, -1, ENOMEM, NULL, NULL, NULL,
|
||
|
+ NULL);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ pthread_mutex_lock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ /* We only update the link count if we previously didn't know it.
|
||
|
+ * Doing it always can lead to races since lookup is not executed
|
||
|
+ * atomically most of the times. */
|
||
|
+ if (pl_inode->links == -2) {
|
||
|
+ pl_inode->links = pl_check_link_count(xdata);
|
||
|
+ if (buf->ia_type == IA_IFDIR) {
|
||
|
+ /* Directories have at least 2 links. To avoid special handling
|
||
|
+ * for directories, we simply decrement the value here to make
|
||
|
+ * them equivalent to regular files. */
|
||
|
+ pl_inode->links--;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
+ }
|
||
|
+
|
||
|
PL_STACK_UNWIND(lookup, xdata, frame, op_ret, op_errno, inode, buf, xdata,
|
||
|
postparent);
|
||
|
return 0;
|
||
|
@@ -2982,9 +3079,17 @@ pl_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
|
||
|
int32_t
|
||
|
pl_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
|
||
|
{
|
||
|
- PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), loc, NULL);
|
||
|
- STACK_WIND(frame, pl_lookup_cbk, FIRST_CHILD(this),
|
||
|
- FIRST_CHILD(this)->fops->lookup, loc, xdata);
|
||
|
+ int32_t error;
|
||
|
+
|
||
|
+ error = pl_request_link_count(&xdata);
|
||
|
+ if (error == 0) {
|
||
|
+ PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), loc, NULL);
|
||
|
+ STACK_WIND(frame, pl_lookup_cbk, FIRST_CHILD(this),
|
||
|
+ FIRST_CHILD(this)->fops->lookup, loc, xdata);
|
||
|
+ dict_unref(xdata);
|
||
|
+ } else {
|
||
|
+ STACK_UNWIND_STRICT(lookup, frame, -1, error, NULL, NULL, NULL, NULL);
|
||
|
+ }
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -3792,6 +3897,10 @@ unlock:
|
||
|
gf_proc_dump_write("posixlk-count", "%d", count);
|
||
|
__dump_posixlks(pl_inode);
|
||
|
}
|
||
|
+
|
||
|
+ gf_proc_dump_write("links", "%d", pl_inode->links);
|
||
|
+ gf_proc_dump_write("removes_pending", "%u", pl_inode->remove_running);
|
||
|
+ gf_proc_dump_write("removed", "%u", pl_inode->removed);
|
||
|
}
|
||
|
pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
|
||
|
@@ -4137,8 +4246,11 @@ pl_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
|
||
|
struct iatt *postoldparent, struct iatt *prenewparent,
|
||
|
struct iatt *postnewparent, dict_t *xdata)
|
||
|
{
|
||
|
+ pl_inode_remove_cbk(this, cookie, op_ret < 0 ? op_errno : 0);
|
||
|
+
|
||
|
PL_STACK_UNWIND(rename, xdata, frame, op_ret, op_errno, buf, preoldparent,
|
||
|
postoldparent, prenewparent, postnewparent, xdata);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4146,10 +4258,15 @@ int32_t
|
||
|
pl_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
|
||
|
dict_t *xdata)
|
||
|
{
|
||
|
- PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), oldloc, newloc);
|
||
|
+ int32_t error;
|
||
|
+
|
||
|
+ error = PL_INODE_REMOVE(rename, frame, this, oldloc, newloc, pl_rename,
|
||
|
+ pl_rename_cbk, oldloc, newloc, xdata);
|
||
|
+ if (error > 0) {
|
||
|
+ STACK_UNWIND_STRICT(rename, frame, -1, error, NULL, NULL, NULL, NULL,
|
||
|
+ NULL, NULL);
|
||
|
+ }
|
||
|
|
||
|
- STACK_WIND(frame, pl_rename_cbk, FIRST_CHILD(this),
|
||
|
- FIRST_CHILD(this)->fops->rename, oldloc, newloc, xdata);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4273,8 +4390,11 @@ pl_unlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
|
||
|
int32_t op_errno, struct iatt *preparent, struct iatt *postparent,
|
||
|
dict_t *xdata)
|
||
|
{
|
||
|
+ pl_inode_remove_cbk(this, cookie, op_ret < 0 ? op_errno : 0);
|
||
|
+
|
||
|
PL_STACK_UNWIND(unlink, xdata, frame, op_ret, op_errno, preparent,
|
||
|
postparent, xdata);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4282,9 +4402,14 @@ int32_t
|
||
|
pl_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag,
|
||
|
dict_t *xdata)
|
||
|
{
|
||
|
- PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), loc, NULL);
|
||
|
- STACK_WIND(frame, pl_unlink_cbk, FIRST_CHILD(this),
|
||
|
- FIRST_CHILD(this)->fops->unlink, loc, xflag, xdata);
|
||
|
+ int32_t error;
|
||
|
+
|
||
|
+ error = PL_INODE_REMOVE(unlink, frame, this, loc, NULL, pl_unlink,
|
||
|
+ pl_unlink_cbk, loc, xflag, xdata);
|
||
|
+ if (error > 0) {
|
||
|
+ STACK_UNWIND_STRICT(unlink, frame, -1, error, NULL, NULL, NULL);
|
||
|
+ }
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4351,8 +4476,11 @@ pl_rmdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
|
||
|
int32_t op_errno, struct iatt *preparent, struct iatt *postparent,
|
||
|
dict_t *xdata)
|
||
|
{
|
||
|
+ pl_inode_remove_cbk(this, cookie, op_ret < 0 ? op_errno : 0);
|
||
|
+
|
||
|
PL_STACK_UNWIND_FOR_CLIENT(rmdir, xdata, frame, op_ret, op_errno, preparent,
|
||
|
postparent, xdata);
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4360,9 +4488,14 @@ int
|
||
|
pl_rmdir(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflags,
|
||
|
dict_t *xdata)
|
||
|
{
|
||
|
- PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), loc, NULL);
|
||
|
- STACK_WIND(frame, pl_rmdir_cbk, FIRST_CHILD(this),
|
||
|
- FIRST_CHILD(this)->fops->rmdir, loc, xflags, xdata);
|
||
|
+ int32_t error;
|
||
|
+
|
||
|
+ error = PL_INODE_REMOVE(rmdir, frame, this, loc, NULL, pl_rmdir,
|
||
|
+ pl_rmdir_cbk, loc, xflags, xdata);
|
||
|
+ if (error > 0) {
|
||
|
+ STACK_UNWIND_STRICT(rmdir, frame, -1, error, NULL, NULL, NULL);
|
||
|
+ }
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4392,6 +4525,19 @@ pl_link_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
|
||
|
int32_t op_errno, inode_t *inode, struct iatt *buf,
|
||
|
struct iatt *preparent, struct iatt *postparent, dict_t *xdata)
|
||
|
{
|
||
|
+ pl_inode_t *pl_inode = (pl_inode_t *)cookie;
|
||
|
+
|
||
|
+ if (op_ret >= 0) {
|
||
|
+ pthread_mutex_lock(&pl_inode->mutex);
|
||
|
+
|
||
|
+ /* TODO: can happen pl_inode->links == 0 ? */
|
||
|
+ if (pl_inode->links >= 0) {
|
||
|
+ pl_inode->links++;
|
||
|
+ }
|
||
|
+
|
||
|
+ pthread_mutex_unlock(&pl_inode->mutex);
|
||
|
+ }
|
||
|
+
|
||
|
PL_STACK_UNWIND_FOR_CLIENT(link, xdata, frame, op_ret, op_errno, inode, buf,
|
||
|
preparent, postparent, xdata);
|
||
|
return 0;
|
||
|
@@ -4401,9 +4547,18 @@ int
|
||
|
pl_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
|
||
|
dict_t *xdata)
|
||
|
{
|
||
|
+ pl_inode_t *pl_inode;
|
||
|
+
|
||
|
+ pl_inode = pl_inode_get(this, oldloc->inode, NULL);
|
||
|
+ if (pl_inode == NULL) {
|
||
|
+ STACK_UNWIND_STRICT(link, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
|
||
|
+ NULL);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), oldloc, newloc);
|
||
|
- STACK_WIND(frame, pl_link_cbk, FIRST_CHILD(this),
|
||
|
- FIRST_CHILD(this)->fops->link, oldloc, newloc, xdata);
|
||
|
+ STACK_WIND_COOKIE(frame, pl_link_cbk, pl_inode, FIRST_CHILD(this),
|
||
|
+ FIRST_CHILD(this)->fops->link, oldloc, newloc, xdata);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
--
|
||
|
1.8.3.1
|
||
|
|