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