mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
![Qu Wenruo](/assets/img/avatar_default.png)
The v0 extent item has been deprecated for a long time, and we don't have any report from the community either. So it's time to remove the v0 extent specific error handling, and just treat them as regular extent tree corruption. This patch would remove the btrfs_print_v0_err() helper, and enhance the involved error handling to treat them just as any extent tree corruption. No reports regarding v0 extents have been seen since the graceful handling was added in 2018. This involves: - btrfs_backref_add_tree_node() This change is a little tricky, the new code is changed to only handle BTRFS_TREE_BLOCK_REF_KEY and BTRFS_SHARED_BLOCK_REF_KEY. But this is safe, as we have rejected any unknown inline refs through btrfs_get_extent_inline_ref_type(). For keyed backrefs, we're safe to skip anything we don't know (that's if it can pass tree-checker in the first place). - btrfs_lookup_extent_info() - lookup_inline_extent_backref() - run_delayed_extent_op() - __btrfs_free_extent() - add_tree_block() Regular error handling of unexpected extent tree item, and abort transaction (if we have a trans handle). - remove_extent_data_ref() It's pretty much the same as the regular rejection of unknown backref key. But for this particular case, we can also remove a BUG_ON(). - extent_data_ref_count() We can remove the BTRFS_EXTENT_REF_V0_KEY BUG_ON(), as it would be rejected by the only caller. - btrfs_print_leaf() Remove the handling for BTRFS_EXTENT_REF_V0_KEY. Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
313 lines
8.6 KiB
C
313 lines
8.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include "fs.h"
|
|
#include "messages.h"
|
|
#include "discard.h"
|
|
#include "transaction.h"
|
|
#include "space-info.h"
|
|
#include "super.h"
|
|
|
|
#ifdef CONFIG_PRINTK
|
|
|
|
#define STATE_STRING_PREFACE ": state "
|
|
#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_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;
|
|
|
|
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]) {
|
|
*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.
|
|
*
|
|
* EUCLEAN: Any sort of corruption that we encounter. The tree-checker for
|
|
* instance will return EUCLEAN if any of the blocks are corrupted in
|
|
* a way that is problematic. We want to reserve EUCLEAN for these
|
|
* sort of corruptions.
|
|
*
|
|
* EROFS: If we check BTRFS_FS_STATE_ERROR and fail out with a return error, we
|
|
* need to use EROFS for this case. We will have no idea of the
|
|
* original failure, that will have been reported at the time we tripped
|
|
* over the error. Each subsequent error that doesn't have any context
|
|
* of the original error should use EROFS when handling BTRFS_FS_STATE_ERROR.
|
|
*/
|
|
const char * __attribute_const__ btrfs_decode_error(int errno)
|
|
{
|
|
char *errstr = "unknown";
|
|
|
|
switch (errno) {
|
|
case -ENOENT: /* -2 */
|
|
errstr = "No such entry";
|
|
break;
|
|
case -EIO: /* -5 */
|
|
errstr = "IO failure";
|
|
break;
|
|
case -ENOMEM: /* -12*/
|
|
errstr = "Out of memory";
|
|
break;
|
|
case -EEXIST: /* -17 */
|
|
errstr = "Object already exists";
|
|
break;
|
|
case -ENOSPC: /* -28 */
|
|
errstr = "No space left";
|
|
break;
|
|
case -EROFS: /* -30 */
|
|
errstr = "Readonly filesystem";
|
|
break;
|
|
case -EOPNOTSUPP: /* -95 */
|
|
errstr = "Operation not supported";
|
|
break;
|
|
case -EUCLEAN: /* -117 */
|
|
errstr = "Filesystem corrupted";
|
|
break;
|
|
case -EDQUOT: /* -122 */
|
|
errstr = "Quota exceeded";
|
|
break;
|
|
}
|
|
|
|
return errstr;
|
|
}
|
|
|
|
/*
|
|
* __btrfs_handle_fs_error decodes expected errors from the caller and
|
|
* invokes the appropriate error response.
|
|
*/
|
|
__cold
|
|
void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function,
|
|
unsigned int line, int errno, const char *fmt, ...)
|
|
{
|
|
struct super_block *sb = fs_info->sb;
|
|
#ifdef CONFIG_PRINTK
|
|
char statestr[STATE_STRING_BUF_LEN];
|
|
const char *errstr;
|
|
#endif
|
|
|
|
#ifdef CONFIG_PRINTK_INDEX
|
|
printk_index_subsys_emit(
|
|
"BTRFS: error (device %s%s) in %s:%d: errno=%d %s", KERN_CRIT, fmt);
|
|
#endif
|
|
|
|
/*
|
|
* Special case: if the error is EROFS, and we're already under
|
|
* SB_RDONLY, then it is safe here.
|
|
*/
|
|
if (errno == -EROFS && sb_rdonly(sb))
|
|
return;
|
|
|
|
#ifdef CONFIG_PRINTK
|
|
errstr = btrfs_decode_error(errno);
|
|
btrfs_state_to_string(fs_info, statestr);
|
|
if (fmt) {
|
|
struct va_format vaf;
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
vaf.fmt = fmt;
|
|
vaf.va = &args;
|
|
|
|
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%s) in %s:%d: errno=%d %s\n",
|
|
sb->s_id, statestr, function, line, errno, errstr);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Today we only save the error info to memory. Long term we'll also
|
|
* send it down to the disk.
|
|
*/
|
|
WRITE_ONCE(fs_info->fs_error, errno);
|
|
|
|
/* Don't go through full error handling during mount. */
|
|
if (!(sb->s_flags & SB_BORN))
|
|
return;
|
|
|
|
if (sb_rdonly(sb))
|
|
return;
|
|
|
|
btrfs_discard_stop(fs_info);
|
|
|
|
/* Handle error by forcing the filesystem readonly. */
|
|
btrfs_set_sb_rdonly(sb);
|
|
btrfs_info(fs_info, "forced readonly");
|
|
/*
|
|
* Note that a running device replace operation is not canceled here
|
|
* although there is no way to update the progress. It would add the
|
|
* risk of a deadlock, therefore the canceling is omitted. The only
|
|
* penalty is that some I/O remains active until the procedure
|
|
* completes. The next time when the filesystem is mounted writable
|
|
* again, the device replace operation continues.
|
|
*/
|
|
}
|
|
|
|
#ifdef CONFIG_PRINTK
|
|
static const char * const logtypes[] = {
|
|
"emergency",
|
|
"alert",
|
|
"critical",
|
|
"error",
|
|
"warning",
|
|
"notice",
|
|
"info",
|
|
"debug",
|
|
};
|
|
|
|
/*
|
|
* Use one ratelimit state per log level so that a flood of less important
|
|
* messages doesn't cause more important ones to be dropped.
|
|
*/
|
|
static struct ratelimit_state printk_limits[] = {
|
|
RATELIMIT_STATE_INIT(printk_limits[0], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[1], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[2], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[3], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[4], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[5], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[6], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
RATELIMIT_STATE_INIT(printk_limits[7], DEFAULT_RATELIMIT_INTERVAL, 100),
|
|
};
|
|
|
|
void __cold _btrfs_printk(const struct btrfs_fs_info *fs_info, const char *fmt, ...)
|
|
{
|
|
char lvl[PRINTK_MAX_SINGLE_HEADER_LEN + 1] = "\0";
|
|
struct va_format vaf;
|
|
va_list args;
|
|
int kern_level;
|
|
const char *type = logtypes[4];
|
|
struct ratelimit_state *ratelimit = &printk_limits[4];
|
|
|
|
#ifdef CONFIG_PRINTK_INDEX
|
|
printk_index_subsys_emit("%sBTRFS %s (device %s): ", NULL, fmt);
|
|
#endif
|
|
|
|
va_start(args, fmt);
|
|
|
|
while ((kern_level = printk_get_level(fmt)) != 0) {
|
|
size_t size = printk_skip_level(fmt) - fmt;
|
|
|
|
if (kern_level >= '0' && kern_level <= '7') {
|
|
memcpy(lvl, fmt, size);
|
|
lvl[size] = '\0';
|
|
type = logtypes[kern_level - '0'];
|
|
ratelimit = &printk_limits[kern_level - '0'];
|
|
}
|
|
fmt += size;
|
|
}
|
|
|
|
vaf.fmt = fmt;
|
|
vaf.va = &args;
|
|
|
|
if (__ratelimit(ratelimit)) {
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
#if BITS_PER_LONG == 32
|
|
void __cold btrfs_warn_32bit_limit(struct btrfs_fs_info *fs_info)
|
|
{
|
|
if (!test_and_set_bit(BTRFS_FS_32BIT_WARN, &fs_info->flags)) {
|
|
btrfs_warn(fs_info, "reaching 32bit limit for logical addresses");
|
|
btrfs_warn(fs_info,
|
|
"due to page cache limit on 32bit systems, btrfs can't access metadata at or beyond %lluT",
|
|
BTRFS_32BIT_MAX_FILE_SIZE >> 40);
|
|
btrfs_warn(fs_info,
|
|
"please consider upgrading to 64bit kernel/hardware");
|
|
}
|
|
}
|
|
|
|
void __cold btrfs_err_32bit_limit(struct btrfs_fs_info *fs_info)
|
|
{
|
|
if (!test_and_set_bit(BTRFS_FS_32BIT_ERROR, &fs_info->flags)) {
|
|
btrfs_err(fs_info, "reached 32bit limit for logical addresses");
|
|
btrfs_err(fs_info,
|
|
"due to page cache limit on 32bit systems, metadata beyond %lluT can't be accessed",
|
|
BTRFS_32BIT_MAX_FILE_SIZE >> 40);
|
|
btrfs_err(fs_info,
|
|
"please consider upgrading to 64bit kernel/hardware");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* __btrfs_panic decodes unexpected, fatal errors from the caller, issues an
|
|
* alert, and either panics or BUGs, depending on mount options.
|
|
*/
|
|
__cold
|
|
void __btrfs_panic(struct btrfs_fs_info *fs_info, const char *function,
|
|
unsigned int line, int errno, const char *fmt, ...)
|
|
{
|
|
char *s_id = "<unknown>";
|
|
const char *errstr;
|
|
struct va_format vaf = { .fmt = fmt };
|
|
va_list args;
|
|
|
|
if (fs_info)
|
|
s_id = fs_info->sb->s_id;
|
|
|
|
va_start(args, fmt);
|
|
vaf.va = &args;
|
|
|
|
errstr = btrfs_decode_error(errno);
|
|
if (fs_info && (btrfs_test_opt(fs_info, PANIC_ON_FATAL_ERROR)))
|
|
panic(KERN_CRIT "BTRFS panic (device %s) in %s:%d: %pV (errno=%d %s)\n",
|
|
s_id, function, line, &vaf, errno, errstr);
|
|
|
|
btrfs_crit(fs_info, "panic in %s:%d: %pV (errno=%d %s)",
|
|
function, line, &vaf, errno, errstr);
|
|
va_end(args);
|
|
/* Caller calls BUG() */
|
|
}
|