Before this patch, function gfs2_ail1_empty could issue a file system
withdraw when IO errors were discovered. However, there are several
callers, including gfs2_flush_revokes() which holds the gfs2_log_lock
before calling gfs2_ail1_empty. If gfs2_ail1_empty needed to withdraw
it would leave the gfs2_log_lock held, which resulted in a deadlock
due to other processes that needed the log_lock.

This patch moves the withdraw out of function gfs2_ail1_empty and
makes each of the callers check for a withdraw by calling new function
check_ail1_withdraw. Function gfs2_flush_revokes now does this check
after releasing the gfs2_log_lock to avoid the deadlock.

Signed-off-by: Bob Peterson <rpete...@redhat.com>
---
 fs/gfs2/log.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index bd2ff5ef4b91..7e0ac87f7d71 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -379,11 +379,6 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp, int 
max_revokes)
        ret = list_empty(&sdp->sd_ail1_list);
        spin_unlock(&sdp->sd_ail_lock);
 
-       if (test_bit(SDF_WITHDRAWING, &sdp->sd_flags)) {
-               gfs2_lm(sdp, "fatal: I/O error(s)\n");
-               gfs2_withdraw(sdp);
-       }
-
        return ret;
 }
 
@@ -801,6 +796,15 @@ void gfs2_glock_remove_revoke(struct gfs2_glock *gl)
        }
 }
 
+static void check_ail1_withdraw(struct gfs2_sbd *sdp)
+{
+       if (!test_bit(SDF_WITHDRAWING, &sdp->sd_flags))
+               return;
+
+       gfs2_lm(sdp, "fatal: I/O error(s)\n");
+       gfs2_withdraw(sdp);
+}
+
 /**
  * gfs2_flush_revokes - Add as many revokes to the system transaction as we can
  * @sdp: The GFS2 superblock
@@ -821,6 +825,7 @@ void gfs2_flush_revokes(struct gfs2_sbd *sdp)
        gfs2_log_lock(sdp);
        gfs2_ail1_empty(sdp, max_revokes);
        gfs2_log_unlock(sdp);
+       check_ail1_withdraw(sdp);
 }
 
 /**
@@ -982,6 +987,7 @@ void gfs2_ail_drain(struct gfs2_sbd *sdp)
 static void empty_ail1_list(struct gfs2_sbd *sdp)
 {
        unsigned long start = jiffies;
+       int empty;
 
        for (;;) {
                if (time_after(jiffies, start + (HZ * 600))) {
@@ -992,7 +998,9 @@ static void empty_ail1_list(struct gfs2_sbd *sdp)
                }
                gfs2_ail1_start(sdp);
                gfs2_ail1_wait(sdp);
-               if (gfs2_ail1_empty(sdp, 0))
+               empty = gfs2_ail1_empty(sdp, 0);
+               check_ail1_withdraw(sdp);
+               if (empty)
                        return;
        }
 }
@@ -1364,6 +1372,7 @@ int gfs2_logd(void *data)
 
                if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
                        gfs2_ail1_empty(sdp, 0);
+                       check_ail1_withdraw(sdp);
                        gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
                                                  GFS2_LFC_LOGD_JFLUSH_REQD);
                }
@@ -1372,6 +1381,7 @@ int gfs2_logd(void *data)
                        gfs2_ail1_start(sdp);
                        gfs2_ail1_wait(sdp);
                        gfs2_ail1_empty(sdp, 0);
+                       check_ail1_withdraw(sdp);
                        gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
                                                  GFS2_LFC_LOGD_AIL_FLUSH_REQD);
                }
-- 
2.31.1

Reply via email to