Skip to content

Commit 0525064

Browse files
fdmananakdave
authored andcommitted
btrfs: fix race with memory mapped writes when activating swap file
When activating the swap file we flush all delalloc and wait for ordered extent completion, so that we don't miss any delalloc and extents before we check that the file's extent layout is usable for a swap file and activate the swap file. We are called with the inode's VFS lock acquired, so we won't race with buffered and direct IO writes, however we can still race with memory mapped writes since they don't acquire the inode's VFS lock. The race window is between flushing all delalloc and locking the whole file's extent range, since memory mapped writes lock an extent range with the length of a page. Fix this by acquiring the inode's mmap lock before we flush delalloc. CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 0fba7be commit 0525064

File tree

1 file changed

+24
-7
lines changed

1 file changed

+24
-7
lines changed

fs/btrfs/inode.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9809,29 +9809,41 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file,
98099809
u64 isize;
98109810
u64 start;
98119811

9812+
/*
9813+
* Acquire the inode's mmap lock to prevent races with memory mapped
9814+
* writes, as they could happen after we flush delalloc below and before
9815+
* we lock the extent range further below. The inode was already locked
9816+
* up in the call chain.
9817+
*/
9818+
btrfs_assert_inode_locked(BTRFS_I(inode));
9819+
down_write(&BTRFS_I(inode)->i_mmap_lock);
9820+
98129821
/*
98139822
* If the swap file was just created, make sure delalloc is done. If the
98149823
* file changes again after this, the user is doing something stupid and
98159824
* we don't really care.
98169825
*/
98179826
ret = btrfs_wait_ordered_range(BTRFS_I(inode), 0, (u64)-1);
98189827
if (ret)
9819-
return ret;
9828+
goto out_unlock_mmap;
98209829

98219830
/*
98229831
* The inode is locked, so these flags won't change after we check them.
98239832
*/
98249833
if (BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS) {
98259834
btrfs_warn(fs_info, "swapfile must not be compressed");
9826-
return -EINVAL;
9835+
ret = -EINVAL;
9836+
goto out_unlock_mmap;
98279837
}
98289838
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW)) {
98299839
btrfs_warn(fs_info, "swapfile must not be copy-on-write");
9830-
return -EINVAL;
9840+
ret = -EINVAL;
9841+
goto out_unlock_mmap;
98319842
}
98329843
if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
98339844
btrfs_warn(fs_info, "swapfile must not be checksummed");
9834-
return -EINVAL;
9845+
ret = -EINVAL;
9846+
goto out_unlock_mmap;
98359847
}
98369848

98379849
/*
@@ -9846,7 +9858,8 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file,
98469858
if (!btrfs_exclop_start(fs_info, BTRFS_EXCLOP_SWAP_ACTIVATE)) {
98479859
btrfs_warn(fs_info,
98489860
"cannot activate swapfile while exclusive operation is running");
9849-
return -EBUSY;
9861+
ret = -EBUSY;
9862+
goto out_unlock_mmap;
98509863
}
98519864

98529865
/*
@@ -9860,7 +9873,8 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file,
98609873
btrfs_exclop_finish(fs_info);
98619874
btrfs_warn(fs_info,
98629875
"cannot activate swapfile because snapshot creation is in progress");
9863-
return -EINVAL;
9876+
ret = -EINVAL;
9877+
goto out_unlock_mmap;
98649878
}
98659879
/*
98669880
* Snapshots can create extents which require COW even if NODATACOW is
@@ -9881,7 +9895,8 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file,
98819895
btrfs_warn(fs_info,
98829896
"cannot activate swapfile because subvolume %llu is being deleted",
98839897
btrfs_root_id(root));
9884-
return -EPERM;
9898+
ret = -EPERM;
9899+
goto out_unlock_mmap;
98859900
}
98869901
atomic_inc(&root->nr_swapfiles);
98879902
spin_unlock(&root->root_item_lock);
@@ -10036,6 +10051,8 @@ static int btrfs_swap_activate(struct swap_info_struct *sis, struct file *file,
1003610051

1003710052
btrfs_exclop_finish(fs_info);
1003810053

10054+
out_unlock_mmap:
10055+
up_write(&BTRFS_I(inode)->i_mmap_lock);
1003910056
if (ret)
1004010057
return ret;
1004110058

0 commit comments

Comments
 (0)