d2787b
From e3813685237dbdf8dc7cf28726fff2caf2288706 Mon Sep 17 00:00:00 2001
d2787b
From: Xavi Hernandez <xhernandez@redhat.com>
d2787b
Date: Mon, 19 Jul 2021 15:37:02 +0200
d2787b
Subject: [PATCH 588/610] locks: Fix null gfid in lock contention notifications
d2787b
d2787b
This patch fixes 3 problems:
d2787b
d2787b
First problem:
d2787b
d2787b
After commit c0bd592e, the pl_inode_t object was also created in the
d2787b
cbk of lookup requests. Lookup requests are a bit different than any
d2787b
other request because the inode received may not be completely
d2787b
initialized. In particular, inode->gfid may be null.
d2787b
d2787b
This caused that the gfid stored in the pl_inode_t object was null in
d2787b
some cases. This gfid is used mostly for logs, but also to send lock
d2787b
contention notifications. This meant that some notifications could be
d2787b
sent with a null gfid, making impossible for the client xlator to
d2787b
correctly identify the contending inode, so the lock was not released
d2787b
immediately when eager-lock was also enabled.
d2787b
d2787b
Second problem:
d2787b
d2787b
The feature introduced by c0bd592e needed to track the number of
d2787b
hardlinks of each inode to detect when it was deleted. However it
d2787b
was done using the 'get-link-count' special xattr on lookup, while
d2787b
posix only implements it for unlink and rename.
d2787b
d2787b
Also, the number of hardlinks was not incremented for mkdir, mknod,
d2787b
rename, ..., so it didn't work correctly for directories.
d2787b
d2787b
Third problem:
d2787b
d2787b
When the last hardlink of an open file is deleted, all locks will be
d2787b
denied with ESTALE error, but that's not correct. Access to the open
d2787b
fd must succeed.
d2787b
d2787b
The first problem is fixed by avoiding creating pl_inode_t objects
d2787b
during lookup. Second and third problems are fixed by completely
d2787b
ignoring if the file has been deleted or not. Even if we grant a
d2787b
lock on a non-existing file, the next operation done by the client
d2787b
inside the lock will return the correct error, which should be enough.
d2787b
d2787b
Upstream patch:
d2787b
> Upstream-patch-link: https://github.com/gluster/glusterfs/pull/2553
d2787b
> Fixes: #2551
d2787b
> Change-Id: Ic73e82f6b725b838c1600b6a128ea36a75f13253
d2787b
> Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
d2787b
d2787b
BUG: 1962972
d2787b
Change-Id: Ic73e82f6b725b838c1600b6a128ea36a75f13253
d2787b
Signed-off-by: Xavi Hernandez <xhernandez@redhat.com>
d2787b
Reviewed-on: https://code.engineering.redhat.com/gerrit/c/rhs-glusterfs/+/279192
d2787b
Tested-by: RHGS Build Bot <nigelb@redhat.com>
d2787b
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
d2787b
---
d2787b
 tests/bugs/locks/issue-2551.t       |  58 ++++++++++++++++++
d2787b
 xlators/features/locks/src/common.c |  31 +++-------
d2787b
 xlators/features/locks/src/locks.h  |   2 -
d2787b
 xlators/features/locks/src/posix.c  | 118 +++---------------------------------
d2787b
 4 files changed, 74 insertions(+), 135 deletions(-)
d2787b
 create mode 100644 tests/bugs/locks/issue-2551.t
