mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 01:59:51 +02:00 
			
		
		
		
	Disable ratelimiting for btrfs_printk when CONFIG_BTRFS_DEBUG is enabled. This allows for more verbose output which is often needed by functions like btrfs_dump_space_info(). Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Leo Martins <loemra.dev@gmail.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 "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_DATA_CSUMS]		= 'C',
 | 
						|
	[BTRFS_FS_STATE_SKIP_META_CSUMS]	= 'S',
 | 
						|
	[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 error)
 | 
						|
{
 | 
						|
	char *errstr = "unknown";
 | 
						|
 | 
						|
	switch (error) {
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 error, 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 (error == -EROFS && sb_rdonly(sb))
 | 
						|
		return;
 | 
						|
 | 
						|
#ifdef CONFIG_PRINTK
 | 
						|
	errstr = btrfs_decode_error(error);
 | 
						|
	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, error, 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, error, 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, error);
 | 
						|
 | 
						|
	/* 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;
 | 
						|
 | 
						|
	/* Do not ratelimit if CONFIG_BTRFS_DEBUG is enabled. */
 | 
						|
	if (IS_ENABLED(CONFIG_BTRFS_DEBUG) || __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
 | 
						|
 | 
						|
/*
 | 
						|
 * Decode unexpected, fatal errors from the caller, issue an alert, and either
 | 
						|
 * panic or BUGs, depending on mount options.
 | 
						|
 */
 | 
						|
__cold
 | 
						|
void __btrfs_panic(const struct btrfs_fs_info *fs_info, const char *function,
 | 
						|
		   unsigned int line, int error, 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(error);
 | 
						|
	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, error, errstr);
 | 
						|
 | 
						|
	btrfs_crit(fs_info, "panic in %s:%d: %pV (errno=%d %s)",
 | 
						|
		   function, line, &vaf, error, errstr);
 | 
						|
	va_end(args);
 | 
						|
	/* Caller calls BUG() */
 | 
						|
}
 |