Skip to content

Commit

Permalink
Merge tag 'for-5.9-rc7-tag' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/kdave/linux

Pull btrfs fixes from David Sterba:
 "Two more fixes.

  One is for a lockdep warning/lockup (also caught by syzbot), that one
  has been seen in practice. Regarding the other syzbot reports
  mentioned last time, they don't seem to be urgent and reliably
  reproducible so they'll be fixed later.

  The second fix is for a potential corruption when device replace
  finishes and the in-memory state of trim is not copied to the new
  device"

* tag 'for-5.9-rc7-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix filesystem corruption after a device replace
  btrfs: move btrfs_rm_dev_replace_free_srcdev outside of all locks
  btrfs: move btrfs_scratch_superblocks into btrfs_dev_replace_finishing
  • Loading branch information
Linus Torvalds committed Oct 2, 2020
2 parents c513091 + 4c8f353 commit 4e3b9ce
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 10 deletions.
46 changes: 44 additions & 2 deletions fs/btrfs/dev-replace.c
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,37 @@ static void btrfs_rm_dev_replace_unblocked(struct btrfs_fs_info *fs_info)
wake_up(&fs_info->dev_replace.replace_wait);
}

/*
* When finishing the device replace, before swapping the source device with the
* target device we must update the chunk allocation state in the target device,
* as it is empty because replace works by directly copying the chunks and not
* through the normal chunk allocation path.
*/
static int btrfs_set_target_alloc_state(struct btrfs_device *srcdev,
struct btrfs_device *tgtdev)
{
struct extent_state *cached_state = NULL;
u64 start = 0;
u64 found_start;
u64 found_end;
int ret = 0;

lockdep_assert_held(&srcdev->fs_info->chunk_mutex);

while (!find_first_extent_bit(&srcdev->alloc_state, start,
&found_start, &found_end,
CHUNK_ALLOCATED, &cached_state)) {
ret = set_extent_bits(&tgtdev->alloc_state, found_start,
found_end, CHUNK_ALLOCATED);
if (ret)
break;
start = found_end + 1;
}

free_extent_state(cached_state);
return ret;
}

static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
int scrub_ret)
{
Expand Down Expand Up @@ -673,8 +704,14 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
dev_replace->time_stopped = ktime_get_real_seconds();
dev_replace->item_needs_writeback = 1;

/* replace old device with new one in mapping tree */
/*
* Update allocation state in the new device and replace the old device
* with the new one in the mapping tree.
*/
if (!scrub_ret) {
scrub_ret = btrfs_set_target_alloc_state(src_device, tgt_device);
if (scrub_ret)
goto error;
btrfs_dev_replace_update_device_in_mapping_tree(fs_info,
src_device,
tgt_device);
Expand All @@ -685,6 +722,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
btrfs_dev_name(src_device),
src_device->devid,
rcu_str_deref(tgt_device->name), scrub_ret);
error:
up_write(&dev_replace->rwsem);
mutex_unlock(&fs_info->chunk_mutex);
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
Expand Down Expand Up @@ -745,7 +783,9 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
/* replace the sysfs entry */
btrfs_sysfs_remove_devices_dir(fs_info->fs_devices, src_device);
btrfs_sysfs_update_devid(tgt_device);
btrfs_rm_dev_replace_free_srcdev(src_device);
if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &src_device->dev_state))
btrfs_scratch_superblocks(fs_info, src_device->bdev,
src_device->name->str);

/* write back the superblocks */
trans = btrfs_start_transaction(root, 0);
Expand All @@ -754,6 +794,8 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,

mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);

btrfs_rm_dev_replace_free_srcdev(src_device);

return 0;
}

Expand Down
13 changes: 5 additions & 8 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1999,9 +1999,9 @@ static u64 btrfs_num_devices(struct btrfs_fs_info *fs_info)
return num_devices;
}

static void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
struct block_device *bdev,
const char *device_path)
void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
struct block_device *bdev,
const char *device_path)
{
struct btrfs_super_block *disk_super;
int copy_num;
Expand Down Expand Up @@ -2224,11 +2224,7 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev)
struct btrfs_fs_info *fs_info = srcdev->fs_info;
struct btrfs_fs_devices *fs_devices = srcdev->fs_devices;

if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &srcdev->dev_state)) {
/* zero out the old super if it is writable */
btrfs_scratch_superblocks(fs_info, srcdev->bdev,
srcdev->name->str);
}
mutex_lock(&uuid_mutex);

btrfs_close_bdev(srcdev);
synchronize_rcu();
Expand Down Expand Up @@ -2258,6 +2254,7 @@ void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev)
close_fs_devices(fs_devices);
free_fs_devices(fs_devices);
}
mutex_unlock(&uuid_mutex);
}

void btrfs_destroy_dev_replace_tgtdev(struct btrfs_device *tgtdev)
Expand Down
3 changes: 3 additions & 0 deletions fs/btrfs/volumes.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,9 @@ void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info);
void btrfs_reset_fs_info_ptr(struct btrfs_fs_info *fs_info);
bool btrfs_check_rw_degradable(struct btrfs_fs_info *fs_info,
struct btrfs_device *failing_dev);
void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
struct block_device *bdev,
const char *device_path);

int btrfs_bg_type_to_factor(u64 flags);
const char *btrfs_bg_type_to_raid_name(u64 flags);
Expand Down

0 comments on commit 4e3b9ce

Please sign in to comment.