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

f2fs: cache global IPU bio

In commit 8648de2c ("f2fs: add bio cache for IPU"), we added
f2fs_submit_ipu_bio() in __write_data_page() as below:

__write_data_page()

	if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode)) {
		f2fs_submit_ipu_bio(sbi, bio, page);
		....
	}

in order to avoid below deadlock:

Thread A				Thread B
- __write_data_page (inode x, page y)
 - f2fs_do_write_data_page
  - set_page_writeback        ---- set writeback flag in page y
  - f2fs_inplace_write_data
 - f2fs_balance_fs
					 - lock gc_mutex
 - lock gc_mutex
					  - f2fs_gc
					   - do_garbage_collect
					    - gc_data_segment
					     - move_data_page
					      - f2fs_wait_on_page_writeback
					       - wait_on_page_writeback  --- wait writeback of page y

However, the bio submission breaks the merge of IPU IOs.

So in this patch let's add a global bio cache for merged IPU pages,
then f2fs_wait_on_page_writeback() is able to submit bio if a
writebacked page is cached in global bio cache.
Signed-off-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 688078e7
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define NUM_PREALLOC_POST_READ_CTXS 128 #define NUM_PREALLOC_POST_READ_CTXS 128
static struct kmem_cache *bio_post_read_ctx_cache; static struct kmem_cache *bio_post_read_ctx_cache;
static struct kmem_cache *bio_entry_slab;
static mempool_t *bio_post_read_ctx_pool; static mempool_t *bio_post_read_ctx_pool;
static bool __is_cp_guaranteed(struct page *page) static bool __is_cp_guaranteed(struct page *page)
...@@ -543,6 +544,126 @@ static bool io_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio, ...@@ -543,6 +544,126 @@ static bool io_is_mergeable(struct f2fs_sb_info *sbi, struct bio *bio,
return io_type_is_mergeable(io, fio); return io_type_is_mergeable(io, fio);
} }
static void add_bio_entry(struct f2fs_sb_info *sbi, struct bio *bio,
struct page *page, enum temp_type temp)
{
struct f2fs_bio_info *io = sbi->write_io[DATA] + temp;
struct bio_entry *be;
be = f2fs_kmem_cache_alloc(bio_entry_slab, GFP_NOFS);
be->bio = bio;
bio_get(bio);
if (bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE)
f2fs_bug_on(sbi, 1);
down_write(&io->bio_list_lock);
list_add_tail(&be->list, &io->bio_list);
up_write(&io->bio_list_lock);
}
static void del_bio_entry(struct bio_entry *be)
{
list_del(&be->list);
kmem_cache_free(bio_entry_slab, be);
}
static int add_ipu_page(struct f2fs_sb_info *sbi, struct bio **bio,
struct page *page)
{
enum temp_type temp;
bool found = false;
int ret = -EAGAIN;
for (temp = HOT; temp < NR_TEMP_TYPE && !found; temp++) {
struct f2fs_bio_info *io = sbi->write_io[DATA] + temp;
struct list_head *head = &io->bio_list;
struct bio_entry *be;
down_write(&io->bio_list_lock);
list_for_each_entry(be, head, list) {
if (be->bio != *bio)
continue;
found = true;
if (bio_add_page(*bio, page, PAGE_SIZE, 0) == PAGE_SIZE) {
ret = 0;
break;
}
/* bio is full */
del_bio_entry(be);
__submit_bio(sbi, *bio, DATA);
break;
}
up_write(&io->bio_list_lock);
}
if (ret) {
bio_put(*bio);
*bio = NULL;
}
return ret;
}
void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
struct bio **bio, struct page *page)
{
enum temp_type temp;
bool found = false;
struct bio *target = bio ? *bio : NULL;
for (temp = HOT; temp < NR_TEMP_TYPE && !found; temp++) {
struct f2fs_bio_info *io = sbi->write_io[DATA] + temp;
struct list_head *head = &io->bio_list;
struct bio_entry *be;
if (list_empty(head))
continue;
down_read(&io->bio_list_lock);
list_for_each_entry(be, head, list) {
if (target)
found = (target == be->bio);
else
found = __has_merged_page(be->bio, NULL,
page, 0);
if (found)
break;
}
up_read(&io->bio_list_lock);
if (!found)
continue;
found = false;
down_write(&io->bio_list_lock);
list_for_each_entry(be, head, list) {
if (target)
found = (target == be->bio);
else
found = __has_merged_page(be->bio, NULL,
page, 0);
if (found) {
target = be->bio;
del_bio_entry(be);
break;
}
}
up_write(&io->bio_list_lock);
}
if (found)
__submit_bio(sbi, target, DATA);
if (bio && *bio) {
bio_put(*bio);
*bio = NULL;
}
}
int f2fs_merge_page_bio(struct f2fs_io_info *fio) int f2fs_merge_page_bio(struct f2fs_io_info *fio)
{ {
struct bio *bio = *fio->bio; struct bio *bio = *fio->bio;
...@@ -557,20 +678,17 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio) ...@@ -557,20 +678,17 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
f2fs_trace_ios(fio, 0); f2fs_trace_ios(fio, 0);
if (bio && !page_is_mergeable(fio->sbi, bio, *fio->last_block, if (bio && !page_is_mergeable(fio->sbi, bio, *fio->last_block,
fio->new_blkaddr)) { fio->new_blkaddr))
__submit_bio(fio->sbi, bio, fio->type); f2fs_submit_merged_ipu_write(fio->sbi, &bio, NULL);
bio = NULL;
}
alloc_new: alloc_new:
if (!bio) { if (!bio) {
bio = __bio_alloc(fio, BIO_MAX_PAGES); bio = __bio_alloc(fio, BIO_MAX_PAGES);
bio_set_op_attrs(bio, fio->op, fio->op_flags); bio_set_op_attrs(bio, fio->op, fio->op_flags);
}
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { add_bio_entry(fio->sbi, bio, page, fio->temp);
__submit_bio(fio->sbi, bio, fio->type); } else {
bio = NULL; if (add_ipu_page(fio->sbi, &bio, page))
goto alloc_new; goto alloc_new;
} }
if (fio->io_wbc) if (fio->io_wbc)
...@@ -584,19 +702,6 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio) ...@@ -584,19 +702,6 @@ int f2fs_merge_page_bio(struct f2fs_io_info *fio)
return 0; return 0;
} }
static void f2fs_submit_ipu_bio(struct f2fs_sb_info *sbi, struct bio **bio,
struct page *page)
{
if (!bio)
return;
if (!__has_merged_page(*bio, NULL, page, 0))
return;
__submit_bio(sbi, *bio, DATA);
*bio = NULL;
}
void f2fs_submit_page_write(struct f2fs_io_info *fio) void f2fs_submit_page_write(struct f2fs_io_info *fio)
{ {
struct f2fs_sb_info *sbi = fio->sbi; struct f2fs_sb_info *sbi = fio->sbi;
...@@ -2215,14 +2320,12 @@ static int __write_data_page(struct page *page, bool *submitted, ...@@ -2215,14 +2320,12 @@ static int __write_data_page(struct page *page, bool *submitted,
unlock_page(page); unlock_page(page);
if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) && if (!S_ISDIR(inode->i_mode) && !IS_NOQUOTA(inode) &&
!F2FS_I(inode)->cp_task) { !F2FS_I(inode)->cp_task)
f2fs_submit_ipu_bio(sbi, bio, page);
f2fs_balance_fs(sbi, need_balance_fs); f2fs_balance_fs(sbi, need_balance_fs);
}
if (unlikely(f2fs_cp_error(sbi))) { if (unlikely(f2fs_cp_error(sbi))) {
f2fs_submit_ipu_bio(sbi, bio, page);
f2fs_submit_merged_write(sbi, DATA); f2fs_submit_merged_write(sbi, DATA);
f2fs_submit_merged_ipu_write(sbi, bio, NULL);
submitted = NULL; submitted = NULL;
} }
...@@ -2342,13 +2445,11 @@ static int f2fs_write_cache_pages(struct address_space *mapping, ...@@ -2342,13 +2445,11 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
} }
if (PageWriteback(page)) { if (PageWriteback(page)) {
if (wbc->sync_mode != WB_SYNC_NONE) { if (wbc->sync_mode != WB_SYNC_NONE)
f2fs_wait_on_page_writeback(page, f2fs_wait_on_page_writeback(page,
DATA, true, true); DATA, true, true);
f2fs_submit_ipu_bio(sbi, &bio, page); else
} else {
goto continue_unlock; goto continue_unlock;
}
} }
if (!clear_page_dirty_for_io(page)) if (!clear_page_dirty_for_io(page))
...@@ -2406,7 +2507,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping, ...@@ -2406,7 +2507,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping,
NULL, 0, DATA); NULL, 0, DATA);
/* submit cached bio of IPU write */ /* submit cached bio of IPU write */
if (bio) if (bio)
__submit_bio(sbi, bio, DATA); f2fs_submit_merged_ipu_write(sbi, &bio, NULL);
return ret; return ret;
} }
...@@ -3211,8 +3312,22 @@ int __init f2fs_init_post_read_processing(void) ...@@ -3211,8 +3312,22 @@ int __init f2fs_init_post_read_processing(void)
return -ENOMEM; return -ENOMEM;
} }
void __exit f2fs_destroy_post_read_processing(void) void f2fs_destroy_post_read_processing(void)
{ {
mempool_destroy(bio_post_read_ctx_pool); mempool_destroy(bio_post_read_ctx_pool);
kmem_cache_destroy(bio_post_read_ctx_cache); kmem_cache_destroy(bio_post_read_ctx_cache);
} }
int __init f2fs_init_bio_entry_cache(void)
{
bio_entry_slab = f2fs_kmem_cache_create("bio_entry_slab",
sizeof(struct bio_entry));
if (!bio_entry_slab)
return -ENOMEM;
return 0;
}
void __exit f2fs_destroy_bio_entry_cache(void)
{
kmem_cache_destroy(bio_entry_slab);
}
...@@ -1068,6 +1068,11 @@ struct f2fs_io_info { ...@@ -1068,6 +1068,11 @@ struct f2fs_io_info {
unsigned char version; /* version of the node */ unsigned char version; /* version of the node */
}; };
struct bio_entry {
struct bio *bio;
struct list_head list;
};
#define is_read_io(rw) ((rw) == READ) #define is_read_io(rw) ((rw) == READ)
struct f2fs_bio_info { struct f2fs_bio_info {
struct f2fs_sb_info *sbi; /* f2fs superblock */ struct f2fs_sb_info *sbi; /* f2fs superblock */
...@@ -1077,6 +1082,8 @@ struct f2fs_bio_info { ...@@ -1077,6 +1082,8 @@ struct f2fs_bio_info {
struct rw_semaphore io_rwsem; /* blocking op for bio */ struct rw_semaphore io_rwsem; /* blocking op for bio */
spinlock_t io_lock; /* serialize DATA/NODE IOs */ spinlock_t io_lock; /* serialize DATA/NODE IOs */
struct list_head io_list; /* track fios */ struct list_head io_list; /* track fios */
struct list_head bio_list; /* bio entry list head */
struct rw_semaphore bio_list_lock; /* lock to protect bio entry list */
}; };
#define FDEV(i) (sbi->devs[i]) #define FDEV(i) (sbi->devs[i])
...@@ -3195,10 +3202,14 @@ void f2fs_destroy_checkpoint_caches(void); ...@@ -3195,10 +3202,14 @@ void f2fs_destroy_checkpoint_caches(void);
*/ */
int f2fs_init_post_read_processing(void); int f2fs_init_post_read_processing(void);
void f2fs_destroy_post_read_processing(void); void f2fs_destroy_post_read_processing(void);
int f2fs_init_bio_entry_cache(void);
void f2fs_destroy_bio_entry_cache(void);
void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type); void f2fs_submit_merged_write(struct f2fs_sb_info *sbi, enum page_type type);
void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi, void f2fs_submit_merged_write_cond(struct f2fs_sb_info *sbi,
struct inode *inode, struct page *page, struct inode *inode, struct page *page,
nid_t ino, enum page_type type); nid_t ino, enum page_type type);
void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
struct bio **bio, struct page *page);
void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi); void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi);
int f2fs_submit_page_bio(struct f2fs_io_info *fio); int f2fs_submit_page_bio(struct f2fs_io_info *fio);
int f2fs_merge_page_bio(struct f2fs_io_info *fio); int f2fs_merge_page_bio(struct f2fs_io_info *fio);
......
...@@ -3379,7 +3379,10 @@ void f2fs_wait_on_page_writeback(struct page *page, ...@@ -3379,7 +3379,10 @@ 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);
/* submit cached LFS IO */
f2fs_submit_merged_write_cond(sbi, NULL, page, 0, type); f2fs_submit_merged_write_cond(sbi, NULL, page, 0, type);
/* sbumit cached IPU IO */
f2fs_submit_merged_ipu_write(sbi, NULL, page);
if (ordered) { if (ordered) {
wait_on_page_writeback(page); wait_on_page_writeback(page);
f2fs_bug_on(sbi, locked && PageWriteback(page)); f2fs_bug_on(sbi, locked && PageWriteback(page));
......
...@@ -3342,6 +3342,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3342,6 +3342,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
sbi->write_io[i][j].bio = NULL; sbi->write_io[i][j].bio = NULL;
spin_lock_init(&sbi->write_io[i][j].io_lock); spin_lock_init(&sbi->write_io[i][j].io_lock);
INIT_LIST_HEAD(&sbi->write_io[i][j].io_list); INIT_LIST_HEAD(&sbi->write_io[i][j].io_list);
INIT_LIST_HEAD(&sbi->write_io[i][j].bio_list);
init_rwsem(&sbi->write_io[i][j].bio_list_lock);
} }
} }
...@@ -3753,8 +3755,13 @@ static int __init init_f2fs_fs(void) ...@@ -3753,8 +3755,13 @@ static int __init init_f2fs_fs(void)
err = f2fs_init_post_read_processing(); err = f2fs_init_post_read_processing();
if (err) if (err)
goto free_root_stats; goto free_root_stats;
err = f2fs_init_bio_entry_cache();
if (err)
goto free_post_read;
return 0; return 0;
free_post_read:
f2fs_destroy_post_read_processing();
free_root_stats: free_root_stats:
f2fs_destroy_root_stats(); f2fs_destroy_root_stats();
unregister_filesystem(&f2fs_fs_type); unregister_filesystem(&f2fs_fs_type);
...@@ -3778,6 +3785,7 @@ static int __init init_f2fs_fs(void) ...@@ -3778,6 +3785,7 @@ static int __init init_f2fs_fs(void)
static void __exit exit_f2fs_fs(void) static void __exit exit_f2fs_fs(void)
{ {
f2fs_destroy_bio_entry_cache();
f2fs_destroy_post_read_processing(); f2fs_destroy_post_read_processing();
f2fs_destroy_root_stats(); f2fs_destroy_root_stats();
unregister_filesystem(&f2fs_fs_type); unregister_filesystem(&f2fs_fs_type);
......
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