btrfs: store the error that turned the fs into error state
authorFilipe Manana <fdmanana@suse.com>
Wed, 26 Jul 2023 15:57:04 +0000 (16:57 +0100)
committerDavid Sterba <dsterba@suse.com>
Mon, 21 Aug 2023 12:52:18 +0000 (14:52 +0200)
Currently when we turn the fs into an error state, typically after a
transaction abort, we don't store the error anywhere, we just set a bit
(BTRFS_FS_STATE_ERROR) at struct btrfs_fs_info::fs_state to signal the
error state.

There are cases where it would be useful to have access to the specific
error in order to provide a more meaningful error to users/applications.
This change adds a member to struct btrfs_fs_info to store the error and
removes the BTRFS_FS_STATE_ERROR bit. When there's no error, the new
member (fs_error) has a value of 0, otherwise its value is a negative
errno value.

Followup changes will make use of this new member.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/disk-io.c
fs/btrfs/fs.h
fs/btrfs/messages.c

index e7a096a..ccc34c6 100644 (file)
@@ -3222,7 +3222,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 
        /* check FS state, whether FS is broken. */
        if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR)
-               set_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state);
+               WRITE_ONCE(fs_info->fs_error, -EUCLEAN);
 
        /*
         * In the long term, we'll store the compression type in the super
index 203d2a2..ef07c6c 100644 (file)
@@ -46,8 +46,6 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE);
  * Runtime (in-memory) states of filesystem
  */
 enum {
-       /* Global indicator of serious filesystem errors */
-       BTRFS_FS_STATE_ERROR,
        /*
         * Filesystem is being remounted, allow to skip some operations, like
         * defrag
@@ -686,6 +684,12 @@ struct btrfs_fs_info {
        bool qgroup_rescan_running;
        u8 qgroup_drop_subtree_thres;
 
+       /*
+        * If this is not 0, then it indicates a serious filesystem error has
+        * happened and it contains that error (negative errno value).
+        */
+       int fs_error;
+
        /* Filesystem state */
        unsigned long fs_state;
 
@@ -962,8 +966,8 @@ static inline void btrfs_wake_unfinished_drop(struct btrfs_fs_info *fs_info)
        clear_and_wake_up_bit(BTRFS_FS_UNFINISHED_DROPS, &fs_info->flags);
 }
 
-#define BTRFS_FS_ERROR(fs_info)        (unlikely(test_bit(BTRFS_FS_STATE_ERROR, \
-                                                  &(fs_info)->fs_state)))
+#define BTRFS_FS_ERROR(fs_info)        (READ_ONCE((fs_info)->fs_error))
+
 #define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info)                            \
        (unlikely(test_bit(BTRFS_FS_STATE_LOG_CLEANUP_ERROR,            \
                           &(fs_info)->fs_state)))
index 23fc11a..e3c9d27 100644 (file)
 #ifdef CONFIG_PRINTK
 
 #define STATE_STRING_PREFACE   ": state "
-#define STATE_STRING_BUF_LEN   (sizeof(STATE_STRING_PREFACE) + BTRFS_FS_STATE_COUNT)
+#define STATE_STRING_BUF_LEN   (sizeof(STATE_STRING_PREFACE) + BTRFS_FS_STATE_COUNT + 1)
 
 /*
  * Characters to print to indicate error conditions or uncommon filesystem state.
  * RO is not an error.
  */
 static const char fs_state_chars[] = {
-       [BTRFS_FS_STATE_ERROR]                  = 'E',
        [BTRFS_FS_STATE_REMOUNTING]             = 'M',
        [BTRFS_FS_STATE_RO]                     = 0,
        [BTRFS_FS_STATE_TRANS_ABORTED]          = 'A',
@@ -37,6 +36,11 @@ static void btrfs_state_to_string(const struct btrfs_fs_info *info, char *buf)
        memcpy(curr, STATE_STRING_PREFACE, sizeof(STATE_STRING_PREFACE));
        curr += sizeof(STATE_STRING_PREFACE) - 1;
 
+       if (BTRFS_FS_ERROR(info)) {
+               *curr++ = 'E';
+               states_printed = true;
+       }
+
        for_each_set_bit(bit, &fs_state, sizeof(fs_state)) {
                WARN_ON_ONCE(bit >= BTRFS_FS_STATE_COUNT);
                if ((bit < BTRFS_FS_STATE_COUNT) && fs_state_chars[bit]) {
@@ -155,7 +159,7 @@ void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function
         * Today we only save the error info to memory.  Long term we'll also
         * send it down to the disk.
         */
-       set_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state);
+       WRITE_ONCE(fs_info->fs_error, errno);
 
        /* Don't go through full error handling during mount. */
        if (!(sb->s_flags & SB_BORN))