Merge tag 'trace-v6.5-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[platform/kernel/linux-starfive.git] / fs / gfs2 / super.c
index a84bf64..9f4d5d6 100644 (file)
@@ -332,7 +332,12 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp)
        struct lfcc *lfcc;
        LIST_HEAD(list);
        struct gfs2_log_header_host lh;
-       int error;
+       int error, error2;
+
+       /*
+        * Grab all the journal glocks in SH mode.  We are *probably* doing
+        * that to prevent recovery.
+        */
 
        list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
                lfcc = kmalloc(sizeof(struct lfcc), GFP_KERNEL);
@@ -349,11 +354,13 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp)
                list_add(&lfcc->list, &list);
        }
 
+       gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+
        error = gfs2_glock_nq_init(sdp->sd_freeze_gl, LM_ST_EXCLUSIVE,
                                   LM_FLAG_NOEXP | GL_NOPID,
                                   &sdp->sd_freeze_gh);
        if (error)
-               goto out;
+               goto relock_shared;
 
        list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
                error = gfs2_jdesc_check(jd);
@@ -368,8 +375,14 @@ static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp)
                }
        }
 
-       if (error)
-               gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+       if (!error)
+               goto out;  /* success */
+
+       gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+
+relock_shared:
+       error2 = gfs2_freeze_lock_shared(sdp);
+       gfs2_assert_withdraw(sdp, !error2);
 
 out:
        while (!list_empty(&list)) {
@@ -463,7 +476,7 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
  * @flags: The type of dirty
  *
  * Unfortunately it can be called under any combination of inode
- * glock and transaction lock, so we have to check carefully.
+ * glock and freeze glock, so we have to check carefully.
  *
  * At the moment this deals only with atime - it should be possible
  * to expand that role in future, once a review of the locking has
@@ -615,6 +628,8 @@ restart:
 
        /*  Release stuff  */
 
+       gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+
        iput(sdp->sd_jindex);
        iput(sdp->sd_statfs_inode);
        iput(sdp->sd_rindex);
@@ -669,59 +684,109 @@ static int gfs2_sync_fs(struct super_block *sb, int wait)
        return sdp->sd_log_error;
 }
 
-void gfs2_freeze_func(struct work_struct *work)
+static int gfs2_freeze_locally(struct gfs2_sbd *sdp)
 {
-       int error;
-       struct gfs2_holder freeze_gh;
-       struct gfs2_sbd *sdp = container_of(work, struct gfs2_sbd, sd_freeze_work);
        struct super_block *sb = sdp->sd_vfs;
+       int error;
 
-       atomic_inc(&sb->s_active);
-       error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
-       if (error) {
-               gfs2_assert_withdraw(sdp, 0);
-       } else {
-               atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN);
-               error = thaw_super(sb);
-               if (error) {
-                       fs_info(sdp, "GFS2: couldn't thaw filesystem: %d\n",
-                               error);
-                       gfs2_assert_withdraw(sdp, 0);
+       error = freeze_super(sb);
+       if (error)
+               return error;
+
+       if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
+               gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE |
+                              GFS2_LFC_FREEZE_GO_SYNC);
+               if (gfs2_withdrawn(sdp)) {
+                       thaw_super(sb);
+                       return -EIO;
                }
-               gfs2_freeze_unlock(&freeze_gh);
        }
