Commit 2e3cbf42 authored by Stefan Schaeckeler's avatar Stefan Schaeckeler Committed by Richard Weinberger
Browse files

ubifs: Export filesystem error counters



Not all ubifs filesystem errors are propagated to userspace.

Export bad magic, bad node and crc errors via sysfs. This allows userspace
to notice filesystem errors:

 /sys/fs/ubifs/ubiX_Y/errors_magic
 /sys/fs/ubifs/ubiX_Y/errors_node
 /sys/fs/ubifs/ubiX_Y/errors_crc

The counters are reset to 0 with a remount.

Signed-off-by: default avatarStefan Schaeckeler <sschaeck@cisco.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 3fea4d9d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ ubifs-y += shrinker.o journal.o file.o dir.o super.o sb.o io.o
ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o
ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o debug.o
ubifs-y += misc.o
ubifs-y += misc.o sysfs.o
ubifs-$(CONFIG_FS_ENCRYPTION) += crypto.o
ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o
ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o
+21 −0
Original line number Diff line number Diff line
@@ -194,6 +194,24 @@ int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
	return err;
}

static void record_magic_error(struct ubifs_stats_info *stats)
{
	if (stats)
		stats->magic_errors++;
}

static void record_node_error(struct ubifs_stats_info *stats)
{
	if (stats)
		stats->node_errors++;
}

static void record_crc_error(struct ubifs_stats_info *stats)
{
	if (stats)
		stats->crc_errors++;
}

/**
 * ubifs_check_node - check node.
 * @c: UBIFS file-system description object
@@ -238,6 +256,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
		if (!quiet)
			ubifs_err(c, "bad magic %#08x, expected %#08x",
				  magic, UBIFS_NODE_MAGIC);
		record_magic_error(c->stats);
		err = -EUCLEAN;
		goto out;
	}
@@ -246,6 +265,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
	if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
		if (!quiet)
			ubifs_err(c, "bad node type %d", type);
		record_node_error(c->stats);
		goto out;
	}

@@ -270,6 +290,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
		if (!quiet)
			ubifs_err(c, "bad CRC: calculated %#08x, read %#08x",
				  crc, node_crc);
		record_crc_error(c->stats);
		err = -EUCLEAN;
		goto out;
	}
+15 −1
Original line number Diff line number Diff line
@@ -1264,6 +1264,10 @@ static int mount_ubifs(struct ubifs_info *c)
	if (err)
		return err;

	err = ubifs_sysfs_register(c);
	if (err)
		goto out_debugging;

	err = check_volume_empty(c);
	if (err)
		goto out_free;
@@ -1640,6 +1644,8 @@ static int mount_ubifs(struct ubifs_info *c)
	vfree(c->sbuf);
	kfree(c->bottom_up_buf);
	kfree(c->sup_node);
	ubifs_sysfs_unregister(c);
out_debugging:
	ubifs_debugging_exit(c);
	return err;
}
@@ -1683,6 +1689,7 @@ static void ubifs_umount(struct ubifs_info *c)
	kfree(c->bottom_up_buf);
	kfree(c->sup_node);
	ubifs_debugging_exit(c);
	ubifs_sysfs_unregister(c);
}

/**
@@ -2433,14 +2440,20 @@ static int __init ubifs_init(void)

	dbg_debugfs_init();

	err = ubifs_sysfs_init();
	if (err)
		goto out_dbg;

	err = register_filesystem(&ubifs_fs_type);
	if (err) {
		pr_err("UBIFS error (pid %d): cannot register file system, error %d",
		       current->pid, err);
		goto out_dbg;
		goto out_sysfs;
	}
	return 0;

out_sysfs:
	ubifs_sysfs_exit();
out_dbg:
	dbg_debugfs_exit();
	ubifs_compressors_exit();
@@ -2459,6 +2472,7 @@ static void __exit ubifs_exit(void)
	WARN_ON(atomic_long_read(&ubifs_clean_zn_cnt) != 0);

	dbg_debugfs_exit();
	ubifs_sysfs_exit();
	ubifs_compressors_exit();
	unregister_shrinker(&ubifs_shrinker_info);

fs/ubifs/sysfs.c

0 → 100644
+153 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file is part of UBIFS.
 *
 * Copyright (C) 2021 Cisco Systems
 *
 * Author: Stefan Schaeckeler
 */


#include <linux/fs.h>
#include "ubifs.h"

enum attr_id_t {
	attr_errors_magic,
	attr_errors_node,
	attr_errors_crc,
};

struct ubifs_attr {
	struct attribute attr;
	enum attr_id_t attr_id;
};

#define UBIFS_ATTR(_name, _mode, _id)					\
static struct ubifs_attr ubifs_attr_##_name = {				\
	.attr = {.name = __stringify(_name), .mode = _mode },		\
	.attr_id = attr_##_id,						\
}

#define UBIFS_ATTR_FUNC(_name, _mode) UBIFS_ATTR(_name, _mode, _name)

UBIFS_ATTR_FUNC(errors_magic, 0444);
UBIFS_ATTR_FUNC(errors_crc, 0444);
UBIFS_ATTR_FUNC(errors_node, 0444);

#define ATTR_LIST(name) (&ubifs_attr_##name.attr)

static struct attribute *ubifs_attrs[] = {
	ATTR_LIST(errors_magic),
	ATTR_LIST(errors_node),
	ATTR_LIST(errors_crc),
	NULL,
};

static ssize_t ubifs_attr_show(struct kobject *kobj,
			       struct attribute *attr, char *buf)
{
	struct ubifs_info *sbi = container_of(kobj, struct ubifs_info,
					      kobj);

