OSDN Git Service

ubifs: Export filesystem error counters
authorStefan Schaeckeler <schaecsn@gmx.net>
Sun, 10 Oct 2021 04:22:39 +0000 (21:22 -0700)
committerRichard Weinberger <richard@nod.at>
Thu, 23 Dec 2021 19:23:42 +0000 (20:23 +0100)
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: Stefan Schaeckeler <sschaeck@cisco.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
fs/ubifs/Makefile
fs/ubifs/io.c
fs/ubifs/super.c
fs/ubifs/sysfs.c [new file with mode: 0644]
fs/ubifs/ubifs.h

index 5c4b845..314c80b 100644 (file)
@@ -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
index 00b61db..789a781 100644 (file)
@@ -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;
        }
index fbec8fc..aa7a138 100644 (file)
@@ -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 @@ out_free:
        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);
 
diff --git a/fs/ubifs/sysfs.c b/fs/ubifs/sysfs.c
new file mode 100644 (file)
index 0000000..0eb3d7d
--- /dev/null
@@ -0,0 +1,153 @@
+// 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);
+}
index c38066c..f55828c 100644 (file)
@@ -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>
 #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.
  */
 enum {
@@ -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"