diff options
Diffstat (limited to 'fs/f2fs/super.c')
-rw-r--r-- | fs/f2fs/super.c | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 70dbb313a7ca..e161a24fbf39 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
@@ -23,17 +23,21 @@ | |||
23 | #include <linux/exportfs.h> | 23 | #include <linux/exportfs.h> |
24 | #include <linux/blkdev.h> | 24 | #include <linux/blkdev.h> |
25 | #include <linux/f2fs_fs.h> | 25 | #include <linux/f2fs_fs.h> |
26 | #include <linux/kobject.h> | ||
27 | #include <linux/sysfs.h> | ||
26 | 28 | ||
27 | #include "f2fs.h" | 29 | #include "f2fs.h" |
28 | #include "node.h" | 30 | #include "node.h" |
29 | #include "segment.h" | 31 | #include "segment.h" |
30 | #include "xattr.h" | 32 | #include "xattr.h" |
33 | #include "gc.h" | ||
31 | 34 | ||
32 | #define CREATE_TRACE_POINTS | 35 | #define CREATE_TRACE_POINTS |
33 | #include <trace/events/f2fs.h> | 36 | #include <trace/events/f2fs.h> |
34 | 37 | ||
35 | static struct proc_dir_entry *f2fs_proc_root; | 38 | static struct proc_dir_entry *f2fs_proc_root; |
36 | static struct kmem_cache *f2fs_inode_cachep; | 39 | static struct kmem_cache *f2fs_inode_cachep; |
40 | static struct kset *f2fs_kset; | ||
37 | 41 | ||
38 | enum { | 42 | enum { |
39 | Opt_gc_background, | 43 | Opt_gc_background, |
@@ -59,6 +63,111 @@ static match_table_t f2fs_tokens = { | |||
59 | {Opt_err, NULL}, | 63 | {Opt_err, NULL}, |
60 | }; | 64 | }; |
61 | 65 | ||
66 | /* Sysfs support for f2fs */ | ||
67 | struct f2fs_attr { | ||
68 | struct attribute attr; | ||
69 | ssize_t (*show)(struct f2fs_attr *, struct f2fs_sb_info *, char *); | ||
70 | ssize_t (*store)(struct f2fs_attr *, struct f2fs_sb_info *, | ||
71 | const char *, size_t); | ||
72 | int offset; | ||
73 | }; | ||
74 | |||
75 | static ssize_t f2fs_sbi_show(struct f2fs_attr *a, | ||
76 | struct f2fs_sb_info *sbi, char *buf) | ||
77 | { | ||
78 | struct f2fs_gc_kthread *gc_kth = sbi->gc_thread; | ||
79 | unsigned int *ui; | ||
80 | |||
81 | if (!gc_kth) | ||
82 | return -EINVAL; | ||
83 | |||
84 | ui = (unsigned int *)(((char *)gc_kth) + a->offset); | ||
85 | |||
86 | return snprintf(buf, PAGE_SIZE, "%u\n", *ui); | ||
87 | } | ||
88 | |||
89 | static ssize_t f2fs_sbi_store(struct f2fs_attr *a, | ||
90 | struct f2fs_sb_info *sbi, | ||
91 | const char *buf, size_t count) | ||
92 | { | ||
93 | struct f2fs_gc_kthread *gc_kth = sbi->gc_thread; | ||
94 | unsigned long t; | ||
95 | unsigned int *ui; | ||
96 | ssize_t ret; | ||
97 | |||
98 | if (!gc_kth) | ||
99 | return -EINVAL; | ||
100 | |||
101 | ui = (unsigned int *)(((char *)gc_kth) + a->offset); | ||
102 | |||
103 | ret = kstrtoul(skip_spaces(buf), 0, &t); | ||
104 | if (ret < 0) | ||
105 | return ret; | ||
106 | *ui = t; | ||
107 | return count; | ||
108 | } | ||
109 | |||
110 | static ssize_t f2fs_attr_show(struct kobject *kobj, | ||
111 | struct attribute *attr, char *buf) | ||
112 | { | ||
113 | struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, | ||
114 | s_kobj); | ||
115 | struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); | ||
116 | |||
117 | return a->show ? a->show(a, sbi, buf) : 0; | ||
118 | } | ||
119 | |||
120 | static ssize_t f2fs_attr_store(struct kobject *kobj, struct attribute *attr, | ||
121 | const char *buf, size_t len) | ||
122 | { | ||
123 | struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, | ||
124 | s_kobj); | ||
125 | struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr); | ||
126 | |||
127 | return a->store ? a->store(a, sbi, buf, len) : 0; | ||
128 | } | ||
129 | |||
130 | static void f2fs_sb_release(struct kobject *kobj) | ||
131 | { | ||
132 | struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info, | ||
133 | s_kobj); | ||
134 | complete(&sbi->s_kobj_unregister); | ||
135 | } | ||
136 | |||
137 | #define F2FS_ATTR_OFFSET(_name, _mode, _show, _store, _elname) \ | ||
138 | static struct f2fs_attr f2fs_attr_##_name = { \ | ||
139 | .attr = {.name = __stringify(_name), .mode = _mode }, \ | ||
140 | .show = _show, \ | ||
141 | .store = _store, \ | ||
142 | .offset = offsetof(struct f2fs_gc_kthread, _elname), \ | ||
143 | } | ||
144 | |||
145 | #define F2FS_RW_ATTR(name, elname) \ | ||
146 | F2FS_ATTR_OFFSET(name, 0644, f2fs_sbi_show, f2fs_sbi_store, elname) | ||
147 | |||
148 | F2FS_RW_ATTR(gc_min_sleep_time, min_sleep_time); | ||
149 | F2FS_RW_ATTR(gc_max_sleep_time, max_sleep_time); | ||
150 | F2FS_RW_ATTR(gc_no_gc_sleep_time, no_gc_sleep_time); | ||
151 | |||
152 | #define ATTR_LIST(name) (&f2fs_attr_##name.attr) | ||
153 | static struct attribute *f2fs_attrs[] = { | ||
154 | ATTR_LIST(gc_min_sleep_time), | ||
155 | ATTR_LIST(gc_max_sleep_time), | ||
156 | ATTR_LIST(gc_no_gc_sleep_time), | ||
157 | NULL, | ||
158 | }; | ||
159 | |||
160 | static const struct sysfs_ops f2fs_attr_ops = { | ||
161 | .show = f2fs_attr_show, | ||
162 | .store = f2fs_attr_store, | ||
163 | }; | ||
164 | |||
165 | static struct kobj_type f2fs_ktype = { | ||
166 | .default_attrs = f2fs_attrs, | ||
167 | .sysfs_ops = &f2fs_attr_ops, | ||
168 | .release = f2fs_sb_release, | ||
169 | }; | ||
170 | |||
62 | void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) | 171 | void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...) |
63 | { | 172 | { |
64 | struct va_format vaf; | 173 | struct va_format vaf; |
@@ -229,6 +338,7 @@ static void f2fs_put_super(struct super_block *sb) | |||
229 | remove_proc_entry("segment_info", sbi->s_proc); | 338 | remove_proc_entry("segment_info", sbi->s_proc); |
230 | remove_proc_entry(sb->s_id, f2fs_proc_root); | 339 | remove_proc_entry(sb->s_id, f2fs_proc_root); |
231 | } | 340 | } |
341 | kobject_del(&sbi->s_kobj); | ||
232 | 342 | ||
233 | f2fs_destroy_stats(sbi); | 343 | f2fs_destroy_stats(sbi); |
234 | stop_gc_thread(sbi); | 344 | stop_gc_thread(sbi); |
@@ -243,6 +353,8 @@ static void f2fs_put_super(struct super_block *sb) | |||
243 | destroy_segment_manager(sbi); | 353 | destroy_segment_manager(sbi); |
244 | 354 | ||
245 | kfree(sbi->ckpt); | 355 | kfree(sbi->ckpt); |
356 | kobject_put(&sbi->s_kobj); | ||
357 | wait_for_completion(&sbi->s_kobj_unregister); | ||
246 | 358 | ||
247 | sb->s_fs_info = NULL; | 359 | sb->s_fs_info = NULL; |
248 | brelse(sbi->raw_super_buf); | 360 | brelse(sbi->raw_super_buf); |
@@ -818,6 +930,13 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) | |||
818 | "the device does not support discard"); | 930 | "the device does not support discard"); |
819 | } | 931 | } |
820 | 932 | ||
933 | sbi->s_kobj.kset = f2fs_kset; | ||
934 | init_completion(&sbi->s_kobj_unregister); | ||
935 | err = kobject_init_and_add(&sbi->s_kobj, &f2fs_ktype, NULL, | ||
936 | "%s", sb->s_id); | ||
937 | if (err) | ||
938 | goto fail; | ||
939 | |||
821 | return 0; | 940 | return 0; |
822 | fail: | 941 | fail: |
823 | stop_gc_thread(sbi); | 942 | stop_gc_thread(sbi); |
@@ -892,6 +1011,9 @@ static int __init init_f2fs_fs(void) | |||
892 | err = create_checkpoint_caches(); | 1011 | err = create_checkpoint_caches(); |
893 | if (err) | 1012 | if (err) |
894 | goto fail; | 1013 | goto fail; |
1014 | f2fs_kset = kset_create_and_add("f2fs", NULL, fs_kobj); | ||
1015 | if (!f2fs_kset) | ||
1016 | goto fail; | ||
895 | err = register_filesystem(&f2fs_fs_type); | 1017 | err = register_filesystem(&f2fs_fs_type); |
896 | if (err) | 1018 | if (err) |
897 | goto fail; | 1019 | goto fail; |
@@ -910,6 +1032,7 @@ static void __exit exit_f2fs_fs(void) | |||
910 | destroy_gc_caches(); | 1032 | destroy_gc_caches(); |
911 | destroy_node_manager_caches(); | 1033 | destroy_node_manager_caches(); |
912 | destroy_inodecache(); | 1034 | destroy_inodecache(); |
1035 | kset_unregister(f2fs_kset); | ||
913 | } | 1036 | } |
914 | 1037 | ||
915 | module_init(init_f2fs_fs) | 1038 | module_init(init_f2fs_fs) |