Skip to content

Commit

Permalink
Btrfs: fix race between direct io and autodefrag
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Liu Bo authored and Chris Mason committed Mar 29, 2012
1 parent 15d1ff8 commit ecb8bea
Showing 1 changed file with 5 additions and 1 deletion.
6 changes: 5 additions & 1 deletion fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit ecb8bea

Please sign in to comment.