From 9a7f143ab529352ebef13d3f0f4a09f13efa9435 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Fri, 15 Nov 2013 10:42:51 +0900 Subject: f2fs: introduce __find_rev_next(_zero)_bit When f2fs_set_bit is used, in a byte MSB and LSB is reversed, in that case we can use __find_rev_next_bit or __find_rev_next_zero_bit. Signed-off-by: Changman Lee [Jaegeuk Kim: change the function names] Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fa284d397199..aa1d30d76719 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -20,6 +20,154 @@ #include "node.h" #include +#define __reverse_ffz(x) __reverse_ffs(~(x)) + +/* + * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since + * MSB and LSB are reversed in a byte by f2fs_set_bit. + */ +static inline unsigned long __reverse_ffs(unsigned long word) +{ + int num = 0; + +#if BITS_PER_LONG == 64 + if ((word & 0xffffffff) == 0) { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf0) == 0) + num += 4; + else + word >>= 4; + if ((word & 0xc) == 0) + num += 2; + else + word >>= 2; + if ((word & 0x2) == 0) + num += 1; + return num; +} + +/* + * __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c becasue + * f2fs_set_bit makes MSB and LSB reversed in a byte. + * Example: + * LSB <--> MSB + * f2fs_set_bit(0, bitmap) => 0000 0001 + * f2fs_set_bit(7, bitmap) => 1000 0000 + */ +static unsigned long __find_rev_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset) +{ + const unsigned long *p = addr + BIT_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long tmp; + unsigned long mask, submask; + unsigned long quot, rest; + + if (offset >= size) + return size; + + size -= result; + offset %= BITS_PER_LONG; + if (!offset) + goto aligned; + + tmp = *(p++); + quot = (offset >> 3) << 3; + rest = offset & 0x7; + mask = ~0UL << quot; + submask = (unsigned char)(0xff << rest) >> rest; + submask <<= quot; + mask &= submask; + tmp &= mask; + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + + size -= BITS_PER_LONG; + result += BITS_PER_LONG; +aligned: + while (size & ~(BITS_PER_LONG-1)) { + tmp = *(p++); + if (tmp) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __reverse_ffs(tmp); +} + +static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, + unsigned long size, unsigned long offset) +{ + const unsigned long *p = addr + BIT_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG - 1); + unsigned long tmp; + unsigned long mask, submask; + unsigned long quot, rest; + + if (offset >= size) + return size; + + size -= result; + offset %= BITS_PER_LONG; + if (!offset) + goto aligned; + + tmp = *(p++); + quot = (offset >> 3) << 3; + rest = offset & 0x7; + mask = ~(~0UL << quot); + submask = (unsigned char)~((unsigned char)(0xff << rest) >> rest); + submask <<= quot; + mask += submask; + tmp |= mask; + if (size < BITS_PER_LONG) + goto found_first; + if (~tmp) + goto found_middle; + + size -= BITS_PER_LONG; + result += BITS_PER_LONG; +aligned: + while (size & ~(BITS_PER_LONG - 1)) { + tmp = *(p++); + if (~tmp) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) /* Are any bits zero? */ + return result + size; /* Nope. */ +found_middle: + return result + __reverse_ffz(tmp); +} + /* * This function balances dirty node and dentry pages. * In addition, it controls garbage collection. -- cgit v1.2.3 From e81c93cf8c7bd413898798cf8c67f18b1fef3360 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Fri, 15 Nov 2013 13:21:16 +0900 Subject: f2fs: improve searching speed of __next_free_blkoff To find a zero bit using the result of OR operation between ckpt_valid_map and cur_valid_map is more fast than find a zero bit in each bitmap. Signed-off-by: Changman Lee [Jaegeuk Kim: adjust changed function name] Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index aa1d30d76719..67f1e5bbdac9 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -607,13 +607,18 @@ static void __next_free_blkoff(struct f2fs_sb_info *sbi, struct curseg_info *seg, block_t start) { struct seg_entry *se = get_seg_entry(sbi, seg->segno); - block_t ofs; - for (ofs = start; ofs < sbi->blocks_per_seg; ofs++) { - if (!f2fs_test_bit(ofs, se->ckpt_valid_map) - && !f2fs_test_bit(ofs, se->cur_valid_map)) - break; - } - seg->next_blkoff = ofs; + int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); + unsigned long target_map[entries]; + unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; + unsigned long *cur_map = (unsigned long *)se->cur_valid_map; + int i, pos; + + for (i = 0; i < entries; i++) + target_map[i] = ckpt_map[i] | cur_map[i]; + + pos = __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start); + + seg->next_blkoff = pos; } /* -- cgit v1.2.3 From 7fd9e544fbb10c6ae4b4953f6063560c8eeae6e8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 15 Nov 2013 13:55:58 +0900 Subject: f2fs: add a slab cache entry for small discards This patch adds a slab cache entry for small discards. Each entry consists of: struct discard_entry { struct list_head list; /* list head */ block_t blkaddr; /* block address to be discarded */ int len; /* # of consecutive blocks of the discard */ }; Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 67f1e5bbdac9..823526ec5243 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -22,6 +22,8 @@ #define __reverse_ffz(x) __reverse_ffs(~(x)) +static struct kmem_cache *discard_entry_slab; + /* * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since * MSB and LSB are reversed in a byte by f2fs_set_bit. @@ -1798,6 +1800,10 @@ 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 = DEF_RECLAIM_PREFREE_SEGMENTS; + INIT_LIST_HEAD(&sm_info->discard_list); + sm_info->nr_discards = 0; + sm_info->max_discards = 0; + err = build_sit_info(sbi); if (err) return err; @@ -1913,3 +1919,17 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi) sbi->sm_info = NULL; kfree(sm_info); } + +int __init create_segment_manager_caches(void) +{ + discard_entry_slab = f2fs_kmem_cache_create("discard_entry", + sizeof(struct discard_entry), NULL); + if (!discard_entry_slab) + return -ENOMEM; + return 0; +} + +void destroy_segment_manager_caches(void) +{ + kmem_cache_destroy(discard_entry_slab); +} -- cgit v1.2.3 From b29555505d81e496fdbd125190c8043f3c09a83c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Nov 2013 14:49:56 +0900 Subject: f2fs: add key functions for small discards This patch adds key functions to activate the small discard feature. Note that this procedure is conducted during the checkpoint only. In flush_sit_entries(), when a new dirty sit entry is flushed, f2fs calls add_discard_addrs() which searches candidates to be discarded. The candidates should be marked *invalidated* and also previous checkpoint recognizes it as *valid*. At the end of a checkpoint procedure, f2fs throws discards based on the discard entry list. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 823526ec5243..505a8894cfa1 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -266,6 +266,47 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } +static void add_discard_addrs(struct f2fs_sb_info *sbi, + unsigned int segno, struct seg_entry *se) +{ + struct list_head *head = &SM_I(sbi)->discard_list; + struct discard_entry *new; + int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); + int max_blocks = sbi->blocks_per_seg; + unsigned long *cur_map = (unsigned long *)se->cur_valid_map; + unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; + unsigned long dmap[entries]; + unsigned int start = 0, end = -1; + int i; + + if (!test_opt(sbi, DISCARD)) + return; + + /* zero block will be discarded through the prefree list */ + if (!se->valid_blocks || se->valid_blocks == max_blocks) + return; + + /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ + for (i = 0; i < entries; i++) + dmap[i] = (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; + + while (SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) { + start = __find_rev_next_bit(dmap, max_blocks, end + 1); + if (start >= max_blocks) + break; + + end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); + + new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); + INIT_LIST_HEAD(&new->list); + new->blkaddr = START_BLOCK(sbi, segno) + start; + new->len = end - start; + + list_add_tail(&new->list, head); + SM_I(sbi)->nr_discards += end - start; + } +} + /* * Should call clear_prefree_segments after checkpoint is done. */ @@ -288,6 +329,9 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) void clear_prefree_segments(struct f2fs_sb_info *sbi) { + struct list_head *head = &(SM_I(sbi)->discard_list); + struct list_head *this, *next; + struct discard_entry *entry; struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; unsigned int total_segs = TOTAL_SEGS(sbi); @@ -318,6 +362,18 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi) GFP_NOFS, 0); } mutex_unlock(&dirty_i->seglist_lock); + + /* send small discards */ + list_for_each_safe(this, next, head) { + entry = list_entry(this, struct discard_entry, list); + blkdev_issue_discard(sbi->sb->s_bdev, + entry->blkaddr << sbi->log_sectors_per_block, + (1 << sbi->log_sectors_per_block) * entry->len, + GFP_NOFS, 0); + list_del(&entry->list); + SM_I(sbi)->nr_discards -= entry->len; + kmem_cache_free(discard_entry_slab, entry); + } } static void __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) @@ -1469,6 +1525,10 @@ void flush_sit_entries(struct f2fs_sb_info *sbi) sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); + /* add discard candidates */ + if (SM_I(sbi)->nr_discards < SM_I(sbi)->max_discards) + add_discard_addrs(sbi, segno, se); + if (flushed) goto to_sit_page; -- cgit v1.2.3 From 3720887910864467a61cd0d64bad3965009cdef8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Nov 2013 16:55:17 +0900 Subject: f2fs: introduce f2fs_issue_discard() to clean up Change log from v1: o fix 32bit drops reported by Dan Carpenter This patch adds f2fs_issue_discard() to clean up blkdev_issue_discard() flows. Dan carpenter reported: "block_t is a 32 bit type and sector_t is a 64 bit type. The upper 32 bits of the sector_t are not used because the shift will wrap." Bug-Reported-by: Dan Carpenter Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 505a8894cfa1..c51fa4bee60b 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -266,6 +266,14 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) mutex_unlock(&dirty_i->seglist_lock); } +static void f2fs_issue_discard(struct f2fs_sb_info *sbi, + block_t blkstart, block_t blklen) +{ + sector_t start = ((sector_t)blkstart) << sbi->log_sectors_per_block; + sector_t len = ((sector_t)blklen) << sbi->log_sectors_per_block; + blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); +} + static void add_discard_addrs(struct f2fs_sb_info *sbi, unsigned int segno, struct seg_entry *se) { @@ -354,22 +362,15 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi) if (!test_opt(sbi, DISCARD)) continue; - blkdev_issue_discard(sbi->sb->s_bdev, - START_BLOCK(sbi, start) << - sbi->log_sectors_per_block, - (1 << (sbi->log_sectors_per_block + - sbi->log_blocks_per_seg)) * (end - start), - GFP_NOFS, 0); + f2fs_issue_discard(sbi, START_BLOCK(sbi, start), + (end - start) << sbi->log_blocks_per_seg); } mutex_unlock(&dirty_i->seglist_lock); /* send small discards */ list_for_each_safe(this, next, head) { entry = list_entry(this, struct discard_entry, list); - blkdev_issue_discard(sbi->sb->s_bdev, - entry->blkaddr << sbi->log_sectors_per_block, - (1 << sbi->log_sectors_per_block) * entry->len, - GFP_NOFS, 0); + f2fs_issue_discard(sbi, entry->blkaddr, entry->len); list_del(&entry->list); SM_I(sbi)->nr_discards -= entry->len; kmem_cache_free(discard_entry_slab, entry); -- cgit v1.2.3 From 1661d07c2d5e6486cab1c189cfb65ff60abf9b92 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Nov 2013 17:01:00 +0900 Subject: f2fs: add a tracepoint for f2fs_issue_discard This patch adds a tracepoint for f2fs_issue_discard. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index c51fa4bee60b..cfc0eb4f24d5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -272,6 +272,7 @@ static void f2fs_issue_discard(struct f2fs_sb_info *sbi, sector_t start = ((sector_t)blkstart) << sbi->log_sectors_per_block; sector_t len = ((sector_t)blklen) << sbi->log_sectors_per_block; blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); + trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); } static void add_discard_addrs(struct f2fs_sb_info *sbi, -- cgit v1.2.3 From 7d5e510944ce60ef0c6c2300a58547679df76db7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 18 Nov 2013 17:13:35 +0900 Subject: f2fs: clean up the do_submit_bio flow This patch introduces PAGE_TYPE_OF_BIO() and cleans up do_submit_bio() with it. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index cfc0eb4f24d5..d5043bdaa1af 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -835,32 +835,35 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, enum page_type type, bool sync) { int rw = sync ? WRITE_SYNC : WRITE; - enum page_type btype = type > META ? META : type; + enum page_type btype = PAGE_TYPE_OF_BIO(type); + struct bio *bio = sbi->bio[btype]; + struct bio_private *p; + + if (!bio) + return; + + sbi->bio[btype] = NULL; if (type >= META_FLUSH) rw = WRITE_FLUSH_FUA; - if (btype == META) rw |= REQ_META; - if (sbi->bio[btype]) { - struct bio_private *p = sbi->bio[btype]->bi_private; - p->sbi = sbi; - sbi->bio[btype]->bi_end_io = f2fs_end_io_write; + p = bio->bi_private; + p->sbi = sbi; + bio->bi_end_io = f2fs_end_io_write; - trace_f2fs_do_submit_bio(sbi->sb, btype, sync, sbi->bio[btype]); + trace_f2fs_do_submit_bio(sbi->sb, btype, sync, bio); - if (type == META_FLUSH) { - DECLARE_COMPLETION_ONSTACK(wait); - p->is_sync = true; - p->wait = &wait; - submit_bio(rw, sbi->bio[btype]); - wait_for_completion(&wait); - } else { - p->is_sync = false; - submit_bio(rw, sbi->bio[btype]); - } - sbi->bio[btype] = NULL; + if (type == META_FLUSH) { + DECLARE_COMPLETION_ONSTACK(wait); + p->is_sync = true; + p->wait = &wait; + submit_bio(rw, bio); + wait_for_completion(&wait); + } else { + p->is_sync = false; + submit_bio(rw, bio); } } -- cgit v1.2.3 From 971767caf632190f77a40b4011c19948232eed75 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 18 Nov 2013 17:16:17 +0900 Subject: f2fs: use sbi->write_mutex for write bios This patch removes an unnecessary semaphore (i.e., sbi->bio_sem). There is no reason to use the semaphore when f2fs submits read and write IOs. Instead, let's use a write mutex and cover the sbi->bio[] by the lock. Change log from v1: o split write_mutex suggested by Chao Yu Chao described, "All DATA/NODE/META bio buffers in superblock is protected by 'sbi->write_mutex', but each bio buffer area is independent, So we should split write_mutex to three for DATA/NODE/META." Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index d5043bdaa1af..68b8a2bdf6d8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -869,9 +869,14 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, void f2fs_submit_bio(struct f2fs_sb_info *sbi, enum page_type type, bool sync) { - down_write(&sbi->bio_sem); + enum page_type btype = PAGE_TYPE_OF_BIO(type); + + if (!sbi->bio[btype]) + return; + + mutex_lock(&sbi->write_mutex[btype]); do_submit_bio(sbi, type, sync); - up_write(&sbi->bio_sem); + mutex_unlock(&sbi->write_mutex[btype]); } static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page, @@ -882,7 +887,7 @@ static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page, verify_block_addr(sbi, blk_addr); - down_write(&sbi->bio_sem); + mutex_lock(&sbi->write_mutex[type]); inc_page_count(sbi, F2FS_WRITEBACK); @@ -917,7 +922,7 @@ retry: sbi->last_block_in_bio[type] = blk_addr; - up_write(&sbi->bio_sem); + mutex_unlock(&sbi->write_mutex[type]); trace_f2fs_submit_write_page(page, blk_addr, type); } -- cgit v1.2.3 From 1ff7bd3bb5f7f57bc7418ee6ed12f3ae217e4e9c Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 19 Nov 2013 12:47:22 +0900 Subject: f2fs: introduce a bio array for per-page write bios The f2fs has three bio types, NODE, DATA, and META, and manages some data structures per each bio types. The codes are a little bit messy, thus, this patch introduces a bio array which groups individual data structures as follows. struct f2fs_bio_info { struct bio *bio; /* bios to merge */ sector_t last_block_in_bio; /* last block number */ struct mutex io_mutex; /* mutex for bio */ }; struct f2fs_sb_info { ... struct f2fs_bio_info write_io[NR_PAGE_TYPE]; /* for write bios */ ... }; The code changes from this new data structure are trivial. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 68b8a2bdf6d8..38c1a89f54cc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -836,65 +836,65 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, { int rw = sync ? WRITE_SYNC : WRITE; enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct bio *bio = sbi->bio[btype]; + struct f2fs_bio_info *io = &sbi->write_io[btype]; struct bio_private *p; - if (!bio) + if (!io->bio) return; - sbi->bio[btype] = NULL; - if (type >= META_FLUSH) rw = WRITE_FLUSH_FUA; if (btype == META) rw |= REQ_META; - p = bio->bi_private; + p = io->bio->bi_private; p->sbi = sbi; - bio->bi_end_io = f2fs_end_io_write; + io->bio->bi_end_io = f2fs_end_io_write; - trace_f2fs_do_submit_bio(sbi->sb, btype, sync, bio); + trace_f2fs_do_submit_bio(sbi->sb, btype, sync, io->bio); if (type == META_FLUSH) { DECLARE_COMPLETION_ONSTACK(wait); p->is_sync = true; p->wait = &wait; - submit_bio(rw, bio); + submit_bio(rw, io->bio); wait_for_completion(&wait); } else { p->is_sync = false; - submit_bio(rw, bio); + submit_bio(rw, io->bio); } + io->bio = NULL; } void f2fs_submit_bio(struct f2fs_sb_info *sbi, enum page_type type, bool sync) { - enum page_type btype = PAGE_TYPE_OF_BIO(type); + struct f2fs_bio_info *io = &sbi->write_io[PAGE_TYPE_OF_BIO(type)]; - if (!sbi->bio[btype]) + if (!io->bio) return; - mutex_lock(&sbi->write_mutex[btype]); + mutex_lock(&io->io_mutex); do_submit_bio(sbi, type, sync); - mutex_unlock(&sbi->write_mutex[btype]); + mutex_unlock(&io->io_mutex); } static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page, block_t blk_addr, enum page_type type) { struct block_device *bdev = sbi->sb->s_bdev; + struct f2fs_bio_info *io = &sbi->write_io[type]; int bio_blocks; verify_block_addr(sbi, blk_addr); - mutex_lock(&sbi->write_mutex[type]); + mutex_lock(&io->io_mutex); inc_page_count(sbi, F2FS_WRITEBACK); - if (sbi->bio[type] && sbi->last_block_in_bio[type] != blk_addr - 1) + if (io->bio && io->last_block_in_bio != blk_addr - 1) do_submit_bio(sbi, type, false); alloc_new: - if (sbi->bio[type] == NULL) { + if (io->bio == NULL) { struct bio_private *priv; retry: priv = kmalloc(sizeof(struct bio_private), GFP_NOFS); @@ -904,9 +904,9 @@ retry: } bio_blocks = MAX_BIO_BLOCKS(max_hw_blocks(sbi)); - sbi->bio[type] = f2fs_bio_alloc(bdev, bio_blocks); - sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); - sbi->bio[type]->bi_private = priv; + io->bio = f2fs_bio_alloc(bdev, bio_blocks); + io->bio->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); + io->bio->bi_private = priv; /* * The end_io will be assigned at the sumbission phase. * Until then, let bio_add_page() merge consecutive IOs as much @@ -914,15 +914,15 @@ retry: */ } - if (bio_add_page(sbi->bio[type], page, PAGE_CACHE_SIZE, 0) < + if (bio_add_page(io->bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { do_submit_bio(sbi, type, false); goto alloc_new; } - sbi->last_block_in_bio[type] = blk_addr; + io->last_block_in_bio = blk_addr; - mutex_unlock(&sbi->write_mutex[type]); + mutex_unlock(&io->io_mutex); trace_f2fs_submit_write_page(page, blk_addr, type); } -- cgit v1.2.3 From 87b8872d5b4a8f9f61123ab913aff4f6047d8b53 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 20 Nov 2013 16:40:10 +0800 Subject: f2fs: adds a tracepoint for submit_read_page This patch adds a tracepoint for submit_read_page. Signed-off-by: Chao Yu [Jaegeuk Kim: integrate tracepoints of f2fs_submit_read(_write)_page] 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 38c1a89f54cc..d42426dc3706 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -923,7 +923,7 @@ retry: io->last_block_in_bio = blk_addr; mutex_unlock(&io->io_mutex); - trace_f2fs_submit_write_page(page, blk_addr, type); + trace_f2fs_submit_write_page(page, WRITE, type, blk_addr); } void f2fs_wait_on_page_writeback(struct page *page, -- cgit v1.2.3 From d4d288bc72c020d335868ce217695c4d5dfd74d0 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Sun, 24 Nov 2013 12:36:42 +0900 Subject: f2fs: adds a tracepoint for f2fs_submit_read_bio This patch adds a tracepoint for f2fs_submit_read_bio. Signed-off-by: Chao Yu [Jaegeuk Kim: integrate tracepoints of f2fs_submit_read(_write)_bio] 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 d42426dc3706..a1acaa025bde 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -844,6 +844,9 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, if (type >= META_FLUSH) rw = WRITE_FLUSH_FUA; + + trace_f2fs_submit_write_bio(sbi->sb, rw, btype, io->bio); + if (btype == META) rw |= REQ_META; @@ -851,8 +854,6 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, p->sbi = sbi; io->bio->bi_end_io = f2fs_end_io_write; - trace_f2fs_do_submit_bio(sbi->sb, btype, sync, io->bio); - if (type == META_FLUSH) { DECLARE_COMPLETION_ONSTACK(wait); p->is_sync = true; -- cgit v1.2.3 From 74de593af77b109f202c47e090c9e134c8882869 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 22 Nov 2013 09:09:59 +0800 Subject: f2fs: read contiguous sit entry pages by merging for mount performance Previously we read sit entries page one by one, this method lost the chance of reading contiguous page together. So we read pages as contiguous as possible for better mount performance. change log: o merge judgements/use 'Continue' or 'Break' instead of 'Goto' as Gu Zheng suggested. o add mark_page_accessed() before release page to delay VM reclaiming. o remove '*order' for simplification of function as Jaegeuk Kim suggested. Signed-off-by: Chao Yu [Jaegeuk Kim: fix a bug on the block address calculation] Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 101 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 26 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a1acaa025bde..6dd1dc16a9d5 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "f2fs.h" #include "segment.h" @@ -1706,41 +1707,89 @@ static int build_curseg(struct f2fs_sb_info *sbi) return restore_curseg_summaries(sbi); } +static int ra_sit_pages(struct f2fs_sb_info *sbi, int start, int nrpages) +{ + struct address_space *mapping = sbi->meta_inode->i_mapping; + struct page *page; + block_t blk_addr, prev_blk_addr = 0; + int sit_blk_cnt = SIT_BLK_CNT(sbi); + int blkno = start; + + for (; blkno < start + nrpages && blkno < sit_blk_cnt; blkno++) { + + blk_addr = current_sit_addr(sbi, blkno * SIT_ENTRY_PER_BLOCK); + + if (blkno != start && prev_blk_addr + 1 != blk_addr) + break; + prev_blk_addr = blk_addr; +repeat: + page = grab_cache_page(mapping, blk_addr); + if (!page) { + cond_resched(); + goto repeat; + } + if (PageUptodate(page)) { + mark_page_accessed(page); + f2fs_put_page(page, 1); + continue; + } + + submit_read_page(sbi, page, blk_addr, READ_SYNC); + + mark_page_accessed(page); + f2fs_put_page(page, 0); + } + + f2fs_submit_read_bio(sbi, READ_SYNC); + return blkno - start; +} + static void build_sit_entries(struct f2fs_sb_info *sbi) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); struct f2fs_summary_block *sum = curseg->sum_blk; - unsigned int start; - - for (start = 0; start < TOTAL_SEGS(sbi); start++) { - struct seg_entry *se = &sit_i->sentries[start]; - struct f2fs_sit_block *sit_blk; - struct f2fs_sit_entry sit; - struct page *page; - int i; + int sit_blk_cnt = SIT_BLK_CNT(sbi); + unsigned int i, start, end; + unsigned int readed, start_blk = 0; + int nrpages = MAX_BIO_BLOCKS(max_hw_blocks(sbi)); - mutex_lock(&curseg->curseg_mutex); - for (i = 0; i < sits_in_cursum(sum); i++) { - if (le32_to_cpu(segno_in_journal(sum, i)) == start) { - sit = sit_in_journal(sum, i); - mutex_unlock(&curseg->curseg_mutex); - goto got_it; + do { + readed = ra_sit_pages(sbi, start_blk, nrpages); + + start = start_blk * sit_i->sents_per_block; + end = (start_blk + readed) * sit_i->sents_per_block; + + for (; start < end && start < TOTAL_SEGS(sbi); start++) { + struct seg_entry *se = &sit_i->sentries[start]; + struct f2fs_sit_block *sit_blk; + struct f2fs_sit_entry sit; + struct page *page; + + mutex_lock(&curseg->curseg_mutex); + for (i = 0; i < sits_in_cursum(sum); i++) { + if (le32_to_cpu(segno_in_journal(sum, i)) == start) { + sit = sit_in_journal(sum, i); + mutex_unlock(&curseg->curseg_mutex); + goto got_it; + } } - } - mutex_unlock(&curseg->curseg_mutex); - page = get_current_sit_page(sbi, start); - sit_blk = (struct f2fs_sit_block *)page_address(page); - sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; - f2fs_put_page(page, 1); + mutex_unlock(&curseg->curseg_mutex); + + page = get_current_sit_page(sbi, start); + sit_blk = (struct f2fs_sit_block *)page_address(page); + sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; + f2fs_put_page(page, 1); got_it: - check_block_count(sbi, start, &sit); - seg_info_from_raw_sit(se, &sit); - if (sbi->segs_per_sec > 1) { - struct sec_entry *e = get_sec_entry(sbi, start); - e->valid_blocks += se->valid_blocks; + check_block_count(sbi, start, &sit); + seg_info_from_raw_sit(se, &sit); + if (sbi->segs_per_sec > 1) { + struct sec_entry *e = get_sec_entry(sbi, start); + e->valid_blocks += se->valid_blocks; + } } - } + start_blk += readed; + } while (start_blk < sit_blk_cnt); } static void init_free_segmap(struct f2fs_sb_info *sbi) -- cgit v1.2.3 From a709f4a2f22c0ebaed1d99aee63ab44ffc2ba3d0 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sun, 24 Nov 2013 14:42:23 +0900 Subject: f2fs: add detailed information of bio types in the tracepoints This patch inserts information of bio types in more detail. So, we can now see REQ_META and REQ_PRIO too. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 6dd1dc16a9d5..33ab378df5bd 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -846,11 +846,11 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, if (type >= META_FLUSH) rw = WRITE_FLUSH_FUA; - trace_f2fs_submit_write_bio(sbi->sb, rw, btype, io->bio); - if (btype == META) rw |= REQ_META; + trace_f2fs_submit_write_bio(sbi->sb, rw, btype, io->bio); + p = io->bio->bi_private; p->sbi = sbi; io->bio->bi_end_io = f2fs_end_io_write; -- cgit v1.2.3 From 03232305ff3cf44761f7ea271f7c9af5105392b9 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Sun, 24 Nov 2013 15:13:08 +0900 Subject: f2fs: send REQ_META or REQ_PRIO when reading meta area Let's send REQ_META or REQ_PRIO when reading meta area such as NAT/SIT etc. Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 33ab378df5bd..1e8371392dcd 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1734,13 +1734,13 @@ repeat: continue; } - submit_read_page(sbi, page, blk_addr, READ_SYNC); + submit_read_page(sbi, page, blk_addr, READ_SYNC | REQ_META); mark_page_accessed(page); f2fs_put_page(page, 0); } - f2fs_submit_read_bio(sbi, READ_SYNC); + f2fs_submit_read_bio(sbi, READ_SYNC | REQ_META); return blkno - start; } -- cgit v1.2.3 From f9a4e6df52edf8ce1040d1b8d340d31234a1bce3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 28 Nov 2013 12:44:05 +0900 Subject: f2fs: bug fix on bit overflow from 32bits to 64bits This patch fixes some bit overflows by the shift operations. Dan Carpenter reported potential bugs on bit overflows as follows. fs/f2fs/segment.c:910 submit_write_page() warn: should 'blk_addr << ((sbi)->log_blocksize - 9)' be a 64 bit type? fs/f2fs/checkpoint.c:429 get_valid_checkpoint() warn: should '1 << ()' be a 64 bit type? fs/f2fs/data.c:408 f2fs_readpage() warn: should 'blk_addr << ((sbi)->log_blocksize - 9)' be a 64 bit type? fs/f2fs/data.c:457 submit_read_page() warn: should 'blk_addr << ((sbi)->log_blocksize - 9)' be a 64 bit type? fs/f2fs/data.c:525 get_data_block_ro() warn: should 'i << blkbits' be a 64 bit type? Bug-Reported-by: Dan Carpenter Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1e8371392dcd..03878634a0fe 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -270,8 +270,8 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) static void f2fs_issue_discard(struct f2fs_sb_info *sbi, block_t blkstart, block_t blklen) { - sector_t start = ((sector_t)blkstart) << sbi->log_sectors_per_block; - sector_t len = ((sector_t)blklen) << sbi->log_sectors_per_block; + sector_t start = SECTOR_FROM_BLOCK(sbi, blkstart); + sector_t len = SECTOR_FROM_BLOCK(sbi, blklen); blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0); trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); } -- cgit v1.2.3 From 187b5b8b3dfcfc73126f2743c89cc47df3bf07be Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 30 Nov 2013 10:10:31 +0900 Subject: f2fs: remove the own bi_private allocation Previously f2fs allocates its own bi_private data structure all the time even though we don't use it. But, can we remove this bi_private allocation? This patch removes such the additional bi_private allocation. 1. Retrieve f2fs_sb_info from its page->mapping->host->i_sb. - This removes the usecases of bi_private in end_io. 2. Use bi_private only when we really need it. - The bi_private is used only when the checkpoint procedure is conducted. - When conducting the checkpoint, f2fs submits a META_FLUSH bio to wait its bio completion. - Since we have no dependancies to remove bi_private now, let's just use bi_private pointer as the completion pointer. Reviewed-by: Gu Zheng Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 03878634a0fe..0db40271f0d8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -791,7 +791,7 @@ static void f2fs_end_io_write(struct bio *bio, int err) { const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; - struct bio_private *p = bio->bi_private; + struct f2fs_sb_info *sbi = F2FS_SB(bvec->bv_page->mapping->host->i_sb); do { struct page *page = bvec->bv_page; @@ -802,21 +802,21 @@ static void f2fs_end_io_write(struct bio *bio, int err) SetPageError(page); if (page->mapping) set_bit(AS_EIO, &page->mapping->flags); - set_ckpt_flags(p->sbi->ckpt, CP_ERROR_FLAG); - p->sbi->sb->s_flags |= MS_RDONLY; + + set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); + sbi->sb->s_flags |= MS_RDONLY; } end_page_writeback(page); - dec_page_count(p->sbi, F2FS_WRITEBACK); + dec_page_count(sbi, F2FS_WRITEBACK); } while (bvec >= bio->bi_io_vec); - if (p->is_sync) - complete(p->wait); + if (bio->bi_private) + complete(bio->bi_private); - if (!get_pages(p->sbi, F2FS_WRITEBACK) && - !list_empty(&p->sbi->cp_wait.task_list)) - wake_up(&p->sbi->cp_wait); + if (!get_pages(sbi, F2FS_WRITEBACK) && + !list_empty(&sbi->cp_wait.task_list)) + wake_up(&sbi->cp_wait); - kfree(p); bio_put(bio); } @@ -838,7 +838,6 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, int rw = sync ? WRITE_SYNC : WRITE; enum page_type btype = PAGE_TYPE_OF_BIO(type); struct f2fs_bio_info *io = &sbi->write_io[btype]; - struct bio_private *p; if (!io->bio) return; @@ -851,18 +850,16 @@ static void do_submit_bio(struct f2fs_sb_info *sbi, trace_f2fs_submit_write_bio(sbi->sb, rw, btype, io->bio); - p = io->bio->bi_private; - p->sbi = sbi; - io->bio->bi_end_io = f2fs_end_io_write; - + /* + * META_FLUSH is only from the checkpoint procedure, and we should wait + * this metadata bio for FS consistency. + */ if (type == META_FLUSH) { DECLARE_COMPLETION_ONSTACK(wait); - p->is_sync = true; - p->wait = &wait; + io->bio->bi_private = &wait; submit_bio(rw, io->bio); wait_for_completion(&wait); } else { - p->is_sync = false; submit_bio(rw, io->bio); } io->bio = NULL; @@ -897,18 +894,10 @@ static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page, do_submit_bio(sbi, type, false); alloc_new: if (io->bio == NULL) { - struct bio_private *priv; -retry: - priv = kmalloc(sizeof(struct bio_private), GFP_NOFS); - if (!priv) { - cond_resched(); - goto retry; - } - bio_blocks = MAX_BIO_BLOCKS(max_hw_blocks(sbi)); io->bio = f2fs_bio_alloc(bdev, bio_blocks); io->bio->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); - io->bio->bi_private = priv; + io->bio->bi_end_io = f2fs_end_io_write; /* * The end_io will be assigned at the sumbission phase. * Until then, let bio_add_page() merge consecutive IOs as much -- cgit v1.2.3 From 93dfe2ac516250755f7d5edd438b0ce67c0e3aa6 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Sat, 30 Nov 2013 12:51:14 +0900 Subject: f2fs: refactor bio-related operations This patch integrates redundant bio operations on read and write IOs. 1. Move bio-related codes to the top of data.c. 2. Replace f2fs_submit_bio with f2fs_submit_merged_bio, which handles read bios additionally. 3. Introduce __submit_merged_bio to submit the merged bio. 4. Change f2fs_readpage to f2fs_submit_page_bio. 5. Introduce f2fs_submit_page_mbio to integrate previous submit_read_page and submit_write_page. Reviewed-by: Gu Zheng Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 164 ++++++------------------------------------------------ 1 file changed, 17 insertions(+), 147 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0db40271f0d8..ca9adf5914cc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -787,146 +787,6 @@ static const struct segment_allocation default_salloc_ops = { .allocate_segment = allocate_segment_by_default, }; -static void f2fs_end_io_write(struct bio *bio, int err) -{ - const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; - struct f2fs_sb_info *sbi = F2FS_SB(bvec->bv_page->mapping->host->i_sb); - - do { - struct page *page = bvec->bv_page; - - if (--bvec >= bio->bi_io_vec) - prefetchw(&bvec->bv_page->flags); - if (!uptodate) { - SetPageError(page); - if (page->mapping) - set_bit(AS_EIO, &page->mapping->flags); - - set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); - sbi->sb->s_flags |= MS_RDONLY; - } - end_page_writeback(page); - dec_page_count(sbi, F2FS_WRITEBACK); - } while (bvec >= bio->bi_io_vec); - - if (bio->bi_private) - complete(bio->bi_private); - - if (!get_pages(sbi, F2FS_WRITEBACK) && - !list_empty(&sbi->cp_wait.task_list)) - wake_up(&sbi->cp_wait); - - bio_put(bio); -} - -struct bio *f2fs_bio_alloc(struct block_device *bdev, int npages) -{ - struct bio *bio; - - /* No failure on bio allocation */ - bio = bio_alloc(GFP_NOIO, npages); - bio->bi_bdev = bdev; - bio->bi_private = NULL; - - return bio; -} - -static void do_submit_bio(struct f2fs_sb_info *sbi, - enum page_type type, bool sync) -{ - int rw = sync ? WRITE_SYNC : WRITE; - enum page_type btype = PAGE_TYPE_OF_BIO(type); - struct f2fs_bio_info *io = &sbi->write_io[btype]; - - if (!io->bio) - return; - - if (type >= META_FLUSH) - rw = WRITE_FLUSH_FUA; - - if (btype == META) - rw |= REQ_META; - - trace_f2fs_submit_write_bio(sbi->sb, rw, btype, io->bio); - - /* - * META_FLUSH is only from the checkpoint procedure, and we should wait - * this metadata bio for FS consistency. - */ - if (type == META_FLUSH) { - DECLARE_COMPLETION_ONSTACK(wait); - io->bio->bi_private = &wait; - submit_bio(rw, io->bio); - wait_for_completion(&wait); - } else { - submit_bio(rw, io->bio); - } - io->bio = NULL; -} - -void f2fs_submit_bio(struct f2fs_sb_info *sbi, enum page_type type, bool sync) -{ - struct f2fs_bio_info *io = &sbi->write_io[PAGE_TYPE_OF_BIO(type)]; - - if (!io->bio) - return; - - mutex_lock(&io->io_mutex); - do_submit_bio(sbi, type, sync); - mutex_unlock(&io->io_mutex); -} - -static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page, - block_t blk_addr, enum page_type type) -{ - struct block_device *bdev = sbi->sb->s_bdev; - struct f2fs_bio_info *io = &sbi->write_io[type]; - int bio_blocks; - - verify_block_addr(sbi, blk_addr); - - mutex_lock(&io->io_mutex); - - inc_page_count(sbi, F2FS_WRITEBACK); - - if (io->bio && io->last_block_in_bio != blk_addr - 1) - do_submit_bio(sbi, type, false); -alloc_new: - if (io->bio == NULL) { - bio_blocks = MAX_BIO_BLOCKS(max_hw_blocks(sbi)); - io->bio = f2fs_bio_alloc(bdev, bio_blocks); - io->bio->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); - io->bio->bi_end_io = f2fs_end_io_write; - /* - * The end_io will be assigned at the sumbission phase. - * Until then, let bio_add_page() merge consecutive IOs as much - * as possible. - */ - } - - if (bio_add_page(io->bio, page, PAGE_CACHE_SIZE, 0) < - PAGE_CACHE_SIZE) { - do_submit_bio(sbi, type, false); - goto alloc_new; - } - - io->last_block_in_bio = blk_addr; - - mutex_unlock(&io->io_mutex); - trace_f2fs_submit_write_page(page, WRITE, type, blk_addr); -} - -void f2fs_wait_on_page_writeback(struct page *page, - enum page_type type, bool sync) -{ - struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); - if (PageWriteback(page)) { - f2fs_submit_bio(sbi, type, sync); - wait_on_page_writeback(page); - } -} - static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) { struct curseg_info *curseg = CURSEG_I(sbi, type); @@ -1040,7 +900,7 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); /* writeout dirty page into bdev */ - submit_write_page(sbi, page, *new_blkaddr, p_type); + f2fs_submit_page_mbio(sbi, page, *new_blkaddr, p_type, WRITE); mutex_unlock(&curseg->curseg_mutex); } @@ -1048,7 +908,7 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { set_page_writeback(page); - submit_write_page(sbi, page, page->index, META); + f2fs_submit_page_mbio(sbi, page, page->index, META, WRITE); } void write_node_page(struct f2fs_sb_info *sbi, struct page *page, @@ -1078,7 +938,7 @@ void write_data_page(struct inode *inode, struct page *page, void rewrite_data_page(struct f2fs_sb_info *sbi, struct page *page, block_t old_blk_addr) { - submit_write_page(sbi, page, old_blk_addr, DATA); + f2fs_submit_page_mbio(sbi, page, old_blk_addr, DATA, WRITE); } void recover_data_page(struct f2fs_sb_info *sbi, @@ -1165,8 +1025,8 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, /* rewrite node page */ set_page_writeback(page); - submit_write_page(sbi, page, new_blkaddr, NODE); - f2fs_submit_bio(sbi, NODE, true); + f2fs_submit_page_mbio(sbi, page, new_blkaddr, NODE, WRITE); + f2fs_submit_merged_bio(sbi, NODE, true, WRITE); refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); locate_dirty_segment(sbi, old_cursegno); @@ -1176,6 +1036,16 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, mutex_unlock(&curseg->curseg_mutex); } +void f2fs_wait_on_page_writeback(struct page *page, + enum page_type type, bool sync) +{ + struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); + if (PageWriteback(page)) { + f2fs_submit_merged_bio(sbi, type, sync, WRITE); + wait_on_page_writeback(page); + } +} + static int read_compacted_summaries(struct f2fs_sb_info *sbi) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); @@ -1723,13 +1593,13 @@ repeat: continue; } - submit_read_page(sbi, page, blk_addr, READ_SYNC | REQ_META); + f2fs_submit_page_mbio(sbi, page, blk_addr, META, READ); mark_page_accessed(page); f2fs_put_page(page, 0); } - f2fs_submit_read_bio(sbi, READ_SYNC | REQ_META); + f2fs_submit_merged_bio(sbi, META, true, READ); return blkno - start; } -- cgit v1.2.3 From 63a0b7cb33d85aeb0df39b984c08e234db4925d1 Mon Sep 17 00:00:00 2001 From: Fan Li Date: Mon, 9 Dec 2013 16:09:00 +0800 Subject: f2fs: merge pages with the same sync_mode flag Previously f2fs submits most of write requests using WRITE_SYNC, but f2fs_write_data_pages submits last write requests by sync_mode flags callers pass. This causes a performance problem since continuous pages with different sync flags can't be merged in cfq IO scheduler(thanks yu chao for pointing it out), and synchronous requests often take more time. This patch makes the following modifies to DATA writebacks: 1. every page will be written back using the sync mode caller pass. 2. only pages with the same sync mode can be merged in one bio request. These changes are restricted to DATA pages.Other types of writebacks are modified To remain synchronous. In my test with tiotest, f2fs sequence write performance is improved by about 7%-10% , and this patch has no obvious impact on other performance tests. Signed-off-by: Fan Li Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index ca9adf5914cc..e5dc41114867 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -856,12 +856,13 @@ static int __get_segment_type(struct page *page, enum page_type p_type) static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, enum page_type p_type) + struct f2fs_summary *sum, enum page_type p_type, + struct writeback_control *wbc) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; unsigned int old_cursegno; - int type; + int type, rw = WRITE; type = __get_segment_type(page, p_type); curseg = CURSEG_I(sbi, type); @@ -900,7 +901,9 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); /* writeout dirty page into bdev */ - f2fs_submit_page_mbio(sbi, page, *new_blkaddr, p_type, WRITE); + if (wbc->sync_mode == WB_SYNC_ALL) + rw |= WRITE_SYNC; + f2fs_submit_page_mbio(sbi, page, *new_blkaddr, p_type, rw); mutex_unlock(&curseg->curseg_mutex); } @@ -915,13 +918,16 @@ void write_node_page(struct f2fs_sb_info *sbi, struct page *page, unsigned int nid, block_t old_blkaddr, block_t *new_blkaddr) { struct f2fs_summary sum; + struct writeback_control wbc = { + .sync_mode = 1, + }; set_summary(&sum, nid, 0, 0); - do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, NODE); + do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, NODE, &wbc); } void write_data_page(struct inode *inode, struct page *page, struct dnode_of_data *dn, block_t old_blkaddr, - block_t *new_blkaddr) + block_t *new_blkaddr, struct writeback_control *wbc) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_summary sum; @@ -932,13 +938,14 @@ void write_data_page(struct inode *inode, struct page *page, set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); do_write_page(sbi, page, old_blkaddr, - new_blkaddr, &sum, DATA); + new_blkaddr, &sum, DATA, wbc); } void rewrite_data_page(struct f2fs_sb_info *sbi, struct page *page, - block_t old_blk_addr) + block_t old_blk_addr, struct writeback_control *wbc) { - f2fs_submit_page_mbio(sbi, page, old_blk_addr, DATA, WRITE); + int rw = wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE; + f2fs_submit_page_mbio(sbi, page, old_blk_addr, DATA, rw); } void recover_data_page(struct f2fs_sb_info *sbi, @@ -1025,7 +1032,7 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, /* rewrite node page */ set_page_writeback(page); - f2fs_submit_page_mbio(sbi, page, new_blkaddr, NODE, WRITE); + f2fs_submit_page_mbio(sbi, page, new_blkaddr, NODE, WRITE_SYNC); f2fs_submit_merged_bio(sbi, NODE, true, WRITE); refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); @@ -1593,7 +1600,7 @@ repeat: continue; } - f2fs_submit_page_mbio(sbi, page, blk_addr, META, READ); + f2fs_submit_page_mbio(sbi, page, blk_addr, META, READ_SYNC); mark_page_accessed(page); f2fs_put_page(page, 0); -- cgit v1.2.3 From 458e6197c37de53f7be0a837644daabb900c3036 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 11 Dec 2013 13:54:01 +0900 Subject: f2fs: refactor bio->rw handling This patch introduces f2fs_io_info to mitigate the complex parameter list. struct f2fs_io_info { enum page_type type; /* contains DATA/NODE/META/META_FLUSH */ int rw; /* contains R/RS/W/WS */ int rw_flag; /* contains REQ_META/REQ_PRIO */ } 1. f2fs_write_data_pages - DATA - WRITE_SYNC is set when wbc->WB_SYNC_ALL. 2. sync_node_pages - NODE - WRITE_SYNC all the time 3. sync_meta_pages - META - WRITE_SYNC all the time - REQ_META | REQ_PRIO all the time ** f2fs_submit_merged_bio() handles META_FLUSH. 4. ra_nat_pages, ra_sit_pages, ra_sum_pages - META - READ_SYNC Cc: Fan Li Cc: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 70 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 28 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index e5dc41114867..0b2e8ceec983 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -856,15 +856,14 @@ static int __get_segment_type(struct page *page, enum page_type p_type) static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, enum page_type p_type, - struct writeback_control *wbc) + struct f2fs_summary *sum, struct f2fs_io_info *fio) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; unsigned int old_cursegno; - int type, rw = WRITE; + int type; - type = __get_segment_type(page, p_type); + type = __get_segment_type(page, fio->type); curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); @@ -897,55 +896,60 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); mutex_unlock(&sit_i->sentry_lock); - if (p_type == NODE) + if (fio->type == NODE) fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); /* writeout dirty page into bdev */ - if (wbc->sync_mode == WB_SYNC_ALL) - rw |= WRITE_SYNC; - f2fs_submit_page_mbio(sbi, page, *new_blkaddr, p_type, rw); + f2fs_submit_page_mbio(sbi, page, *new_blkaddr, fio); mutex_unlock(&curseg->curseg_mutex); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { + struct f2fs_io_info fio = { + .type = META, + .rw = WRITE_SYNC, + .rw_flag = REQ_META | REQ_PRIO + }; + set_page_writeback(page); - f2fs_submit_page_mbio(sbi, page, page->index, META, WRITE); + f2fs_submit_page_mbio(sbi, page, page->index, &fio); } void write_node_page(struct f2fs_sb_info *sbi, struct page *page, unsigned int nid, block_t old_blkaddr, block_t *new_blkaddr) { struct f2fs_summary sum; - struct writeback_control wbc = { - .sync_mode = 1, + struct f2fs_io_info fio = { + .type = NODE, + .rw = WRITE_SYNC, + .rw_flag = 0 }; + set_summary(&sum, nid, 0, 0); - do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, NODE, &wbc); + do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, &fio); } -void write_data_page(struct inode *inode, struct page *page, - struct dnode_of_data *dn, block_t old_blkaddr, - block_t *new_blkaddr, struct writeback_control *wbc) +void write_data_page(struct page *page, struct dnode_of_data *dn, + block_t *new_blkaddr, struct f2fs_io_info *fio) { - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); struct f2fs_summary sum; struct node_info ni; - f2fs_bug_on(old_blkaddr == NULL_ADDR); + f2fs_bug_on(dn->data_blkaddr == NULL_ADDR); get_node_info(sbi, dn->nid, &ni); set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); - do_write_page(sbi, page, old_blkaddr, - new_blkaddr, &sum, DATA, wbc); + do_write_page(sbi, page, dn->data_blkaddr, new_blkaddr, &sum, fio); } -void rewrite_data_page(struct f2fs_sb_info *sbi, struct page *page, - block_t old_blk_addr, struct writeback_control *wbc) +void rewrite_data_page(struct page *page, block_t old_blkaddr, struct f2fs_io_info *fio) { - int rw = wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC : WRITE; - f2fs_submit_page_mbio(sbi, page, old_blk_addr, DATA, rw); + struct inode *inode = page->mapping->host; + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + f2fs_submit_page_mbio(sbi, page, old_blkaddr, fio); } void recover_data_page(struct f2fs_sb_info *sbi, @@ -1004,6 +1008,11 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, unsigned int segno, old_cursegno; block_t next_blkaddr = next_blkaddr_of_node(page); unsigned int next_segno = GET_SEGNO(sbi, next_blkaddr); + struct f2fs_io_info fio = { + .type = NODE, + .rw = WRITE_SYNC, + .rw_flag = 0 + }; curseg = CURSEG_I(sbi, type); @@ -1032,8 +1041,8 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, /* rewrite node page */ set_page_writeback(page); - f2fs_submit_page_mbio(sbi, page, new_blkaddr, NODE, WRITE_SYNC); - f2fs_submit_merged_bio(sbi, NODE, true, WRITE); + f2fs_submit_page_mbio(sbi, page, new_blkaddr, &fio); + f2fs_submit_merged_bio(sbi, NODE, WRITE); refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); locate_dirty_segment(sbi, old_cursegno); @@ -1048,7 +1057,7 @@ void f2fs_wait_on_page_writeback(struct page *page, { struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); if (PageWriteback(page)) { - f2fs_submit_merged_bio(sbi, type, sync, WRITE); + f2fs_submit_merged_bio(sbi, type, WRITE); wait_on_page_writeback(page); } } @@ -1580,6 +1589,11 @@ static int ra_sit_pages(struct f2fs_sb_info *sbi, int start, int nrpages) block_t blk_addr, prev_blk_addr = 0; int sit_blk_cnt = SIT_BLK_CNT(sbi); int blkno = start; + struct f2fs_io_info fio = { + .type = META, + .rw = READ_SYNC, + .rw_flag = REQ_META | REQ_PRIO + }; for (; blkno < start + nrpages && blkno < sit_blk_cnt; blkno++) { @@ -1600,13 +1614,13 @@ repeat: continue; } - f2fs_submit_page_mbio(sbi, page, blk_addr, META, READ_SYNC); + f2fs_submit_page_mbio(sbi, page, blk_addr, &fio); mark_page_accessed(page); f2fs_put_page(page, 0); } - f2fs_submit_merged_bio(sbi, META, true, READ); + f2fs_submit_merged_bio(sbi, META, READ); return blkno - start; } -- cgit v1.2.3 From 216fbd64437452d23db54ae845916facd7215caa Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 7 Nov 2013 13:13:42 +0900 Subject: f2fs: introduce sysfs entry to control in-place-update policy This patch introduces new sysfs entries for users to control the policy of in-place-updates, namely IPU, in f2fs. Sometimes f2fs suffers from performance degradation due to its out-of-place update policy that produces many additional node block writes. If the storage performance is very dependant on the amount of data writes instead of IO patterns, we'd better drop this out-of-place update policy. This patch suggests 5 polcies and their triggering conditions as follows. [sysfs entry name = ipu_policy] 0: F2FS_IPU_FORCE all the time, 1: F2FS_IPU_SSR if SSR mode is activated, 2: F2FS_IPU_UTIL if FS utilization is over threashold, 3: F2FS_IPU_SSR_UTIL if SSR mode is activated and FS utilization is over threashold, 4: F2FS_IPU_DISABLE disable IPU. (=default option) [sysfs entry name = min_ipu_util] This parameter controls the threshold to trigger in-place-updates. The number indicates percentage of the filesystem utilization, and used by F2FS_IPU_UTIL and F2FS_IPU_SSR_UTIL policies. For more details, see need_inplace_update() in segment.h. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0b2e8ceec983..5b890ce74b15 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1799,6 +1799,8 @@ int build_segment_manager(struct f2fs_sb_info *sbi) sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main); sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); sm_info->rec_prefree_segments = DEF_RECLAIM_PREFREE_SEGMENTS; + sm_info->ipu_policy = F2FS_IPU_DISABLE; + sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; INIT_LIST_HEAD(&sm_info->discard_list); sm_info->nr_discards = 0; -- cgit v1.2.3 From bfad7c2d40332be6a1d7a89660bceb0f6ea1d73a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 16 Dec 2013 19:04:05 +0900 Subject: f2fs: introduce a new direct_IO write path Previously, f2fs doesn't support direct IOs with high performance, which throws every write requests via the buffered write path, resulting in highly performance degradation due to memory opeations like copy_from_user. This patch introduces a new direct IO path in which every write requests are processed by generic blockdev_direct_IO() with enhanced get_block function. The get_data_block() in f2fs handles: 1. if original data blocks are allocates, then give them to blockdev. 2. otherwise, a. preallocate requested block addresses b. do not use extent cache for better performance c. give the block addresses to blockdev This policy induces that: - new allocated data are sequentially written to the disk - updated data are randomly written to the disk. - f2fs gives consistency on its file meta, not file data. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 5b890ce74b15..9f8bdd02e3a8 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -854,16 +854,14 @@ static int __get_segment_type(struct page *page, enum page_type p_type) return __get_segment_type_6(page, p_type); } -static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, - block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, struct f2fs_io_info *fio) +void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, + block_t old_blkaddr, block_t *new_blkaddr, + struct f2fs_summary *sum, int type) { struct sit_info *sit_i = SIT_I(sbi); struct curseg_info *curseg; unsigned int old_cursegno; - int type; - type = __get_segment_type(page, fio->type); curseg = CURSEG_I(sbi, type); mutex_lock(&curseg->curseg_mutex); @@ -896,13 +894,22 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); mutex_unlock(&sit_i->sentry_lock); - if (fio->type == NODE) + if (page && IS_NODESEG(type)) fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); + mutex_unlock(&curseg->curseg_mutex); +} + +static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, + block_t old_blkaddr, block_t *new_blkaddr, + struct f2fs_summary *sum, struct f2fs_io_info *fio) +{ + int type = __get_segment_type(page, fio->type); + + allocate_data_block(sbi, page, old_blkaddr, new_blkaddr, sum, type); + /* writeout dirty page into bdev */ f2fs_submit_page_mbio(sbi, page, *new_blkaddr, fio); - - mutex_unlock(&curseg->curseg_mutex); } void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) -- cgit v1.2.3 From 7e8f23081ab3a11de90d7389f2c6fd44676c8df9 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Fri, 20 Dec 2013 18:17:49 +0800 Subject: f2fs: remove the rw_flag domain from f2fs_io_info When using the f2fs_io_info in the low level, we still need to merge the rw and rw_flag, so use the rw to hold all the io flags directly, and remove the rw_flag field. ps.It is based on the previous patch: f2fs: move all the bio initialization into __bio_alloc Signed-off-by: Gu Zheng Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9f8bdd02e3a8..555ae7693ea0 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -916,8 +916,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { struct f2fs_io_info fio = { .type = META, - .rw = WRITE_SYNC, - .rw_flag = REQ_META | REQ_PRIO + .rw = WRITE_SYNC | REQ_META | REQ_PRIO }; set_page_writeback(page); @@ -931,7 +930,6 @@ void write_node_page(struct f2fs_sb_info *sbi, struct page *page, struct f2fs_io_info fio = { .type = NODE, .rw = WRITE_SYNC, - .rw_flag = 0 }; set_summary(&sum, nid, 0, 0); @@ -1018,7 +1016,6 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, struct f2fs_io_info fio = { .type = NODE, .rw = WRITE_SYNC, - .rw_flag = 0 }; curseg = CURSEG_I(sbi, type); @@ -1598,8 +1595,7 @@ static int ra_sit_pages(struct f2fs_sb_info *sbi, int start, int nrpages) int blkno = start; struct f2fs_io_info fio = { .type = META, - .rw = READ_SYNC, - .rw_flag = REQ_META | REQ_PRIO + .rw = READ_SYNC | REQ_META | REQ_PRIO }; for (; blkno < start + nrpages && blkno < sit_blk_cnt; blkno++) { -- cgit v1.2.3 From fb5566da9181d33ecdd9892e44f90320e7d4cc9f Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 8 Jan 2014 10:09:51 +0900 Subject: f2fs: improve write performance under frequent fsync calls When considering a bunch of data writes with very frequent fsync calls, we are able to think the following performance regression. N: Node IO, D: Data IO, IO scheduler: cfq Issue pending IOs D1 D2 D3 D4 D1 D2 D3 D4 N1 D2 D3 D4 N1 N2 N1 D3 D4 N2 D1 --> N1 can be selected by cfq becase of the same priority of N and D. Then D3 and D4 would be delayed, resuling in performance degradation. So, when processing the fsync call, it'd better give higher priority to data IOs than node IOs by assigning WRITE and WRITE_SYNC respectively. This patch improves the random wirte performance with frequent fsync calls by up to 10%. Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 555ae7693ea0..5f84639354e3 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -924,16 +924,12 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) } void write_node_page(struct f2fs_sb_info *sbi, struct page *page, + struct f2fs_io_info *fio, unsigned int nid, block_t old_blkaddr, block_t *new_blkaddr) { struct f2fs_summary sum; - struct f2fs_io_info fio = { - .type = NODE, - .rw = WRITE_SYNC, - }; - set_summary(&sum, nid, 0, 0); - do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, &fio); + do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, fio); } void write_data_page(struct page *page, struct dnode_of_data *dn, -- cgit v1.2.3 From 5514f0aadddcdfaaaea697b60203f5402552eb7b Mon Sep 17 00:00:00 2001 From: Yuan Zhong Date: Fri, 10 Jan 2014 07:26:14 +0000 Subject: f2fs: remove the needless parameter of f2fs_wait_on_page_writeback "boo sync" parameter is never referenced in f2fs_wait_on_page_writeback. We should remove this parameter. Signed-off-by: Yuan Zhong 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 5f84639354e3..a934e6f2738b 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1053,7 +1053,7 @@ void rewrite_node_page(struct f2fs_sb_info *sbi, } void f2fs_wait_on_page_writeback(struct page *page, - enum page_type type, bool sync) + enum page_type type) { struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); if (PageWriteback(page)) { -- cgit v1.2.3 From 6c311ec6c2d9e015d454b4e3fda8008b5bebf316 Mon Sep 17 00:00:00 2001 From: Chris Fries Date: Fri, 17 Jan 2014 14:44:39 -0600 Subject: f2fs: clean checkpatch warnings Fixed a variety of trivial checkpatch warnings. The only delta should be some minor formatting on log strings that were split / too long. Signed-off-by: Chris Fries Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs/f2fs/segment.c') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index a934e6f2738b..e82423fbcb9d 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -946,7 +946,8 @@ void write_data_page(struct page *page, struct dnode_of_data *dn, do_write_page(sbi, page, dn->data_blkaddr, new_blkaddr, &sum, fio); } -void rewrite_data_page(struct page *page, block_t old_blkaddr, struct f2fs_io_info *fio) +void rewrite_data_page(struct page *page, block_t old_blkaddr, + struct f2fs_io_info *fio) { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); @@ -1647,7 +1648,8 @@ static void build_sit_entries(struct f2fs_sb_info *sbi) mutex_lock(&curseg->curseg_mutex); for (i = 0; i < sits_in_cursum(sum); i++) { - if (le32_to_cpu(segno_in_journal(sum, i)) == start) { + if (le32_to_cpu(segno_in_journal(sum, i)) + == start) { sit = sit_in_journal(sum, i); mutex_unlock(&curseg->curseg_mutex); goto got_it; -- cgit v1.2.3 From 9df27d982d58b9372bc476fb6b9bab861d617029 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Mon, 20 Jan 2014 18:37:04 +0800 Subject: f2fs: add help function META_MAPPING Introduce help function META_MAPPING() to get the cache meta blocks' address space. Signed-off-by: Gu Zheng 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 e82423fbcb9d..7caac5f2ca9e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -1585,7 +1585,7 @@ static int build_curseg(struct f2fs_sb_info *sbi) static int ra_sit_pages(struct f2fs_sb_info *sbi, int start, int nrpages) { - struct address_space *mapping = sbi->meta_inode->i_mapping; + struct address_space *mapping = META_MAPPING(sbi); struct page *page; block_t blk_addr, prev_blk_addr = 0; int sit_blk_cnt = SIT_BLK_CNT(sbi); -- cgit v1.2.3