Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Dec 2020 22:16:02 +0000 (14:16 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 24 Dec 2020 22:16:02 +0000 (14:16 -0800)
Pull ext4 updates from Ted Ts'o:
 "Various bug fixes and cleanups for ext4; no new features this cycle"

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (29 commits)
  ext4: remove unnecessary wbc parameter from ext4_bio_write_page
  ext4: avoid s_mb_prefetch to be zero in individual scenarios
  ext4: defer saving error info from atomic context
  ext4: simplify ext4 error translation
  ext4: move functions in super.c
  ext4: make ext4_abort() use __ext4_error()
  ext4: standardize error message in ext4_protect_reserved_inode()
  ext4: remove redundant sb checksum recomputation
  ext4: don't remount read-only with errors=continue on reboot
  ext4: fix deadlock with fs freezing and EA inodes
  jbd2: add a helper to find out number of fast commit blocks
  ext4: make fast_commit.h byte identical with e2fsprogs/fast_commit.h
  ext4: fix fall-through warnings for Clang
  ext4: add docs about fast commit idempotence
  ext4: remove the unused EXT4_CURRENT_REV macro
  ext4: fix an IS_ERR() vs NULL check
  ext4: check for invalid block size early when mounting a file system
  ext4: fix a memory leak of ext4_free_data
  ext4: delete nonsensical (commented-out) code inside ext4_xattr_block_set()
  ext4: update ext4_data_block_valid related comments
  ...

1  2 
fs/ext4/ext4.h
fs/ext4/namei.c
fs/ext4/super.c

diff --combined fs/ext4/ext4.h
  #define ext_debug(ino, fmt, ...)      no_printk(fmt, ##__VA_ARGS__)
  #endif
  
+ #define ASSERT(assert)                                                \
+ do {                                                                  \
+       if (unlikely(!(assert))) {                                      \
+               printk(KERN_EMERG                                       \
+                      "Assertion failure in %s() at %s:%d: '%s'\n",    \
+                      __func__, __FILE__, __LINE__, #assert);          \
+               BUG();                                                  \
+       }                                                               \
+ } while (0)
  /* data type for block offset of block group */
  typedef int ext4_grpblk_t;
  
@@@ -1619,6 -1629,27 +1629,27 @@@ struct ext4_sb_info 
        errseq_t s_bdev_wb_err;
        spinlock_t s_bdev_wb_lock;
  
+       /* Information about errors that happened during this mount */
+       spinlock_t s_error_lock;
+       int s_add_error_count;
+       int s_first_error_code;
+       __u32 s_first_error_line;
+       __u32 s_first_error_ino;
+       __u64 s_first_error_block;
+       const char *s_first_error_func;
+       time64_t s_first_error_time;
+       int s_last_error_code;
+       __u32 s_last_error_line;
+       __u32 s_last_error_ino;
+       __u64 s_last_error_block;
+       const char *s_last_error_func;
+       time64_t s_last_error_time;
+       /*
+        * If we are in a context where we cannot update error information in
+        * the on-disk superblock, we queue this work to do it.
+        */
+       struct work_struct s_error_work;
        /* Ext4 fast commit stuff */
        atomic_t s_fc_subtid;
        atomic_t s_fc_ineligible_updates;
@@@ -1858,7 -1889,6 +1889,6 @@@ static inline bool ext4_verity_in_progr
  #define EXT4_GOOD_OLD_REV     0       /* The good old (original) format */
  #define EXT4_DYNAMIC_REV      1       /* V2 format w/ dynamic inode sizes */
  
- #define EXT4_CURRENT_REV      EXT4_GOOD_OLD_REV
  #define EXT4_MAX_SUPP_REV     EXT4_DYNAMIC_REV
  
  #define EXT4_GOOD_OLD_INODE_SIZE 128
@@@ -2952,9 -2982,9 +2982,9 @@@ extern void ext4_mark_group_bitmap_corr
                                             ext4_group_t block_group,
                                             unsigned int flags);
  
- extern __printf(6, 7)
- void __ext4_error(struct super_block *, const char *, unsigned int, int, __u64,
-                 const char *, ...);
+ extern __printf(7, 8)
+ void __ext4_error(struct super_block *, const char *, unsigned int, bool,
+                 int, __u64, const char *, ...);
  extern __printf(6, 7)
  void __ext4_error_inode(struct inode *, const char *, unsigned int,
                        ext4_fsblk_t, int, const char *, ...);
@@@ -2963,9 -2993,6 +2993,6 @@@ void __ext4_error_file(struct file *, c
                     const char *, ...);
  extern void __ext4_std_error(struct super_block *, const char *,
                             unsigned int, int);
- extern __printf(5, 6)
- void __ext4_abort(struct super_block *, const char *, unsigned int, int,
-                 const char *, ...);
  extern __printf(4, 5)
  void __ext4_warning(struct super_block *, const char *, unsigned int,
                    const char *, ...);
@@@ -2995,6 -3022,9 +3022,9 @@@ void __ext4_grp_locked_error(const cha
  #define EXT4_ERROR_FILE(file, block, fmt, a...)                               \
        ext4_error_file((file), __func__, __LINE__, (block), (fmt), ## a)
  
+ #define ext4_abort(sb, err, fmt, a...)                                        \
+       __ext4_error((sb), __func__, __LINE__, true, (err), 0, (fmt), ## a)
  #ifdef CONFIG_PRINTK
  
  #define ext4_error_inode(inode, func, line, block, fmt, ...)          \
  #define ext4_error_file(file, func, line, block, fmt, ...)            \
        __ext4_error_file(file, func, line, block, fmt, ##__VA_ARGS__)
  #define ext4_error(sb, fmt, ...)                                      \
-       __ext4_error((sb), __func__, __LINE__, 0, 0, (fmt), ##__VA_ARGS__)
+       __ext4_error((sb), __func__, __LINE__, false, 0, 0, (fmt),      \
+               ##__VA_ARGS__)
  #define ext4_error_err(sb, err, fmt, ...)                             \
-       __ext4_error((sb), __func__, __LINE__, (err), 0, (fmt), ##__VA_ARGS__)
- #define ext4_abort(sb, err, fmt, ...)                                 \
-       __ext4_abort((sb), __func__, __LINE__, (err), (fmt), ##__VA_ARGS__)
+       __ext4_error((sb), __func__, __LINE__, false, (err), 0, (fmt),  \
+               ##__VA_ARGS__)
  #define ext4_warning(sb, fmt, ...)                                    \
        __ext4_warning(sb, __func__, __LINE__, fmt, ##__VA_ARGS__)
  #define ext4_warning_inode(inode, fmt, ...)                           \
@@@ -3042,17 -3072,12 +3072,12 @@@ do {                                                                 
  #define ext4_error(sb, fmt, ...)                                      \
  do {                                                                  \
        no_printk(fmt, ##__VA_ARGS__);                                  \
-       __ext4_error(sb, "", 0, 0, 0, " ");                             \
+       __ext4_error(sb, "", 0, false, 0, 0, " ");                      \
  } while (0)
  #define ext4_error_err(sb, err, fmt, ...)                             \
  do {                                                                  \
        no_printk(fmt, ##__VA_ARGS__);                                  \
-       __ext4_error(sb, "", 0, err, 0, " ");                           \
- } while (0)
- #define ext4_abort(sb, err, fmt, ...)                                 \
- do {                                                                  \
-       no_printk(fmt, ##__VA_ARGS__);                                  \
-       __ext4_abort(sb, "", 0, err, " ");                              \
+       __ext4_error(sb, "", 0, false, err, 0, " ");                    \
  } while (0)
  #define ext4_warning(sb, fmt, ...)                                    \
  do {                                                                  \
@@@ -3361,6 -3386,21 +3386,21 @@@ static inline void ext4_unlock_group(st
        spin_unlock(ext4_group_lock_ptr(sb, group));
  }
  
+ #ifdef CONFIG_QUOTA
+ static inline bool ext4_quota_capable(struct super_block *sb)
+ {
+       return (test_opt(sb, QUOTA) || ext4_has_feature_quota(sb));
+ }
+ static inline bool ext4_is_quota_journalled(struct super_block *sb)
+ {
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       return (ext4_has_feature_quota(sb) ||
+               sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]);
+ }
+ #endif
  /*
   * Block validity checking
   */
  /* dir.c */
  extern const struct file_operations ext4_dir_operations;
  
 -#ifdef CONFIG_UNICODE
 -extern const struct dentry_operations ext4_dentry_ops;
 -#endif
 -
  /* file.c */
  extern const struct inode_operations ext4_file_inode_operations;
  extern const struct file_operations ext4_file_operations;
@@@ -3609,7 -3653,6 +3649,6 @@@ extern void ext4_io_submit(struct ext4_
  extern int ext4_bio_write_page(struct ext4_io_submit *io,
                               struct page *page,
                               int len,
-                              struct writeback_control *wbc,
                               bool keep_towrite);
  extern struct ext4_io_end_vec *ext4_alloc_io_end_vec(ext4_io_end_t *io_end);
  extern struct ext4_io_end_vec *ext4_last_io_end_vec(ext4_io_end_t *io_end);
diff --combined fs/ext4/namei.c
@@@ -182,10 -182,6 +182,6 @@@ static struct buffer_head *__ext4_read_
        return bh;
  }
  
- #ifndef assert
- #define assert(test) J_ASSERT(test)
- #endif
  #ifdef DX_DEBUG
  #define dxtrace(command) command
  #else
@@@ -643,7 -639,13 +639,7 @@@ static struct stats dx_show_leaf(struc
  
                                name  = de->name;
                                len = de->name_len;
 -                              if (IS_ENCRYPTED(dir))
 -                                      res = fscrypt_get_encryption_info(dir);
 -                              if (res) {
 -                                      printk(KERN_WARNING "Error setting up"
 -                                             " fname crypto: %d\n", res);
 -                              }
 -                              if (!fscrypt_has_encryption_key(dir)) {
 +                              if (!IS_ENCRYPTED(dir)) {
                                        /* Directory is not encrypted */
                                        ext4fs_dirhash(dir, de->name,
                                                de->name_len, &h);
@@@ -843,7 -845,7 +839,7 @@@ dx_probe(struct ext4_filename *fname, s
                                        break;
                                }
                        }
-                       assert (at == p - 1);
+                       ASSERT(at == p - 1);
                }
  
                at = p - 1;
@@@ -1004,7 -1006,7 +1000,7 @@@ static int htree_dirblock_to_tree(struc
                                           EXT4_DIR_REC_LEN(0));
        /* Check if the directory is encrypted */
        if (IS_ENCRYPTED(dir)) {
 -              err = fscrypt_get_encryption_info(dir);
 +              err = fscrypt_prepare_readdir(dir);
                if (err < 0) {
                        brelse(bh);
                        return err;
@@@ -1259,8 -1261,8 +1255,8 @@@ static void dx_insert_block(struct dx_f
        struct dx_entry *old = frame->at, *new = old + 1;
        int count = dx_get_count(entries);
  
-       assert(count < dx_get_limit(entries));
-       assert(old < entries + count);
+       ASSERT(count < dx_get_limit(entries));
+       ASSERT(old < entries + count);
        memmove(new + 1, new, (char *)(entries + count) - (char *)(new));
        dx_set_hash(new, hash);
        dx_set_block(new, block);
@@@ -1608,7 -1610,6 +1604,7 @@@ static struct buffer_head *ext4_lookup_
        struct buffer_head *bh;
  
        err = ext4_fname_prepare_lookup(dir, dentry, &fname);
 +      generic_set_encrypted_ci_d_ops(dentry);
        if (err == -ENOENT)
                return NULL;
        if (err)
@@@ -2190,9 -2191,6 +2186,9 @@@ static int ext4_add_entry(handle_t *han
        if (!dentry->d_name.len)
                return -EINVAL;
  
 +      if (fscrypt_is_nokey_name(dentry))
 +              return -ENOKEY;
 +
  #ifdef CONFIG_UNICODE
        if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) &&
            sb->s_encoding && utf8_validate(sb->s_encoding, &dentry->d_name))
@@@ -2959,7 -2957,7 +2955,7 @@@ int ext4_orphan_add(handle_t *handle, s
         * hold i_mutex, or the inode can not be referenced from outside,
         * so i_nlink should not be bumped due to race
         */
-       J_ASSERT((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+       ASSERT((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
                  S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
  
        BUFFER_TRACE(sbi->s_sbh, "get_write_access");
diff --combined fs/ext4/super.c
@@@ -404,10 -404,8 +404,8 @@@ void ext4_itable_unused_set(struct supe
                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 +417,11 @@@ static time64_t __ext4_get_tstamp(__le3
        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 +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.
   *
   * 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)                                      \
                             "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;
                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,
        }
        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,
        }
        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 +850,7 @@@ void __ext4_std_error(struct super_bloc
        }
  
        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 +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;
                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 +1122,7 @@@ static void ext4_put_super(struct super
        ext4_unregister_li_request(sb);
        ext4_quota_off_umount(sb);
  
+       flush_work(&sbi->s_error_work);
        destroy_workqueue(sbi->rsv_conversion_wq);
  
        /*
         * 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 +3947,21 @@@ static void ext4_set_resv_clusters(stru
        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);
        sbi->s_sb = sb;
        sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
        sbi->s_sb_block = sb_block;
 -      if (sb->s_bdev->bd_part)
 -              sbi->s_sectors_written_start =
 -                      part_stat_read(sb->s_bdev->bd_part, sectors[STAT_WRITE]);
 +      sbi->s_sectors_written_start =
 +              part_stat_read(sb->s_bdev, sectors[STAT_WRITE]);
  
        /* Cleanup superblock name */
        strreplace(sb->s_id, '/', '!');
        if (IS_ERR(bh)) {
                ext4_msg(sb, KERN_ERR, "unable to read superblock");
                ret = PTR_ERR(bh);
-               bh = NULL;
                goto out_fail;
        }
        /*
         */
        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;
        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",
                               "can't read group descriptor %d", i);
                        db_count = i;
                        ret = PTR_ERR(bh);
-                       bh = NULL;
                        goto failed_mount2;
                }
                rcu_read_lock();
        }
  
        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))
                               "requested data journaling mode");
                        goto failed_mount_wq;
                }
+               break;
        default:
                break;
        }
@@@ -4963,6 -4914,11 +4913,6 @@@ no_journal
                goto failed_mount4;
        }
  
 -#ifdef CONFIG_UNICODE
 -      if (sb->s_encoding)
 -              sb->s_d_op = &ext4_dentry_ops;
 -#endif
 -
        sb->s_root = d_make_root(root);
        if (!sb->s_root) {
                ext4_msg(sb, KERN_ERR, "get root dentry failed");
        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);
        }
  
        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 -5109,7 +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 -5436,7 +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;
         */
        if (!(sb->s_flags & SB_RDONLY))
                ext4_update_tstamp(es, s_wtime);
 -      if (sb->s_bdev->bd_part)
 -              es->s_kbytes_written =
 -                      cpu_to_le64(EXT4_SB(sb)->s_kbytes_written +
 -                          ((part_stat_read(sb->s_bdev->bd_part,
 -                                           sectors[STAT_WRITE]) -
 -                            EXT4_SB(sb)->s_sectors_written_start) >> 1));
 -      else
 -              es->s_kbytes_written =
 -                      cpu_to_le64(EXT4_SB(sb)->s_kbytes_written);
 +      es->s_kbytes_written =
 +              cpu_to_le64(EXT4_SB(sb)->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))
                ext4_free_blocks_count_set(es,
                        EXT4_C2B(EXT4_SB(sb), percpu_counter_sum_positive(
                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 -5866,9 +5855,9 @@@ static int ext4_remount(struct super_bl
                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;
         */
        *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 -6207,8 +6196,8 @@@ static int ext4_release_dquot(struct dq
  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 {