aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ocfs2
diff options
context:
space:
mode:
authorLarry Chen <lchen@suse.com>2018-10-05 18:51:37 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-10-05 19:32:04 -0400
commit69eb7765b9c6902444c89c54e7043242faf981e5 (patch)
treedfcd61abd6c76d9c175de5b85b5e0ae8cf9b497a /fs/ocfs2
parentdff11abe280b47c21b804a8ace318e0638bb9a49 (diff)
ocfs2: fix crash in ocfs2_duplicate_clusters_by_page()
ocfs2_duplicate_clusters_by_page() may crash if one of the extent's pages is dirty. When a page has not been written back, it is still in dirty state. If ocfs2_duplicate_clusters_by_page() is called against the dirty page, the crash happens. To fix this bug, we can just unlock the page and wait until the page until its not dirty. The following is the backtrace: kernel BUG at /root/code/ocfs2/refcounttree.c:2961! [exception RIP: ocfs2_duplicate_clusters_by_page+822] __ocfs2_move_extent+0x80/0x450 [ocfs2] ? __ocfs2_claim_clusters+0x130/0x250 [ocfs2] ocfs2_defrag_extent+0x5b8/0x5e0 [ocfs2] __ocfs2_move_extents_range+0x2a4/0x470 [ocfs2] ocfs2_move_extents+0x180/0x3b0 [ocfs2] ? ocfs2_wait_for_recovery+0x13/0x70 [ocfs2] ocfs2_ioctl_move_extents+0x133/0x2d0 [ocfs2] ocfs2_ioctl+0x253/0x640 [ocfs2] do_vfs_ioctl+0x90/0x5f0 SyS_ioctl+0x74/0x80 do_syscall_64+0x74/0x140 entry_SYSCALL_64_after_hwframe+0x3d/0xa2 Once we find the page is dirty, we do not wait until it's clean, rather we use write_one_page() to write it back Link: http://lkml.kernel.org/r/20180829074740.9438-1-lchen@suse.com [lchen@suse.com: update comments] Link: http://lkml.kernel.org/r/20180830075041.14879-1-lchen@suse.com [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Larry Chen <lchen@suse.com> Acked-by: Changwei Ge <ge.changwei@h3c.com> Cc: Mark Fasheh <mark@fasheh.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Joseph Qi <jiangqi903@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r--fs/ocfs2/refcounttree.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index 7869622af22a..7a5ee145c733 100644
--- a/fs/ocfs2/refcounttree.c
+++ b/fs/ocfs2/refcounttree.c
@@ -2946,6 +2946,7 @@ int ocfs2_duplicate_clusters_by_page(handle_t *handle,
2946 if (map_end & (PAGE_SIZE - 1)) 2946 if (map_end & (PAGE_SIZE - 1))
2947 to = map_end & (PAGE_SIZE - 1); 2947 to = map_end & (PAGE_SIZE - 1);
2948 2948
2949retry:
2949 page = find_or_create_page(mapping, page_index, GFP_NOFS); 2950 page = find_or_create_page(mapping, page_index, GFP_NOFS);
2950 if (!page) { 2951 if (!page) {
2951 ret = -ENOMEM; 2952 ret = -ENOMEM;
@@ -2954,11 +2955,18 @@ int ocfs2_duplicate_clusters_by_page(handle_t *handle,
2954 } 2955 }
2955 2956
2956 /* 2957 /*
2957 * In case PAGE_SIZE <= CLUSTER_SIZE, This page 2958 * In case PAGE_SIZE <= CLUSTER_SIZE, we do not expect a dirty
2958 * can't be dirtied before we CoW it out. 2959 * page, so write it back.
2959 */ 2960 */
2960 if (PAGE_SIZE <= OCFS2_SB(sb)->s_clustersize) 2961 if (PAGE_SIZE <= OCFS2_SB(sb)->s_clustersize) {
2961 BUG_ON(PageDirty(page)); 2962 if (PageDirty(page)) {
2963 /*
2964 * write_on_page will unlock the page on return
2965 */
2966 ret = write_one_page(page);
2967 goto retry;
2968 }
2969 }
2962 2970
2963 if (!PageUptodate(page)) { 2971 if (!PageUptodate(page)) {
2964 ret = block_read_full_page(page, ocfs2_get_block); 2972 ret = block_read_full_page(page, ocfs2_get_block);