diff options
Diffstat (limited to 'fs/btrfs')
| -rw-r--r-- | fs/btrfs/dev-replace.c | 7 | ||||
| -rw-r--r-- | fs/btrfs/scrub.c | 26 | 
2 files changed, 31 insertions, 2 deletions
| diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c index 71fd99b48283..f26202621989 100644 --- a/fs/btrfs/dev-replace.c +++ b/fs/btrfs/dev-replace.c @@ -734,7 +734,12 @@ static int btrfs_dev_replace_start(struct btrfs_fs_info *fs_info,  	btrfs_wait_ordered_roots(fs_info, U64_MAX, 0, (u64)-1); -	/* Commit dev_replace state and reserve 1 item for it. */ +	/* +	 * Commit dev_replace state and reserve 1 item for it. +	 * This is crucial to ensure we won't miss copying extents for new block +	 * groups that are allocated after we started the device replace, and +	 * must be done after setting up the device replace state. +	 */  	trans = btrfs_start_transaction(root, 1);  	if (IS_ERR(trans)) {  		ret = PTR_ERR(trans); diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 11089568b287..8cd713d37ad2 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -3699,6 +3699,31 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,  		if (!cache)  			goto skip; +		ASSERT(cache->start <= chunk_offset); +		/* +		 * We are using the commit root to search for device extents, so +		 * that means we could have found a device extent item from a +		 * block group that was deleted in the current transaction. The +		 * logical start offset of the deleted block group, stored at +		 * @chunk_offset, might be part of the logical address range of +		 * a new block group (which uses different physical extents). +		 * In this case btrfs_lookup_block_group() has returned the new +		 * block group, and its start address is less than @chunk_offset. +		 * +		 * We skip such new block groups, because it's pointless to +		 * process them, as we won't find their extents because we search +		 * for them using the commit root of the extent tree. For a device +		 * replace it's also fine to skip it, we won't miss copying them +		 * to the target device because we have the write duplication +		 * setup through the regular write path (by btrfs_map_block()), +		 * and we have committed a transaction when we started the device +		 * replace, right after setting up the device replace state. +		 */ +		if (cache->start < chunk_offset) { +			btrfs_put_block_group(cache); +			goto skip; +		} +  		if (sctx->is_dev_replace && btrfs_is_zoned(fs_info)) {  			spin_lock(&cache->lock);  			if (!cache->to_copy) { @@ -3822,7 +3847,6 @@ int scrub_enumerate_chunks(struct scrub_ctx *sctx,  		dev_replace->item_needs_writeback = 1;  		up_write(&dev_replace->rwsem); -		ASSERT(cache->start == chunk_offset);  		ret = scrub_chunk(sctx, cache, scrub_dev, found_key.offset,  				  dev_extent_len); |