d2787b
d2787b
diff --git a/tests/bugs/locks/issue-2551.t b/tests/bugs/locks/issue-2551.t
d2787b
new file mode 100644
d2787b
index 0000000..a32af02
d2787b
--- /dev/null
d2787b
+++ b/tests/bugs/locks/issue-2551.t
d2787b
@@ -0,0 +1,58 @@
d2787b
+#!/bin/bash
d2787b
+
d2787b
+. $(dirname $0)/../../include.rc
d2787b
+. $(dirname $0)/../../volume.rc
d2787b
+
d2787b
+function check_time() {
d2787b
+    local max="${1}"
d2787b
+    local start="$(date +"%s")"
d2787b
+
d2787b
+    shift
d2787b
+
d2787b
+    if "${@}"; then
d2787b
+        if [[ $(($(date +"%s") - ${start})) -lt ${max} ]]; then
d2787b
+            return 0
d2787b
+        fi
d2787b
+    fi
d2787b
+
d2787b
+    return 1
d2787b
+}
d2787b
+
d2787b
+cleanup
d2787b
+
d2787b
+TEST glusterd
d2787b
+TEST pidof glusterd
d2787b
+TEST $CLI volume create $V0 disperse 3 redundancy 1 $H0:$B0/brick{0..2}
d2787b
+TEST $CLI volume set $V0 disperse.eager-lock on
d2787b
+TEST $CLI volume set $V0 disperse.eager-lock-timeout 30
d2787b
+TEST $CLI volume set $V0 features.locks-notify-contention on
d2787b
+TEST $CLI volume set $V0 performance.write-behind off
d2787b
+TEST $CLI volume set $V0 performance.open-behind off
d2787b
+TEST $CLI volume set $V0 performance.quick-read off
d2787b
+
d2787b
+TEST $CLI volume start $V0
d2787b
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/brick0
d2787b
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/brick1
d2787b
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/brick2
d2787b
+
d2787b
+TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M0
d2787b
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "3" ec_child_up_count $V0 0 $M0
d2787b
+
d2787b
+TEST mkdir $M0/dir
d2787b
+TEST dd if=/dev/zero of=$M0/dir/test bs=4k count=1
d2787b
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" force_umount $M0
d2787b
+
d2787b
+TEST $CLI volume stop $V0
d2787b
+TEST $CLI volume start $V0
d2787b
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/brick0
d2787b
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/brick1
d2787b
+EXPECT_WITHIN $PROCESS_UP_TIMEOUT "1" brick_up_status $V0 $H0 $B0/brick2
d2787b
+
d2787b
+TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M0
d2787b
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "3" ec_child_up_count $V0 0 $M0
d2787b
+
d2787b
+TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M1
d2787b
+EXPECT_WITHIN $CHILD_UP_TIMEOUT "3" ec_child_up_count $V0 0 $M1
d2787b
+
d2787b
+TEST dd if=/dev/zero of=$M0/dir/test bs=4k count=1 conv=notrunc
d2787b
+TEST check_time 5 dd if=/dev/zero of=$M1/dir/test bs=4k count=1 conv=notrunc
d2787b
diff --git a/xlators/features/locks/src/common.c b/xlators/features/locks/src/common.c
d2787b
index cddbfa6..5403086 100644
d2787b
--- a/xlators/features/locks/src/common.c
d2787b
+++ b/xlators/features/locks/src/common.c
d2787b
@@ -468,9 +468,7 @@ pl_inode_get(xlator_t *this, inode_t *inode, pl_local_t *local)
d2787b
         pl_inode->check_mlock_info = _gf_true;
d2787b
         pl_inode->mlock_enforced = _gf_false;
d2787b
 
d2787b
-        /* -2 means never looked up. -1 means something went wrong and link
d2787b
-         * tracking is disabled. */
d2787b
-        pl_inode->links = -2;
d2787b
+        pl_inode->remove_running = 0;
d2787b
 
d2787b
         ret = __inode_ctx_put(inode, this, (uint64_t)(long)(pl_inode));
