From: Andreas Gruenbacher <agruenba@redhat.com>
To: gfs2@lists.linux.dev
Cc: Andreas Gruenbacher <agruenba@redhat.com>
Subject: [PATCH 04/13] gfs2: Do not cancel internal demote requests
Date: Fri, 23 Jan 2026 16:30:54 +0100 [thread overview]
Message-ID: <20260123153105.797382-5-agruenba@redhat.com> (raw)
In-Reply-To: <20260123153105.797382-1-agruenba@redhat.com>
Trying to change the state of a glock may result in a "conversion
deadlock" error, indicating that the requested state transition would
cause a deadlock. In this case, we unlock and retry the state change.
It makes no sense to try canceling those unlock requests, but the
current code is not aware of that.
In addition, if a locking request is canceled shortly after it is made,
the cancelation request can currently overtake the locking request.
This may result in deadlocks.
Fix both of these bugs by repurposing the GLF_PENDING_REPLY flag into a
GLF_MAY_CANCEL flag which is set only when the current locking request
can be canceled. When trying to cancel a locking request in
gfs2_glock_dq(), wait for this flag to be set.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/gfs2/glock.c | 46 +++++++++++++++++++++++++++++++-------------
fs/gfs2/incore.h | 2 +-
fs/gfs2/trace_gfs2.h | 2 +-
3 files changed, 35 insertions(+), 15 deletions(-)
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 776179933315..7c3d488327ee 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -60,7 +60,8 @@ struct gfs2_glock_iter {
typedef void (*glock_examiner) (struct gfs2_glock * gl);
-static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, unsigned int target);
+static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh,
+ unsigned int target, bool may_cancel);
static void request_demote(struct gfs2_glock *gl, unsigned int state,
unsigned long delay, bool remote);
@@ -600,12 +601,14 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
switch(gl->gl_state) {
/* Unlocked due to conversion deadlock, try again */
case LM_ST_UNLOCKED:
- do_xmote(gl, gh, gl->gl_target);
+ do_xmote(gl, gh, gl->gl_target,
+ !test_bit(GLF_DEMOTE_IN_PROGRESS,
+ &gl->gl_flags));
break;
/* Conversion fails, unlock and try again */
case LM_ST_SHARED:
case LM_ST_DEFERRED:
- do_xmote(gl, gh, LM_ST_UNLOCKED);
+ do_xmote(gl, gh, LM_ST_UNLOCKED, false);
break;
default: /* Everything else */
fs_err(gl->gl_name.ln_sbd,
@@ -638,7 +641,7 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
}
out:
if (!test_bit(GLF_CANCELING, &gl->gl_flags))
- clear_bit(GLF_LOCK, &gl->gl_flags);
+ clear_and_wake_up_bit(GLF_LOCK, &gl->gl_flags);
}
/**
@@ -646,11 +649,12 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
* @gl: The lock state
* @gh: The holder (only for promotes)
* @target: The target lock state
+ * @may_cancel: Operation may be canceled
*
*/
static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh,
- unsigned int target)
+ unsigned int target, bool may_cancel)
__releases(&gl->gl_lockref.lock)
__acquires(&gl->gl_lockref.lock)
{
@@ -703,17 +707,20 @@ __acquires(&gl->gl_lockref.lock)
}
if (ls->ls_ops->lm_lock) {
- set_bit(GLF_PENDING_REPLY, &gl->gl_flags);
spin_unlock(&gl->gl_lockref.lock);
ret = ls->ls_ops->lm_lock(gl, target, gh ? gh->gh_flags : 0);
spin_lock(&gl->gl_lockref.lock);
if (!ret) {
+ if (may_cancel) {
+ set_bit(GLF_MAY_CANCEL, &gl->gl_flags);
+ smp_mb__after_atomic();
+ wake_up_bit(&gl->gl_flags, GLF_LOCK);
+ }
/* The operation will be completed asynchronously. */
gl->gl_lockref.count++;
return;
}
- clear_bit(GLF_PENDING_REPLY, &gl->gl_flags);
if (ret == -ENODEV) {
/*
@@ -774,7 +781,7 @@ __acquires(&gl->gl_lockref.lock)
GLOCK_BUG_ON(gl, gl->gl_demote_state == LM_ST_EXCLUSIVE);
gl->gl_target = gl->gl_demote_state;
set_bit(GLF_LOCK, &gl->gl_flags);
- do_xmote(gl, NULL, gl->gl_target);
+ do_xmote(gl, NULL, gl->gl_target, false);
return;
}
@@ -791,7 +798,7 @@ __acquires(&gl->gl_lockref.lock)
if (!(gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)))
do_error(gl, 0); /* Fail queued try locks */
set_bit(GLF_LOCK, &gl->gl_flags);
- do_xmote(gl, gh, gl->gl_target);
+ do_xmote(gl, gh, gl->gl_target, true);
return;
out_sched:
@@ -1588,6 +1595,7 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
{
struct gfs2_glock *gl = gh->gh_gl;
+again:
spin_lock(&gl->gl_lockref.lock);
if (!gfs2_holder_queued(gh)) {
/*
@@ -1602,13 +1610,25 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
test_bit(GLF_LOCK, &gl->gl_flags) &&
!test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags) &&
!test_bit(GLF_CANCELING, &gl->gl_flags)) {
+ if (!test_bit(GLF_MAY_CANCEL, &gl->gl_flags)) {
+ struct wait_queue_head *wq;
+ DEFINE_WAIT(wait);
+
+ wq = bit_waitqueue(&gl->gl_flags, GLF_LOCK);
+ prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE);
+ spin_unlock(&gl->gl_lockref.lock);
+ schedule();
+ finish_wait(wq, &wait);
+ goto again;
+ }
+
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);
+ clear_and_wake_up_bit(GLF_LOCK, &gl->gl_flags);
if (!gfs2_holder_queued(gh))
goto out;
}
@@ -1838,7 +1858,7 @@ void gfs2_glock_complete(struct gfs2_glock *gl, int ret)
struct lm_lockstruct *ls = &gl->gl_name.ln_sbd->sd_lockstruct;
spin_lock(&gl->gl_lockref.lock);
- clear_bit(GLF_PENDING_REPLY, &gl->gl_flags);
+ clear_bit(GLF_MAY_CANCEL, &gl->gl_flags);
gl->gl_reply = ret;
if (unlikely(test_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags))) {
@@ -2245,8 +2265,8 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
*p++ = 'y';
if (test_bit(GLF_LFLUSH, gflags))
*p++ = 'f';
- if (test_bit(GLF_PENDING_REPLY, gflags))
- *p++ = 'R';
+ if (test_bit(GLF_MAY_CANCEL, gflags))
+ *p++ = 'c';
if (test_bit(GLF_HAVE_REPLY, gflags))
*p++ = 'r';
if (test_bit(GLF_INITIAL, gflags))
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index d05d8fe4e456..f7909607936a 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -326,7 +326,7 @@ enum {
GLF_BLOCKING = 15,
GLF_TRY_TO_EVICT = 17, /* iopen glocks only */
GLF_VERIFY_DELETE = 18, /* iopen glocks only */
- GLF_PENDING_REPLY = 19,
+ GLF_MAY_CANCEL = 19,
GLF_DEFER_DELETE = 20, /* iopen glocks only */
GLF_CANCELING = 21,
};
diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h
index fcfbf68ec725..a308228d5c2d 100644
--- a/fs/gfs2/trace_gfs2.h
+++ b/fs/gfs2/trace_gfs2.h
@@ -52,7 +52,7 @@
{(1UL << GLF_DEMOTE_IN_PROGRESS), "p" }, \
{(1UL << GLF_DIRTY), "y" }, \
{(1UL << GLF_LFLUSH), "f" }, \
- {(1UL << GLF_PENDING_REPLY), "R" }, \
+ {(1UL << GLF_MAY_CANCEL), "c" }, \
{(1UL << GLF_HAVE_REPLY), "r" }, \
{(1UL << GLF_INITIAL), "a" }, \
{(1UL << GLF_HAVE_FROZEN_REPLY), "F" }, \
--
2.52.0
next prev parent reply other threads:[~2026-01-23 15:31 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-23 15:30 [PATCH 00/13] gfs2 patches on for-next Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 01/13] gfs2: glock cancelation flag fix Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 02/13] gfs2: Retries missing in gfs2_{rename,exchange} Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 03/13] gfs2: run_queue cleanup Andreas Gruenbacher
2026-01-23 15:30 ` Andreas Gruenbacher [this message]
2026-01-23 15:30 ` [PATCH 05/13] Revert "gfs2: Fix use of bio_chain" Andreas Gruenbacher
2026-01-26 14:25 ` Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 06/13] gfs2: Rename gfs2_log_submit_{bio -> write} Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 07/13] gfs2: Initialize bio->bi_opf early Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 08/13] gfs2: gfs2_chain_bio start sector fix Andreas Gruenbacher
2026-01-23 15:30 ` [PATCH 09/13] gfs2: Fix gfs2_log_get_bio argument type Andreas Gruenbacher
2026-01-23 15:31 ` [PATCH 10/13] gfs: Use fixed GL_GLOCK_MIN_HOLD time Andreas Gruenbacher
2026-01-23 15:31 ` [PATCH 11/13] gfs2: gfs2_glock_hold cleanup Andreas Gruenbacher
2026-01-23 15:31 ` [PATCH 12/13] gfs2: Introduce glock_{type,number,sbd} helpers Andreas Gruenbacher
2026-01-23 15:31 ` [PATCH 13/13] gfs2: Fix slab-use-after-free in qd_put Andreas Gruenbacher
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260123153105.797382-5-agruenba@redhat.com \
--to=agruenba@redhat.com \
--cc=gfs2@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox