Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-microblaze.git] / fs / ext4 / super.c
index 830c196..2112178 100644 (file)
@@ -404,10 +404,8 @@ void ext4_itable_unused_set(struct super_block *sb,
                bg->bg_itable_unused_hi = cpu_to_le16(count >> 16);
 }
 
-static void __ext4_update_tstamp(__le32 *lo, __u8 *hi)
+static void __ext4_update_tstamp(__le32 *lo, __u8 *hi, time64_t now)
 {
-       time64_t now = ktime_get_real_seconds();
-
        now = clamp_val(now, 0, (1ull << 40) - 1);
 
        *lo = cpu_to_le32(lower_32_bits(now));
@@ -419,108 +417,11 @@ static time64_t __ext4_get_tstamp(__le32 *lo, __u8 *hi)
        return ((time64_t)(*hi) << 32) + le32_to_cpu(*lo);
 }
 #define ext4_update_tstamp(es, tstamp) \
-       __ext4_update_tstamp(&(es)->tstamp, &(es)->tstamp ## _hi)
+       __ext4_update_tstamp(&(es)->tstamp, &(es)->tstamp ## _hi, \
+                            ktime_get_real_seconds())
 #define ext4_get_tstamp(es, tstamp) \
        __ext4_get_tstamp(&(es)->tstamp, &(es)->tstamp ## _hi)
 
-static void __save_error_info(struct super_block *sb, int error,
-                             __u32 ino, __u64 block,
-                             const char *func, unsigned int line)
-{
-       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
-       int err;
-
-       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-       if (bdev_read_only(sb->s_bdev))
-               return;
-       es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
-       ext4_update_tstamp(es, s_last_error_time);
-       strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func));
-       es->s_last_error_line = cpu_to_le32(line);
-       es->s_last_error_ino = cpu_to_le32(ino);
-       es->s_last_error_block = cpu_to_le64(block);
-       switch (error) {
-       case EIO:
-               err = EXT4_ERR_EIO;
-               break;
-       case ENOMEM:
-               err = EXT4_ERR_ENOMEM;
-               break;
-       case EFSBADCRC:
-               err = EXT4_ERR_EFSBADCRC;
-               break;
-       case 0:
-       case EFSCORRUPTED:
-               err = EXT4_ERR_EFSCORRUPTED;
-               break;
-       case ENOSPC:
-               err = EXT4_ERR_ENOSPC;
-               break;
-       case ENOKEY:
-               err = EXT4_ERR_ENOKEY;
-               break;
-       case EROFS:
-               err = EXT4_ERR_EROFS;
-               break;
-       case EFBIG:
-               err = EXT4_ERR_EFBIG;
-               break;
-       case EEXIST:
-               err = EXT4_ERR_EEXIST;
-               break;
-       case ERANGE:
-               err = EXT4_ERR_ERANGE;
-               break;
-       case EOVERFLOW:
-               err = EXT4_ERR_EOVERFLOW;
-               break;
-       case EBUSY:
-               err = EXT4_ERR_EBUSY;
-               break;
-       case ENOTDIR:
-               err = EXT4_ERR_ENOTDIR;
-               break;
-       case ENOTEMPTY:
-               err = EXT4_ERR_ENOTEMPTY;
-               break;
-       case ESHUTDOWN:
-               err = EXT4_ERR_ESHUTDOWN;
-               break;
-       case EFAULT:
-               err = EXT4_ERR_EFAULT;
-               break;
-       default:
-               err = EXT4_ERR_UNKNOWN;
-       }
-       es->s_last_error_errcode = err;
-       if (!es->s_first_error_time) {
-               es->s_first_error_time = es->s_last_error_time;
-               es->s_first_error_time_hi = es->s_last_error_time_hi;
-               strncpy(es->s_first_error_func, func,
-                       sizeof(es->s_first_error_func));
-               es->s_first_error_line = cpu_to_le32(line);
-               es->s_first_error_ino = es->s_last_error_ino;
-               es->s_first_error_block = es->s_last_error_block;
-               es->s_first_error_errcode = es->s_last_error_errcode;
-       }
-       /*
-        * Start the daily error reporting function if it hasn't been
-        * started already
-        */
-       if (!es->s_error_count)
-               mod_timer(&EXT4_SB(sb)->s_err_report, jiffies + 24*60*60*HZ);
-       le32_add_cpu(&es->s_error_count, 1);
-}
-
-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);
-}
-
 /*
  * The del_gendisk() function uninitializes the disk-specific data
  * structures, including the bdi structure, without telling anyone
@@ -649,6 +550,83 @@ static bool system_going_down(void)
                || system_state == SYSTEM_RESTART;
 }
 
+struct ext4_err_translation {
+       int code;
+       int errno;
+};
+
+#define EXT4_ERR_TRANSLATE(err) { .code = EXT4_ERR_##err, .errno = err }
+
+static struct ext4_err_translation err_translation[] = {
+       EXT4_ERR_TRANSLATE(EIO),
+       EXT4_ERR_TRANSLATE(ENOMEM),
+       EXT4_ERR_TRANSLATE(EFSBADCRC),
+       EXT4_ERR_TRANSLATE(EFSCORRUPTED),
+       EXT4_ERR_TRANSLATE(ENOSPC),
+       EXT4_ERR_TRANSLATE(ENOKEY),
+       EXT4_ERR_TRANSLATE(EROFS),
+       EXT4_ERR_TRANSLATE(EFBIG),
+       EXT4_ERR_TRANSLATE(EEXIST),
+       EXT4_ERR_TRANSLATE(ERANGE),
+       EXT4_ERR_TRANSLATE(EOVERFLOW),
+       EXT4_ERR_TRANSLATE(EBUSY),
+       EXT4_ERR_TRANSLATE(ENOTDIR),
+       EXT4_ERR_TRANSLATE(ENOTEMPTY),
+       EXT4_ERR_TRANSLATE(ESHUTDOWN),
+       EXT4_ERR_TRANSLATE(EFAULT),
+};
+
+static int ext4_errno_to_code(int errno)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(err_translation); i++)
+               if (err_translation[i].errno == errno)
+                       return err_translation[i].code;
+       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)
+{
+       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;
+
+       spin_lock(&sbi->s_error_lock);
+       sbi->s_add_error_count++;
+       sbi->s_last_error_code = error;
+       sbi->s_last_error_line = line;
+       sbi->s_last_error_ino = ino;
+       sbi->s_last_error_block = block;
+       sbi->s_last_error_func = func;
+       sbi->s_last_error_time = ktime_get_real_seconds();
+       if (!sbi->s_first_error_time) {
+               sbi->s_first_error_code = error;
+               sbi->s_first_error_line = line;
+               sbi->s_first_error_ino = ino;
+               sbi->s_first_error_block = block;
+               sbi->s_first_error_func = func;
+               sbi->s_first_error_time = sbi->s_last_error_time;
+       }
+       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.
  *
@@ -662,40 +640,50 @@ static bool system_going_down(void)
  * We'll just use the jbd2_journal_abort() error code to record an error in
  * the journal instead.  On recovery, the journal will complain about
  * that error until we've noted it down and cleared it.
+ *
+ * If force_ro is set, we unconditionally force the filesystem into an
+ * ABORT|READONLY state, unless the error response on the fs has been set to
+ * panic in which case we take the easy way out and panic immediately. This is
+ * 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)
+static void ext4_handle_error(struct super_block *sb, bool force_ro)
 {
+       journal_t *journal = EXT4_SB(sb)->s_journal;
+
        if (test_opt(sb, WARN_ON_ERROR))
                WARN_ON_ONCE(1);
 
-       if (sb_rdonly(sb))
+       if (sb_rdonly(sb) || (!force_ro && test_opt(sb, ERRORS_CONT)))
                return;
 
-       if (!test_opt(sb, ERRORS_CONT)) {
-               journal_t *journal = EXT4_SB(sb)->s_journal;
-
-               ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
-               if (journal)
-                       jbd2_journal_abort(journal, -EIO);
-       }
+       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
         * disabled.
         */
-       if (test_opt(sb, ERRORS_RO) || system_going_down()) {
-               ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
-               /*
-                * Make sure updated value of ->s_mount_flags will be visible
-                * before ->s_flags update
-                */
-               smp_wmb();
-               sb->s_flags |= SB_RDONLY;
-       } else if (test_opt(sb, ERRORS_PANIC)) {
+       if (test_opt(sb, ERRORS_PANIC) && !system_going_down()) {
                panic("EXT4-fs (device %s): panic forced after error\n",
                        sb->s_id);
        }
+       ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
+       /*
+        * Make sure updated value of ->s_mount_flags will be visible before
+        * ->s_flags update
+        */
+       smp_wmb();
+       sb->s_flags |= SB_RDONLY;
+}
+
+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);
+
+       ext4_commit_super(sbi->s_sb, 1);
 }
 
 #define ext4_error_ratelimit(sb)                                       \
@@ -703,7 +691,7 @@ static void ext4_handle_error(struct super_block *sb)
                             "EXT4-fs error")
 
 void __ext4_error(struct super_block *sb, const char *function,
-                 unsigned int line, int error, __u64 block,
+                 unsigned int line, bool force_ro, int error, __u64 block,
                  const char *fmt, ...)
 {
        struct va_format vaf;
@@ -723,7 +711,7 @@ void __ext4_error(struct super_block *sb, const char *function,
                va_end(args);
        }
        save_error_info(sb, error, 0, block, function, line);
-       ext4_handle_error(sb);
+       ext4_handle_error(sb, force_ro);
 }
 
 void __ext4_error_inode(struct inode *inode, const char *function,
@@ -755,7 +743,7 @@ void __ext4_error_inode(struct inode *inode, const char *function,
        }
        save_error_info(inode->i_sb, error, inode->i_ino, block,
                        function, line);
-       ext4_handle_error(inode->i_sb);
+       ext4_handle_error(inode->i_sb, false);
 }
 
 void __ext4_error_file(struct file *file, const char *function,
@@ -794,7 +782,7 @@ void __ext4_error_file(struct file *file, const char *function,
        }
        save_error_info(inode->i_sb, EFSCORRUPTED, inode->i_ino, block,
                        function, line);
-       ext4_handle_error(inode->i_sb);
+       ext4_handle_error(inode->i_sb, false);
 }
 
 const char *ext4_decode_error(struct super_block *sb, int errno,
@@ -862,51 +850,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
        }
 
        save_error_info(sb, -errno, 0, 0, function, line);
-       ext4_handle_error(sb);
-}
-
-/*
- * ext4_abort is a much stronger failure handler than ext4_error.  The
- * abort function may be used to deal with unrecoverable failures such
- * as journal IO errors or ENOMEM at a critical moment in log management.
- *
- * We unconditionally force the filesystem into an ABORT|READONLY state,
- * unless the error response on the fs has been set to panic in which
- * case we take the easy way out and panic immediately.
- */
-
-void __ext4_abort(struct super_block *sb, const char *function,
-                 unsigned int line, int error, const char *fmt, ...)
-{
-       struct va_format vaf;
-       va_list args;
-
-       if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
-               return;
-
-       save_error_info(sb, error, 0, 0, function, line);
-       va_start(args, fmt);
-       vaf.fmt = fmt;
-       vaf.va = &args;
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: %pV\n",
-              sb->s_id, function, line, &vaf);
-       va_end(args);
-
-       if (sb_rdonly(sb) == 0) {
-               ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
-               if (EXT4_SB(sb)->s_journal)
-                       jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
-
-               ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
-               /*
-                * Make sure updated value of ->s_mount_flags will be visible
-                * before ->s_flags update
-                */
-               smp_wmb();
-               sb->s_flags |= SB_RDONLY;
-       }
-       if (test_opt(sb, ERRORS_PANIC) && !system_going_down())
-               panic("EXT4-fs panic from previous error\n");
+       ext4_handle_error(sb, false);
 }
 
 void __ext4_msg(struct super_block *sb,
@@ -982,8 +926,6 @@ __acquires(bitlock)
                return;
 
        trace_ext4_error(sb, function, line);
-       __save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
-
        if (ext4_error_ratelimit(sb)) {
                va_start(args, fmt);
                vaf.fmt = fmt;
@@ -999,17 +941,16 @@ __acquires(bitlock)
                va_end(args);
        }
 
-       if (test_opt(sb, WARN_ON_ERROR))
-               WARN_ON_ONCE(1);
-
        if (test_opt(sb, ERRORS_CONT)) {
-               ext4_commit_super(sb, 0);
+               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);
                return;
        }
-
        ext4_unlock_group(sb, grp);
-       ext4_commit_super(sb, 1);
-       ext4_handle_error(sb);
+       save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
+       ext4_handle_error(sb, false);
        /*
         * We only get here in the ERRORS_RO case; relocking the group
         * may be dangerous, but nothing bad will happen since the
@@ -1181,6 +1122,7 @@ static void ext4_put_super(struct super_block *sb)
        ext4_unregister_li_request(sb);
        ext4_quota_off_umount(sb);
 
+       flush_work(&sbi->s_error_work);
        destroy_workqueue(sbi->rsv_conversion_wq);
 
        /*
@@ -1240,7 +1182,7 @@ static void ext4_put_super(struct super_block *sb)
         * in-memory list had better be clean by this point. */
        if (!list_empty(&sbi->s_orphan))
                dump_orphan_list(sb, sbi);
-       J_ASSERT(list_empty(&sbi->s_orphan));
+       ASSERT(list_empty(&sbi->s_orphan));
 
        sync_blockdev(sb->s_bdev);
        invalidate_bdev(sb->s_bdev);
@@ -4005,6 +3947,21 @@ static void ext4_set_resv_clusters(struct super_block *sb)
        atomic64_set(&sbi->s_resv_clusters, resv_clusters);
 }
 
+static const char *ext4_quota_mode(struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+       if (!ext4_quota_capable(sb))
+               return "none";
+
+       if (EXT4_SB(sb)->s_journal && ext4_is_quota_journalled(sb))
+               return "journalled";
+       else
+               return "writeback";
+#else
+       return "disabled";
+#endif
+}
+
 static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
@@ -4073,7 +4030,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        if (IS_ERR(bh)) {
                ext4_msg(sb, KERN_ERR, "unable to read superblock");
                ret = PTR_ERR(bh);
-               bh = NULL;
                goto out_fail;
        }
        /*
@@ -4187,19 +4143,26 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
         */
        sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
 
-       blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
-
-       if (blocksize == PAGE_SIZE)
-               set_opt(sb, DIOREAD_NOLOCK);
-
-       if (blocksize < EXT4_MIN_BLOCK_SIZE ||
-           blocksize > EXT4_MAX_BLOCK_SIZE) {
+       if (le32_to_cpu(es->s_log_block_size) >
+           (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+               ext4_msg(sb, KERN_ERR,
+                        "Invalid log block size: %u",
+                        le32_to_cpu(es->s_log_block_size));
+               goto failed_mount;
+       }
+       if (le32_to_cpu(es->s_log_cluster_size) >
+           (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
                ext4_msg(sb, KERN_ERR,
-                      "Unsupported filesystem blocksize %d (%d log_block_size)",
-                        blocksize, le32_to_cpu(es->s_log_block_size));
+                        "Invalid log cluster size: %u",
+                        le32_to_cpu(es->s_log_cluster_size));
                goto failed_mount;
        }
 
+       blocksize = EXT4_MIN_BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
+
+       if (blocksize == PAGE_SIZE)
+               set_opt(sb, DIOREAD_NOLOCK);
+
        if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) {
                sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE;
                sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;
@@ -4417,21 +4380,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        if (!ext4_feature_set_ok(sb, (sb_rdonly(sb))))
                goto failed_mount;
 
-       if (le32_to_cpu(es->s_log_block_size) >
-           (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
-               ext4_msg(sb, KERN_ERR,
-                        "Invalid log block size: %u",
-                        le32_to_cpu(es->s_log_block_size));
-               goto failed_mount;
-       }
-       if (le32_to_cpu(es->s_log_cluster_size) >
-           (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
-               ext4_msg(sb, KERN_ERR,
-                        "Invalid log cluster size: %u",
-                        le32_to_cpu(es->s_log_cluster_size));
-               goto failed_mount;
-       }
-
        if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
                ext4_msg(sb, KERN_ERR,
                         "Number of reserved GDT blocks insanely large: %d",
@@ -4702,7 +4650,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                               "can't read group descriptor %d", i);
                        db_count = i;
                        ret = PTR_ERR(bh);
-                       bh = NULL;
                        goto failed_mount2;
                }
                rcu_read_lock();
@@ -4717,6 +4664,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        }
 
        timer_setup(&sbi->s_err_report, print_daily_error_info, 0);
+       spin_lock_init(&sbi->s_error_lock);
+       INIT_WORK(&sbi->s_error_work, flush_stashed_error_work);
 
        /* Register extent status tree shrinker */
        if (ext4_es_register_shrinker(sbi))
@@ -4872,6 +4821,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                               "requested data journaling mode");
                        goto failed_mount_wq;
                }
+               break;
        default:
                break;
        }
@@ -5000,13 +4950,11 @@ no_journal:
        block = ext4_count_free_clusters(sb);
        ext4_free_blocks_count_set(sbi->s_es, 
                                   EXT4_C2B(sbi, block));
-       ext4_superblock_csum_set(sb);
        err = percpu_counter_init(&sbi->s_freeclusters_counter, block,
                                  GFP_KERNEL);
        if (!err) {
                unsigned long freei = ext4_count_free_inodes(sb);
                sbi->s_es->s_free_inodes_count = cpu_to_le32(freei);
-               ext4_superblock_csum_set(sb);
                err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,
                                          GFP_KERNEL);
        }
@@ -5086,10 +5034,11 @@ no_journal:
 
        if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
                ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
