diff options
-rw-r--r-- | Documentation/filesystems/f2fs.txt | 4 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 16 | ||||
-rw-r--r-- | fs/f2fs/file.c | 2 | ||||
-rw-r--r-- | fs/f2fs/segment.c | 89 | ||||
-rw-r--r-- | fs/f2fs/super.c | 7 |
5 files changed, 117 insertions, 1 deletions
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index 2f6d0218dd22..25311e113e75 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt | |||
@@ -122,6 +122,10 @@ disable_ext_identify Disable the extension list configured by mkfs, so f2fs | |||
122 | inline_xattr Enable the inline xattrs feature. | 122 | inline_xattr Enable the inline xattrs feature. |
123 | inline_data Enable the inline data feature: New created small(<~3.4k) | 123 | inline_data Enable the inline data feature: New created small(<~3.4k) |
124 | files can be written into inode block. | 124 | files can be written into inode block. |
125 | flush_merge Merge concurrent cache_flush commands as much as possible | ||
126 | to eliminate redundant command issues. If the underlying | ||
127 | device handles the cache_flush command relatively slowly, | ||
128 | recommend to enable this option. | ||
125 | 129 | ||
126 | ================================================================================ | 130 | ================================================================================ |
127 | DEBUGFS ENTRIES | 131 | DEBUGFS ENTRIES |
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1e3d869b60cd..2ecac8312359 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -40,6 +40,7 @@ | |||
40 | #define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 | 40 | #define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 |
41 | #define F2FS_MOUNT_INLINE_XATTR 0x00000080 | 41 | #define F2FS_MOUNT_INLINE_XATTR 0x00000080 |
42 | #define F2FS_MOUNT_INLINE_DATA 0x00000100 | 42 | #define F2FS_MOUNT_INLINE_DATA 0x00000100 |
43 | #define F2FS_MOUNT_FLUSH_MERGE 0x00000200 | ||
43 | 44 | ||
44 | #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) | 45 | #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) |
45 | #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) | 46 | #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) |
@@ -316,6 +317,12 @@ enum { | |||
316 | NO_CHECK_TYPE | 317 | NO_CHECK_TYPE |
317 | }; | 318 | }; |
318 | 319 | ||
320 | struct flush_cmd { | ||
321 | struct flush_cmd *next; | ||
322 | struct completion wait; | ||
323 | int ret; | ||
324 | }; | ||
325 | |||
319 | struct f2fs_sm_info { | 326 | struct f2fs_sm_info { |
320 | struct sit_info *sit_info; /* whole segment information */ | 327 | struct sit_info *sit_info; /* whole segment information */ |
321 | struct free_segmap_info *free_info; /* free segment information */ | 328 | struct free_segmap_info *free_info; /* free segment information */ |
@@ -344,6 +351,14 @@ struct f2fs_sm_info { | |||
344 | 351 | ||
345 | unsigned int ipu_policy; /* in-place-update policy */ | 352 | unsigned int ipu_policy; /* in-place-update policy */ |
346 | unsigned int min_ipu_util; /* in-place-update threshold */ | 353 | unsigned int min_ipu_util; /* in-place-update threshold */ |
354 | |||
355 | /* for flush command control */ | ||
356 | struct task_struct *f2fs_issue_flush; /* flush thread */ | ||
357 | wait_queue_head_t flush_wait_queue; /* waiting queue for wake-up */ | ||
358 | struct flush_cmd *issue_list; /* list for command issue */ | ||
359 | struct flush_cmd *dispatch_list; /* list for command dispatch */ | ||
360 | spinlock_t issue_lock; /* for issue list lock */ | ||
361 | struct flush_cmd *issue_tail; /* list tail of issue list */ | ||
347 | }; | 362 | }; |
348 | 363 | ||
349 | /* | 364 | /* |
@@ -1160,6 +1175,7 @@ void destroy_node_manager_caches(void); | |||
1160 | */ | 1175 | */ |
1161 | void f2fs_balance_fs(struct f2fs_sb_info *); | 1176 | void f2fs_balance_fs(struct f2fs_sb_info *); |
1162 | void f2fs_balance_fs_bg(struct f2fs_sb_info *); | 1177 | void f2fs_balance_fs_bg(struct f2fs_sb_info *); |
1178 | int f2fs_issue_flush(struct f2fs_sb_info *); | ||
1163 | void invalidate_blocks(struct f2fs_sb_info *, block_t); | 1179 | void invalidate_blocks(struct f2fs_sb_info *, block_t); |
1164 | void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); | 1180 | void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); |
1165 | void clear_prefree_segments(struct f2fs_sb_info *); | 1181 | void clear_prefree_segments(struct f2fs_sb_info *); |
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6ba26680c468..302d552afea5 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c | |||
@@ -186,7 +186,7 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) | |||
186 | ret = wait_on_node_pages_writeback(sbi, inode->i_ino); | 186 | ret = wait_on_node_pages_writeback(sbi, inode->i_ino); |
187 | if (ret) | 187 | if (ret) |
188 | goto out; | 188 | goto out; |
189 | ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); | 189 | ret = f2fs_issue_flush(F2FS_SB(inode->i_sb)); |
190 | } | 190 | } |
191 | out: | 191 | out: |
192 | trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); | 192 | trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); |
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index f799c6a34c39..085f548be7a3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/bio.h> | 13 | #include <linux/bio.h> |
14 | #include <linux/blkdev.h> | 14 | #include <linux/blkdev.h> |
15 | #include <linux/prefetch.h> | 15 | #include <linux/prefetch.h> |
16 | #include <linux/kthread.h> | ||
16 | #include <linux/vmalloc.h> | 17 | #include <linux/vmalloc.h> |
17 | #include <linux/swap.h> | 18 | #include <linux/swap.h> |
18 | 19 | ||
@@ -24,6 +25,7 @@ | |||
24 | #define __reverse_ffz(x) __reverse_ffs(~(x)) | 25 | #define __reverse_ffz(x) __reverse_ffs(~(x)) |
25 | 26 | ||
26 | static struct kmem_cache *discard_entry_slab; | 27 | static struct kmem_cache *discard_entry_slab; |
28 | static struct kmem_cache *flush_cmd_slab; | ||
27 | 29 | ||
28 | /* | 30 | /* |
29 | * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since | 31 | * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since |
@@ -195,6 +197,73 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) | |||
195 | f2fs_sync_fs(sbi->sb, true); | 197 | f2fs_sync_fs(sbi->sb, true); |
196 | } | 198 | } |
197 | 199 | ||
200 | static int issue_flush_thread(void *data) | ||
201 | { | ||
202 | struct f2fs_sb_info *sbi = data; | ||
203 | struct f2fs_sm_info *sm_i = SM_I(sbi); | ||
204 | wait_queue_head_t *q = &sm_i->flush_wait_queue; | ||
205 | repeat: | ||
206 | if (kthread_should_stop()) | ||
207 | return 0; | ||
208 | |||
209 | spin_lock(&sm_i->issue_lock); | ||
210 | if (sm_i->issue_list) { | ||
211 | sm_i->dispatch_list = sm_i->issue_list; | ||
212 | sm_i->issue_list = sm_i->issue_tail = NULL; | ||
213 | } | ||
214 | spin_unlock(&sm_i->issue_lock); | ||
215 | |||
216 | if (sm_i->dispatch_list) { | ||
217 | struct bio *bio = bio_alloc(GFP_NOIO, 0); | ||
218 | struct flush_cmd *cmd, *next; | ||
219 | int ret; | ||
220 | |||
221 | bio->bi_bdev = sbi->sb->s_bdev; | ||
222 | ret = submit_bio_wait(WRITE_FLUSH, bio); | ||
223 | |||
224 | for (cmd = sm_i->dispatch_list; cmd; cmd = next) { | ||
225 | cmd->ret = ret; | ||
226 | next = cmd->next; | ||
227 | complete(&cmd->wait); | ||
228 | } | ||
229 | sm_i->dispatch_list = NULL; | ||
230 | } | ||
231 | |||
232 | wait_event_interruptible(*q, kthread_should_stop() || sm_i->issue_list); | ||
233 | goto repeat; | ||
234 | } | ||
235 | |||
236 | int f2fs_issue_flush(struct f2fs_sb_info *sbi) | ||
237 | { | ||
238 | struct f2fs_sm_info *sm_i = SM_I(sbi); | ||
239 | struct flush_cmd *cmd; | ||
240 | int ret; | ||
241 | |||
242 | if (!test_opt(sbi, FLUSH_MERGE)) | ||
243 | return blkdev_issue_flush(sbi->sb->s_bdev, GFP_KERNEL, NULL); | ||
244 | |||
245 | cmd = f2fs_kmem_cache_alloc(flush_cmd_slab, GFP_ATOMIC); | ||
246 | cmd->next = NULL; | ||
247 | cmd->ret = 0; | ||
248 | init_completion(&cmd->wait); | ||
249 | |||
250 | spin_lock(&sm_i->issue_lock); | ||
251 | if (sm_i->issue_list) | ||
252 | sm_i->issue_tail->next = cmd; | ||
253 | else | ||
254 | sm_i->issue_list = cmd; | ||
255 | sm_i->issue_tail = cmd; | ||
256 | spin_unlock(&sm_i->issue_lock); | ||
257 | |||
258 | if (!sm_i->dispatch_list) | ||
259 | wake_up(&sm_i->flush_wait_queue); | ||
260 | |||
261 | wait_for_completion(&cmd->wait); | ||
262 | ret = cmd->ret; | ||
263 | kmem_cache_free(flush_cmd_slab, cmd); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
198 | static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, | 267 | static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, |
199 | enum dirty_type dirty_type) | 268 | enum dirty_type dirty_type) |
200 | { | 269 | { |
@@ -1763,6 +1832,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi) | |||
1763 | { | 1832 | { |
1764 | struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); | 1833 | struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); |
1765 | struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); | 1834 | struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); |
1835 | dev_t dev = sbi->sb->s_bdev->bd_dev; | ||
1766 | struct f2fs_sm_info *sm_info; | 1836 | struct f2fs_sm_info *sm_info; |
1767 | int err; | 1837 | int err; |
1768 | 1838 | ||
@@ -1790,6 +1860,16 @@ int build_segment_manager(struct f2fs_sb_info *sbi) | |||
1790 | sm_info->nr_discards = 0; | 1860 | sm_info->nr_discards = 0; |
1791 | sm_info->max_discards = 0; | 1861 | sm_info->max_discards = 0; |
1792 | 1862 | ||
1863 | if (test_opt(sbi, FLUSH_MERGE)) { | ||
1864 | spin_lock_init(&sm_info->issue_lock); | ||
1865 | init_waitqueue_head(&sm_info->flush_wait_queue); | ||
1866 | |||
1867 | sm_info->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, | ||
1868 | "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); | ||
1869 | if (IS_ERR(sm_info->f2fs_issue_flush)) | ||
1870 | return PTR_ERR(sm_info->f2fs_issue_flush); | ||
1871 | } | ||
1872 | |||
1793 | err = build_sit_info(sbi); | 1873 | err = build_sit_info(sbi); |
1794 | if (err) | 1874 | if (err) |
1795 | return err; | 1875 | return err; |
@@ -1898,6 +1978,8 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi) | |||
1898 | struct f2fs_sm_info *sm_info = SM_I(sbi); | 1978 | struct f2fs_sm_info *sm_info = SM_I(sbi); |
1899 | if (!sm_info) | 1979 | if (!sm_info) |
1900 | return; | 1980 | return; |
1981 | if (sm_info->f2fs_issue_flush) | ||
1982 | kthread_stop(sm_info->f2fs_issue_flush); | ||
1901 | destroy_dirty_segmap(sbi); | 1983 | destroy_dirty_segmap(sbi); |
1902 | destroy_curseg(sbi); | 1984 | destroy_curseg(sbi); |
1903 | destroy_free_segmap(sbi); | 1985 | destroy_free_segmap(sbi); |
@@ -1912,10 +1994,17 @@ int __init create_segment_manager_caches(void) | |||
1912 | sizeof(struct discard_entry)); | 1994 | sizeof(struct discard_entry)); |
1913 | if (!discard_entry_slab) | 1995 | if (!discard_entry_slab) |
1914 | return -ENOMEM; | 1996 | return -ENOMEM; |
1997 | flush_cmd_slab = f2fs_kmem_cache_create("flush_command", | ||
1998 | sizeof(struct flush_cmd)); | ||
1999 | if (!flush_cmd_slab) { | ||
2000 | kmem_cache_destroy(discard_entry_slab); | ||
2001 | return -ENOMEM; | ||
2002 | } | ||
1915 | return 0; | 2003 | return 0; |
1916 | } | 2004 | } |
1917 | 2005 | ||
1918 | void destroy_segment_manager_caches(void) | 2006 | void destroy_segment_manager_caches(void) |
1919 | { | 2007 | { |
1920 | kmem_cache_destroy(discard_entry_slab); | 2008 | kmem_cache_destroy(discard_entry_slab); |
2009 | kmem_cache_destroy(flush_cmd_slab); | ||
1921 | } | 2010 | } |
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 959834066d60..d31b767fde73 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
@@ -51,6 +51,7 @@ enum { | |||
51 | Opt_disable_ext_identify, | 51 | Opt_disable_ext_identify, |
52 | Opt_inline_xattr, | 52 | Opt_inline_xattr, |
53 | Opt_inline_data, | 53 | Opt_inline_data, |
54 | Opt_flush_merge, | ||
54 | Opt_err, | 55 | Opt_err, |
55 | }; | 56 | }; |
56 | 57 | ||
@@ -67,6 +68,7 @@ static match_table_t f2fs_tokens = { | |||
67 | {Opt_disable_ext_identify, "disable_ext_identify"}, | 68 | {Opt_disable_ext_identify, "disable_ext_identify"}, |
68 | {Opt_inline_xattr, "inline_xattr"}, | 69 | {Opt_inline_xattr, "inline_xattr"}, |
69 | {Opt_inline_data, "inline_data"}, | 70 | {Opt_inline_data, "inline_data"}, |
71 | {Opt_flush_merge, "flush_merge"}, | ||
70 | {Opt_err, NULL}, | 72 | {Opt_err, NULL}, |
71 | }; | 73 | }; |
72 | 74 | ||
@@ -334,6 +336,9 @@ static int parse_options(struct super_block *sb, char *options) | |||
334 | case Opt_inline_data: | 336 | case Opt_inline_data: |
335 | set_opt(sbi, INLINE_DATA); | 337 | set_opt(sbi, INLINE_DATA); |
336 | break; | 338 | break; |
339 | case Opt_flush_merge: | ||
340 | set_opt(sbi, FLUSH_MERGE); | ||
341 | break; | ||
337 | default: | 342 | default: |
338 | f2fs_msg(sb, KERN_ERR, | 343 | f2fs_msg(sb, KERN_ERR, |
339 | "Unrecognized mount option \"%s\" or missing value", | 344 | "Unrecognized mount option \"%s\" or missing value", |
@@ -537,6 +542,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) | |||
537 | seq_puts(seq, ",disable_ext_identify"); | 542 | seq_puts(seq, ",disable_ext_identify"); |
538 | if (test_opt(sbi, INLINE_DATA)) | 543 | if (test_opt(sbi, INLINE_DATA)) |
539 | seq_puts(seq, ",inline_data"); | 544 | seq_puts(seq, ",inline_data"); |
545 | if (test_opt(sbi, FLUSH_MERGE)) | ||
546 | seq_puts(seq, ",flush_merge"); | ||
540 | seq_printf(seq, ",active_logs=%u", sbi->active_logs); | 547 | seq_printf(seq, ",active_logs=%u", sbi->active_logs); |
541 | 548 | ||
542 | return 0; | 549 | return 0; |