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