From 91942321e4c9f8460f260cdfcf0a7a48a73a84a4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 20 May 2016 10:13:22 -0700 Subject: f2fs: use inode pointer for {set, clear}_inode_flag This patch refactors to use inode pointer for set_inode_flag and clear_inode_flag. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2e6f537a0e7d..77dc929715cf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -241,7 +241,7 @@ void drop_inmem_pages(struct inode *inode) { struct f2fs_inode_info *fi = F2FS_I(inode); - clear_inode_flag(F2FS_I(inode), FI_ATOMIC_FILE); + clear_inode_flag(inode, FI_ATOMIC_FILE); mutex_lock(&fi->inmem_lock); __revoke_inmem_pages(inode, &fi->inmem_pages, true, false); -- cgit v1.2.3 From 0a87f664d1ad29cc4712303d2142fe386368f07d Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 23 May 2016 12:04:56 -0700 Subject: f2fs: detect congestion of flush command issues If flush commands do not incur any congestion, we don't need to throw that to dispatching queue which causes unnecessary latency. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bf1c8b0ef6c7..f02a3572fff3 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -603,6 +603,7 @@ struct flush_cmd { struct flush_cmd_control { struct task_struct *f2fs_issue_flush; /* flush thread */ wait_queue_head_t flush_wait_queue; /* waiting queue for wake-up */ + atomic_t submit_flush; /* # of issued flushes */ struct llist_head issue_list; /* list for command issue */ struct llist_node *dispatch_list; /* list for command dispatch */ }; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 77dc929715cf..34a9159cf5ac 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -433,24 +433,28 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) if (test_opt(sbi, NOBARRIER)) return 0; - if (!test_opt(sbi, FLUSH_MERGE)) { + if (!test_opt(sbi, FLUSH_MERGE) || !atomic_read(&fcc->submit_flush)) { struct bio *bio = f2fs_bio_alloc(0); int ret; + atomic_inc(&fcc->submit_flush); bio->bi_bdev = sbi->sb->s_bdev; ret = submit_bio_wait(WRITE_FLUSH, bio); + atomic_dec(&fcc->submit_flush); bio_put(bio); return ret; } init_completion(&cmd.wait); + atomic_inc(&fcc->submit_flush); llist_add(&cmd.llnode, &fcc->issue_list); if (!fcc->dispatch_list) wake_up(&fcc->flush_wait_queue); wait_for_completion(&cmd.wait); + atomic_dec(&fcc->submit_flush); return cmd.ret; } @@ -464,6 +468,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); if (!fcc) return -ENOMEM; + atomic_set(&fcc->submit_flush, 0); init_waitqueue_head(&fcc->flush_wait_queue); init_llist_head(&fcc->issue_list); SM_I(sbi)->cmd_control_info = fcc; -- cgit v1.2.3 From e589c2c477b44e06754508a4e8b883e5ae7294aa Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 2 Jun 2016 15:24:24 -0700 Subject: f2fs: control not to exceed # of cached nat entries This is to avoid cache entry management overhead including radix tree. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++++ fs/f2fs/node.h | 7 +++++++ fs/f2fs/segment.c | 5 +++++ 3 files changed, 16 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 16532b31dcd6..b448c8fec7fc 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -52,6 +52,10 @@ bool available_free_memory(struct f2fs_sb_info *sbi, int type) mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> PAGE_SHIFT; res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2); + if (excess_cached_nats(sbi)) + res = false; + if (nm_i->nat_cnt > DEF_NAT_CACHE_THRESHOLD) + res = false; } else if (type == DIRTY_DENTS) { if (sbi->sb->s_bdi->wb.dirty_exceeded) return false; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 2c2a797e18a8..673ce926cf09 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -27,6 +27,8 @@ /* control dirty nats ratio threshold (default: 10% over max nid count) */ #define DEF_DIRTY_NAT_RATIO_THRESHOLD 10 +/* control total # of nats */ +#define DEF_NAT_CACHE_THRESHOLD 100000 /* vector size for gang look-up from nat cache that consists of radix tree */ #define NATVEC_SIZE 64 @@ -126,6 +128,11 @@ static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi) NM_I(sbi)->dirty_nats_ratio / 100; } +static inline bool excess_cached_nats(struct f2fs_sb_info *sbi) +{ + return NM_I(sbi)->nat_cnt >= DEF_NAT_CACHE_THRESHOLD; +} + enum mem_type { FREE_NIDS, /* indicates the free nid list */ NAT_ENTRIES, /* indicates the cached nat entry */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 34a9159cf5ac..9011bffd1dd0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -345,6 +345,11 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) { if (!need) return; + + /* balance_fs_bg is able to be pending */ + if (excess_cached_nats(sbi)) + f2fs_balance_fs_bg(sbi); + /* * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. -- cgit v1.2.3 From 7dfeaa32204841aade36ba243a1cb45c54f42c15 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 4 Jun 2016 14:21:28 -0700 Subject: f2fs: avoid reverse IO order for NODE and DATA There is a data race between allocate_data_block() and f2fs_sbumit_page_mbio(), which incur unnecessary reversed bio submission. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 + fs/f2fs/segment.c | 6 ++++++ fs/f2fs/super.c | 2 ++ 3 files changed, 9 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e21f9df133d1..24c7cde84905 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -766,6 +766,7 @@ struct f2fs_sb_info { /* for bio operations */ struct f2fs_bio_info read_io; /* for read bios */ struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ + struct mutex wio_mutex[NODE + 1]; /* bio ordering for NODE/DATA */ /* for checkpoint */ struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9011bffd1dd0..7b58bfbd84a3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1399,11 +1399,17 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) { int type = __get_segment_type(fio->page, fio->type); + if (fio->type == NODE || fio->type == DATA) + mutex_lock(&fio->sbi->wio_mutex[fio->type]); + allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, &fio->new_blkaddr, sum, type); /* writeout dirty page into bdev */ f2fs_submit_page_mbio(fio); + + if (fio->type == NODE || fio->type == DATA) + mutex_unlock(&fio->sbi->wio_mutex[fio->type]); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 94bb87dd4b31..dc66f1623e06 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1436,6 +1436,8 @@ static void init_sb_info(struct f2fs_sb_info *sbi) INIT_LIST_HEAD(&sbi->s_list); mutex_init(&sbi->umount_mutex); + mutex_init(&sbi->wio_mutex[NODE]); + mutex_init(&sbi->wio_mutex[DATA]); #ifdef CONFIG_F2FS_FS_ENCRYPTION memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, -- cgit v1.2.3 From 19a5f5e2ef37f032efd840ada257bce2e91c8066 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 4 Jun 2016 14:25:24 -0700 Subject: f2fs: drop any block plugging In f2fs, we don't need to keep block plugging for NODE and DATA writes, since we already merged bios as much as possible. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 4 ---- fs/f2fs/data.c | 17 ++++++++++------- fs/f2fs/gc.c | 5 ----- fs/f2fs/segment.c | 7 +------ 4 files changed, 11 insertions(+), 22 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 5ddd15cd6c6c..4179c7b971fc 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -897,11 +897,8 @@ static int block_operations(struct f2fs_sb_info *sbi) .nr_to_write = LONG_MAX, .for_reclaim = 0, }; - struct blk_plug plug; int err = 0; - blk_start_plug(&plug); - retry_flush_dents: f2fs_lock_all(sbi); /* write all the dirty dentry pages */ @@ -938,7 +935,6 @@ retry_flush_nodes: goto retry_flush_nodes; } out: - blk_finish_plug(&plug); return err; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 30dc448cae60..5f655d0c5b1f 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -98,10 +98,13 @@ static struct bio *__bio_alloc(struct f2fs_sb_info *sbi, block_t blk_addr, } static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, - struct bio *bio) + struct bio *bio, enum page_type type) { - if (!is_read_io(rw)) + if (!is_read_io(rw)) { atomic_inc(&sbi->nr_wb_bios); + if (current->plug && (type == DATA || type == NODE)) + blk_finish_plug(current->plug); + } submit_bio(rw, bio); } @@ -117,7 +120,7 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) else trace_f2fs_submit_write_bio(io->sbi->sb, fio, io->bio); - __submit_bio(io->sbi, fio->rw, io->bio); + __submit_bio(io->sbi, fio->rw, io->bio, fio->type); io->bio = NULL; } @@ -235,7 +238,7 @@ int f2fs_submit_page_bio(struct f2fs_io_info *fio) return -EFAULT; } - __submit_bio(fio->sbi, fio->rw, bio); + __submit_bio(fio->sbi, fio->rw, bio, fio->type); return 0; } @@ -1040,7 +1043,7 @@ got_it: */ if (bio && (last_block_in_bio != block_nr - 1)) { submit_and_realloc: - __submit_bio(F2FS_I_SB(inode), READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); bio = NULL; } if (bio == NULL) { @@ -1083,7 +1086,7 @@ set_error_page: goto next_page; confused: if (bio) { - __submit_bio(F2FS_I_SB(inode), READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); bio = NULL; } unlock_page(page); @@ -1093,7 +1096,7 @@ next_page: } BUG_ON(pages && !list_empty(pages)); if (bio) - __submit_bio(F2FS_I_SB(inode), READ, bio); + __submit_bio(F2FS_I_SB(inode), READ, bio, DATA); return 0; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 4a03076074af..67fd2855ccc9 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -777,7 +777,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, { struct page *sum_page; struct f2fs_summary_block *sum; - struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; int seg_freed = 0; @@ -795,8 +794,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unlock_page(sum_page); } - blk_start_plug(&plug); - for (segno = start_segno; segno < end_segno; segno++) { /* find segment summary of victim */ sum_page = find_get_page(META_MAPPING(sbi), @@ -830,8 +827,6 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, f2fs_submit_merged_bio(sbi, (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE); - blk_finish_plug(&plug); - if (gc_type == FG_GC) { while (start_segno < end_segno) if (get_valid_blocks(sbi, start_segno++, 1) == 0) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7b58bfbd84a3..eff046a792ad 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -379,13 +379,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) excess_prefree_segs(sbi) || excess_dirty_nats(sbi) || (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { - if (test_opt(sbi, DATA_FLUSH)) { - struct blk_plug plug; - - blk_start_plug(&plug); + if (test_opt(sbi, DATA_FLUSH)) sync_dirty_inodes(sbi, FILE_INODE); - blk_finish_plug(&plug); - } f2fs_sync_fs(sbi->sb, true); stat_inc_bg_cp_count(sbi->stat_info); } -- cgit v1.2.3 From 36abef4e796d382e81a0c2d21ea5327481dd7154 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 3 Jun 2016 19:29:38 -0700 Subject: f2fs: introduce mode=lfs mount option This mount option is to enable original log-structured filesystem forcefully. So, there should be no random writes for main area. Especially, this supports host-managed SMR device. Signed-off-by: Jaegeuk Kim --- Documentation/filesystems/f2fs.txt | 3 +++ fs/f2fs/checkpoint.c | 2 +- fs/f2fs/data.c | 2 ++ fs/f2fs/f2fs.h | 2 ++ fs/f2fs/file.c | 8 +++++++- fs/f2fs/recovery.c | 6 +++++- fs/f2fs/segment.c | 20 +++++++++++++++++++- fs/f2fs/segment.h | 7 +++++++ fs/f2fs/super.c | 28 ++++++++++++++++++++++++++++ 9 files changed, 74 insertions(+), 4 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt index e1c9f0849da6..3a5ce24021d9 100644 --- a/Documentation/filesystems/f2fs.txt +++ b/Documentation/filesystems/f2fs.txt @@ -151,6 +151,9 @@ noinline_data Disable the inline data feature, inline data feature is enabled by default. data_flush Enable data flushing before checkpoint in order to persist data of regular and symlink. +mode=%s Control block allocation mode which supports "adaptive" + and "lfs". In "lfs" mode, there should be no random + writes towards main area. ================================================================================ DEBUGFS ENTRIES diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 4179c7b971fc..837e6bcad5ce 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -981,7 +981,7 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) * This avoids to conduct wrong roll-forward operations and uses * metapages, so should be called prior to sync_meta_pages below. */ - if (discard_next_dnode(sbi, discard_blk)) + if (!test_opt(sbi, LFS) && discard_next_dnode(sbi, discard_blk)) invalidate = true; /* Flush all the NAT/SIT pages */ diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5f655d0c5b1f..607ef4397330 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1710,6 +1710,8 @@ static ssize_t f2fs_direct_IO(struct kiocb *iocb, struct iov_iter *iter) if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) return 0; + if (test_opt(F2FS_I_SB(inode), LFS)) + return 0; trace_f2fs_direct_IO_enter(inode, offset, count, iov_iter_rw(iter)); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 24c7cde84905..82acdec022ef 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -111,6 +111,8 @@ static inline bool time_to_inject(int type) #define F2FS_MOUNT_FORCE_FG_GC 0x00004000 #define F2FS_MOUNT_DATA_FLUSH 0x00008000 #define F2FS_MOUNT_FAULT_INJECTION 0x00010000 +#define F2FS_MOUNT_ADAPTIVE 0x00020000 +#define F2FS_MOUNT_LFS 0x00040000 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 7a8d262bc488..b9d745ef5b08 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -878,9 +878,15 @@ static int __exchange_data_block(struct inode *inode, pgoff_t src, return full ? truncate_hole(inode, dst, dst + 1) : 0; if (do_replace) { - struct page *ipage = get_node_page(sbi, inode->i_ino); + struct page *ipage; struct node_info ni; + if (test_opt(sbi, LFS)) { + ret = -ENOTSUPP; + goto err_out; + } + + ipage = get_node_page(sbi, inode->i_ino); if (IS_ERR(ipage)) { ret = PTR_ERR(ipage); goto err_out; diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index b568b28c74f2..a39d84ab66b2 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -624,8 +624,12 @@ out: if (err) { bool invalidate = false; - if (discard_next_dnode(sbi, blkaddr)) + if (test_opt(sbi, LFS)) { + update_meta_page(sbi, NULL, blkaddr); invalidate = true; + } else if (discard_next_dnode(sbi, blkaddr)) { + invalidate = true; + } /* Flush all the NAT/SIT pages */ while (get_pages(sbi, F2FS_DIRTY_META)) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index eff046a792ad..4792f94089f7 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -707,6 +707,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; + unsigned int secno, start_segno; mutex_lock(&dirty_i->seglist_lock); @@ -726,8 +727,22 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) if (!test_opt(sbi, DISCARD)) continue; - f2fs_issue_discard(sbi, START_BLOCK(sbi, start), + if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) { + f2fs_issue_discard(sbi, START_BLOCK(sbi, start), (end - start) << sbi->log_blocks_per_seg); + continue; + } +next: + secno = GET_SECNO(sbi, start); + start_segno = secno * sbi->segs_per_sec; + if (!IS_CURSEC(sbi, secno) && + !get_valid_blocks(sbi, start, sbi->segs_per_sec)) + f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), + sbi->segs_per_sec << sbi->log_blocks_per_seg); + + start = start_segno + sbi->segs_per_sec; + if (start < end) + goto next; } mutex_unlock(&dirty_i->seglist_lock); @@ -1221,6 +1236,9 @@ void allocate_new_segments(struct f2fs_sb_info *sbi) { int i; + if (test_opt(sbi, LFS)) + return; + for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) __allocate_new_segments(sbi, i); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 890bb28d2082..d74cc330ae13 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -470,6 +470,10 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + + if (test_opt(sbi, LFS)) + return false; + return free_sections(sbi) <= (node_secs + 2 * dent_secs + reserved_sections(sbi) + 1); } @@ -533,6 +537,9 @@ static inline bool need_inplace_update(struct inode *inode) if (S_ISDIR(inode->i_mode) || f2fs_is_atomic_file(inode)) return false; + if (test_opt(sbi, LFS)) + return false; + if (policy & (0x1 << F2FS_IPU_FORCE)) return true; if (policy & (0x1 << F2FS_IPU_SSR) && need_SSR(sbi)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index dc66f1623e06..edc736de8ee9 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -94,6 +94,7 @@ enum { Opt_noextent_cache, Opt_noinline_data, Opt_data_flush, + Opt_mode, Opt_fault_injection, Opt_lazytime, Opt_nolazytime, @@ -123,6 +124,7 @@ static match_table_t f2fs_tokens = { {Opt_noextent_cache, "noextent_cache"}, {Opt_noinline_data, "noinline_data"}, {Opt_data_flush, "data_flush"}, + {Opt_mode, "mode=%s"}, {Opt_fault_injection, "fault_injection=%u"}, {Opt_lazytime, "lazytime"}, {Opt_nolazytime, "nolazytime"}, @@ -506,6 +508,25 @@ static int parse_options(struct super_block *sb, char *options) case Opt_data_flush: set_opt(sbi, DATA_FLUSH); break; + case Opt_mode: + name = match_strdup(&args[0]); + + if (!name) + return -ENOMEM; + if (strlen(name) == 8 && + !strncmp(name, "adaptive", 8)) { + set_opt(sbi, ADAPTIVE); + clear_opt(sbi, LFS); + } else if (strlen(name) == 3 && + !strncmp(name, "lfs", 3)) { + clear_opt(sbi, ADAPTIVE); + set_opt(sbi, LFS); + } else { + kfree(name); + return -EINVAL; + } + kfree(name); + break; case Opt_fault_injection: if (args->from && match_int(args, &arg)) return -EINVAL; @@ -870,6 +891,12 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",noextent_cache"); if (test_opt(sbi, DATA_FLUSH)) seq_puts(seq, ",data_flush"); + + seq_puts(seq, ",mode="); + if (test_opt(sbi, ADAPTIVE)) + seq_puts(seq, "adaptive"); + else if (test_opt(sbi, LFS)) + seq_puts(seq, "lfs"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); return 0; @@ -953,6 +980,7 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, EXTENT_CACHE); sbi->sb->s_flags |= MS_LAZYTIME; set_opt(sbi, FLUSH_MERGE); + set_opt(sbi, ADAPTIVE); #ifdef CONFIG_F2FS_FS_XATTR set_opt(sbi, XATTR_USER); -- cgit v1.2.3 From 52763a4b7a2112743745c5bbfe43fe6f54d4b39a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 13 Jun 2016 09:47:48 -0700 Subject: f2fs: detect host-managed SMR by feature flag If mkfs.f2fs gives a feature flag for host-managed SMR, we can set mode=lfs by default. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 3 ++- fs/f2fs/f2fs.h | 21 +++++++++++++++++++++ fs/f2fs/segment.c | 3 ++- fs/f2fs/super.c | 16 ++++++++++------ 4 files changed, 35 insertions(+), 8 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 607ef4397330..08325280d03c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -102,7 +102,8 @@ static inline void __submit_bio(struct f2fs_sb_info *sbi, int rw, { if (!is_read_io(rw)) { atomic_inc(&sbi->nr_wb_bios); - if (current->plug && (type == DATA || type == NODE)) + if (f2fs_sb_mounted_hmsmr(sbi->sb) && + current->plug && (type == DATA || type == NODE)) blk_finish_plug(current->plug); } submit_bio(rw, bio); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bbcd4688f97b..b3aeb58a6285 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -133,6 +133,7 @@ struct f2fs_mount_info { }; #define F2FS_FEATURE_ENCRYPT 0x0001 +#define F2FS_FEATURE_HMSMR 0x0002 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -2334,6 +2335,26 @@ static inline int f2fs_sb_has_crypto(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_ENCRYPT); } +static inline int f2fs_sb_mounted_hmsmr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_HMSMR); +} + +static inline void set_opt_mode(struct f2fs_sb_info *sbi, unsigned int mt) +{ + clear_opt(sbi, ADAPTIVE); + clear_opt(sbi, LFS); + + switch (mt) { + case F2FS_MOUNT_ADAPTIVE: + set_opt(sbi, ADAPTIVE); + break; + case F2FS_MOUNT_LFS: + set_opt(sbi, LFS); + break; + } +} + static inline bool f2fs_may_encrypt(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4792f94089f7..782975e791f1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2402,7 +2402,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); sm_info->rec_prefree_segments = sm_info->main_segments * DEF_RECLAIM_PREFREE_SEGMENTS / 100; - sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; + if (!test_opt(sbi, LFS)) + sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 41347c0829cd..8c698e183d2b 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -515,12 +515,10 @@ static int parse_options(struct super_block *sb, char *options) return -ENOMEM; if (strlen(name) == 8 && !strncmp(name, "adaptive", 8)) { - set_opt(sbi, ADAPTIVE); - clear_opt(sbi, LFS); + set_opt_mode(sbi, F2FS_MOUNT_ADAPTIVE); } else if (strlen(name) == 3 && !strncmp(name, "lfs", 3)) { - clear_opt(sbi, ADAPTIVE); - set_opt(sbi, LFS); + set_opt_mode(sbi, F2FS_MOUNT_LFS); } else { kfree(name); return -EINVAL; @@ -966,7 +964,12 @@ static void default_options(struct f2fs_sb_info *sbi) set_opt(sbi, EXTENT_CACHE); sbi->sb->s_flags |= MS_LAZYTIME; set_opt(sbi, FLUSH_MERGE); - set_opt(sbi, ADAPTIVE); + if (f2fs_sb_mounted_hmsmr(sbi->sb)) { + set_opt_mode(sbi, F2FS_MOUNT_LFS); + set_opt(sbi, DISCARD); + } else { + set_opt_mode(sbi, F2FS_MOUNT_ADAPTIVE); + } #ifdef CONFIG_F2FS_FS_XATTR set_opt(sbi, XATTR_USER); @@ -1615,6 +1618,8 @@ try_onemore: goto free_sbi; sb->s_fs_info = sbi; + sbi->raw_super = raw_super; + default_options(sbi); /* parse mount options */ options = kstrdup((const char *)data, GFP_KERNEL); @@ -1644,7 +1649,6 @@ try_onemore: memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); /* init f2fs-specific super block info */ - sbi->raw_super = raw_super; sbi->valid_super_block = valid_super_block; mutex_init(&sbi->gc_mutex); mutex_init(&sbi->cp_mutex); -- cgit v1.2.3 From ad4edb83143fdeef9e6fdd9daaa735b59476565b Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 16 Jun 2016 16:41:49 -0700 Subject: f2fs: produce more nids and reduce readahead nats The readahead nat pages are more likely to be reclaimed quickly, so it'd better to gather more free nids in advance. And, let's keep some free nids as much as possible. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 2 ++ fs/f2fs/f2fs.h | 1 + fs/f2fs/node.c | 9 ++++++--- fs/f2fs/node.h | 5 +++-- fs/f2fs/segment.c | 4 +++- fs/f2fs/shrinker.c | 5 +++-- 6 files changed, 18 insertions(+), 8 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8534b98c0712..2b43d4013e92 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -941,6 +941,8 @@ out: static void unblock_operations(struct f2fs_sb_info *sbi) { up_write(&sbi->node_write); + + build_free_nids(sbi); f2fs_unlock_all(sbi); } diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b3aeb58a6285..32884a7bdcc4 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1965,6 +1965,7 @@ void move_node_page(struct page *, int); int fsync_node_pages(struct f2fs_sb_info *, struct inode *, struct writeback_control *, bool); int sync_node_pages(struct f2fs_sb_info *, struct writeback_control *); +void build_free_nids(struct f2fs_sb_info *); bool alloc_nid(struct f2fs_sb_info *, nid_t *); void alloc_nid_done(struct f2fs_sb_info *, nid_t); void alloc_nid_failed(struct f2fs_sb_info *, nid_t); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b448c8fec7fc..729fb1eb86ce 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1765,7 +1765,7 @@ static void scan_nat_page(struct f2fs_sb_info *sbi, } } -static void build_free_nids(struct f2fs_sb_info *sbi) +void build_free_nids(struct f2fs_sb_info *sbi) { struct f2fs_nm_info *nm_i = NM_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -1774,7 +1774,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi) nid_t nid = nm_i->next_scan_nid; /* Enough entries */ - if (nm_i->fcnt > NAT_ENTRY_PER_BLOCK) + if (nm_i->fcnt >= NAT_ENTRY_PER_BLOCK) return; /* readahead nat pages to be scanned */ @@ -1912,12 +1912,15 @@ int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) struct free_nid *i, *next; int nr = nr_shrink; + if (nm_i->fcnt <= MAX_FREE_NIDS) + return 0; + if (!mutex_trylock(&nm_i->build_lock)) return 0; spin_lock(&nm_i->free_nid_list_lock); list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { - if (nr_shrink <= 0 || nm_i->fcnt <= NAT_ENTRY_PER_BLOCK) + if (nr_shrink <= 0 || nm_i->fcnt <= MAX_FREE_NIDS) break; if (i->state == NID_ALLOC) continue; diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 673ce926cf09..fc7684554b1a 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h @@ -15,9 +15,10 @@ #define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) /* # of pages to perform synchronous readahead before building free nids */ -#define FREE_NID_PAGES 4 +#define FREE_NID_PAGES 8 +#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES) -#define DEF_RA_NID_PAGES 4 /* # of nid pages to be readaheaded */ +#define DEF_RA_NID_PAGES 0 /* # of nid pages to be readaheaded */ /* maximum readahead size for node during getting data blocks */ #define MAX_RA_NODE 128 diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 782975e791f1..6d16ecf9d29e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -371,7 +371,9 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK); if (!available_free_memory(sbi, FREE_NIDS)) - try_to_free_nids(sbi, NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES); + try_to_free_nids(sbi, MAX_FREE_NIDS); + else + build_free_nids(sbi); /* checkpoint is the only way to shrink partial cached entries */ if (!available_free_memory(sbi, NAT_ENTRIES) || diff --git a/fs/f2fs/shrinker.c b/fs/f2fs/shrinker.c index 93606f281bf9..46c915425923 100644 --- a/fs/f2fs/shrinker.c +++ b/fs/f2fs/shrinker.c @@ -13,6 +13,7 @@ #include #include "f2fs.h" +#include "node.h" static LIST_HEAD(f2fs_list); static DEFINE_SPINLOCK(f2fs_list_lock); @@ -25,8 +26,8 @@ static unsigned long __count_nat_entries(struct f2fs_sb_info *sbi) static unsigned long __count_free_nids(struct f2fs_sb_info *sbi) { - if (NM_I(sbi)->fcnt > NAT_ENTRY_PER_BLOCK) - return NM_I(sbi)->fcnt - NAT_ENTRY_PER_BLOCK; + if (NM_I(sbi)->fcnt > MAX_FREE_NIDS) + return NM_I(sbi)->fcnt - MAX_FREE_NIDS; return 0; } -- cgit v1.2.3 From c7b41e161368388487238d71986a65290f83d74a Mon Sep 17 00:00:00 2001 From: Yunlei He Date: Thu, 7 Jul 2016 12:13:33 +0800 Subject: f2fs: avoid mismatching block range for discard This patch skip discard block range smaller than trim_minlen, and can not be merged by neighbour Signed-off-by: Yunlei He Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6d16ecf9d29e..9e13db05e3f0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -672,6 +672,10 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) break; end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); + if (force && start && end != max_blocks + && (end - start) < cpc->trim_minlen) + continue; + __add_discard_entry(sbi, cpc, se, start, end); } } -- cgit v1.2.3 From c24a0fd655431e9f14c8a8754d0a6cc247f9e9e5 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Thu, 7 Jul 2016 22:46:55 +0800 Subject: f2fs: fix to avoid redundant discard during fstrim With below test steps, f2fs will issue redundant discard when doing fstrim, the reason is that we issue discards for both prefree segments and consecutive freed region user wants to trim, part regions they covered are overlapped, here, we change to do not to issue any discards for prefree segments in trimmed range. 1. mount -t f2fs -o discard /dev/zram0 /mnt/f2fs 2. fstrim -o 0 -l 3221225472 -m 2097152 -v /mnt/f2fs/ 3. dd if=/dev/zero of=/mnt/f2fs/a bs=2M count=1 4. dd if=/dev/zero of=/mnt/f2fs/b bs=1M count=1 5. sync 6. rm /mnt/f2fs/a /mnt/f2fs/b 7. fstrim -o 0 -l 3221225472 -m 2097152 -v /mnt/f2fs/ Before: <...>-5428 [001] ...1 9511.052125: f2fs_issue_discard: dev = (251,0), blkstart = 0x2200, blklen = 0x200 <...>-5428 [001] ...1 9511.052787: f2fs_issue_discard: dev = (251,0), blkstart = 0x2200, blklen = 0x300 After: <...>-6764 [000] ...1 9720.382504: f2fs_issue_discard: dev = (251,0), blkstart = 0x2200, blklen = 0x300 Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9e13db05e3f0..08f6c0be20cf 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -714,6 +714,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int start = 0, end = -1; unsigned int secno, start_segno; + bool force = (cpc->reason == CP_DISCARD); mutex_lock(&dirty_i->seglist_lock); @@ -730,7 +731,7 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc) dirty_i->nr_dirty[PRE] -= end - start; - if (!test_opt(sbi, DISCARD)) + if (force || !test_opt(sbi, DISCARD)) continue; if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) { @@ -754,7 +755,7 @@ next: /* send small discards */ list_for_each_entry_safe(entry, this, head, list) { - if (cpc->reason == CP_DISCARD && entry->len < cpc->trim_minlen) + if (force && entry->len < cpc->trim_minlen) goto skip; f2fs_issue_discard(sbi, entry->blkaddr, entry->len); cpc->trimmed += entry->len; -- cgit v1.2.3 From 44a83499dda714d9262a9bf4fdac8c077893c9e6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 13 Jul 2016 18:23:35 -0700 Subject: f2fs: add maximum prefree segments In 1TB storage, we need to admit 22841 prefree segments, which can consume too much segments. This patch sets 8GB in max. prefree segments in that case. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 3 +++ fs/f2fs/segment.h | 1 + 2 files changed, 4 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 08f6c0be20cf..e87aa058f57a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2409,6 +2409,9 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); sm_info->rec_prefree_segments = sm_info->main_segments * DEF_RECLAIM_PREFREE_SEGMENTS / 100; + if (sm_info->rec_prefree_segments > DEF_MAX_RECLAIM_PREFREE_SEGMENTS) + sm_info->rec_prefree_segments = DEF_MAX_RECLAIM_PREFREE_SEGMENTS; + if (!test_opt(sbi, LFS)) sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 57d450fb4643..b33f73ec60a4 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -16,6 +16,7 @@ #define NULL_SECNO ((unsigned int)(~0)) #define DEF_RECLAIM_PREFREE_SEGMENTS 5 /* 5% over total segments */ +#define DEF_MAX_RECLAIM_PREFREE_SEGMENTS 4096 /* 8GB in maximum */ /* L: Logical segment # in volume, R: Relative segment # in main area */ #define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) -- cgit v1.2.3 From 9dfa1baff76d08843aaf5e3c78f6da6950957702 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 13 Jul 2016 19:33:19 -0700 Subject: f2fs: use blk_plug in all the possible paths This patch reverts 19a5f5e2ef37 (f2fs: drop any block plugging), and adds blk_plug in write paths additionally. The main reason is that blk_start_plug can be used to wake up from low-power mode before submitting further bios. Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 7 +++++++ fs/f2fs/data.c | 3 +++ fs/f2fs/file.c | 6 +++++- fs/f2fs/gc.c | 5 +++++ fs/f2fs/node.c | 3 +++ fs/f2fs/segment.c | 7 ++++++- 6 files changed, 29 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8ea895389ae4..be1c54b62388 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -265,6 +265,7 @@ static int f2fs_write_meta_pages(struct address_space *mapping, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + struct blk_plug plug; long diff, written; /* collect a number of dirty meta pages and write together */ @@ -277,7 +278,9 @@ static int f2fs_write_meta_pages(struct address_space *mapping, /* if mounting is failed, skip writing node pages */ mutex_lock(&sbi->cp_mutex); diff = nr_pages_to_write(sbi, META, wbc); + blk_start_plug(&plug); written = sync_meta_pages(sbi, META, wbc->nr_to_write); + blk_finish_plug(&plug); mutex_unlock(&sbi->cp_mutex); wbc->nr_to_write = max((long)0, wbc->nr_to_write - written - diff); return 0; @@ -899,8 +902,11 @@ static int block_operations(struct f2fs_sb_info *sbi) .nr_to_write = LONG_MAX, .for_reclaim = 0, }; + struct blk_plug plug; int err = 0; + blk_start_plug(&plug); + retry_flush_dents: f2fs_lock_all(sbi); /* write all the dirty dentry pages */ @@ -937,6 +943,7 @@ retry_flush_nodes: goto retry_flush_nodes; } out: + blk_finish_plug(&plug); return err; } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index adfe47b21991..87a458fb8afe 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1438,6 +1438,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, { struct inode *inode = mapping->host; struct f2fs_sb_info *sbi = F2FS_I_SB(inode); + struct blk_plug plug; int ret; /* deal with chardevs and other special file */ @@ -1463,7 +1464,9 @@ static int f2fs_write_data_pages(struct address_space *mapping, trace_f2fs_writepages(mapping->host, wbc, DATA); + blk_start_plug(&plug); ret = f2fs_write_cache_pages(mapping, wbc); + blk_finish_plug(&plug); /* * if some pages were truncated, we cannot guarantee its mapping->host * to detect pending bios. diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 72e52cd15b60..47f1f5e36d1c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -2102,6 +2102,7 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); + struct blk_plug plug; ssize_t ret; if (f2fs_encrypted_inode(inode) && @@ -2113,8 +2114,11 @@ static ssize_t f2fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ret = generic_write_checks(iocb, from); if (ret > 0) { ret = f2fs_preallocate_blocks(iocb, from); - if (!ret) + if (!ret) { + blk_start_plug(&plug); ret = __generic_file_write_iter(iocb, from); + blk_finish_plug(&plug); + } } inode_unlock(inode); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 5c8acf754513..de6c41c32c62 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -808,6 +808,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, { struct page *sum_page; struct f2fs_summary_block *sum; + struct blk_plug plug; unsigned int segno = start_segno; unsigned int end_segno = start_segno + sbi->segs_per_sec; int seg_freed = 0; @@ -825,6 +826,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unlock_page(sum_page); } + blk_start_plug(&plug); + for (segno = start_segno; segno < end_segno; segno++) { if (get_valid_blocks(sbi, segno, 1) == 0) @@ -862,6 +865,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, f2fs_submit_merged_bio(sbi, (type == SUM_TYPE_NODE) ? NODE : DATA, WRITE); + blk_finish_plug(&plug); + if (gc_type == FG_GC) { while (start_segno < end_segno) if (get_valid_blocks(sbi, start_segno++, 1) == 0) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d78f61d647c0..79a93c6e632f 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1618,6 +1618,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_M_SB(mapping); + struct blk_plug plug; long diff; /* balancing f2fs's metadata in background */ @@ -1631,7 +1632,9 @@ static int f2fs_write_node_pages(struct address_space *mapping, diff = nr_pages_to_write(sbi, NODE, wbc); wbc->sync_mode = WB_SYNC_NONE; + blk_start_plug(&plug); sync_node_pages(sbi, wbc); + blk_finish_plug(&plug); wbc->nr_to_write = max((long)0, wbc->nr_to_write - diff); return 0; diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e87aa058f57a..d45e6bbf8493 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -381,8 +381,13 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi) excess_prefree_segs(sbi) || excess_dirty_nats(sbi) || (is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { - if (test_opt(sbi, DATA_FLUSH)) + if (test_opt(sbi, DATA_FLUSH)) { + struct blk_plug plug; + + blk_start_plug(&plug); sync_dirty_inodes(sbi, FILE_INODE); + blk_finish_plug(&plug); + } f2fs_sync_fs(sbi->sb, true); stat_inc_bg_cp_count(sbi->stat_info); } -- cgit v1.2.3