d2787b
         if (ret) {
d2787b
@@ -1403,11 +1401,6 @@ pl_inode_remove_prepare(xlator_t *xl, call_frame_t *frame, loc_t *loc,
d2787b
 
d2787b
     pthread_mutex_lock(&pl_inode->mutex);
d2787b
 
d2787b
-    if (pl_inode->removed) {
d2787b
-        error = ESTALE;
d2787b
-        goto unlock;
d2787b
-    }
d2787b
-
d2787b
     if (pl_inode_has_owners(xl, frame->root->client, pl_inode, &now, contend)) {
d2787b
         error = -1;
d2787b
         /* We skip the unlock here because the caller must create a stub when
d2787b
@@ -1420,7 +1413,6 @@ pl_inode_remove_prepare(xlator_t *xl, call_frame_t *frame, loc_t *loc,
d2787b
     pl_inode->is_locked = _gf_true;
d2787b
     pl_inode->remove_running++;
d2787b
 
d2787b
-unlock:
d2787b
     pthread_mutex_unlock(&pl_inode->mutex);
d2787b
 
d2787b
 done:
d2787b
@@ -1490,20 +1482,18 @@ pl_inode_remove_cbk(xlator_t *xl, pl_inode_t *pl_inode, int32_t error)
d2787b
 
d2787b
     pthread_mutex_lock(&pl_inode->mutex);
d2787b
 
d2787b
-    if (error == 0) {
d2787b
-        if (pl_inode->links >= 0) {
d2787b
-            pl_inode->links--;
d2787b
-        }
d2787b
-        if (pl_inode->links == 0) {
d2787b
-            pl_inode->removed = _gf_true;
d2787b
-        }
d2787b
-    }
d2787b
-
d2787b
     pl_inode->remove_running--;
d2787b
 
d2787b
     if ((pl_inode->remove_running == 0) && list_empty(&pl_inode->waiting)) {
d2787b
         pl_inode->is_locked = _gf_false;
d2787b
 
d2787b
+        /* At this point it's possible that the inode has been deleted, but
d2787b
+         * there could be open fd's still referencing it, so we can't prevent
d2787b
+         * pending locks from being granted. If the file has really been
d2787b
+         * deleted, whatever the client does once the lock is granted will
d2787b
+         * fail with the appropriate error, so we don't need to worry about
d2787b
+         * it here. */
d2787b
+
d2787b
         list_for_each_entry(dom, &pl_inode->dom_list, inode_list)
d2787b
         {
d2787b
             __grant_blocked_inode_locks(xl, pl_inode, &granted, dom, &now,
d2787b
@@ -1555,11 +1545,6 @@ pl_inode_remove_inodelk(pl_inode_t *pl_inode, pl_inode_lock_t *lock)
d2787b
     pl_dom_list_t *dom;
d2787b
     pl_inode_lock_t *ilock;
d2787b
 
d2787b
-    /* If the inode has been deleted, we won't allow any lock. */
d2787b
-    if (pl_inode->removed) {
d2787b
-        return -ESTALE;
d2787b
-    }
d2787b
-
d2787b
     /* We only synchronize with locks made for regular operations coming from
d2787b
      * the user. Locks done for internal purposes are hard to control and could
d2787b
      * lead to long delays or deadlocks quite easily. */
d2787b
diff --git a/xlators/features/locks/src/locks.h b/xlators/features/locks/src/locks.h
d2787b
index 6666feb..2406dcd 100644
d2787b
--- a/xlators/features/locks/src/locks.h
d2787b
+++ b/xlators/features/locks/src/locks.h
d2787b
@@ -202,10 +202,8 @@ struct __pl_inode {
d2787b
     int fop_wind_count;
d2787b
     pthread_cond_t check_fop_wind_count;
d2787b
 
d2787b
-    int32_t links;           /* Number of hard links the inode has. */
d2787b
     uint32_t remove_running; /* Number of remove operations running. */
d2787b
     gf_boolean_t is_locked;  /* Regular locks will be blocked. */
d2787b
-    gf_boolean_t removed;    /* The inode has been deleted. */
d2787b
 };
d2787b
 typedef struct __pl_inode pl_inode_t;
d2787b
 
d2787b
diff --git a/xlators/features/locks/src/posix.c b/xlators/features/locks/src/posix.c
d2787b
index 22ef5b8..d5effef 100644
d2787b
--- a/xlators/features/locks/src/posix.c
d2787b
+++ b/xlators/features/locks/src/posix.c
d2787b
@@ -2975,104 +2975,24 @@ out:
d2787b
     return ret;
d2787b
 }
d2787b
 
d2787b
-static int32_t
d2787b
-pl_request_link_count(dict_t **pxdata)
d2787b
-{
d2787b
-    dict_t *xdata;
d2787b
-
d2787b
-    xdata = *pxdata;
d2787b
-    if (xdata == NULL) {
d2787b
-        xdata = dict_new();
d2787b
-        if (xdata == NULL) {
d2787b
-            return ENOMEM;
d2787b
-        }
d2787b
-    } else {
d2787b
-        dict_ref(xdata);
d2787b
-    }
d2787b
-
d2787b
-    if (dict_set_uint32(xdata, GET_LINK_COUNT, 0) != 0) {
d2787b
-        dict_unref(xdata);
d2787b
-        return ENOMEM;
d2787b
-    }
d2787b
-
d2787b
-    *pxdata = xdata;
d2787b
-
d2787b
-    return 0;
d2787b
-}
d2787b
-
d2787b
-static int32_t
d2787b
-pl_check_link_count(dict_t *xdata)
d2787b
-{
d2787b
-    int32_t count;
d2787b
-
d2787b
-    /* In case we are unable to read the link count from xdata, we take a
d2787b
-     * conservative approach and return -2, which will prevent the inode from
d2787b
-     * being considered deleted. In fact it will cause link tracking for this
d2787b
-     * inode to be disabled completely to avoid races. */
d2787b
-
d2787b
-    if (xdata == NULL) {
d2787b
-        return -2;
d2787b
-    }
d2787b
-
d2787b
-    if (dict_get_int32(xdata, GET_LINK_COUNT, &count) != 0) {
d2787b
-        return -2;
d2787b
-    }
d2787b
-
d2787b
-    return count;
d2787b
-}
d2787b
-
d2787b
 int32_t
d2787b
 pl_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
d2787b
               int32_t op_errno, inode_t *inode, struct iatt *buf, dict_t *xdata,
d2787b
               struct iatt *postparent)
d2787b
 {
d2787b
-    pl_inode_t *pl_inode;
d2787b
-
d2787b
-    if (op_ret >= 0) {
d2787b
-        pl_inode = pl_inode_get(this, inode, NULL);
d2787b
-        if (pl_inode == NULL) {
d2787b
-            PL_STACK_UNWIND(lookup, xdata, frame, -1, ENOMEM, NULL, NULL, NULL,
d2787b
-                            NULL);
d2787b
-            return 0;
d2787b
-        }
d2787b
-
d2787b
-        pthread_mutex_lock(&pl_inode->mutex);
d2787b
-
d2787b
-        /* We only update the link count if we previously didn't know it.
d2787b
-         * Doing it always can lead to races since lookup is not executed
d2787b
-         * atomically most of the times. */
d2787b
-        if (pl_inode->links == -2) {
d2787b
-            pl_inode->links = pl_check_link_count(xdata);
d2787b
-            if (buf->ia_type == IA_IFDIR) {
d2787b
-                /* Directories have at least 2 links. To avoid special handling
d2787b
-                 * for directories, we simply decrement the value here to make
d2787b
-                 * them equivalent to regular files. */
d2787b
-                pl_inode->links--;
d2787b
-            }
d2787b
-        }
d2787b
-
d2787b
-        pthread_mutex_unlock(&pl_inode->mutex);
d2787b
-    }
d2787b
-
d2787b
     PL_STACK_UNWIND(lookup, xdata, frame, op_ret, op_errno, inode, buf, xdata,
d2787b
                     postparent);
d2787b
+
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
 int32_t
d2787b
 pl_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata)
d2787b
 {
d2787b
-    int32_t error;
d2787b
+    PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), loc, NULL);
d2787b
+    STACK_WIND(frame, pl_lookup_cbk, FIRST_CHILD(this),
d2787b
+               FIRST_CHILD(this)->fops->lookup, loc, xdata);
d2787b
 
d2787b
-    error = pl_request_link_count(&xdata);
d2787b
-    if (error == 0) {
d2787b
-        PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), loc, NULL);
d2787b
-        STACK_WIND(frame, pl_lookup_cbk, FIRST_CHILD(this),
d2787b
-                   FIRST_CHILD(this)->fops->lookup, loc, xdata);
d2787b
-        dict_unref(xdata);
d2787b
-    } else {
d2787b
-        STACK_UNWIND_STRICT(lookup, frame, -1, error, NULL, NULL, NULL, NULL);
d2787b
-    }
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
@@ -3881,9 +3801,7 @@ unlock:
d2787b
             __dump_posixlks(pl_inode);
