Commit 0c3a5797 authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim

f2fs: introduce f2fs_submit_merged_bio_cond

f2fs use single bio buffer per type data (META/NODE/DATA) for caching
writes locating in continuous block address as many as possible, after
submitting, these writes may be still cached in bio buffer, so we have
to flush cached writes in bio buffer by calling f2fs_submit_merged_bio.

Unfortunately, in the scenario of high concurrency, bio buffer could be
flushed by someone else before we submit it as below reasons:
a) there is no space in bio buffer.
b) add a request of different type (SYNC, ASYNC).
c) add a discontinuous block address.

For this condition, f2fs_submit_merged_bio will be devastating, because
it could break the following merging of writes in bio buffer, split one
big bio into two smaller one.

This patch introduces f2fs_submit_merged_bio_cond which can do a
conditional submitting with bio buffer, before submitting it will judge
whether:
 - page in DATA type bio buffer is matching with specified page;
 - page in DATA type bio buffer is belong to specified inode;
 - page in NODE type bio buffer is belong to specified inode;
If there is no eligible page in bio buffer, we will skip submitting step,
result in gaining more chance to merge consecutive block IOs in bio cache.
Signed-off-by: default avatarChao Yu <chao2.yu@samsung.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent d48dfc20
...@@ -234,10 +234,15 @@ static int f2fs_write_meta_page(struct page *page, ...@@ -234,10 +234,15 @@ static int f2fs_write_meta_page(struct page *page,
write_meta_page(sbi, page); write_meta_page(sbi, page);
dec_page_count(sbi, F2FS_DIRTY_META); dec_page_count(sbi, F2FS_DIRTY_META);
if (wbc->for_reclaim)
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, META, WRITE);
unlock_page(page); unlock_page(page);
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, META, WRITE); f2fs_submit_merged_bio(sbi, META, WRITE);
return 0; return 0;
redirty_out: redirty_out:
......
...@@ -114,20 +114,18 @@ static void __submit_merged_bio(struct f2fs_bio_info *io) ...@@ -114,20 +114,18 @@ static void __submit_merged_bio(struct f2fs_bio_info *io)
io->bio = NULL; io->bio = NULL;
} }
bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, static bool __has_merged_page(struct f2fs_bio_info *io, struct inode *inode,
enum page_type type) struct page *page, nid_t ino)
{ {
enum page_type btype = PAGE_TYPE_OF_BIO(type);
struct f2fs_bio_info *io = &sbi->write_io[btype];
struct bio_vec *bvec; struct bio_vec *bvec;
struct page *target; struct page *target;
int i; int i;
down_read(&io->io_rwsem); if (!io->bio)
if (!io->bio) {
up_read(&io->io_rwsem);
return false; return false;
}
if (!inode && !page && !ino)
return true;
bio_for_each_segment_all(bvec, io->bio, i) { bio_for_each_segment_all(bvec, io->bio, i) {
...@@ -142,18 +140,34 @@ bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page, ...@@ -142,18 +140,34 @@ bool is_merged_page(struct f2fs_sb_info *sbi, struct page *page,
target = ctx->w.control_page; target = ctx->w.control_page;
} }
if (page == target) { if (inode && inode == target->mapping->host)
up_read(&io->io_rwsem); return true;
if (page && page == target)
return true;
if (ino && ino == ino_of_node(target))
return true; return true;
}
} }
up_read(&io->io_rwsem);
return false; return false;
} }
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, static bool has_merged_page(struct f2fs_sb_info *sbi, struct inode *inode,
enum page_type type, int rw) struct page *page, nid_t ino,
enum page_type type)
{
enum page_type btype = PAGE_TYPE_OF_BIO(type);
struct f2fs_bio_info *io = &sbi->write_io[btype];
bool ret;
down_read(&io->io_rwsem);
ret = __has_merged_page(io, inode, page, ino);
up_read(&io->io_rwsem);
return ret;
}
static void __f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
struct inode *inode, struct page *page,
nid_t ino, enum page_type type, int rw)
{ {
enum page_type btype = PAGE_TYPE_OF_BIO(type); enum page_type btype = PAGE_TYPE_OF_BIO(type);
struct f2fs_bio_info *io; struct f2fs_bio_info *io;
...@@ -162,6 +176,9 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, ...@@ -162,6 +176,9 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
down_write(&io->io_rwsem); down_write(&io->io_rwsem);
if (!__has_merged_page(io, inode, page, ino))
goto out;
/* change META to META_FLUSH in the checkpoint procedure */ /* change META to META_FLUSH in the checkpoint procedure */
if (type >= META_FLUSH) { if (type >= META_FLUSH) {
io->fio.type = META_FLUSH; io->fio.type = META_FLUSH;
...@@ -171,9 +188,24 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, ...@@ -171,9 +188,24 @@ void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi,
io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO; io->fio.rw = WRITE_FLUSH_FUA | REQ_META | REQ_PRIO;
} }
__submit_merged_bio(io); __submit_merged_bio(io);
out:
up_write(&io->io_rwsem); up_write(&io->io_rwsem);
} }
void f2fs_submit_merged_bio(struct f2fs_sb_info *sbi, enum page_type type,
int rw)
{
__f2fs_submit_merged_bio(sbi, NULL, NULL, 0, type, rw);
}
void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *sbi,
struct inode *inode, struct page *page,
nid_t ino, enum page_type type, int rw)
{
if (has_merged_page(sbi, inode, page, ino, type))
__f2fs_submit_merged_bio(sbi, inode, page, ino, type, rw);
}
/* /*
* Fill the locked page with data located in the block address. * Fill the locked page with data located in the block address.
* Return unlocked page. * Return unlocked page.
...@@ -1140,12 +1172,18 @@ static int f2fs_write_data_page(struct page *page, ...@@ -1140,12 +1172,18 @@ static int f2fs_write_data_page(struct page *page,
inode_dec_dirty_pages(inode); inode_dec_dirty_pages(inode);
if (err) if (err)
ClearPageUptodate(page); ClearPageUptodate(page);
if (wbc->for_reclaim) {
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, DATA, WRITE);
remove_dirty_inode(inode);
}
unlock_page(page); unlock_page(page);
f2fs_balance_fs(sbi, need_balance_fs); f2fs_balance_fs(sbi, need_balance_fs);
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) {
if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, DATA, WRITE); f2fs_submit_merged_bio(sbi, DATA, WRITE);
remove_dirty_inode(inode);
}
return 0; return 0;
redirty_out: redirty_out:
...@@ -1333,7 +1371,7 @@ static int f2fs_write_data_pages(struct address_space *mapping, ...@@ -1333,7 +1371,7 @@ static int f2fs_write_data_pages(struct address_space *mapping,
locked = true; locked = true;
} }
ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping); ret = f2fs_write_cache_pages(mapping, wbc, __f2fs_writepage, mapping);
f2fs_submit_merged_bio(sbi, DATA, WRITE); f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
if (locked) if (locked)
mutex_unlock(&sbi->writepages); mutex_unlock(&sbi->writepages);
......
...@@ -1897,8 +1897,9 @@ void destroy_checkpoint_caches(void); ...@@ -1897,8 +1897,9 @@ void destroy_checkpoint_caches(void);
/* /*
* data.c * data.c
*/ */
bool is_merged_page(struct f2fs_sb_info *, struct page *, enum page_type);
void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int); void f2fs_submit_merged_bio(struct f2fs_sb_info *, enum page_type, int);
void f2fs_submit_merged_bio_cond(struct f2fs_sb_info *, struct inode *,
struct page *, nid_t, enum page_type, int);
int f2fs_submit_page_bio(struct f2fs_io_info *); int f2fs_submit_page_bio(struct f2fs_io_info *);
void f2fs_submit_page_mbio(struct f2fs_io_info *); void f2fs_submit_page_mbio(struct f2fs_io_info *);
void set_data_blkaddr(struct dnode_of_data *); void set_data_blkaddr(struct dnode_of_data *);
......
...@@ -1337,8 +1337,13 @@ int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, ...@@ -1337,8 +1337,13 @@ int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino,
goto next_step; goto next_step;
} }
if (wrote) if (wrote) {
f2fs_submit_merged_bio(sbi, NODE, WRITE); if (ino)
f2fs_submit_merged_bio_cond(sbi, NULL, NULL,
ino, NODE, WRITE);
else
f2fs_submit_merged_bio(sbi, NODE, WRITE);
}
return nwritten; return nwritten;
} }
...@@ -1433,9 +1438,13 @@ static int f2fs_write_node_page(struct page *page, ...@@ -1433,9 +1438,13 @@ static int f2fs_write_node_page(struct page *page,
set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page)); set_node_addr(sbi, &ni, fio.blk_addr, is_fsync_dnode(page));
dec_page_count(sbi, F2FS_DIRTY_NODES); dec_page_count(sbi, F2FS_DIRTY_NODES);
up_read(&sbi->node_write); up_read(&sbi->node_write);
if (wbc->for_reclaim)
f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, NODE, WRITE);
unlock_page(page); unlock_page(page);
if (wbc->for_reclaim || unlikely(f2fs_cp_error(sbi))) if (unlikely(f2fs_cp_error(sbi)))
f2fs_submit_merged_bio(sbi, NODE, WRITE); f2fs_submit_merged_bio(sbi, NODE, WRITE);
return 0; return 0;
......
...@@ -254,7 +254,8 @@ int commit_inmem_pages(struct inode *inode, bool abort) ...@@ -254,7 +254,8 @@ int commit_inmem_pages(struct inode *inode, bool abort)
if (!abort) { if (!abort) {
f2fs_unlock_op(sbi); f2fs_unlock_op(sbi);
if (submit_bio) if (submit_bio)
f2fs_submit_merged_bio(sbi, DATA, WRITE); f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0,
DATA, WRITE);
} }
return err; return err;
} }
...@@ -1422,8 +1423,7 @@ void f2fs_wait_on_page_writeback(struct page *page, ...@@ -1422,8 +1423,7 @@ void f2fs_wait_on_page_writeback(struct page *page,
if (PageWriteback(page)) { if (PageWriteback(page)) {
struct f2fs_sb_info *sbi = F2FS_P_SB(page); struct f2fs_sb_info *sbi = F2FS_P_SB(page);
if (is_merged_page(sbi, page, type)) f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE);
f2fs_submit_merged_bio(sbi, type, WRITE);
if (ordered) if (ordered)
wait_on_page_writeback(page); wait_on_page_writeback(page);
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment