]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - fs/ext4/super.c
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
[mirror_ubuntu-hirsute-kernel.git] / fs / ext4 / super.c
index 21121787c874e186ed547177adcffdebbd3891a6..9a6f9875aa3499fb7ac3a48a062f933b162aa2b9 100644 (file)
@@ -65,7 +65,8 @@ static struct ratelimit_state ext4_mount_msg_ratelimit;
 static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
                             unsigned long journal_devnum);
 static int ext4_show_options(struct seq_file *seq, struct dentry *root);
-static int ext4_commit_super(struct super_block *sb, int sync);
+static void ext4_update_super(struct super_block *sb);
+static int ext4_commit_super(struct super_block *sb);
 static int ext4_mark_recovery_complete(struct super_block *sb,
                                        struct ext4_super_block *es);
 static int ext4_clear_journal_err(struct super_block *sb,
@@ -586,15 +587,12 @@ static int ext4_errno_to_code(int errno)
        return EXT4_ERR_UNKNOWN;
 }
 
-static void __save_error_info(struct super_block *sb, int error,
-                             __u32 ino, __u64 block,
-                             const char *func, unsigned int line)
+static void save_error_info(struct super_block *sb, int error,
+                           __u32 ino, __u64 block,
+                           const char *func, unsigned int line)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
 
-       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-       if (bdev_read_only(sb->s_bdev))
-               return;
        /* We default to EFSCORRUPTED error... */
        if (error == 0)
                error = EFSCORRUPTED;
@@ -618,15 +616,6 @@ static void __save_error_info(struct super_block *sb, int error,
        spin_unlock(&sbi->s_error_lock);
 }
 
-static void save_error_info(struct super_block *sb, int error,
-                           __u32 ino, __u64 block,
-                           const char *func, unsigned int line)
-{
-       __save_error_info(sb, error, ino, block, func, line);
-       if (!bdev_read_only(sb->s_bdev))
-               ext4_commit_super(sb, 1);
-}
-
 /* Deal with the reporting of failure conditions on a filesystem such as
  * inconsistencies detected or read IO failures.
  *
@@ -647,19 +636,40 @@ static void save_error_info(struct super_block *sb, int error,
  * used to deal with unrecoverable failures such as journal IO errors or ENOMEM
  * at a critical moment in log management.
  */
-static void ext4_handle_error(struct super_block *sb, bool force_ro)
+static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
+                             __u32 ino, __u64 block,
+                             const char *func, unsigned int line)
 {
        journal_t *journal = EXT4_SB(sb)->s_journal;
+       bool continue_fs = !force_ro && test_opt(sb, ERRORS_CONT);
 
+       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
        if (test_opt(sb, WARN_ON_ERROR))
                WARN_ON_ONCE(1);
 
-       if (sb_rdonly(sb) || (!force_ro && test_opt(sb, ERRORS_CONT)))
+       if (!continue_fs && !sb_rdonly(sb)) {
+               ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
+               if (journal)
+                       jbd2_journal_abort(journal, -EIO);
+       }
+
+       if (!bdev_read_only(sb->s_bdev)) {
+               save_error_info(sb, error, ino, block, func, line);
+               /*
+                * In case the fs should keep running, we need to writeout
+                * superblock through the journal. Due to lock ordering
+                * constraints, it may not be safe to do it right here so we
+                * defer superblock flushing to a workqueue.
+                */
+               if (continue_fs)
+                       schedule_work(&EXT4_SB(sb)->s_error_work);
+               else
+                       ext4_commit_super(sb);
+       }
+
+       if (sb_rdonly(sb) || continue_fs)
                return;
 
-       ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
-       if (journal)
-               jbd2_journal_abort(journal, -EIO);
        /*
         * We force ERRORS_RO behavior when system is rebooting. Otherwise we
         * could panic during 'reboot -f' as the underlying device got already
@@ -682,8 +692,39 @@ static void flush_stashed_error_work(struct work_struct *work)
 {
        struct ext4_sb_info *sbi = container_of(work, struct ext4_sb_info,
                                                s_error_work);
+       journal_t *journal = sbi->s_journal;
+       handle_t *handle;
 
-       ext4_commit_super(sbi->s_sb, 1);
+       /*
+        * If the journal is still running, we have to write out superblock
+        * through the journal to avoid collisions of other journalled sb
+        * updates.
+        *
+        * We use directly jbd2 functions here to avoid recursing back into
+        * ext4 error handling code during handling of previous errors.
+        */
+       if (!sb_rdonly(sbi->s_sb) && journal) {
+               handle = jbd2_journal_start(journal, 1);
+               if (IS_ERR(handle))
+                       goto write_directly;
+               if (jbd2_journal_get_write_access(handle, sbi->s_sbh)) {
+                       jbd2_journal_stop(handle);
+                       goto write_directly;
+               }
+               ext4_update_super(sbi->s_sb);
+               if (jbd2_journal_dirty_metadata(handle, sbi->s_sbh)) {
+                       jbd2_journal_stop(handle);
+                       goto write_directly;
+               }
+               jbd2_journal_stop(handle);
+               return;
+       }
+write_directly:
+       /*
+        * Write through journal failed. Write sb directly to get error info
+        * out and hope for the best.
+        */
+       ext4_commit_super(sbi->s_sb);
 }
 
 #define ext4_error_ratelimit(sb)                                       \
@@ -710,8 +751,7 @@ void __ext4_error(struct super_block *sb, const char *function,
                       sb->s_id, function, line, current->comm, &vaf);
                va_end(args);
        }
-       save_error_info(sb, error, 0, block, function, line);
-       ext4_handle_error(sb, force_ro);
+       ext4_handle_error(sb, force_ro, error, 0, block, function, line);
 }
 
 void __ext4_error_inode(struct inode *inode, const char *function,
@@ -741,9 +781,8 @@ void __ext4_error_inode(struct inode *inode, const char *function,
                               current->comm, &vaf);
                va_end(args);
        }
-       save_error_info(inode->i_sb, error, inode->i_ino, block,
-                       function, line);
-       ext4_handle_error(inode->i_sb, false);
+       ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
+                         function, line);
 }
 
 void __ext4_error_file(struct file *file, const char *function,
@@ -780,9 +819,8 @@ void __ext4_error_file(struct file *file, const char *function,
                               current->comm, path, &vaf);
                va_end(args);
        }
-       save_error_info(inode->i_sb, EFSCORRUPTED, inode->i_ino, block,
-                       function, line);
-       ext4_handle_error(inode->i_sb, false);
+       ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
+                         function, line);
 }
 
 const char *ext4_decode_error(struct super_block *sb, int errno,
@@ -849,8 +887,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
                       sb->s_id, function, line, errstr);
        }
 
-       save_error_info(sb, -errno, 0, 0, function, line);
-       ext4_handle_error(sb, false);
+       ext4_handle_error(sb, false, -errno, 0, 0, function, line);
 }
 
 void __ext4_msg(struct super_block *sb,
@@ -944,13 +981,16 @@ __acquires(bitlock)
        if (test_opt(sb, ERRORS_CONT)) {
                if (test_opt(sb, WARN_ON_ERROR))
                        WARN_ON_ONCE(1);
-               __save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
-               schedule_work(&EXT4_SB(sb)->s_error_work);
+               EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
+               if (!bdev_read_only(sb->s_bdev)) {
+                       save_error_info(sb, EFSCORRUPTED, ino, block, function,
+                                       line);
+                       schedule_work(&EXT4_SB(sb)->s_error_work);
+               }
                return;
        }
        ext4_unlock_group(sb, grp);
-       save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
-       ext4_handle_error(sb, false);
+       ext4_handle_error(sb, false, EFSCORRUPTED, ino, block, function, line);
        /*
         * We only get here in the ERRORS_RO case; relocking the group
         * may be dangerous, but nothing bad will happen since the
@@ -1152,7 +1192,7 @@ static void ext4_put_super(struct super_block *sb)
                es->s_state = cpu_to_le16(sbi->s_mount_state);
        }
        if (!sb_rdonly(sb))
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
 
        rcu_read_lock();
        group_desc = rcu_dereference(sbi->s_group_desc);
@@ -2642,7 +2682,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
        if (sbi->s_journal)
                ext4_set_feature_journal_needs_recovery(sb);
 
-       err = ext4_commit_super(sb, 1);
+       err = ext4_commit_super(sb);
 done:
        if (test_opt(sb, DEBUG))
                printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
@@ -4868,7 +4908,7 @@ no_journal:
        if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) &&
            !ext4_has_feature_encrypt(sb)) {
                ext4_set_feature_encrypt(sb);
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
        }
 
        /*
@@ -5418,7 +5458,7 @@ static int ext4_load_journal(struct super_block *sb,
                es->s_journal_dev = cpu_to_le32(journal_devnum);
 
                /* Make sure we flush the recovery flag to disk. */
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
        }
 
        return 0;
@@ -5428,16 +5468,14 @@ err_out:
        return err;
 }
 
-static int ext4_commit_super(struct super_block *sb, int sync)
+/* Copy state of EXT4_SB(sb) into buffer for on-disk superblock */
+static void ext4_update_super(struct super_block *sb)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
-       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
-       struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
-       int error = 0;
-
-       if (!sbh || block_device_ejected(sb))
-               return error;
+       struct ext4_super_block *es = sbi->s_es;
+       struct buffer_head *sbh = sbi->s_sbh;
 
+       lock_buffer(sbh);
        /*
         * If the file system is mounted read-only, don't update the
         * superblock write time.  This avoids updating the superblock
@@ -5451,17 +5489,17 @@ static int ext4_commit_super(struct super_block *sb, int sync)
        if (!(sb->s_flags & SB_RDONLY))
                ext4_update_tstamp(es, s_wtime);
        es->s_kbytes_written =
-               cpu_to_le64(EXT4_SB(sb)->s_kbytes_written +
+               cpu_to_le64(sbi->s_kbytes_written +
                    ((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
-                     EXT4_SB(sb)->s_sectors_written_start) >> 1));
-       if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeclusters_counter))
+                     sbi->s_sectors_written_start) >> 1));
+       if (percpu_counter_initialized(&sbi->s_freeclusters_counter))
                ext4_free_blocks_count_set(es,
-                       EXT4_C2B(EXT4_SB(sb), percpu_counter_sum_positive(
-                               &EXT4_SB(sb)->s_freeclusters_counter)));
-       if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeinodes_counter))
+                       EXT4_C2B(sbi, percpu_counter_sum_positive(
+                               &sbi->s_freeclusters_counter)));
+       if (percpu_counter_initialized(&sbi->s_freeinodes_counter))
                es->s_free_inodes_count =
                        cpu_to_le32(percpu_counter_sum_positive(
-                               &EXT4_SB(sb)->s_freeinodes_counter));
+                               &sbi->s_freeinodes_counter));
        /* Copy error information to the on-disk superblock */
        spin_lock(&sbi->s_error_lock);
        if (sbi->s_add_error_count > 0) {
@@ -5502,10 +5540,20 @@ static int ext4_commit_super(struct super_block *sb, int sync)
        }
        spin_unlock(&sbi->s_error_lock);
 
-       BUFFER_TRACE(sbh, "marking dirty");
        ext4_superblock_csum_set(sb);
-       if (sync)
-               lock_buffer(sbh);
+       unlock_buffer(sbh);
+}
+
+static int ext4_commit_super(struct super_block *sb)
+{
+       struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
+       int error = 0;
+
+       if (!sbh || block_device_ejected(sb))
+               return error;
+
+       ext4_update_super(sb);
+
        if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
                /*
                 * Oh, dear.  A previous attempt to write the
@@ -5520,17 +5568,15 @@ static int ext4_commit_super(struct super_block *sb, int sync)
                clear_buffer_write_io_error(sbh);
                set_buffer_uptodate(sbh);
        }
+       BUFFER_TRACE(sbh, "marking dirty");
        mark_buffer_dirty(sbh);
-       if (sync) {
-               unlock_buffer(sbh);
-               error = __sync_dirty_buffer(sbh,
-                       REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
-               if (buffer_write_io_error(sbh)) {
-                       ext4_msg(sb, KERN_ERR, "I/O error while writing "
-                              "superblock");
-                       clear_buffer_write_io_error(sbh);
-                       set_buffer_uptodate(sbh);
-               }
+       error = __sync_dirty_buffer(sbh,
+               REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
+       if (buffer_write_io_error(sbh)) {
+               ext4_msg(sb, KERN_ERR, "I/O error while writing "
+                      "superblock");
+               clear_buffer_write_io_error(sbh);
+               set_buffer_uptodate(sbh);
        }
        return error;
 }
@@ -5561,7 +5607,7 @@ static int ext4_mark_recovery_complete(struct super_block *sb,
 
        if (ext4_has_feature_journal_needs_recovery(sb) && sb_rdonly(sb)) {
                ext4_clear_feature_journal_needs_recovery(sb);
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
        }
 out:
        jbd2_journal_unlock_updates(journal);
@@ -5603,7 +5649,7 @@ static int ext4_clear_journal_err(struct super_block *sb,
 
                EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
                es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
 
                jbd2_journal_clear_err(journal);
                jbd2_journal_update_sb_errno(journal);
@@ -5705,7 +5751,7 @@ static int ext4_freeze(struct super_block *sb)
                ext4_clear_feature_journal_needs_recovery(sb);
        }
 
-       error = ext4_commit_super(sb, 1);
+       error = ext4_commit_super(sb);
 out:
        if (journal)
                /* we rely on upper layer to stop further updates */
@@ -5727,7 +5773,7 @@ static int ext4_unfreeze(struct super_block *sb)
                ext4_set_feature_journal_needs_recovery(sb);
        }
 
-       ext4_commit_super(sb, 1);
+       ext4_commit_super(sb);
        return 0;
 }
 
@@ -5987,7 +6033,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        }
 
        if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY)) {
-               err = ext4_commit_super(sb, 1);
+               err = ext4_commit_super(sb);
                if (err)
                        goto restore_opts;
        }