From: Liu Bo Date: Thu, 29 Mar 2012 13:57:44 +0000 (-0400) Subject: Btrfs: fix race between direct io and autodefrag X-Git-Tag: v3.12-rc1~3266^2~44 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ecb8bea87d05fd2d1fc0718e1e4bbf09c7c6045a;p=kernel%2Fkernel-generic.git Btrfs: fix race between direct io and autodefrag The bug is from running xfstests 209 with autodefrag. The race is as follows: t1 t2(autodefrag) direct IO invalidate pagecache dio(old data) add_inode_defrag invalidate pagecache endio direct IO invalidate pagecache run_defrag readpage(old data) set page dirty (old data) dio(new data, rewrite) invalidate pagecache (*) endio t2(autodefrag) will get old data into pagecache via readpage and set pagecache dirty. Meanwhile, invalidate pagecache(*) will fail due to dirty flags in pages. So the old data may be flushed into disk by flush thread, which will lead to data loss. And so does the case of user defragment progs. The patch fixes this race by holding i_mutex when we readpage and set page dirty. Signed-off-by: Liu Bo Signed-off-by: Miao Xie Signed-off-by: Chris Mason --- diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a979ab7..45910d4 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1137,12 +1137,16 @@ int btrfs_defrag_file(struct inode *inode, struct file *file, ra_index += max_cluster; } + mutex_lock(&inode->i_mutex); ret = cluster_pages_for_defrag(inode, pages, i, cluster); - if (ret < 0) + if (ret < 0) { + mutex_unlock(&inode->i_mutex); goto out_ra; + } defrag_count += ret; balance_dirty_pages_ratelimited_nr(inode->i_mapping, ret); + mutex_unlock(&inode->i_mutex); if (newer_than) { if (newer_off == (u64)-1)