diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-11-25 13:59:45 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-11-25 15:08:30 -0500 |
commit | 5f72739583a29bfaa57448ec2c9b122995d0ae4f (patch) | |
tree | 0c329857102424b596a22ac0a3335257bb226b2a /fs/f2fs | |
parent | ce3e6d25f3fbaf48a3e1914d5ac0ddfdc1b38349 (diff) |
f2fs: fix deadlock during inline_data conversion
A deadlock can be occurred:
Thread 1] Thread 2]
- f2fs_write_data_pages - f2fs_write_begin
- lock_page(page #0)
- grab_cache_page(page #X)
- get_node_page(inode_page)
- grab_cache_page(page #0)
: to convert inline_data
- f2fs_write_data_page
- f2fs_write_inline_data
- get_node_page(inode_page)
In this case, trying to lock inode_page and page #0 causes deadlock.
In order to avoid this, this patch adds a rule for this locking policy,
which is that page #0 should be locked followed by inode_page lock.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fs/f2fs')
-rw-r--r-- | fs/f2fs/data.c | 28 |
1 files changed, 14 insertions, 14 deletions
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 12dd58aa569a..c7bc62641103 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c | |||
@@ -936,6 +936,17 @@ static int f2fs_write_begin(struct file *file, struct address_space *mapping, | |||
936 | trace_f2fs_write_begin(inode, pos, len, flags); | 936 | trace_f2fs_write_begin(inode, pos, len, flags); |
937 | 937 | ||
938 | f2fs_balance_fs(sbi); | 938 | f2fs_balance_fs(sbi); |
939 | |||
940 | /* | ||
941 | * We should check this at this moment to avoid deadlock on inode page | ||
942 | * and #0 page. The locking rule for inline_data conversion should be: | ||
943 | * lock_page(page #0) -> lock_page(inode_page) | ||
944 | */ | ||
945 | if (index != 0) { | ||
946 | err = f2fs_convert_inline_inode(inode); | ||
947 | if (err) | ||
948 | goto fail; | ||
949 | } | ||
939 | repeat: | 950 | repeat: |
940 | page = grab_cache_page_write_begin(mapping, index, flags); | 951 | page = grab_cache_page_write_begin(mapping, index, flags); |
941 | if (!page) { | 952 | if (!page) { |
@@ -960,21 +971,10 @@ repeat: | |||
960 | set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); | 971 | set_inode_flag(F2FS_I(inode), FI_DATA_EXIST); |
961 | sync_inode_page(&dn); | 972 | sync_inode_page(&dn); |
962 | goto put_next; | 973 | goto put_next; |
963 | } else if (page->index == 0) { | ||
964 | err = f2fs_convert_inline_page(&dn, page); | ||
965 | if (err) | ||
966 | goto put_fail; | ||
967 | } else { | ||
968 | struct page *p = grab_cache_page(inode->i_mapping, 0); | ||
969 | if (!p) { | ||
970 | err = -ENOMEM; | ||
971 | goto put_fail; | ||
972 | } | ||
973 | err = f2fs_convert_inline_page(&dn, p); | ||
974 | f2fs_put_page(p, 1); | ||
975 | if (err) | ||
976 | goto put_fail; | ||
977 | } | 974 | } |
975 | err = f2fs_convert_inline_page(&dn, page); | ||
976 | if (err) | ||
977 | goto put_fail; | ||
978 | } | 978 | } |
979 | err = f2fs_reserve_block(&dn, index); | 979 | err = f2fs_reserve_block(&dn, index); |
980 | if (err) | 980 | if (err) |