+       return 0;
+}
+
+static int gfs2_do_thaw(struct gfs2_sbd *sdp)
+{
+       struct super_block *sb = sdp->sd_vfs;
+       int error;
+
+       error = gfs2_freeze_lock_shared(sdp);
+       if (error)
+               goto fail;
+       error = thaw_super(sb);
+       if (!error)
+               return 0;
+
+fail:
+       fs_info(sdp, "GFS2: couldn't thaw filesystem: %d\n", error);
+       gfs2_assert_withdraw(sdp, 0);
+       return error;
+}
+
+void gfs2_freeze_func(struct work_struct *work)
+{
+       struct gfs2_sbd *sdp = container_of(work, struct gfs2_sbd, sd_freeze_work);
+       struct super_block *sb = sdp->sd_vfs;
+       int error;
+
+       mutex_lock(&sdp->sd_freeze_mutex);
+       error = -EBUSY;
+       if (test_bit(SDF_FROZEN, &sdp->sd_flags))
+               goto freeze_failed;
+
+       error = gfs2_freeze_locally(sdp);
+       if (error)
+               goto freeze_failed;
+
+       gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+       set_bit(SDF_FROZEN, &sdp->sd_flags);
+
+       error = gfs2_do_thaw(sdp);
+       if (error)
+               goto out;
+
+       clear_bit(SDF_FROZEN, &sdp->sd_flags);
+       goto out;
+
+freeze_failed:
+       fs_info(sdp, "GFS2: couldn't freeze filesystem: %d\n", error);
+
+out:
+       mutex_unlock(&sdp->sd_freeze_mutex);
        deactivate_super(sb);
-       clear_bit_unlock(SDF_FS_FROZEN, &sdp->sd_flags);
-       wake_up_bit(&sdp->sd_flags, SDF_FS_FROZEN);
-       return;
 }
 
 /**
- * gfs2_freeze - prevent further writes to the filesystem
+ * gfs2_freeze_super - prevent further writes to the filesystem
  * @sb: the VFS structure for the filesystem
  *
  */
 
-static int gfs2_freeze(struct super_block *sb)
+static int gfs2_freeze_super(struct super_block *sb)
 {
        struct gfs2_sbd *sdp = sb->s_fs_info;
        int error;
 
-       mutex_lock(&sdp->sd_freeze_mutex);
-       if (atomic_read(&sdp->sd_freeze_state) != SFS_UNFROZEN) {
-               error = -EBUSY;
+       if (!mutex_trylock(&sdp->sd_freeze_mutex))
+               return -EBUSY;
+       error = -EBUSY;
+       if (test_bit(SDF_FROZEN, &sdp->sd_flags))
                goto out;
-       }
 
        for (;;) {
-               if (gfs2_withdrawn(sdp)) {
-                       error = -EINVAL;
+               error = gfs2_freeze_locally(sdp);
+               if (error) {
+                       fs_info(sdp, "GFS2: couldn't freeze filesystem: %d\n",
+                               error);
                        goto out;
                }
 
                error = gfs2_lock_fs_check_clean(sdp);
                if (!error)
-                       break;
+                       break;  /* success */
+
+               error = gfs2_do_thaw(sdp);
+               if (error)
+                       goto out;
 
                if (error == -EBUSY)
                        fs_err(sdp, "waiting for recovery before freeze\n");
@@ -735,32 +800,58 @@ static int gfs2_freeze(struct super_block *sb)
                fs_err(sdp, "retrying...\n");
                msleep(1000);
        }
-       set_bit(SDF_FS_FROZEN, &sdp->sd_flags);
+
 out:
+       if (!error) {
+               set_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags);
+               set_bit(SDF_FROZEN, &sdp->sd_flags);
+       }
        mutex_unlock(&sdp->sd_freeze_mutex);
        return error;
 }
 
 /**
- * gfs2_unfreeze - reallow writes to the filesystem
+ * gfs2_thaw_super - reallow writes to the filesystem
  * @sb: the VFS structure for the filesystem
  *
  */
 
-static int gfs2_unfreeze(struct super_block *sb)
+static int gfs2_thaw_super(struct super_block *sb)
 {
        struct gfs2_sbd *sdp = sb->s_fs_info;
+       int error;
 
-       mutex_lock(&sdp->sd_freeze_mutex);
-       if (atomic_read(&sdp->sd_freeze_state) != SFS_FROZEN ||
-           !gfs2_holder_initialized(&sdp->sd_freeze_gh)) {
-               mutex_unlock(&sdp->sd_freeze_mutex);
-               return -EINVAL;
+       if (!mutex_trylock(&sdp->sd_freeze_mutex))
+               return -EBUSY;
+       error = -EINVAL;
+       if (!test_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags))
+               goto out;
+
+       gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+
+       error = gfs2_do_thaw(sdp);
+
+       if (!error) {
+               clear_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags);
+               clear_bit(SDF_FROZEN, &sdp->sd_flags);
        }
