btrfs: store the error that turned the fs into error state

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>
This commit is contained in:
Filipe Manana 2023-07-26 16:57:04 +01:00 committed by David Sterba
parent 1b6948acb8
commit ae3364e521
3 changed files with 16 additions and 8 deletions

View 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. */ /* check FS state, whether FS is broken. */
if (btrfs_super_flags(disk_super) & BTRFS_SUPER_FLAG_ERROR) 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 * In the long term, we'll store the compression type in the super

View File

@ -46,8 +46,6 @@ static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE);
* Runtime (in-memory) states of filesystem * Runtime (in-memory) states of filesystem
*/ */
enum { enum {
/* Global indicator of serious filesystem errors */
BTRFS_FS_STATE_ERROR,
/* /*
* Filesystem is being remounted, allow to skip some operations, like * Filesystem is being remounted, allow to skip some operations, like
* defrag * defrag
@ -686,6 +684,12 @@ struct btrfs_fs_info {
bool qgroup_rescan_running; bool qgroup_rescan_running;
u8 qgroup_drop_subtree_thres; 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 */ /* Filesystem state */
unsigned long fs_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); 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, \ #define BTRFS_FS_ERROR(fs_info) (READ_ONCE((fs_info)->fs_error))
&(fs_info)->fs_state)))
#define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info) \ #define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info) \
(unlikely(test_bit(BTRFS_FS_STATE_LOG_CLEANUP_ERROR, \ (unlikely(test_bit(BTRFS_FS_STATE_LOG_CLEANUP_ERROR, \
&(fs_info)->fs_state))) &(fs_info)->fs_state)))

View File

@ -10,14 +10,13 @@
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
#define STATE_STRING_PREFACE ": state " #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. * Characters to print to indicate error conditions or uncommon filesystem state.
* RO is not an error. * RO is not an error.
*/ */
static const char fs_state_chars[] = { static const char fs_state_chars[] = {
[BTRFS_FS_STATE_ERROR] = 'E',
[BTRFS_FS_STATE_REMOUNTING] = 'M', [BTRFS_FS_STATE_REMOUNTING] = 'M',
[BTRFS_FS_STATE_RO] = 0, [BTRFS_FS_STATE_RO] = 0,
[BTRFS_FS_STATE_TRANS_ABORTED] = 'A', [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)); memcpy(curr, STATE_STRING_PREFACE, sizeof(STATE_STRING_PREFACE));
curr += sizeof(STATE_STRING_PREFACE) - 1; 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)) { for_each_set_bit(bit, &fs_state, sizeof(fs_state)) {
WARN_ON_ONCE(bit >= BTRFS_FS_STATE_COUNT); WARN_ON_ONCE(bit >= BTRFS_FS_STATE_COUNT);
if ((bit < BTRFS_FS_STATE_COUNT) && fs_state_chars[bit]) { 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 * Today we only save the error info to memory. Long term we'll also
* send it down to the disk. * 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. */ /* Don't go through full error handling during mount. */
if (!(sb->s_flags & SB_BORN)) if (!(sb->s_flags & SB_BORN))