Skip to content

Commit 2e3cbf4

Browse files
schaecsnrichardweinberger
authored andcommitted
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: Stefan Schaeckeler <sschaeck@cisco.com> Signed-off-by: Richard Weinberger <richard@nod.at>
1 parent 3fea4d9 commit 2e3cbf4

5 files changed

Lines changed: 225 additions & 2 deletions

File tree

fs/ubifs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ubifs-y += shrinker.o journal.o file.o dir.o super.o sb.o io.o
55
ubifs-y += tnc.o master.o scan.o replay.o log.o commit.o gc.o orphan.o
66
ubifs-y += budget.o find.o tnc_commit.o compress.o lpt.o lprops.o
77
ubifs-y += recovery.o ioctl.o lpt_commit.o tnc_misc.o debug.o
8-
ubifs-y += misc.o
8+
ubifs-y += misc.o sysfs.o
99
ubifs-$(CONFIG_FS_ENCRYPTION) += crypto.o
1010
ubifs-$(CONFIG_UBIFS_FS_XATTR) += xattr.o
1111
ubifs-$(CONFIG_UBIFS_FS_AUTHENTICATION) += auth.o

fs/ubifs/io.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,24 @@ int ubifs_is_mapped(const struct ubifs_info *c, int lnum)
194194
return err;
195195
}
196196