+out:
+       mutex_unlock(&sdp->sd_freeze_mutex);
+       return error;
+}
+
+void gfs2_thaw_freeze_initiator(struct super_block *sb)
+{
+       struct gfs2_sbd *sdp = sb->s_fs_info;
+
+       mutex_lock(&sdp->sd_freeze_mutex);
+       if (!test_bit(SDF_FREEZE_INITIATOR, &sdp->sd_flags))
+               goto out;
 
        gfs2_freeze_unlock(&sdp->sd_freeze_gh);
+
+out:
        mutex_unlock(&sdp->sd_freeze_mutex);
-       return wait_on_bit(&sdp->sd_flags, SDF_FS_FROZEN, TASK_INTERRUPTIBLE);
 }
 
 /**
@@ -1004,7 +1095,14 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root)
 {
        struct gfs2_sbd *sdp = root->d_sb->s_fs_info;
        struct gfs2_args *args = &sdp->sd_args;
-       int val;
+       unsigned int logd_secs, statfs_slow, statfs_quantum, quota_quantum;
+
+       spin_lock(&sdp->sd_tune.gt_spin);
+       logd_secs = sdp->sd_tune.gt_logd_secs;
+       quota_quantum = sdp->sd_tune.gt_quota_quantum;
+       statfs_quantum = sdp->sd_tune.gt_statfs_quantum;
+       statfs_slow = sdp->sd_tune.gt_statfs_slow;
+       spin_unlock(&sdp->sd_tune.gt_spin);
 
        if (is_ancestor(root, sdp->sd_master_dir))
                seq_puts(s, ",meta");
@@ -1059,17 +1157,14 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root)
        }
        if (args->ar_discard)
                seq_puts(s, ",discard");
-       val = sdp->sd_tune.gt_logd_secs;
-       if (val != 30)
-               seq_printf(s, ",commit=%d", val);
-       val = sdp->sd_tune.gt_statfs_quantum;
-       if (val != 30)
-               seq_printf(s, ",statfs_quantum=%d", val);
-       else if (sdp->sd_tune.gt_statfs_slow)
+       if (logd_secs != 30)
+               seq_printf(s, ",commit=%d", logd_secs);
+       if (statfs_quantum != 30)
+               seq_printf(s, ",statfs_quantum=%d", statfs_quantum);
+       else if (statfs_slow)
                seq_puts(s, ",statfs_quantum=0");
-       val = sdp->sd_tune.gt_quota_quantum;
-       if (val != 60)
-               seq_printf(s, ",quota_quantum=%d", val);
+       if (quota_quantum != 60)
+               seq_printf(s, ",quota_quantum=%d", quota_quantum);
        if (args->ar_statfs_percent)
                seq_printf(s, ",statfs_percent=%d", args->ar_statfs_percent);
        if (args->ar_errors != GFS2_ERRORS_DEFAULT) {
@@ -1131,9 +1226,7 @@ static int gfs2_dinode_dealloc(struct gfs2_inode *ip)
                return -EIO;
        }
 
-       error = gfs2_rindex_update(sdp);
-       if (error)
-               return error;
+       gfs2_rindex_update(sdp);
 
        error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE);
        if (error)
@@ -1334,9 +1427,6 @@ static int evict_unlinked_inode(struct inode *inode)
                        goto out;
        }
 
-       if (ip->i_gl)
-               gfs2_inode_remember_delete(ip->i_gl, ip->i_no_formal_ino);
-
        /*
         * As soon as we clear the bitmap for the dinode, gfs2_create_inode()
         * can get called to recreate it, or even gfs2_inode_lookup() if the
@@ -1350,6 +1440,9 @@ static int evict_unlinked_inode(struct inode *inode)
         */
 
        ret = gfs2_dinode_dealloc(ip);
+       if (!ret && ip->i_gl)
+               gfs2_inode_remember_delete(ip->i_gl, ip->i_no_formal_ino);
+
 out:
        return ret;
 }
@@ -1528,8 +1621,8 @@ const struct super_operations gfs2_super_ops = {
        .evict_inode            = gfs2_evict_inode,
        .put_super              = gfs2_put_super,
        .sync_fs                = gfs2_sync_fs,
-       .freeze_super           = gfs2_freeze,
-       .thaw_super             = gfs2_unfreeze,
+       .freeze_super           = gfs2_freeze_super,
+       .thaw_super             = gfs2_thaw_super,
        .statfs                 = gfs2_statfs,
        .drop_inode             = gfs2_drop_inode,
        .show_options           = gfs2_show_options,