On Tue, Dec 22, 2020 at 9:43 PM Bob Peterson <[email protected]> wrote:
>
> Before this patch, sister functions gfs2_make_fs_rw and gfs2_make_fs_ro locked
> (held) the freeze glock by calling gfs2_freeze_lock and gfs2_freeze_unlock.
> The problem is, not all the callers of gfs2_make_fs_ro should be doing this.
> The three callers of gfs2_make_fs_ro are: remount (gfs2_reconfigure),
> signal_our_withdraw, and unmount (gfs2_put_super). But when unmounting the
> file system we can get into the following circular lock dependency:
>
> deactivate_super
>    down_write(&s->s_umount); <-------------------------------------- s_umount
>    deactivate_locked_super
>       gfs2_kill_sb
>          kill_block_super
>             generic_shutdown_super
>                gfs2_put_super
>                   gfs2_make_fs_ro
>                      gfs2_glock_nq_init sd_freeze_gl
>                         freeze_go_sync
>                            if (freeze glock in SH)
>                               freeze_super (vfs)
>                                  down_write(&sb->s_umount); <------- s_umount
>
> This patch moves the hold of the freeze glock outside the two sister rw/ro
> functions to their callers, but it doesn't request the glock from
> gfs2_put_super, thus eliminating the circular dependency.
>
> Signed-off-by: Bob Peterson <[email protected]>
> ---
>  fs/gfs2/ops_fstype.c | 31 +++++++++++++++++--------------
>  fs/gfs2/super.c      | 23 -----------------------
>  fs/gfs2/util.c       | 18 ++++++++++++++++--
>  3 files changed, 33 insertions(+), 39 deletions(-)
>
> diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
> index c71c7beb9257..cd3fd252c771 100644
> --- a/fs/gfs2/ops_fstype.c
> +++ b/fs/gfs2/ops_fstype.c
> @@ -1084,6 +1084,7 @@ static int gfs2_fill_super(struct super_block *sb, 
> struct fs_context *fc)
>         int silent = fc->sb_flags & SB_SILENT;
>         struct gfs2_sbd *sdp;
>         struct gfs2_holder mount_gh;
> +       struct gfs2_holder freeze_gh;
>         int error;
>
>         sdp = init_sbd(sb);
> @@ -1195,23 +1196,18 @@ static int gfs2_fill_super(struct super_block *sb, 
> struct fs_context *fc)
>                 goto fail_per_node;
>         }
>
> -       if (sb_rdonly(sb)) {
> -               struct gfs2_holder freeze_gh;
> +       error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
> +       if (error)
> +               goto fail_per_node;
>
> -               error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
> -               if (error) {
> -                       fs_err(sdp, "can't make FS RO: %d\n", error);
> -                       goto fail_per_node;
> -               }
> -               gfs2_freeze_unlock(&freeze_gh);
> -       } else {
> +       if (!sb_rdonly(sb))
>                 error = gfs2_make_fs_rw(sdp);
> -               if (error) {
> -                       fs_err(sdp, "can't make FS RW: %d\n", error);
> -                       goto fail_per_node;
> -               }
> -       }
>
> +       gfs2_freeze_unlock(&freeze_gh);
> +       if (error) {
> +               fs_err(sdp, "can't make FS RW: %d\n", error);
> +               goto fail_per_node;
> +       }
>         gfs2_glock_dq_uninit(&mount_gh);
>         gfs2_online_uevent(sdp);
>         return 0;
> @@ -1512,6 +1508,12 @@ static int gfs2_reconfigure(struct fs_context *fc)
>                 fc->sb_flags |= SB_RDONLY;
>
>         if ((sb->s_flags ^ fc->sb_flags) & SB_RDONLY) {
> +               struct gfs2_holder freeze_gh;
> +
> +               error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
> +               if (error)
> +                       return -EINVAL;
> +
>                 if (fc->sb_flags & SB_RDONLY) {
>                         error = gfs2_make_fs_ro(sdp);
>                         if (error)
> @@ -1521,6 +1523,7 @@ static int gfs2_reconfigure(struct fs_context *fc)
>                         if (error)
>                                 errorfc(fc, "unable to remount read-write");
>                 }
> +               gfs2_freeze_unlock(&freeze_gh);
>         }
>         sdp->sd_args = *newargs;
>
> diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
> index ea312a94ce69..754ea2a137b4 100644
> --- a/fs/gfs2/super.c
> +++ b/fs/gfs2/super.c
> @@ -165,7 +165,6 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
>  {
>         struct gfs2_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode);
>         struct gfs2_glock *j_gl = ip->i_gl;
> -       struct gfs2_holder freeze_gh;
>         struct gfs2_log_header_host head;
>         int error;
>
> @@ -173,10 +172,6 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
>         if (error)
>                 return error;
>
> -       error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
> -       if (error)
> -               goto fail_threads;
> -
>         j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
>         if (gfs2_withdrawn(sdp)) {
>                 error = -EIO;
> @@ -203,13 +198,9 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
>
>         set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
>
> -       gfs2_freeze_unlock(&freeze_gh);
> -
>         return 0;
>
>  fail:
> -       gfs2_freeze_unlock(&freeze_gh);
> -fail_threads:
>         if (sdp->sd_quotad_process)
>                 kthread_stop(sdp->sd_quotad_process);
>         sdp->sd_quotad_process = NULL;
> @@ -607,21 +598,9 @@ static void gfs2_dirty_inode(struct inode *inode, int 
> flags)
>
>  int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
>  {
> -       struct gfs2_holder freeze_gh;
>         int error = 0;
>         int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
>
> -       gfs2_holder_mark_uninitialized(&freeze_gh);
> -       if (sdp->sd_freeze_gl &&
> -           !gfs2_glock_is_locked_by_me(sdp->sd_freeze_gl)) {
> -               error = gfs2_freeze_lock(sdp, &freeze_gh,
> -                                        log_write_allowed ? 0 : LM_FLAG_TRY);
> -               if (error == GLR_TRYFAILED)
> -                       error = 0;
> -               if (error && !gfs2_withdrawn(sdp))
> -                       return error;
> -       }
> -
>         gfs2_flush_delete_work(sdp);
>         if (!log_write_allowed && current == sdp->sd_quotad_process)
>                 fs_warn(sdp, "The quotad daemon is withdrawing.\n");
> @@ -650,8 +629,6 @@ int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
>                                    atomic_read(&sdp->sd_reserving_log) == 0,
>                                    HZ * 5);
>         }
> -       gfs2_freeze_unlock(&freeze_gh);
> -
>         gfs2_quota_cleanup(sdp);
>
>         if (!log_write_allowed)
> diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
> index a115c441e2a1..87070e433f96 100644
> --- a/fs/gfs2/util.c
> +++ b/fs/gfs2/util.c
> @@ -122,6 +122,7 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
>         struct inode *inode = sdp->sd_jdesc->jd_inode;
>         struct gfs2_inode *ip = GFS2_I(inode);
>         u64 no_formal_ino = ip->i_no_formal_ino;
> +       int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
>         int ret = 0;
>         int tries;
>
> @@ -142,8 +143,21 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
>          * therefore we need to clear SDF_JOURNAL_LIVE manually.
>          */
>         clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
> -       if (!sb_rdonly(sdp->sd_vfs))
> -               ret = gfs2_make_fs_ro(sdp);
> +       if (!sb_rdonly(sdp->sd_vfs)) {
> +               struct gfs2_holder freeze_gh;
> +
> +               gfs2_holder_mark_uninitialized(&freeze_gh);
> +               if (sdp->sd_freeze_gl &&
> +                   !gfs2_glock_is_locked_by_me(sdp->sd_freeze_gl))

I'm adding a little cleanup here, but otherwise this patch looks good now:

--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -148,15 +148,15 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)

         gfs2_holder_mark_uninitialized(&freeze_gh);
         if (sdp->sd_freeze_gl &&
-            !gfs2_glock_is_locked_by_me(sdp->sd_freeze_gl))
+            !gfs2_glock_is_locked_by_me(sdp->sd_freeze_gl)) {
             ret = gfs2_freeze_lock(sdp, &freeze_gh,
                        log_write_allowed ? 0 : LM_FLAG_TRY);
-        if (ret == GLR_TRYFAILED)
-            ret = 0;
-        if (!ret) {
-            ret = gfs2_make_fs_ro(sdp);
-            gfs2_freeze_unlock(&freeze_gh);
+            if (ret == GLR_TRYFAILED)
+                ret = 0;
         }
+        if (!ret)
+            ret = gfs2_make_fs_ro(sdp);
+        gfs2_freeze_unlock(&freeze_gh);
     }

     if (sdp->sd_lockstruct.ls_ops->lm_lock == NULL) { /* lock_nolock */

> +                       ret = gfs2_freeze_lock(sdp, &freeze_gh,
> +                                      log_write_allowed ? 0 : LM_FLAG_TRY);
> +               if (ret == GLR_TRYFAILED)
> +                       ret = 0;
> +               if (!ret) {
> +                       ret = gfs2_make_fs_ro(sdp);
> +                       gfs2_freeze_unlock(&freeze_gh);
> +               }
> +       }
>
>         if (sdp->sd_lockstruct.ls_ops->lm_lock == NULL) { /* lock_nolock */
>                 if (!ret)
> --
> 2.29.2
>

Thanks,
Andreas

Reply via email to