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 | ATTRIBUTE_GROUPS(ubifs); |
46 | |
47 | static ssize_t ubifs_attr_show(struct kobject *kobj, |
48 | struct attribute *attr, char *buf) |
49 | { |
50 | struct ubifs_info *sbi = container_of(kobj, struct ubifs_info, |
51 | kobj); |
52 | |
53 | struct ubifs_attr *a = container_of(attr, struct ubifs_attr, attr); |
54 | |
55 | switch (a->attr_id) { |
56 | case attr_errors_magic: |
57 | return sysfs_emit(buf, fmt: "%u\n" , sbi->stats->magic_errors); |
58 | case attr_errors_node: |
59 | return sysfs_emit(buf, fmt: "%u\n" , sbi->stats->node_errors); |
60 | case attr_errors_crc: |
61 | return sysfs_emit(buf, fmt: "%u\n" , sbi->stats->crc_errors); |
62 | } |
63 | return 0; |
64 | }; |
65 | |
66 | static void ubifs_sb_release(struct kobject *kobj) |
67 | { |
68 | struct ubifs_info *c = container_of(kobj, struct ubifs_info, kobj); |
69 | |
70 | complete(&c->kobj_unregister); |
71 | } |
72 | |
73 | static const struct sysfs_ops ubifs_attr_ops = { |
74 | .show = ubifs_attr_show, |
75 | }; |
76 | |
77 | static const struct kobj_type ubifs_sb_ktype = { |
78 | .default_groups = ubifs_groups, |
79 | .sysfs_ops = &ubifs_attr_ops, |
80 | .release = ubifs_sb_release, |
81 | }; |
82 | |
83 | static const struct kobj_type ubifs_ktype = { |
84 | .sysfs_ops = &ubifs_attr_ops, |
85 | }; |
86 | |
87 | static struct kset ubifs_kset = { |
88 | .kobj = {.ktype = &ubifs_ktype}, |
89 | }; |
90 | |
91 | int ubifs_sysfs_register(struct ubifs_info *c) |
92 | { |
93 | int ret, n; |
94 | char dfs_dir_name[UBIFS_DFS_DIR_LEN+1]; |
95 | |
96 | c->stats = kzalloc(size: sizeof(struct ubifs_stats_info), GFP_KERNEL); |
97 | if (!c->stats) { |
98 | ret = -ENOMEM; |
99 | goto out_last; |
100 | } |
101 | n = snprintf(buf: dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME, |
102 | c->vi.ubi_num, c->vi.vol_id); |
103 | |
104 | if (n > UBIFS_DFS_DIR_LEN) { |
105 | /* The array size is too small */ |
106 | ret = -EINVAL; |
107 | goto out_free; |
108 | } |
109 | |
110 | c->kobj.kset = &ubifs_kset; |
111 | init_completion(x: &c->kobj_unregister); |
112 | |
113 | ret = kobject_init_and_add(kobj: &c->kobj, ktype: &ubifs_sb_ktype, NULL, |
114 | fmt: "%s" , dfs_dir_name); |
115 | if (ret) |
116 | goto out_put; |
117 | |
118 | return 0; |
119 | |
120 | out_put: |
121 | kobject_put(kobj: &c->kobj); |
122 | wait_for_completion(&c->kobj_unregister); |
123 | out_free: |
124 | kfree(objp: c->stats); |
125 | out_last: |
126 | ubifs_err(c, fmt: "cannot create sysfs entry for ubifs%d_%d, error %d\n" , |
127 | c->vi.ubi_num, c->vi.vol_id, ret); |
128 | return ret; |
129 | } |
130 | |
131 | void ubifs_sysfs_unregister(struct ubifs_info *c) |
132 | { |
133 | kobject_del(kobj: &c->kobj); |
134 | kobject_put(kobj: &c->kobj); |
135 | wait_for_completion(&c->kobj_unregister); |
136 | |
137 | kfree(objp: c->stats); |
138 | } |
139 | |
140 | int __init ubifs_sysfs_init(void) |
141 | { |
142 | int ret; |
143 | |
144 | kobject_set_name(kobj: &ubifs_kset.kobj, name: "ubifs" ); |
145 | ubifs_kset.kobj.parent = fs_kobj; |
146 | ret = kset_register(kset: &ubifs_kset); |
147 | if (ret) |
148 | kset_put(k: &ubifs_kset); |
149 | |
150 | return ret; |
151 | } |
152 | |
153 | void ubifs_sysfs_exit(void) |
154 | { |
155 | kset_unregister(kset: &ubifs_kset); |
156 | } |
157 | |