diff options
author | Namjae Jeon <namjae.jeon@samsung.com> | 2013-08-04 10:09:40 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-08-06 08:53:34 -0400 |
commit | b59d0bae6ca30c496f298881616258f9cde0d9c6 (patch) | |
tree | 04c6a23cfed2ef045a56ca090cbfd510ca3e5e5e /fs/f2fs | |
parent | f0c5e565bb05a4cd6105bb197c56078462252e78 (diff) |
f2fs: add sysfs support for controlling the gc_thread
Add sysfs entries to control the timing parameters for
f2fs gc thread.
Various Sysfs options introduced are:
gc_min_sleep_time: Min Sleep time for GC in ms
gc_max_sleep_time: Max Sleep time for GC in ms
gc_no_gc_sleep_time: Default Sleep time for GC in ms
Cc: Gu Zheng <guz.fnst@cn.fujitsu.com>
Signed-off-by: Namjae Jeon <namjae.jeon@samsung.com>
Signed-off-by: Pankaj Kumar <pankaj.km@samsung.com>
Reviewed-by: Gu Zheng <guz.fnst@cn.fujitsu.com>
[Jaegeuk Kim: fix an umount bug and some minor changes]
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Diffstat (limited to 'fs/f2fs')
-rw-r--r-- | fs/f2fs/f2fs.h | 4 | ||||
-rw-r--r-- | fs/f2fs/gc.c | 17 | ||||
-rw-r--r-- | fs/f2fs/gc.h | 33 | ||||
-rw-r--r-- | fs/f2fs/super.c | 123 |
4 files changed, 157 insertions, 20 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 78777cdb89de..63813befdd82 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -430,6 +430,10 @@ struct f2fs_sb_info { | |||
430 | #endif | 430 | #endif |
431 | unsigned int last_victim[2]; /* last victim segment # */ | 431 | unsigned int last_victim[2]; /* last victim segment # */ |
432 | spinlock_t stat_lock; /* lock for stat operations */ | 432 | spinlock_t stat_lock; /* lock for stat operations */ |
433 | |||
434 | /* For sysfs suppport */ | ||
435 | struct kobject s_kobj; | ||
436 | struct completion s_kobj_unregister; | ||
433 | }; | 437 | }; |
434 | 438 | ||
435 | /* | 439 | /* |
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 35f9b1a196aa..60d4f674efa7 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c | |||
@@ -29,10 +29,11 @@ static struct kmem_cache *winode_slab; | |||
29 | static int gc_thread_func(void *data) | 29 | static int gc_thread_func(void *data) |
30 | { | 30 | { |
31 | struct f2fs_sb_info *sbi = data; | 31 | struct f2fs_sb_info *sbi = data; |
32 | struct f2fs_gc_kthread *gc_th = sbi->gc_thread; | ||
32 | wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; | 33 | wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; |
33 | long wait_ms; | 34 | long wait_ms; |
34 | 35 | ||
35 | wait_ms = GC_THREAD_MIN_SLEEP_TIME; | 36 | wait_ms = gc_th->min_sleep_time; |
36 | 37 | ||
37 | do { | 38 | do { |
38 | if (try_to_freeze()) | 39 | if (try_to_freeze()) |
@@ -45,7 +46,7 @@ static int gc_thread_func(void *data) | |||
45 | break; | 46 | break; |
46 | 47 | ||
47 | if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { | 48 | if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { |
48 | wait_ms = GC_THREAD_MAX_SLEEP_TIME; | 49 | wait_ms = increase_sleep_time(gc_th, wait_ms); |
49 | continue; | 50 | continue; |
50 | } | 51 | } |
51 | 52 | ||
@@ -66,15 +67,15 @@ static int gc_thread_func(void *data) | |||
66 | continue; | 67 | continue; |
67 | 68 | ||
68 | if (!is_idle(sbi)) { | 69 | if (!is_idle(sbi)) { |
69 | wait_ms = increase_sleep_time(wait_ms); | 70 | wait_ms = increase_sleep_time(gc_th, wait_ms); |
70 | mutex_unlock(&sbi->gc_mutex); | 71 | mutex_unlock(&sbi->gc_mutex); |
71 | continue; | 72 | continue; |
72 | } | 73 | } |
73 | 74 | ||
74 | if (has_enough_invalid_blocks(sbi)) | 75 | if (has_enough_invalid_blocks(sbi)) |
75 | wait_ms = decrease_sleep_time(wait_ms); | 76 | wait_ms = decrease_sleep_time(gc_th, wait_ms); |
76 | else | 77 | else |
77 | wait_ms = increase_sleep_time(wait_ms); | 78 | wait_ms = increase_sleep_time(gc_th, wait_ms); |
78 | 79 | ||
79 | #ifdef CONFIG_F2FS_STAT_FS | 80 | #ifdef CONFIG_F2FS_STAT_FS |
80 | sbi->bg_gc++; | 81 | sbi->bg_gc++; |
@@ -82,7 +83,7 @@ static int gc_thread_func(void *data) | |||
82 | 83 | ||
83 | /* if return value is not zero, no victim was selected */ | 84 | /* if return value is not zero, no victim was selected */ |
84 | if (f2fs_gc(sbi)) | 85 | if (f2fs_gc(sbi)) |
85 | wait_ms = GC_THREAD_NOGC_SLEEP_TIME; | 86 | wait_ms = gc_th->no_gc_sleep_time; |
86 | } while (!kthread_should_stop()); | 87 | } while (!kthread_should_stop()); |
87 | return 0; | 88 | return 0; |
88 | } | 89 | } |
@@ -101,6 +102,10 @@ int start_gc_thread(struct f2fs_sb_info *sbi) | |||
101 | goto out; | 102 | goto out; |
102 | } | 103 | } |
103 | 104 | ||
105 | gc_th->min_sleep_time = DEF_GC_THREAD_MIN_SLEEP_TIME; | ||
106 | gc_th->max_sleep_time = DEF_GC_THREAD_MAX_SLEEP_TIME; | ||
107 | gc_th->no_gc_sleep_time = DEF_GC_THREAD_NOGC_SLEEP_TIME; | ||
108 | |||
104 | sbi->gc_thread = gc_th; | 109 | sbi->gc_thread = gc_th; |
105 | init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); | 110 | init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); |
106 | sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, | 111 | sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, |
diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 2c6a6bd08322..f4bf44c9deda 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h | |||
@@ -13,9 +13,9 @@ | |||
13 | * whether IO subsystem is idle | 13 | * whether IO subsystem is idle |
14 | * or not | 14 | * or not |
15 | */ | 15 | */ |
16 | #define GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */ | 16 | #define DEF_GC_THREAD_MIN_SLEEP_TIME 30000 /* milliseconds */ |
17 | #define GC_THREAD_MAX_SLEEP_TIME 60000 | 17 | #define DEF_GC_THREAD_MAX_SLEEP_TIME 60000 |
18 | #define GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */ | 18 | #define DEF_GC_THREAD_NOGC_SLEEP_TIME 300000 /* wait 5 min */ |
19 | #define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ | 19 | #define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ |
20 | #define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ | 20 | #define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ |
21 | 21 | ||
@@ -25,6 +25,11 @@ | |||
25 | struct f2fs_gc_kthread { | 25 | struct f2fs_gc_kthread { |
26 | struct task_struct *f2fs_gc_task; | 26 | struct task_struct *f2fs_gc_task; |
27 | wait_queue_head_t gc_wait_queue_head; | 27 | wait_queue_head_t gc_wait_queue_head; |
28 | |||
29 | /* for gc sleep time */ | ||
30 | unsigned int min_sleep_time; | ||
31 | unsigned int max_sleep_time; | ||
32 | unsigned int no_gc_sleep_time; | ||
28 | }; | 33 | }; |
29 | 34 | ||
30 | struct inode_entry { | 35 | struct inode_entry { |
@@ -56,25 +61,25 @@ static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi) | |||
56 | return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100; | 61 | return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100; |
57 | } | 62 | } |
58 | 63 | ||
59 | static inline long increase_sleep_time(long wait) | 64 | static inline long increase_sleep_time(struct f2fs_gc_kthread *gc_th, long wait) |
60 | { | 65 | { |
61 | if (wait == GC_THREAD_NOGC_SLEEP_TIME) | 66 | if (wait == gc_th->no_gc_sleep_time) |
62 | return wait; | 67 | return wait; |
63 | 68 | ||
64 | wait += GC_THREAD_MIN_SLEEP_TIME; | 69 | wait += gc_th->min_sleep_time; |
65 | if (wait > GC_THREAD_MAX_SLEEP_TIME) | 70 | if (wait > gc_th->max_sleep_time) |
66 | wait = GC_THREAD_MAX_SLEEP_TIME; | 71 | wait = gc_th->max_sleep_time; |
67 | return wait; | 72 | return wait; |
68 | } | 73 | } |
69 | 74 | ||
70 | static inline long decrease_sleep_time(long wait) | 75 | static inline long decrease_sleep_time(struct f2fs_gc_kthread *gc_th, long wait) |
71 | { | 76 | { |
72 | if (wait == GC_THREAD_NOGC_SLEEP_TIME) | 77 | if (wait == gc_th->no_gc_sleep_time) |
73 | wait = GC_THREAD_MAX_SLEEP_TIME; | 78 | wait = gc_th->max_sleep_time; |
74 | 79 | ||
75 | wait -= GC_THREAD_MIN_SLEEP_TIME; | 80 | wait -= gc_th->min_sleep_time; |
76 | if (wait <= GC_THREAD_MIN_SLEEP_TIME) | 81 | if (wait <= gc_th->min_sleep_time) |
77 | wait = GC_THREAD_MIN_SLEEP_TIME; | 82 | wait = gc_th->min_sleep_time; |
78 | return wait; | 83 | return wait; |
79 | } | 84 | } |
80 | 85 | ||
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) |