Skip to content

Commit 9f89d5d

Browse files
fdmananakdave
authored andcommitted
Btrfs: send, flush dellaloc in order to avoid data loss
When we set a subvolume to read-only mode we do not flush dellaloc for any of its inodes (except if the filesystem is mounted with -o flushoncommit), since it does not affect correctness for any subsequent operations - except for a future send operation. The send operation will not be able to see the delalloc data since the respective file extent items, inode item updates, backreferences, etc, have not hit yet the subvolume and extent trees. Effectively this means data loss, since the send stream will not contain any data from existing delalloc. Another problem from this is that if the writeback starts and finishes while the send operation is in progress, we have the subvolume tree being being modified concurrently which can result in send failing unexpectedly with EIO or hitting runtime errors, assertion failures or hitting BUG_ONs, etc. Simple reproducer: $ mkfs.btrfs -f /dev/sdb $ mount /dev/sdb /mnt $ btrfs subvolume create /mnt/sv $ xfs_io -f -c "pwrite -S 0xea 0 108K" /mnt/sv/foo $ btrfs property set /mnt/sv ro true $ btrfs send -f /tmp/send.stream /mnt/sv $ od -t x1 -A d /mnt/sv/foo 0000000 ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea ea * 0110592 $ umount /mnt $ mkfs.btrfs -f /dev/sdc $ mount /dev/sdc /mnt $ btrfs receive -f /tmp/send.stream /mnt $ echo $? 0 $ od -t x1 -A d /mnt/sv/foo 0000000 # ---> empty file Since this a problem that affects send only, fix it in send by flushing dellaloc for all the roots used by the send operation before send starts to process the commit roots. This is a problem that affects send since it was introduced (commit 31db9f7 ("Btrfs: introduce BTRFS_IOC_SEND for btrfs send/receive")) but backporting it to older kernels has some dependencies: - For kernels between 3.19 and 4.20, it depends on commit 3cd24c6 ("btrfs: use tagged writepage to mitigate livelock of snapshot") because the function btrfs_start_delalloc_snapshot() does not exist before that commit. So one has to either pick that commit or replace the calls to btrfs_start_delalloc_snapshot() in this patch with calls to btrfs_start_delalloc_inodes(). - For kernels older than 3.19 it also requires commit e5fa8f8 ("Btrfs: ensure send always works on roots without orphans") because it depends on the function ensure_commit_roots_uptodate() which that commits introduced. - No dependencies for 5.0+ kernels. A test case for fstests follows soon. CC: [email protected] # 3.19+ Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 03628cd commit 9f89d5d

File tree

1 file changed

+36
-0
lines changed

1 file changed

+36
-0
lines changed

fs/btrfs/send.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6593,6 +6593,38 @@ static int ensure_commit_roots_uptodate(struct send_ctx *sctx)
65936593
return btrfs_commit_transaction(trans);
65946594
}
65956595

6596+
/*
6597+
* Make sure any existing dellaloc is flushed for any root used by a send
6598+
* operation so that we do not miss any data and we do not race with writeback
6599+
* finishing and changing a tree while send is using the tree. This could
6600+
* happen if a subvolume is in RW mode, has delalloc, is turned to RO mode and
6601+
* a send operation then uses the subvolume.
6602+
* After flushing delalloc ensure_commit_roots_uptodate() must be called.
6603+
*/
6604+
static int flush_delalloc_roots(struct send_ctx *sctx)
6605+
{
6606+
struct btrfs_root *root = sctx->parent_root;
6607+
int ret;
6608+
int i;
6609+
6610+
if (root) {
6611+
ret = btrfs_start_delalloc_snapshot(root);
6612+
if (ret)
6613+
return ret;
6614+
btrfs_wait_ordered_extents(root, U64_MAX, 0, U64_MAX);
6615+
}
6616+
6617+
for (i = 0; i < sctx->clone_roots_cnt; i++) {
6618+
root = sctx->clone_roots[i].root;
6619+
ret = btrfs_start_delalloc_snapshot(root);
6620+
if (ret)
6621+
return ret;
6622+
btrfs_wait_ordered_extents(root, U64_MAX, 0, U64_MAX);
6623+
}
6624+
6625+
return 0;
6626+
}
6627+
65966628
static void btrfs_root_dec_send_in_progress(struct btrfs_root* root)
65976629
{
65986630
spin_lock(&root->root_item_lock);
@@ -6817,6 +6849,10 @@ long btrfs_ioctl_send(struct file *mnt_file, struct btrfs_ioctl_send_args *arg)
68176849
NULL);
68186850
sort_clone_roots = 1;
68196851

6852+
ret = flush_delalloc_roots(sctx);
6853+
if (ret)
6854+
goto out;
6855+
68206856
ret = ensure_commit_roots_uptodate(sctx);
68216857
if (ret)
68226858
goto out;

0 commit comments

Comments
 (0)