1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright 2021 Google LLC
4 */
5 #include <linux/fs.h>
6 #include <linux/kobject.h>
7
8 #include <uapi/linux/incrementalfs.h>
9
10 #include "sysfs.h"
11 #include "data_mgmt.h"
12 #include "vfs.h"
13
14 /******************************************************************************
15 * Define sys/fs/incrementalfs & sys/fs/incrementalfs/features
16 *****************************************************************************/
17 #define INCFS_NODE_FEATURES "features"
18 #define INCFS_NODE_INSTANCES "instances"
19
20 static struct kobject *sysfs_root;
21 static struct kobject *features_node;
22 static struct kobject *instances_node;
23
24 #define DECLARE_FEATURE_FLAG(name) \
25 static ssize_t name##_show(struct kobject *kobj, \
26 struct kobj_attribute *attr, char *buff) \
27 { \
28 return sysfs_emit(buff, "supported\n"); \
29 } \
30 \
31 static struct kobj_attribute name##_attr = __ATTR_RO(name)
32
33 DECLARE_FEATURE_FLAG(corefs);
34 DECLARE_FEATURE_FLAG(zstd);
35 DECLARE_FEATURE_FLAG(v2);
36
37 static struct attribute *attributes[] = {
38 &corefs_attr.attr,
39 &zstd_attr.attr,
40 &v2_attr.attr,
41 NULL,
42 };
43
44 static const struct attribute_group attr_group = {
45 .attrs = attributes,
46 };
47
incfs_init_sysfs(void)48 int __init incfs_init_sysfs(void)
49 {
50 int res = -ENOMEM;
51
52 sysfs_root = kobject_create_and_add(INCFS_NAME, fs_kobj);
53 if (!sysfs_root)
54 return -ENOMEM;
55
56 instances_node = kobject_create_and_add(INCFS_NODE_INSTANCES,
57 sysfs_root);
58 if (!instances_node)
59 goto err_put_root;
60
61 features_node = kobject_create_and_add(INCFS_NODE_FEATURES,
62 sysfs_root);
63 if (!features_node)
64 goto err_put_instances;
65
66 res = sysfs_create_group(features_node, &attr_group);
67 if (res)
68 goto err_put_features;
69
70 return 0;
71
72 err_put_features:
73 kobject_put(features_node);
74 err_put_instances:
75 kobject_put(instances_node);
76 err_put_root:
77 kobject_put(sysfs_root);
78
79 return res;
80 }
81
incfs_cleanup_sysfs(void)82 void incfs_cleanup_sysfs(void)
83 {
84 if (features_node) {
85 sysfs_remove_group(features_node, &attr_group);
86 kobject_put(features_node);
87 }
88
89 kobject_put(instances_node);
90 kobject_put(sysfs_root);
91 }
92
93 /******************************************************************************
94 * Define sys/fs/incrementalfs/instances/<name>/
95 *****************************************************************************/
96 #define __DECLARE_STATUS_FLAG(name) \
97 static ssize_t name##_show(struct kobject *kobj, \
98 struct kobj_attribute *attr, char *buff) \
99 { \
100 struct incfs_sysfs_node *node = container_of(kobj, \
101 struct incfs_sysfs_node, isn_sysfs_node); \
102 \
103 return sysfs_emit(buff, "%d\n", node->isn_mi->mi_##name); \
104 } \
105 \
106 static struct kobj_attribute name##_attr = __ATTR_RO(name)
107
108 #define __DECLARE_STATUS_FLAG64(name) \
109 static ssize_t name##_show(struct kobject *kobj, \
110 struct kobj_attribute *attr, char *buff) \
111 { \
112 struct incfs_sysfs_node *node = container_of(kobj, \
113 struct incfs_sysfs_node, isn_sysfs_node); \
114 \
115 return sysfs_emit(buff, "%lld\n", node->isn_mi->mi_##name); \
116 } \
117 \
118 static struct kobj_attribute name##_attr = __ATTR_RO(name)
119
120 __DECLARE_STATUS_FLAG(reads_failed_timed_out);
121 __DECLARE_STATUS_FLAG(reads_failed_hash_verification);
122 __DECLARE_STATUS_FLAG(reads_failed_other);
123 __DECLARE_STATUS_FLAG(reads_delayed_pending);
124 __DECLARE_STATUS_FLAG64(reads_delayed_pending_us);
125 __DECLARE_STATUS_FLAG(reads_delayed_min);
126 __DECLARE_STATUS_FLAG64(reads_delayed_min_us);
127
128 static struct attribute *mount_attributes[] = {
129 &reads_failed_timed_out_attr.attr,
130 &reads_failed_hash_verification_attr.attr,
131 &reads_failed_other_attr.attr,
132 &reads_delayed_pending_attr.attr,
133 &reads_delayed_pending_us_attr.attr,
134 &reads_delayed_min_attr.attr,
135 &reads_delayed_min_us_attr.attr,
136 NULL,
137 };
138
incfs_sysfs_release(struct kobject * kobj)139 static void incfs_sysfs_release(struct kobject *kobj)
140 {
141 struct incfs_sysfs_node *node = container_of(kobj,
142 struct incfs_sysfs_node, isn_sysfs_node);
143
144 complete(&node->isn_completion);
145 }
146
147 static const struct attribute_group mount_attr_group = {
148 .attrs = mount_attributes,
149 };
150
151 static struct kobj_type incfs_kobj_node_ktype = {
152 .sysfs_ops = &kobj_sysfs_ops,
153 .release = &incfs_sysfs_release,
154 };
155
incfs_add_sysfs_node(const char * name,struct mount_info * mi)156 struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name,
157 struct mount_info *mi)
158 {
159 struct incfs_sysfs_node *node = NULL;
160 int error;
161
162 if (!name)
163 return NULL;
164
165 node = kzalloc(sizeof(*node), GFP_NOFS);
166 if (!node)
167 return ERR_PTR(-ENOMEM);
168
169 node->isn_mi = mi;
170
171 init_completion(&node->isn_completion);
172 kobject_init(&node->isn_sysfs_node, &incfs_kobj_node_ktype);
173 error = kobject_add(&node->isn_sysfs_node, instances_node, "%s", name);
174 if (error)
175 goto err;
176
177 error = sysfs_create_group(&node->isn_sysfs_node, &mount_attr_group);
178 if (error)
179 goto err;
180
181 return node;
182
183 err:
184 /*
185 * Note kobject_put always calls release, so incfs_sysfs_release will
186 * free node
187 */
188 kobject_put(&node->isn_sysfs_node);
189 return ERR_PTR(error);
190 }
191
incfs_free_sysfs_node(struct incfs_sysfs_node * node)192 void incfs_free_sysfs_node(struct incfs_sysfs_node *node)
193 {
194 if (!node)
195 return;
196
197 sysfs_remove_group(&node->isn_sysfs_node, &mount_attr_group);
198 kobject_put(&node->isn_sysfs_node);
199 wait_for_completion_interruptible(&node->isn_completion);
200 kfree(node);
201 }
202