197+
static void record_magic_error(struct ubifs_stats_info *stats)
198+
{
199+
if (stats)
200+
stats->magic_errors++;
201+
}
202+
203+
static void record_node_error(struct ubifs_stats_info *stats)
204+
{
205+
if (stats)
206+
stats->node_errors++;
207+
}
208+
209+
static void record_crc_error(struct ubifs_stats_info *stats)
210+
{
211+
if (stats)
212+
stats->crc_errors++;
213+
}
214+
197215
/**
198216
* ubifs_check_node - check node.
199217
* @c: UBIFS file-system description object
@@ -238,6 +256,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
238256
if (!quiet)
239257
ubifs_err(c, "bad magic %#08x, expected %#08x",
240258
magic, UBIFS_NODE_MAGIC);
259+
record_magic_error(c->stats);
241260
err = -EUCLEAN;
242261
goto out;
243262
}
@@ -246,6 +265,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
246265
if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) {
247266
if (!quiet)
248267
ubifs_err(c, "bad node type %d", type);
268+
record_node_error(c->stats);
249269
goto out;
250270
}
251271

@@ -270,6 +290,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int len,
270290
if (!quiet)
271291
ubifs_err(c, "bad CRC: calculated %#08x, read %#08x",
272292
crc, node_crc);
293+
record_crc_error(c->stats);
273294
err = -EUCLEAN;
274295
goto out;
275296
}

fs/ubifs/super.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,10 @@ static int mount_ubifs(struct ubifs_info *c)
12641264
if (err)
12651265
return err;
12661266

1267+
err = ubifs_sysfs_register(c);
1268+
if (err)
1269+
goto out_debugging;
1270+
12671271
err = check_volume_empty(c);
12681272
if (err)
12691273
goto out_free;
@@ -1640,6 +1644,8 @@ static int mount_ubifs(struct ubifs_info *c)
16401644
vfree(c->sbuf);
16411645
kfree(c->bottom_up_buf);
16421646
kfree(c->sup_node);
1647+
ubifs_sysfs_unregister(c);
1648+
out_debugging:
16431649
ubifs_debugging_exit(c);
16441650
return err;
16451651
}
@@ -1683,6 +1689,7 @@ static void ubifs_umount(struct ubifs_info *c)
16831689
kfree(c->bottom_up_buf);
16841690
kfree(c->sup_node);
16851691
ubifs_debugging_exit(c);
1692+
ubifs_sysfs_unregister(c);
16861693
}
16871694

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

24342441
dbg_debugfs_init();
24352442

2443+
err = ubifs_sysfs_init();
2444+
if (err)
2445+
goto out_dbg;
2446+
24362447
err = register_filesystem(&ubifs_fs_type);
24372448
if (err) {
24382449
pr_err("UBIFS error (pid %d): cannot register file system, error %d",
24392450
current->pid, err);
2440-
goto out_dbg;
2451+
goto out_sysfs;
24412452
}
24422453
return 0;
24432454

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

24612474
dbg_debugfs_exit();
2475+
ubifs_sysfs_exit();
24622476
ubifs_compressors_exit();
24632477
unregister_shrinker(&ubifs_shrinker_info);
24642478

fs/ubifs/sysfs.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* This file is part of UBIFS.
4+
*
5+
* Copyright (C) 2021 Cisco Systems
6+
*
7+
* Author: Stefan Schaeckeler
8+
*/
9+
10+
11+
#include <linux/fs.h>
12+
#include "ubifs.h"
13+
14+
enum attr_id_t {
15+
attr_errors_magic,
16+
attr_errors_node,
17+
attr_errors_crc,
18+
};
19+
20+
struct ubifs_attr {
21+
struct attribute attr;
22+
enum attr_id_t attr_id;
23+
};
24+
25+
#define UBIFS_ATTR(_name, _mode, _id) \
26+
static struct ubifs_attr ubifs_attr_##_name = { \
27+
.attr = {.name = __stringify(_name), .mode = _mode }, \
28+
.attr_id = attr_##_id, \
29+
}
30+
31+
#define UBIFS_ATTR_FUNC(_name, _mode) UBIFS_ATTR(_name, _mode, _name)
32+
33+
UBIFS_ATTR_FUNC(errors_magic, 0444);
34+
UBIFS_ATTR_FUNC(errors_crc, 0444);
35+
UBIFS_ATTR_FUNC(errors_node, 0444);
36+
37+
#define ATTR_LIST(name) (&ubifs_attr_##name.attr)
38+
39+
static struct attribute *ubifs_attrs[] = {
40+
ATTR_LIST(errors_magic),
41+
ATTR_LIST(errors_node),
42+
ATTR_LIST(errors_crc),
43+
NULL,
44+
};
45+
46+
static ssize_t ubifs_attr_show(struct kobject *kobj,
47+
struct attribute *attr, char *buf)
48+
{
49+
struct ubifs_info *sbi = container_of(kobj, struct ubifs_info,
50+
kobj);
51+
52+
struct ubifs_attr *a = container_of(attr, struct ubifs_attr, attr);
53+
54+
switch (a->attr_id) {
55+
case attr_errors_magic:
56+
return sysfs_emit(buf, "%u\n", sbi->stats->magic_errors);
57+
case attr_errors_node:
58+
return sysfs_emit(buf, "%u\n", sbi->stats->node_errors);
59+
case attr_errors_crc:
60+
return sysfs_emit(buf, "%u\n", sbi->stats->crc_errors);
61+
}
62+
return 0;
63+
};
64+
65+
static void ubifs_sb_release(struct kobject *kobj)
66+
{
67+
struct ubifs_info *c = container_of(kobj, struct ubifs_info, kobj);
68+
69+
complete(&c->kobj_unregister);
70+
}
71+
72+
static const struct sysfs_ops ubifs_attr_ops = {
73+
.show = ubifs_attr_show,
74+
};
75+
76+
static struct kobj_type ubifs_sb_ktype = {
77+
.default_attrs = ubifs_attrs,
78+
.sysfs_ops = &ubifs_attr_ops,
79+
.release = ubifs_sb_release,
80+
};
81+
82+
static struct kobj_type ubifs_ktype = {
83+
.sysfs_ops = &ubifs_attr_ops,
84+
};
85+
86+
static struct kset ubifs_kset = {
87+
.kobj = {.ktype = &ubifs_ktype},
88+
};
89+
90+
int ubifs_sysfs_register(struct ubifs_info *c)
91+
{
92+
int ret, n;
93+
char dfs_dir_name[UBIFS_DFS_DIR_LEN+1];
94+
95+
c->stats = kzalloc(sizeof(struct ubifs_stats_info), GFP_KERNEL);
96+
if (!c->stats) {
97+
ret = -ENOMEM;
98+
goto out_last;
99+
}
100+
n = snprintf(dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
101+
c->vi.ubi_num, c->vi.vol_id);
102+
103+
if (n == UBIFS_DFS_DIR_LEN) {
104+
/* The array size is too small */
105+
ret = -EINVAL;
106+
goto out_free;
107+
}
108+
109+
c->kobj.kset = &ubifs_kset;
110+
init_completion(&c->kobj_unregister);
111+
112+
ret = kobject_init_and_add(&c->kobj, &ubifs_sb_ktype, NULL,
113+
"%s", dfs_dir_name);
114+
if (ret)
115+
goto out_put;
116+
117+
return 0;
118+
119+
out_put:
120+
kobject_put(&c->kobj);
121+
wait_for_completion(&c->kobj_unregister);
122+
out_free:
123+
kfree(c->stats);
124+
out_last:
125+
ubifs_err(c, "cannot create sysfs entry for ubifs%d_%d, error %d\n",
126+
c->vi.ubi_num, c->vi.vol_id, ret);
127+
return ret;
128+
}
129+
130+
void ubifs_sysfs_unregister(struct ubifs_info *c)
131+
{
132+
kobject_del(&c->kobj);
133+
kobject_put(&c->kobj);
134+
wait_for_completion(&c->kobj_unregister);
135+
136+
kfree(c->stats);
137+
}
138+
139+
int __init ubifs_sysfs_init(void)
140+
{
141+
int ret;
142+
143+
kobject_set_name(&ubifs_kset.kobj, "ubifs");
144+
ubifs_kset.kobj.parent = fs_kobj;
145+
ret = kset_register(&ubifs_kset);
146+
147+
return ret;
148+
}
149+
150+
void ubifs_sysfs_exit(void)
151+
{
152+
kset_unregister(&ubifs_kset);
153+
}