-                        "Opts: %.*s%s%s", descr,
+                        "Opts: %.*s%s%s. Quota mode: %s.", descr,
                         (int) sizeof(sbi->s_es->s_mount_opts),
                         sbi->s_es->s_mount_opts,
-                        *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
+                        *sbi->s_es->s_mount_opts ? "; " : "", orig_data,
+                        ext4_quota_mode(sb));
 
        if (es->s_error_count)
                mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
@@ -5154,6 +5103,7 @@ failed_mount3a:
        ext4_es_unregister_shrinker(sbi);
 failed_mount3:
        del_timer_sync(&sbi->s_err_report);
+       flush_work(&sbi->s_error_work);
        if (sbi->s_mmp_tsk)
                kthread_stop(sbi->s_mmp_tsk);
 failed_mount2:
@@ -5480,6 +5430,7 @@ err_out:
 
 static int ext4_commit_super(struct super_block *sb, int sync)
 {
+       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;
@@ -5511,6 +5462,46 @@ static int ext4_commit_super(struct super_block *sb, int sync)
                es->s_free_inodes_count =
                        cpu_to_le32(percpu_counter_sum_positive(
                                &EXT4_SB(sb)->s_freeinodes_counter));
+       /* Copy error information to the on-disk superblock */
+       spin_lock(&sbi->s_error_lock);
+       if (sbi->s_add_error_count > 0) {
+               es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
+               if (!es->s_first_error_time && !es->s_first_error_time_hi) {
+                       __ext4_update_tstamp(&es->s_first_error_time,
+                                            &es->s_first_error_time_hi,
+                                            sbi->s_first_error_time);
+                       strncpy(es->s_first_error_func, sbi->s_first_error_func,
+                               sizeof(es->s_first_error_func));
+                       es->s_first_error_line =
+                               cpu_to_le32(sbi->s_first_error_line);
+                       es->s_first_error_ino =
+                               cpu_to_le32(sbi->s_first_error_ino);
+                       es->s_first_error_block =
+                               cpu_to_le64(sbi->s_first_error_block);
+                       es->s_first_error_errcode =
+                               ext4_errno_to_code(sbi->s_first_error_code);
+               }
+               __ext4_update_tstamp(&es->s_last_error_time,
+                                    &es->s_last_error_time_hi,
+                                    sbi->s_last_error_time);
+               strncpy(es->s_last_error_func, sbi->s_last_error_func,
+                       sizeof(es->s_last_error_func));
+               es->s_last_error_line = cpu_to_le32(sbi->s_last_error_line);
+               es->s_last_error_ino = cpu_to_le32(sbi->s_last_error_ino);
+               es->s_last_error_block = cpu_to_le64(sbi->s_last_error_block);
+               es->s_last_error_errcode =
+                               ext4_errno_to_code(sbi->s_last_error_code);
+               /*
+                * Start the daily error reporting function if it hasn't been
+                * started already
+                */
+               if (!es->s_error_count)
+                       mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ);
+               le32_add_cpu(&es->s_error_count, sbi->s_add_error_count);
+               sbi->s_add_error_count = 0;
+       }
+       spin_unlock(&sbi->s_error_lock);
+
        BUFFER_TRACE(sbh, "marking dirty");
        ext4_superblock_csum_set(sb);
        if (sync)
@@ -5864,6 +5855,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                set_task_ioprio(sbi->s_journal->j_task, journal_ioprio);
        }
 
+       /* Flush outstanding errors before changing fs state */
+       flush_work(&sbi->s_error_work);
+
        if ((bool)(*flags & SB_RDONLY) != sb_rdonly(sb)) {
                if (ext4_test_mount_flag(sb, EXT4_MF_FS_ABORTED)) {
                        err = -EROFS;
@@ -6022,7 +6016,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
         */
        *flags = (*flags & ~vfs_flags) | (sb->s_flags & vfs_flags);
 
-       ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
+       ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s. Quota mode: %s.",
+                orig_data, ext4_quota_mode(sb));
        kfree(orig_data);
        return 0;
 
@@ -6201,11 +6196,8 @@ static int ext4_release_dquot(struct dquot *dquot)
 static int ext4_mark_dquot_dirty(struct dquot *dquot)
 {
        struct super_block *sb = dquot->dq_sb;
-       struct ext4_sb_info *sbi = EXT4_SB(sb);
 
-       /* Are we journaling quotas? */
-       if (ext4_has_feature_quota(sb) ||
-           sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+       if (ext4_is_quota_journalled(sb)) {
                dquot_mark_dquot_dirty(dquot);
                return ext4_write_dquot(dquot);
        } else {