btrfs: add filesystems state details to error messages
authorSweet Tea Dorminy <sweettea-kernel@dorminy.me>
Wed, 23 Feb 2022 19:38:06 +0000 (14:38 -0500)
committerDavid Sterba <dsterba@suse.com>
Mon, 14 Mar 2022 12:13:52 +0000 (13:13 +0100)
When a filesystem goes read-only due to an error, multiple errors tend
to be reported, some of which are knock-on failures. Logging fs_states,
in btrfs_handle_fs_error() and btrfs_printk() helps distinguish the
first error from subsequent messages which may only exist due to an
error state.

Under the new format, most initial errors will look like:
`BTRFS: error (device loop0) in ...`
while subsequent errors will begin with:
`error (device loop0: state E) in ...`

An initial transaction abort error will look like
`error (device loop0: state A) in ...`
and subsequent messages will contain
`(device loop0: state EA) in ...`

In addition to the error states we can also print other states that are
temporary, like remounting, device replace, or indicate a global state
that may affect functionality.

Now implemented:

E - filesystem error detected
A - transaction aborted
L - log tree errors

M - remounting in progress
R - device replace in progress
C - data checksums not verified (mounted with ignoredatacsums)

Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.h
fs/btrfs/super.c

index 5745b37a7e55293d85182e8030554b0c47581a25..8e79e3ded8825364d04d97fe1e79e0f75f389e72 100644 (file)
@@ -149,6 +149,8 @@ enum {
 
        /* Indicates there was an error cleaning up a log tree. */
        BTRFS_FS_STATE_LOG_CLEANUP_ERROR,
+
+       BTRFS_FS_STATE_COUNT
 };
 
 #define BTRFS_BACKREF_REV_MAX          256
index 33cfc9e27451e22887ef731c29080cc0f0c49de1..b228efe8ab6e2e19e863e60ded3338a195fabad0 100644 (file)
@@ -66,6 +66,52 @@ static struct file_system_type btrfs_root_fs_type;
 
 static int btrfs_remount(struct super_block *sb, int *flags, char *data);
 
+#ifdef CONFIG_PRINTK
+
+#define STATE_STRING_PREFACE   ": state "
+#define STATE_STRING_BUF_LEN   (sizeof(STATE_STRING_PREFACE) + BTRFS_FS_STATE_COUNT)
+
+/*
+ * Characters to print to indicate error conditions or uncommon filesystem sate.
+ * 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',
+       [BTRFS_FS_STATE_DEV_REPLACING]          = 'R',
+       [BTRFS_FS_STATE_DUMMY_FS_INFO]          = 0,
+       [BTRFS_FS_STATE_NO_CSUMS]               = 'C',
+       [BTRFS_FS_STATE_LOG_CLEANUP_ERROR]      = 'L',
+};
+
+static void btrfs_state_to_string(const struct btrfs_fs_info *info, char *buf)
+{
+       unsigned int bit;
+       bool states_printed = false;
+       unsigned long fs_state = READ_ONCE(info->fs_state);
+       char *curr = buf;
+
+       memcpy(curr, STATE_STRING_PREFACE, sizeof(STATE_STRING_PREFACE));
+       curr += sizeof(STATE_STRING_PREFACE) - 1;
+
+       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]) {
+                       *curr++ = fs_state_chars[bit];
+                       states_printed = true;
+               }
+       }
+
+       /* If no states were printed, reset the buffer */
+       if (!states_printed)
+               curr = buf;
+
+       *curr++ = 0;
+}
+#endif
+
 /*
  * Generally the error codes correspond to their respective errors, but there
  * are a few special cases.
@@ -128,6 +174,7 @@ void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function
 {
        struct super_block *sb = fs_info->sb;
 #ifdef CONFIG_PRINTK
+       char statestr[STATE_STRING_BUF_LEN];
        const char *errstr;
 #endif
 
@@ -140,6 +187,7 @@ void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function
 
 #ifdef CONFIG_PRINTK
        errstr = btrfs_decode_error(errno);
+       btrfs_state_to_string(fs_info, statestr);
        if (fmt) {
                struct va_format vaf;
                va_list args;
@@ -148,12 +196,12 @@ void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function
                vaf.fmt = fmt;
                vaf.va = &args;
 
-               pr_crit("BTRFS: error (device %s) in %s:%d: errno=%d %s (%pV)\n",
-                       sb->s_id, function, line, errno, errstr, &vaf);
+               pr_crit("BTRFS: error (device %s%s) in %s:%d: errno=%d %s (%pV)\n",
+                       sb->s_id, statestr, function, line, errno, errstr, &vaf);
                va_end(args);
        } else {
-               pr_crit("BTRFS: error (device %s) in %s:%d: errno=%d %s\n",
-                       sb->s_id, function, line, errno, errstr);
+               pr_crit("BTRFS: error (device %s%s) in %s:%d: errno=%d %s\n",
+                       sb->s_id, statestr, function, line, errno, errstr);
        }
 #endif
 
@@ -240,11 +288,15 @@ void __cold btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, .
        vaf.va = &args;
 
        if (__ratelimit(ratelimit)) {
-               if (fs_info)
-                       printk("%sBTRFS %s (device %s): %pV\n", lvl, type,
-                               fs_info->sb->s_id, &vaf);
-               else
+               if (fs_info) {
+                       char statestr[STATE_STRING_BUF_LEN];
+
+                       btrfs_state_to_string(fs_info, statestr);
+                       printk("%sBTRFS %s (device %s%s): %pV\n", lvl, type,
+                               fs_info->sb->s_id, statestr, &vaf);
+               } else {
                        printk("%sBTRFS %s: %pV\n", lvl, type, &vaf);
+               }
        }
 
        va_end(args);