diff options
author | Liu Bo <[email protected]> | 2012-03-29 09:57:44 -0400 |
---|---|---|
committer | Chris Mason <[email protected]> | 2012-03-29 09:57:44 -0400 |
commit | ecb8bea87d05fd2d1fc0718e1e4bbf09c7c6045a (patch) | |
tree | bb95b921ade128c3d434fd244d150a0d27d47e7f | |
parent | 15d1ff8111aad85d8b40ee396758990d17a2caac (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 <[email protected]>
Signed-off-by: Miao Xie <[email protected]>
Signed-off-by: Chris Mason <[email protected]>
-rw-r--r-- | fs/btrfs/ioctl.c | 6 |
1 files changed, 5 insertions, 1 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a979ab7d3967..45910d4b8f65 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) |