diff options
author | Jin Xu <jinuxstyle@gmail.com> | 2013-08-05 08:02:04 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-08-06 09:00:36 -0400 |
commit | a569469e967022d9ceeaa4b73619f96614087d2d (patch) | |
tree | 530ddfa1940ce086cbc7e3711ea01a7c7f783786 /fs | |
parent | df273efc36024a9ea97fd687d638a70c3ea8c37a (diff) |
f2fs: fix a deadlock in fsync
This patch fixes a deadlock bug that occurs quite often when there are
concurrent write and fsync on a same file.
Following is the simplified call trace when tasks get hung.
fsync thread:
- f2fs_sync_file
...
- f2fs_write_data_pages
...
- update_extent_cache
...
- update_inode
- wait_on_page_writeback
bdi writeback thread
- __writeback_single_inode
- f2fs_write_data_pages
- mutex_lock(sbi->writepages)
The deadlock happens when the fsync thread waits on a inode page that has
been added to the f2fs' cached bio sbi->bio[NODE], and unfortunately,
no one else could be able to submit the cached bio to block layer for
writeback. This is because the fsync thread already hold a sbi->fs_lock and
the sbi->writepages lock, causing the bdi thread being blocked when attempt
to write data pages for the same inode. At the same time, f2fs_gc thread
does not notice the situation and could not help. Even the sync syscall
gets blocked.
To fix it, we could submit the cached bio first before waiting on a inode page
that is being written back.
Signed-off-by: Jin Xu <jinuxstyle@gmail.com>
[Jaegeuk Kim: add more cases to use f2fs_wait_on_page_writeback]
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/f2fs/data.c | 2 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 3 | ||||
-rw-r--r-- | fs/f2fs/gc.c | 8 | ||||
-rw-r--r-- | fs/f2fs/inode.c | 2 | ||||
-rw-r--r-- | fs/f2fs/segment.c | 10 |
5 files changed, 16 insertions, 9 deletions
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index f458883af815..a7eb52925723 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c | |||
@@ -37,7 +37,7 @@ static void __set_data_blkaddr(struct dnode_of_data *dn, block_t new_addr) | |||
37 | struct page *node_page = dn->node_page; | 37 | struct page *node_page = dn->node_page; |
38 | unsigned int ofs_in_node = dn->ofs_in_node; | 38 | unsigned int ofs_in_node = dn->ofs_in_node; |
39 | 39 | ||
40 | wait_on_page_writeback(node_page); | 40 | f2fs_wait_on_page_writeback(node_page, NODE, false); |
41 | 41 | ||
42 | rn = F2FS_NODE(node_page); | 42 | rn = F2FS_NODE(node_page); |
43 | 43 | ||
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 63813befdd82..13db10b70e66 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -1023,7 +1023,8 @@ int npages_for_summary_flush(struct f2fs_sb_info *); | |||
1023 | void allocate_new_segments(struct f2fs_sb_info *); | 1023 | void allocate_new_segments(struct f2fs_sb_info *); |
1024 | struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); | 1024 | struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); |
1025 | struct bio *f2fs_bio_alloc(struct block_device *, int); | 1025 | struct bio *f2fs_bio_alloc(struct block_device *, int); |
1026 | void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool sync); | 1026 | void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool); |
1027 | void f2fs_wait_on_page_writeback(struct page *, enum page_type, bool); | ||
1027 | void write_meta_page(struct f2fs_sb_info *, struct page *); | 1028 | void write_meta_page(struct f2fs_sb_info *, struct page *); |
1028 | void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int, | 1029 | void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int, |
1029 | block_t, block_t *); | 1030 | block_t, block_t *); |
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index d286d8be8e68..e6b3ffd5ff6a 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c | |||
@@ -422,8 +422,7 @@ next_step: | |||
422 | 422 | ||
423 | /* set page dirty and write it */ | 423 | /* set page dirty and write it */ |
424 | if (gc_type == FG_GC) { | 424 | if (gc_type == FG_GC) { |
425 | f2fs_submit_bio(sbi, NODE, true); | 425 | f2fs_wait_on_page_writeback(node_page, NODE, true); |
426 | wait_on_page_writeback(node_page); | ||
427 | set_page_dirty(node_page); | 426 | set_page_dirty(node_page); |
428 | } else { | 427 | } else { |
429 | if (!PageWriteback(node_page)) | 428 | if (!PageWriteback(node_page)) |
@@ -523,10 +522,7 @@ static void move_data_page(struct inode *inode, struct page *page, int gc_type) | |||
523 | } else { | 522 | } else { |
524 | struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); | 523 | struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); |
525 | 524 | ||
526 | if (PageWriteback(page)) { | 525 | f2fs_wait_on_page_writeback(page, DATA, true); |
527 | f2fs_submit_bio(sbi, DATA, true); | ||
528 | wait_on_page_writeback(page); | ||
529 | } | ||
530 | 526 | ||
531 | if (clear_page_dirty_for_io(page) && | 527 | if (clear_page_dirty_for_io(page) && |
532 | S_ISDIR(inode->i_mode)) { | 528 | S_ISDIR(inode->i_mode)) { |
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index debf74308045..9ab81e7472c5 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c | |||
@@ -151,7 +151,7 @@ void update_inode(struct inode *inode, struct page *node_page) | |||
151 | struct f2fs_node *rn; | 151 | struct f2fs_node *rn; |
152 | struct f2fs_inode *ri; | 152 | struct f2fs_inode *ri; |
153 | 153 | ||
154 | wait_on_page_writeback(node_page); | 154 | f2fs_wait_on_page_writeback(node_page, NODE, false); |
155 | 155 | ||
156 | rn = F2FS_NODE(node_page); | 156 | rn = F2FS_NODE(node_page); |
157 | ri = &(rn->i); | 157 | ri = &(rn->i); |
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 9b74ae2137d1..68e344f9e042 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c | |||
@@ -705,6 +705,16 @@ retry: | |||
705 | trace_f2fs_submit_write_page(page, blk_addr, type); | 705 | trace_f2fs_submit_write_page(page, blk_addr, type); |
706 | } | 706 | } |
707 | 707 | ||
708 | void f2fs_wait_on_page_writeback(struct page *page, | ||
709 | enum page_type type, bool sync) | ||
710 | { | ||
711 | struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); | ||
712 | if (PageWriteback(page)) { | ||
713 | f2fs_submit_bio(sbi, type, sync); | ||
714 | wait_on_page_writeback(page); | ||
715 | } | ||
716 | } | ||
717 | |||
708 | static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) | 718 | static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) |
709 | { | 719 | { |
710 | struct curseg_info *curseg = CURSEG_I(sbi, type); | 720 | struct curseg_info *curseg = CURSEG_I(sbi, type); |