1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Simple file system for zoned block devices exposing zones as files. |
4 | * |
5 | * Copyright (C) 2022 Western Digital Corporation or its affiliates. |
6 | */ |
7 | #include <linux/fs.h> |
8 | #include <linux/seq_file.h> |
9 | #include <linux/blkdev.h> |
10 | |
11 | #include "zonefs.h" |
12 | |
13 | struct zonefs_sysfs_attr { |
14 | struct attribute attr; |
15 | ssize_t (*show)(struct zonefs_sb_info *sbi, char *buf); |
16 | }; |
17 | |
18 | #define ZONEFS_SYSFS_ATTR_RO(name) \ |
19 | static struct zonefs_sysfs_attr zonefs_sysfs_attr_##name = __ATTR_RO(name) |
20 | |
21 | #define ATTR_LIST(name) &zonefs_sysfs_attr_##name.attr |
22 | |
23 | static ssize_t zonefs_sysfs_attr_show(struct kobject *kobj, |
24 | struct attribute *attr, char *buf) |
25 | { |
26 | struct zonefs_sb_info *sbi = |
27 | container_of(kobj, struct zonefs_sb_info, s_kobj); |
28 | struct zonefs_sysfs_attr *zonefs_attr = |
29 | container_of(attr, struct zonefs_sysfs_attr, attr); |
30 | |
31 | if (!zonefs_attr->show) |
32 | return 0; |
33 | |
34 | return zonefs_attr->show(sbi, buf); |
35 | } |
36 | |
37 | static ssize_t max_wro_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
38 | { |
39 | return sysfs_emit(buf, fmt: "%u\n" , sbi->s_max_wro_seq_files); |
40 | } |
41 | ZONEFS_SYSFS_ATTR_RO(max_wro_seq_files); |
42 | |
43 | static ssize_t nr_wro_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
44 | { |
45 | return sysfs_emit(buf, fmt: "%d\n" , atomic_read(v: &sbi->s_wro_seq_files)); |
46 | } |
47 | ZONEFS_SYSFS_ATTR_RO(nr_wro_seq_files); |
48 | |
49 | static ssize_t max_active_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
50 | { |
51 | return sysfs_emit(buf, fmt: "%u\n" , sbi->s_max_active_seq_files); |
52 | } |
53 | ZONEFS_SYSFS_ATTR_RO(max_active_seq_files); |
54 | |
55 | static ssize_t nr_active_seq_files_show(struct zonefs_sb_info *sbi, char *buf) |
56 | { |
57 | return sysfs_emit(buf, fmt: "%d\n" , atomic_read(v: &sbi->s_active_seq_files)); |
58 | } |
59 | ZONEFS_SYSFS_ATTR_RO(nr_active_seq_files); |
60 | |
61 | static struct attribute *zonefs_sysfs_attrs[] = { |
62 | ATTR_LIST(max_wro_seq_files), |
63 | ATTR_LIST(nr_wro_seq_files), |
64 | ATTR_LIST(max_active_seq_files), |
65 | ATTR_LIST(nr_active_seq_files), |
66 | NULL, |
67 | }; |
68 | ATTRIBUTE_GROUPS(zonefs_sysfs); |
69 | |
70 | static void zonefs_sysfs_sb_release(struct kobject *kobj) |
71 | { |
72 | struct zonefs_sb_info *sbi = |
73 | container_of(kobj, struct zonefs_sb_info, s_kobj); |
74 | |
75 | complete(&sbi->s_kobj_unregister); |
76 | } |
77 | |
78 | static const struct sysfs_ops zonefs_sysfs_attr_ops = { |
79 | .show = zonefs_sysfs_attr_show, |
80 | }; |
81 | |
82 | static const struct kobj_type zonefs_sb_ktype = { |
83 | .default_groups = zonefs_sysfs_groups, |
84 | .sysfs_ops = &zonefs_sysfs_attr_ops, |
85 | .release = zonefs_sysfs_sb_release, |
86 | }; |
87 | |
88 | static struct kobject *zonefs_sysfs_root; |
89 | |
90 | int zonefs_sysfs_register(struct super_block *sb) |
91 | { |
92 | struct zonefs_sb_info *sbi = ZONEFS_SB(sb); |
93 | int ret; |
94 | |
95 | init_completion(x: &sbi->s_kobj_unregister); |
96 | ret = kobject_init_and_add(kobj: &sbi->s_kobj, ktype: &zonefs_sb_ktype, |
97 | parent: zonefs_sysfs_root, fmt: "%s" , sb->s_id); |
98 | if (ret) { |
99 | kobject_put(kobj: &sbi->s_kobj); |
100 | wait_for_completion(&sbi->s_kobj_unregister); |
101 | return ret; |
102 | } |
103 | |
104 | sbi->s_sysfs_registered = true; |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | void zonefs_sysfs_unregister(struct super_block *sb) |
110 | { |
111 | struct zonefs_sb_info *sbi = ZONEFS_SB(sb); |
112 | |
113 | if (!sbi || !sbi->s_sysfs_registered) |
114 | return; |
115 | |
116 | kobject_del(kobj: &sbi->s_kobj); |
117 | kobject_put(kobj: &sbi->s_kobj); |
118 | wait_for_completion(&sbi->s_kobj_unregister); |
119 | } |
120 | |
121 | int __init zonefs_sysfs_init(void) |
122 | { |
123 | zonefs_sysfs_root = kobject_create_and_add(name: "zonefs" , parent: fs_kobj); |
124 | if (!zonefs_sysfs_root) |
125 | return -ENOMEM; |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | void zonefs_sysfs_exit(void) |
131 | { |
132 | kobject_put(kobj: zonefs_sysfs_root); |
133 | zonefs_sysfs_root = NULL; |
134 | } |
135 | |