]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gfs2: Fix additional unlikely request cancelation race
authorAndreas Gruenbacher <agruenba@redhat.com>
Wed, 12 Feb 2025 16:29:36 +0000 (17:29 +0100)
committerAndreas Gruenbacher <agruenba@redhat.com>
Mon, 10 Mar 2025 17:15:38 +0000 (18:15 +0100)
In gfs2_glock_dq(), we must drop the glock spin lock before calling
->lm_cancel, but this means that in the meantime, the operation we are
trying to cancel could complete.  If the operation completes
unsuccessfully, another holder can end up at the head of the queue and
another ->lm_lock operation can get started.  In this case, we would end
up canceling that second operation by accident.

To prevent that, introduce a new GLF_CANCELING flag.  Set that flag in
gfs2_glock_dq() when trying to cancel an operation.  When seeing that
flag, finish_xmote() will then keep the GLF_LOCK flag set to prevent
other glock operations from taking place.  gfs2_glock_dq() then
completes the cancelation attempt by clearing GLF_LOCK and
GLF_CANCELING.

In addition, add a missing GLF_DEMOTE_IN_PROGRESS check in
gfs2_glock_dq() to make sure that we won't accidentally cancel a demote
request.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/glock.c
fs/gfs2/incore.h
fs/gfs2/trace_gfs2.h

index 4d152e0e585ce8feddf83b688d3ccd20b6b9355e..ea98b8bc166abb8e410c27f489f5d44329b28137 100644 (file)
@@ -665,7 +665,8 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
                do_promote(gl);
        }
 out:
-       clear_bit(GLF_LOCK, &gl->gl_flags);
+       if (!test_bit(GLF_CANCELING, &gl->gl_flags))
+               clear_bit(GLF_LOCK, &gl->gl_flags);
 }
 
 static bool is_system_glock(struct gfs2_glock *gl)
@@ -1671,11 +1672,17 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
        }
 
        if (list_is_first(&gh->gh_list, &gl->gl_holders) &&
-           !test_bit(HIF_HOLDER, &gh->gh_iflags)) {
+           !test_bit(HIF_HOLDER, &gh->gh_iflags) &&
+           test_bit(GLF_LOCK, &gl->gl_flags) &&
+           !test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags) &&
+           !test_bit(GLF_CANCELING, &gl->gl_flags)) {
+               set_bit(GLF_CANCELING, &gl->gl_flags);
                spin_unlock(&gl->gl_lockref.lock);
                gl->gl_name.ln_sbd->sd_lockstruct.ls_ops->lm_cancel(gl);
                wait_on_bit(&gh->gh_iflags, HIF_WAIT, TASK_UNINTERRUPTIBLE);
                spin_lock(&gl->gl_lockref.lock);
+               clear_bit(GLF_CANCELING, &gl->gl_flags);
+               clear_bit(GLF_LOCK, &gl->gl_flags);
                if (!gfs2_holder_queued(gh))
                        goto out;
        }
@@ -2352,6 +2359,8 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
                *p++ = 'E';
        if (test_bit(GLF_DEFER_DELETE, gflags))
                *p++ = 's';
+       if (test_bit(GLF_CANCELING, gflags))
+               *p++ = 'C';
        *p = 0;
        return buf;
 }
index 40e66b46718b0bc99502c35af2f5617d0cbb2fa6..74abbd4970f80b4c577088b7bf68f42584be2b4e 100644 (file)
@@ -332,6 +332,7 @@ enum {
        GLF_VERIFY_DELETE               = 18, /* iopen glocks only */
        GLF_PENDING_REPLY               = 19,
        GLF_DEFER_DELETE                = 20, /* iopen glocks only */
+       GLF_CANCELING                   = 21,
 };
 
 struct gfs2_glock {
index 43de603ab347e0d862059a5f955be1a39e8cd2f6..26036ffc3f338e7a46a3c5c97b90d5fcb13ce863 100644 (file)
@@ -65,7 +65,8 @@
        {(1UL << GLF_INSTANTIATE_IN_PROG),      "N" },          \
        {(1UL << GLF_TRY_TO_EVICT),             "e" },          \
        {(1UL << GLF_VERIFY_DELETE),            "E" },          \
-       {(1UL << GLF_DEFER_DELETE),             "s" })
+       {(1UL << GLF_DEFER_DELETE),             "s" },          \
+       {(1UL << GLF_CANCELING),                "C" })
 
 #ifndef NUMPTY
 #define NUMPTY