d2787b
         }
d2787b
 
d2787b
-        gf_proc_dump_write("links", "%d", pl_inode->links);
d2787b
         gf_proc_dump_write("removes_pending", "%u", pl_inode->remove_running);
d2787b
-        gf_proc_dump_write("removed", "%u", pl_inode->removed);
d2787b
     }
d2787b
     pthread_mutex_unlock(&pl_inode->mutex);
d2787b
 
d2787b
@@ -4508,21 +4426,9 @@ pl_link_cbk(call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
d2787b
             int32_t op_errno, inode_t *inode, struct iatt *buf,
d2787b
             struct iatt *preparent, struct iatt *postparent, dict_t *xdata)
d2787b
 {
d2787b
-    pl_inode_t *pl_inode = (pl_inode_t *)cookie;
d2787b
-
d2787b
-    if (op_ret >= 0) {
d2787b
-        pthread_mutex_lock(&pl_inode->mutex);
d2787b
-
d2787b
-        /* TODO: can happen pl_inode->links == 0 ? */
d2787b
-        if (pl_inode->links >= 0) {
d2787b
-            pl_inode->links++;
d2787b
-        }
d2787b
-
d2787b
-        pthread_mutex_unlock(&pl_inode->mutex);
d2787b
-    }
d2787b
-
d2787b
     PL_STACK_UNWIND_FOR_CLIENT(link, xdata, frame, op_ret, op_errno, inode, buf,
d2787b
                                preparent, postparent, xdata);
d2787b
+
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
@@ -4530,18 +4436,10 @@ int
d2787b
 pl_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
d2787b
         dict_t *xdata)
d2787b
 {
d2787b
-    pl_inode_t *pl_inode;
d2787b
-
d2787b
-    pl_inode = pl_inode_get(this, oldloc->inode, NULL);
d2787b
-    if (pl_inode == NULL) {
d2787b
-        STACK_UNWIND_STRICT(link, frame, -1, ENOMEM, NULL, NULL, NULL, NULL,
d2787b
-                            NULL);
d2787b
-        return 0;
d2787b
-    }
d2787b
-
d2787b
     PL_LOCAL_GET_REQUESTS(frame, this, xdata, ((fd_t *)NULL), oldloc, newloc);
d2787b
-    STACK_WIND_COOKIE(frame, pl_link_cbk, pl_inode, FIRST_CHILD(this),
d2787b
-                      FIRST_CHILD(this)->fops->link, oldloc, newloc, xdata);
d2787b
+    STACK_WIND(frame, pl_link_cbk, FIRST_CHILD(this),
d2787b
+               FIRST_CHILD(this)->fops->link, oldloc, newloc, xdata);
d2787b
+
d2787b
     return 0;
d2787b
 }
d2787b
 
d2787b
-- 
d2787b
1.8.3.1
d2787b