]> git.itanic.dy.fi Git - linux-stable/commitdiff
ext4: save the error code which triggered an ext4_error() in the superblock
authorTheodore Ts'o <tytso@mit.edu>
Wed, 20 Nov 2019 02:54:15 +0000 (21:54 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 26 Dec 2019 16:28:23 +0000 (11:28 -0500)
This allows the cause of an ext4_error() report to be categorized
based on whether it was triggered due to an I/O error, or an memory
allocation error, or other possible causes.  Most errors are caused by
a detected file system inconsistency, so the default code stored in
the superblock will be EXT4_ERR_EFSCORRUPTED.

Link: https://lore.kernel.org/r/20191204032335.7683-1-tytso@mit.edu
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
12 files changed:
fs/ext4/balloc.c
fs/ext4/ext4.h
fs/ext4/ext4_jbd2.c
fs/ext4/extents.c
fs/ext4/ialloc.c
fs/ext4/inline.c
fs/ext4/inode.c
fs/ext4/mballoc.c
fs/ext4/mmp.c
fs/ext4/namei.c
fs/ext4/super.c
fs/ext4/xattr.c

index 0b202e00d93f8642041f2149aab4e6a4532a134c..102c38527a10078b8051e76d54ffeb238301f879 100644 (file)
@@ -506,6 +506,7 @@ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group,
                return -EFSCORRUPTED;
        wait_on_buffer(bh);
        if (!buffer_uptodate(bh)) {
+               ext4_set_errno(sb, EIO);
                ext4_error(sb, "Cannot read block bitmap - "
                           "block_group = %u, block_bitmap = %llu",
                           block_group, (unsigned long long) bh->b_blocknr);
index f8578caba40d5b899e7950581eb41ced929f7834..b00d07bad45ba2e286a72a3055b2cb10e67ca775 100644 (file)
@@ -1343,7 +1343,8 @@ struct ext4_super_block {
        __u8    s_lastcheck_hi;
        __u8    s_first_error_time_hi;
        __u8    s_last_error_time_hi;
-       __u8    s_pad[2];
+       __u8    s_first_error_errcode;
+       __u8    s_last_error_errcode;
        __le16  s_encoding;             /* Filename charset encoding */
        __le16  s_encoding_flags;       /* Filename charset encoding flags */
        __le32  s_reserved[95];         /* Padding to the end of the block */
@@ -1574,6 +1575,32 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
                 ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
 }
 
+/*
+ * Error number codes for s_{first,last}_error_errno
+ *
+ * Linux errno numbers are architecture specific, so we need to translate
+ * them into something which is architecture independent.   We don't define
+ * codes for all errno's; just the ones which are most likely to be the cause
+ * of an ext4_error() call.
+ */
+#define EXT4_ERR_UNKNOWN        1
+#define EXT4_ERR_EIO            2
+#define EXT4_ERR_ENOMEM                 3
+#define EXT4_ERR_EFSBADCRC      4
+#define EXT4_ERR_EFSCORRUPTED   5
+#define EXT4_ERR_ENOSPC                 6
+#define EXT4_ERR_ENOKEY                 7
+#define EXT4_ERR_EROFS          8
+#define EXT4_ERR_EFBIG          9
+#define EXT4_ERR_EEXIST                10
+#define EXT4_ERR_ERANGE                11
+#define EXT4_ERR_EOVERFLOW     12
+#define EXT4_ERR_EBUSY         13
+#define EXT4_ERR_ENOTDIR       14
+#define EXT4_ERR_ENOTEMPTY     15
+#define EXT4_ERR_ESHUTDOWN     16
+#define EXT4_ERR_EFAULT                17
+
 /*
  * Inode dynamic state flags
  */
@@ -2688,6 +2715,7 @@ extern const char *ext4_decode_error(struct super_block *sb, int errno,
 extern void ext4_mark_group_bitmap_corrupted(struct super_block *sb,
                                             ext4_group_t block_group,
                                             unsigned int flags);
+extern void ext4_set_errno(struct super_block *sb, int err);
 
 extern __printf(4, 5)
 void __ext4_error(struct super_block *, const char *, unsigned int,
index d3b8cdea5df75ec6ddce7a1ee80217cc5c6e100d..19217a3f1ae4a18d34c8fda6dd917eb64782d17e 100644 (file)
@@ -58,6 +58,7 @@ static int ext4_journal_check_start(struct super_block *sb)
         * take the FS itself readonly cleanly.
         */
        if (journal && is_journal_aborted(journal)) {
+               ext4_set_errno(sb, -journal->j_errno);
                ext4_abort(sb, "Detected aborted journal");
                return -EROFS;
        }
@@ -249,6 +250,7 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle,
        if (err) {
                ext4_journal_abort_handle(where, line, __func__,
                                          bh, handle, err);
+               ext4_set_errno(inode->i_sb, -err);
                __ext4_abort(inode->i_sb, where, line,
                           "error %d when attempting revoke", err);
        }
@@ -320,6 +322,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
                                es = EXT4_SB(inode->i_sb)->s_es;
                                es->s_last_error_block =
                                        cpu_to_le64(bh->b_blocknr);
+                               ext4_set_errno(inode->i_sb, EIO);
                                ext4_error_inode(inode, where, line,
                                                 bh->b_blocknr,
                                        "IO error syncing itable block");
index 0e8708b77da68269af57a7cc94f39689b902887f..ee83fe7c98aaf5bbb3267e1d67004a6d0491984f 100644 (file)
@@ -492,6 +492,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
        return 0;
 
 corrupted:
+       ext4_set_errno(inode->i_sb, -err);
        ext4_error_inode(inode, function, line, 0,
                         "pblk %llu bad header/extent: %s - magic %x, "
                         "entries %u, max %u(%u), depth %u(%u)",
index 8ca4a23129aafd389f0a6a126e25d3640b38dd37..0151ba8ea4390c80b91fab390bba10b59d2a740f 100644 (file)
@@ -194,6 +194,7 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
        wait_on_buffer(bh);
        if (!buffer_uptodate(bh)) {
                put_bh(bh);
+               ext4_set_errno(sb, EIO);
                ext4_error(sb, "Cannot read inode bitmap - "
                           "block_group = %u, inode_bitmap = %llu",
                           block_group, bitmap_blk);
@@ -1223,6 +1224,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
        inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
        if (IS_ERR(inode)) {
                err = PTR_ERR(inode);
+               ext4_set_errno(sb, -err);
                ext4_error(sb, "couldn't read orphan inode %lu (err %d)",
                           ino, err);
                return inode;
index 2fec62d764fa91562db22047be9172d6a2b0a1db..e61603f470358b1d6100bf3040466503a6575fb3 100644 (file)
@@ -98,6 +98,7 @@ int ext4_get_max_inline_size(struct inode *inode)
 
        error = ext4_get_inode_loc(inode, &iloc);
        if (error) {
+               ext4_set_errno(inode->i_sb, -error);
                ext4_error_inode(inode, __func__, __LINE__, 0,
                                 "can't get inode location %lu",
                                 inode->i_ino);
@@ -1761,6 +1762,7 @@ bool empty_inline_dir(struct inode *dir, int *has_inline_data)
 
        err = ext4_get_inode_loc(dir, &iloc);
        if (err) {
+               ext4_set_errno(dir->i_sb, -err);
                EXT4_ERROR_INODE(dir, "error %d getting inode %lu block",
                                 err, dir->i_ino);
                return true;
index 629a25d999f07d17977783439162163dd4f89d7a..23fa585206f012ed3f4b84e6fd19f59011566bed 100644 (file)
@@ -271,6 +271,7 @@ void ext4_evict_inode(struct inode *inode)
        if (inode->i_blocks) {
                err = ext4_truncate(inode);
                if (err) {
+                       ext4_set_errno(inode->i_sb, -err);
                        ext4_error(inode->i_sb,
                                   "couldn't truncate inode %lu (err %d)",
                                   inode->i_ino, err);
@@ -2478,10 +2479,12 @@ static int mpage_map_and_submit_extent(handle_t *handle,
                        EXT4_I(inode)->i_disksize = disksize;
                up_write(&EXT4_I(inode)->i_data_sem);
                err2 = ext4_mark_inode_dirty(handle, inode);
-               if (err2)
+               if (err2) {
+                       ext4_set_errno(inode->i_sb, -err2);
                        ext4_error(inode->i_sb,
                                   "Failed to mark inode %lu dirty",
                                   inode->i_ino);
+               }
                if (!err)
                        err = err2;
        }
@@ -4338,6 +4341,7 @@ static int __ext4_get_inode_loc(struct inode *inode,
                blk_finish_plug(&plug);
                wait_on_buffer(bh);
                if (!buffer_uptodate(bh)) {
+                       ext4_set_errno(inode->i_sb, EIO);
                        EXT4_ERROR_INODE_BLOCK(inode, block,
                                               "unable to read itable block");
                        brelse(bh);
@@ -4552,6 +4556,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
        }
 
        if (!ext4_inode_csum_verify(inode, raw_inode, ei)) {
+               ext4_set_errno(inode->i_sb, EFSBADCRC);
                ext4_error_inode(inode, function, line, 0,
                                 "iget: checksum invalid");
                ret = -EFSBADCRC;
@@ -5090,6 +5095,7 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
                if (wbc->sync_mode == WB_SYNC_ALL && !wbc->for_sync)
                        sync_dirty_buffer(iloc.bh);
                if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) {
+                       ext4_set_errno(inode->i_sb, EIO);
                        EXT4_ERROR_INODE_BLOCK(inode, iloc.bh->b_blocknr,
                                         "IO error syncing inode");
                        err = -EIO;
index a3e2767bdf2f59ac8f587f2dc1dc315cb8a41bdd..f64838187559f25b859ba5acce4d0186a2c4415b 100644 (file)
@@ -3895,6 +3895,7 @@ ext4_mb_discard_group_preallocations(struct super_block *sb,
        bitmap_bh = ext4_read_block_bitmap(sb, group);
        if (IS_ERR(bitmap_bh)) {
                err = PTR_ERR(bitmap_bh);
+               ext4_set_errno(sb, -err);
                ext4_error(sb, "Error %d reading block bitmap for %u",
                           err, group);
                return 0;
@@ -4063,6 +4064,7 @@ void ext4_discard_preallocations(struct inode *inode)
                err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
                                             GFP_NOFS|__GFP_NOFAIL);
                if (err) {
+                       ext4_set_errno(sb, -err);
                        ext4_error(sb, "Error %d loading buddy information for %u",
                                   err, group);
                        continue;
@@ -4071,6 +4073,7 @@ void ext4_discard_preallocations(struct inode *inode)
                bitmap_bh = ext4_read_block_bitmap(sb, group);
                if (IS_ERR(bitmap_bh)) {
                        err = PTR_ERR(bitmap_bh);
+                       ext4_set_errno(sb, -err);
                        ext4_error(sb, "Error %d reading block bitmap for %u",
                                        err, group);
                        ext4_mb_unload_buddy(&e4b);
@@ -4325,6 +4328,7 @@ ext4_mb_discard_lg_preallocations(struct super_block *sb,
                err = ext4_mb_load_buddy_gfp(sb, group, &e4b,
                                             GFP_NOFS|__GFP_NOFAIL);
                if (err) {
+                       ext4_set_errno(sb, -err);
                        ext4_error(sb, "Error %d loading buddy information for %u",
                                   err, group);
                        continue;
index 2305b4374fd3e020eea80de3293040fd59d5de84..1c44b1a320015d5eda08adffddd4aba47630817c 100644 (file)
@@ -173,8 +173,10 @@ static int kmmpd(void *data)
                 * (s_mmp_update_interval * 60) seconds.
                 */
                if (retval) {
-                       if ((failed_writes % 60) == 0)
+                       if ((failed_writes % 60) == 0) {
+                               ext4_set_errno(sb, -retval);
                                ext4_error(sb, "Error writing to MMP block");
+                       }
                        failed_writes++;
                }
 
@@ -205,6 +207,7 @@ static int kmmpd(void *data)
 
                        retval = read_mmp_block(sb, &bh_check, mmp_block);
                        if (retval) {
+                               ext4_set_errno(sb, -retval);
                                ext4_error(sb, "error reading MMP data: %d",
                                           retval);
                                goto exit_thread;
@@ -218,6 +221,7 @@ static int kmmpd(void *data)
                                             "Error while updating MMP info. "
                                             "The filesystem seems to have been"
                                             " multiply mounted.");
+                               ext4_set_errno(sb, EBUSY);
                                ext4_error(sb, "abort");
                                put_bh(bh_check);
                                retval = -EBUSY;
index 1cb42d94078479dc9a9e107c79e361e0c385a02b..1bb6099397afd8735f28dbe051383ed41a9fce27 100644 (file)
@@ -156,6 +156,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
                if (ext4_dx_csum_verify(inode, dirent))
                        set_buffer_verified(bh);
                else {
+                       ext4_set_errno(inode->i_sb, EFSBADCRC);
                        ext4_error_inode(inode, func, line, block,
                                         "Directory index failed checksum");
                        brelse(bh);
@@ -166,6 +167,7 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
                if (ext4_dirblock_csum_verify(inode, bh))
                        set_buffer_verified(bh);
                else {
+                       ext4_set_errno(inode->i_sb, EFSBADCRC);
                        ext4_error_inode(inode, func, line, block,
                                         "Directory block failed checksum");
                        brelse(bh);
@@ -1527,6 +1529,7 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir,
                        goto next;
                wait_on_buffer(bh);
                if (!buffer_uptodate(bh)) {
+                       ext4_set_errno(sb, EIO);
                        EXT4_ERROR_INODE(dir, "reading directory lblock %lu",
                                         (unsigned long) block);
                        brelse(bh);
@@ -1537,6 +1540,7 @@ static struct buffer_head *__ext4_find_entry(struct inode *dir,
                    !is_dx_internal_node(dir, block,
                                         (struct ext4_dir_entry *)bh->b_data) &&
                    !ext4_dirblock_csum_verify(dir, bh)) {
+                       ext4_set_errno(sb, EFSBADCRC);
                        EXT4_ERROR_INODE(dir, "checksumming directory "
                                         "block %lu", (unsigned long)block);
                        brelse(bh);
index c3d66bb7fd960e0b5c740c8ec0e20c309abfd109..f1a5c14c2a930f5fa0a4994b560c8384293e3f79 100644 (file)
@@ -367,6 +367,8 @@ static void __save_error_info(struct super_block *sb, const char *func,
        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);
+       if (es->s_last_error_errcode == 0)
+               es->s_last_error_errcode = EXT4_ERR_EFSCORRUPTED;
        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;
@@ -375,6 +377,7 @@ static void __save_error_info(struct super_block *sb, const char *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
@@ -631,6 +634,66 @@ const char *ext4_decode_error(struct super_block *sb, int errno,
        return errstr;
 }
 
+void ext4_set_errno(struct super_block *sb, int err)
+{
+       if (err < 0)
+               err = -err;
+
+       switch (err) {
+       case EIO:
+               err = EXT4_ERR_EIO;
+               break;
+       case ENOMEM:
+               err = EXT4_ERR_ENOMEM;
+               break;
+       case EFSBADCRC:
+               err = EXT4_ERR_EFSBADCRC;
+               break;
+       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;
+       }
+       EXT4_SB(sb)->s_es->s_last_error_errcode = err;
+}
+
 /* __ext4_std_error decodes expected errors from journaling functions
  * automatically and invokes the appropriate error response.  */
 
@@ -655,6 +718,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
                       sb->s_id, function, line, errstr);
        }
 
+       ext4_set_errno(sb, -errno);
        save_error_info(sb, function, line);
        ext4_handle_error(sb);
 }
@@ -982,8 +1046,10 @@ static void ext4_put_super(struct super_block *sb)
                aborted = is_journal_aborted(sbi->s_journal);
                err = jbd2_journal_destroy(sbi->s_journal);
                sbi->s_journal = NULL;
-               if ((err < 0) && !aborted)
+               if ((err < 0) && !aborted) {
+                       ext4_set_errno(sb, -err);
                        ext4_abort(sb, "Couldn't clean up the journal");
+               }
        }
 
        ext4_unregister_sysfs(sb);
index 8966a5439a2231e6065a03ab19ed0a4286758654..246fbeeb6366028fff064b7228e1263f008b88a4 100644 (file)
@@ -2879,9 +2879,11 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
                bh = ext4_sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl, REQ_PRIO);
                if (IS_ERR(bh)) {
                        error = PTR_ERR(bh);
-                       if (error == -EIO)
+                       if (error == -EIO) {
+                               ext4_set_errno(inode->i_sb, EIO);
                                EXT4_ERROR_INODE(inode, "block %llu read error",
                                                 EXT4_I(inode)->i_file_acl);
+                       }
                        bh = NULL;
                        goto cleanup;
                }