aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorLiu Bo <liubo2009@cn.fujitsu.com>2012-03-29 09:57:44 -0400
committerChris Mason <chris.mason@oracle.com>2012-03-29 09:57:44 -0400
commitecb8bea87d05fd2d1fc0718e1e4bbf09c7c6045a (patch)
treebb95b921ade128c3d434fd244d150a0d27d47e7f /fs/btrfs
parent15d1ff8111aad85d8b40ee396758990d17a2caac (diff)
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 <liubo2009@cn.fujitsu.com> Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ioctl.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index a979ab7d396..45910d4b8f6 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,
1137 ra_index += max_cluster; 1137 ra_index += max_cluster;
1138 } 1138 }
1139 1139
1140 mutex_lock(&inode->i_mutex);
1140 ret = cluster_pages_for_defrag(inode, pages, i, cluster); 1141 ret = cluster_pages_for_defrag(inode, pages, i, cluster);
1141 if (ret < 0) 1142 if (ret < 0) {
1143 mutex_unlock(&inode->i_mutex);
1142 goto out_ra; 1144 goto out_ra;
1145 }
1143 1146
1144 defrag_count += ret; 1147 defrag_count += ret;
1145 balance_dirty_pages_ratelimited_nr(inode->i_mapping, ret); 1148 balance_dirty_pages_ratelimited_nr(inode->i_mapping, ret);
1149 mutex_unlock(&inode->i_mutex);
1146 1150
1147 if (newer_than) { 1151 if (newer_than) {
1148 if (newer_off == (u64)-1) 1152 if (newer_off == (u64)-1)