	struct ubifs_attr *a = container_of(attr, struct ubifs_attr, attr);

	switch (a->attr_id) {
	case attr_errors_magic:
		return sysfs_emit(buf, "%u\n", sbi->stats->magic_errors);
	case attr_errors_node:
		return sysfs_emit(buf, "%u\n", sbi->stats->node_errors);
	case attr_errors_crc:
		return sysfs_emit(buf, "%u\n", sbi->stats->crc_errors);
	}
	return 0;
};

static void ubifs_sb_release(struct kobject *kobj)
{
	struct ubifs_info *c = container_of(kobj, struct ubifs_info, kobj);

	complete(&c->kobj_unregister);
}

static const struct sysfs_ops ubifs_attr_ops = {
	.show	= ubifs_attr_show,
};

static struct kobj_type ubifs_sb_ktype = {
	.default_attrs	= ubifs_attrs,
	.sysfs_ops	= &ubifs_attr_ops,
	.release	= ubifs_sb_release,
};

static struct kobj_type ubifs_ktype = {
	.sysfs_ops	= &ubifs_attr_ops,
};

static struct kset ubifs_kset = {
	.kobj	= {.ktype = &ubifs_ktype},
};

int ubifs_sysfs_register(struct ubifs_info *c)
{
	int ret, n;
	char dfs_dir_name[UBIFS_DFS_DIR_LEN+1];

	c->stats = kzalloc(sizeof(struct ubifs_stats_info), GFP_KERNEL);
	if (!c->stats) {
		ret = -ENOMEM;
		goto out_last;
	}
	n = snprintf(dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
		     c->vi.ubi_num, c->vi.vol_id);

	if (n == UBIFS_DFS_DIR_LEN) {
		/* The array size is too small */
		ret = -EINVAL;
		goto out_free;
	}

	c->kobj.kset = &ubifs_kset;
	init_completion(&c->kobj_unregister);

	ret = kobject_init_and_add(&c->kobj, &ubifs_sb_ktype, NULL,
				   "%s", dfs_dir_name);
	if (ret)
		goto out_put;

	return 0;

out_put:
	kobject_put(&c->kobj);
	wait_for_completion(&c->kobj_unregister);
out_free:
	kfree(c->stats);
out_last:
	ubifs_err(c, "cannot create sysfs entry for ubifs%d_%d, error %d\n",
		  c->vi.ubi_num, c->vi.vol_id, ret);
	return ret;
}

void ubifs_sysfs_unregister(struct ubifs_info *c)
{
	kobject_del(&c->kobj);
	kobject_put(&c->kobj);
	wait_for_completion(&c->kobj_unregister);

	kfree(c->stats);
}

int __init ubifs_sysfs_init(void)
{
	int ret;

	kobject_set_name(&ubifs_kset.kobj, "ubifs");
	ubifs_kset.kobj.parent = fs_kobj;
	ret = kset_register(&ubifs_kset);

	return ret;
}

void ubifs_sysfs_exit(void)
{
	kset_unregister(&ubifs_kset);
}
+35 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@
#include <linux/security.h>
#include <linux/xattr.h>
#include <linux/random.h>
#include <linux/sysfs.h>
#include <linux/completion.h>
#include <crypto/hash_info.h>
#include <crypto/hash.h>
#include <crypto/algapi.h>
@@ -155,6 +157,13 @@
#define UBIFS_HMAC_ARR_SZ 0
#endif

/*
 * The UBIFS sysfs directory name pattern and maximum name length (3 for "ubi"
 * + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
 */
#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
#define UBIFS_DFS_DIR_LEN  (3 + 1 + 2*2 + 1)

/*
 * Lockdep classes for UBIFS inode @ui_mutex.
 */
@@ -990,6 +999,18 @@ struct ubifs_budg_info {
	int dent_budget;
};

/**
 * ubifs_stats_info - per-FS statistics information.
 * @magic_errors: number of bad magic numbers (will be reset with a new mount).
 * @node_errors: number of bad nodes (will be reset with a new mount).
 * @crc_errors: number of bad crcs (will be reset with a new mount).
 */
struct ubifs_stats_info {
	unsigned int magic_errors;
	unsigned int node_errors;
	unsigned int crc_errors;
};

struct ubifs_debug_info;

/**
@@ -1251,6 +1272,10 @@ struct ubifs_debug_info;
 * @mount_opts: UBIFS-specific mount options
 *
 * @dbg: debugging-related information
 * @stats: statistics exported over sysfs
 *
 * @kobj: kobject for /sys/fs/ubifs/
 * @kobj_unregister: completion to unregister sysfs kobject
 */
struct ubifs_info {
	struct super_block *vfs_sb;
@@ -1286,6 +1311,9 @@ struct ubifs_info {
	spinlock_t cs_lock;
	wait_queue_head_t cmt_wq;

	struct kobject kobj;
	struct completion kobj_unregister;

	unsigned int big_lpt:1;
	unsigned int space_fixup:1;
	unsigned int double_hash:1;
@@ -1493,6 +1521,7 @@ struct ubifs_info {
	struct ubifs_mount_opts mount_opts;

	struct ubifs_debug_info *dbg;
	struct ubifs_stats_info *stats;
};

extern struct list_head ubifs_infos;
@@ -2072,6 +2101,12 @@ void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len,
int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
		     void *out, int *out_len, int compr_type);

/* sysfs.c */
int ubifs_sysfs_init(void);
void ubifs_sysfs_exit(void);
int ubifs_sysfs_register(struct ubifs_info *c);
void ubifs_sysfs_unregister(struct ubifs_info *c);

#include "debug.h"
#include "misc.h"
#include "key.h"