fs/ubifs/ubifs.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <linux/security.h>
2828
#include <linux/xattr.h>
2929
#include <linux/random.h>
30+
#include <linux/sysfs.h>
31+
#include <linux/completion.h>
3032
#include <crypto/hash_info.h>
3133
#include <crypto/hash.h>
3234
#include <crypto/algapi.h>
@@ -155,6 +157,13 @@
155157
#define UBIFS_HMAC_ARR_SZ 0
156158
#endif
157159

160+
/*
161+
* The UBIFS sysfs directory name pattern and maximum name length (3 for "ubi"
162+
* + 1 for "_" and plus 2x2 for 2 UBI numbers and 1 for the trailing zero byte.
163+
*/
164+
#define UBIFS_DFS_DIR_NAME "ubi%d_%d"
165+
#define UBIFS_DFS_DIR_LEN (3 + 1 + 2*2 + 1)
166+
158167
/*
159168
* Lockdep classes for UBIFS inode @ui_mutex.
160169
*/
@@ -990,6 +999,18 @@ struct ubifs_budg_info {
990999
int dent_budget;
9911000
};
9921001

1002+
/**
1003+
* ubifs_stats_info - per-FS statistics information.
1004+
* @magic_errors: number of bad magic numbers (will be reset with a new mount).
1005+
* @node_errors: number of bad nodes (will be reset with a new mount).
1006+
* @crc_errors: number of bad crcs (will be reset with a new mount).
1007+
*/
1008+
struct ubifs_stats_info {
1009+
unsigned int magic_errors;
1010+
unsigned int node_errors;
1011+
unsigned int crc_errors;
1012+
};
1013+
9931014
struct ubifs_debug_info;
9941015

9951016
/**
@@ -1251,6 +1272,10 @@ struct ubifs_debug_info;
12511272
* @mount_opts: UBIFS-specific mount options
12521273
*
12531274
* @dbg: debugging-related information
1275+
* @stats: statistics exported over sysfs
1276+
*
1277+
* @kobj: kobject for /sys/fs/ubifs/
1278+
* @kobj_unregister: completion to unregister sysfs kobject
12541279
*/
12551280
struct ubifs_info {
12561281
struct super_block *vfs_sb;
@@ -1286,6 +1311,9 @@ struct ubifs_info {
12861311
spinlock_t cs_lock;
12871312
wait_queue_head_t cmt_wq;
12881313

1314+
struct kobject kobj;
1315+
struct completion kobj_unregister;
1316+
12891317
unsigned int big_lpt:1;
12901318
unsigned int space_fixup:1;
12911319
unsigned int double_hash:1;
@@ -1493,6 +1521,7 @@ struct ubifs_info {
14931521
struct ubifs_mount_opts mount_opts;
14941522

14951523
struct ubifs_debug_info *dbg;
1524+
struct ubifs_stats_info *stats;
14961525
};
14971526

14981527
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,
20722101
int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len,
20732102
void *out, int *out_len, int compr_type);
20742103

2104+
/* sysfs.c */
2105+
int ubifs_sysfs_init(void);
2106+
void ubifs_sysfs_exit(void);
2107+
int ubifs_sysfs_register(struct ubifs_info *c);
2108+
void ubifs_sysfs_unregister(struct ubifs_info *c);
2109+
20752110
#include "debug.h"
20762111
#include "misc.h"
20772112
#include "key.h"

0 commit comments

Comments
 (0)