root/fs/btrfs/messages.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_LOG_REPLAY_ABORTED]     = 'O',
        [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',
        [BTRFS_FS_STATE_EMERGENCY_SHUTDOWN]     = 'E',
};

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),
};

__printf(3, 4) __cold
void _btrfs_printk(const struct btrfs_fs_info *fs_info, unsigned int level, const char *fmt, ...)
{
        struct va_format vaf;
        va_list args;
        const char *type = logtypes[level];
        struct ratelimit_state *ratelimit = &printk_limits[level];

#ifdef CONFIG_PRINTK_INDEX
        printk_index_subsys_emit("%sBTRFS %s (device %s): ", NULL, fmt);
#endif

        va_start(args, fmt);
        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(KERN_SOH "%dBTRFS %s (device %s%s): %pV\n", level, type,
                                fs_info->sb->s_id, statestr, &vaf);
                } else {
                        _printk(KERN_SOH "%dBTRFS %s: %pV\n", level, 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() */
}