dev_replace->item_needs_writeback = 0; switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: /* *Wedon'thaveanactivereplaceitembutifthereisa *replacetarget,failthemount.
*/ if (btrfs_find_device(fs_info->fs_devices, &args)) {
btrfs_err(fs_info, "replace without active item, run 'device scan --forget' on the target device");
ret = -EUCLEAN;
} else {
dev_replace->srcdev = NULL;
dev_replace->tgtdev = NULL;
} break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
dev_replace->tgtdev = btrfs_find_device(fs_info->fs_devices, &args);
args.devid = src_devid;
dev_replace->srcdev = btrfs_find_device(fs_info->fs_devices, &args);
/* *allow'btrfsdevreplace_cancel'ifsrc/tgtdeviceis *missing
*/ if (!dev_replace->srcdev &&
!btrfs_test_opt(fs_info, DEGRADED)) {
ret = -EIO;
btrfs_warn(fs_info, "cannot mount because device replace operation is ongoing and");
btrfs_warn(fs_info, "srcdev (devid %llu) is missing, need to run 'btrfs dev scan'?",
src_devid);
} if (!dev_replace->tgtdev &&
!btrfs_test_opt(fs_info, DEGRADED)) {
ret = -EIO;
btrfs_warn(fs_info, "cannot mount because device replace operation is ongoing and");
btrfs_warn(fs_info, "tgtdev (devid %llu) is missing, need to run 'btrfs dev scan'?",
BTRFS_DEV_REPLACE_DEVID);
} if (dev_replace->tgtdev) { if (dev_replace->srcdev) {
dev_replace->tgtdev->total_bytes =
dev_replace->srcdev->total_bytes;
dev_replace->tgtdev->disk_total_bytes =
dev_replace->srcdev->disk_total_bytes;
dev_replace->tgtdev->commit_total_bytes =
dev_replace->srcdev->commit_total_bytes;
dev_replace->tgtdev->bytes_used =
dev_replace->srcdev->bytes_used;
dev_replace->tgtdev->commit_bytes_used =
dev_replace->srcdev->commit_bytes_used;
}
set_bit(BTRFS_DEV_STATE_REPLACE_TGT,
&dev_replace->tgtdev->dev_state);
if (!btrfs_check_device_zone_type(fs_info, bdev)) {
btrfs_err(fs_info, "dev-replace: zoned type of target device mismatch with filesystem");
ret = -EINVAL; goto error;
}
sync_blockdev(bdev);
list_for_each_entry(device, &fs_devices->devices, dev_list) { if (device->bdev == bdev) {
btrfs_err(fs_info, "target device is in the filesystem!");
ret = -EEXIST; goto error;
}
}
if (bdev_nr_bytes(bdev) < btrfs_device_get_total_bytes(srcdev)) {
btrfs_err(fs_info, "target device is smaller than source device!");
ret = -EINVAL; goto error;
}
device = btrfs_alloc_device(NULL, &devid, NULL, device_path); if (IS_ERR(device)) {
ret = PTR_ERR(device); goto error;
}
ret = lookup_bdev(device_path, &device->devt); if (ret) goto error;
/* Do not use "to_copy" on non zoned filesystem for now */ if (!btrfs_is_zoned(fs_info)) return0;
mutex_lock(&fs_info->chunk_mutex);
/* Ensure we don't have pending new block group */
spin_lock(&fs_info->trans_lock); while (fs_info->running_transaction &&
!list_empty(&fs_info->running_transaction->dev_update_list)) {
spin_unlock(&fs_info->trans_lock);
mutex_unlock(&fs_info->chunk_mutex);
trans = btrfs_attach_transaction(root); if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
mutex_lock(&fs_info->chunk_mutex); if (ret == -ENOENT) {
spin_lock(&fs_info->trans_lock); continue;
} else { goto unlock;
}
}
ret = btrfs_commit_transaction(trans);
mutex_lock(&fs_info->chunk_mutex); if (ret) goto unlock;
num_extents = 0;
cur_extent = 0; for (i = 0; i < map->num_stripes; i++) { /* We have more device extent to copy */ if (srcdev != map->stripes[i].dev) continue;
num_extents++; if (physical == map->stripes[i].physical)
cur_extent = i;
}
src_device = btrfs_find_device_by_devspec(fs_info, srcdevid,
srcdev_name); if (IS_ERR(src_device)) return PTR_ERR(src_device);
if (btrfs_pinned_by_swapfile(fs_info, src_device)) {
btrfs_warn(fs_info, "cannot replace device %s (devid %llu) due to active swapfile",
btrfs_dev_name(src_device), src_device->devid); return -ETXTBSY;
}
/* *Herewecommitthetransactiontomakesurecommit_total_bytes *ofallthedevicesareupdated.
*/
trans = btrfs_attach_transaction(root); if (!IS_ERR(trans)) {
ret = btrfs_commit_transaction(trans); if (ret) return ret;
} elseif (PTR_ERR(trans) != -ENOENT) { return PTR_ERR(trans);
}
ret = btrfs_init_dev_replace_tgtdev(fs_info, tgtdev_name,
src_device, &tgt_device); if (ret) return ret;
ret = mark_block_group_to_copy(fs_info, src_device); if (ret) return ret;
down_write(&dev_replace->rwsem);
dev_replace->replace_task = current; switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
DEBUG_WARN("unexpected STARTED ot SUSPENDED dev-replace state");
ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED;
up_write(&dev_replace->rwsem); goto leave;
}
ret = btrfs_sysfs_add_device(tgt_device); if (ret)
btrfs_err(fs_info, "kobj add dev failed %d", ret);
btrfs_wait_ordered_roots(fs_info, U64_MAX, NULL);
/* *Commitdev_replacestateandreserve1itemforit. *Thisiscrucialtoensurewewon'tmisscopyingextentsfornewblock *groupsthatareallocatedafterwestartedthedevicereplace,and *mustbedoneaftersettingupthedevicereplacestate.
*/
trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
down_write(&dev_replace->rwsem);
dev_replace->replace_state =
BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED;
dev_replace->srcdev = NULL;
dev_replace->tgtdev = NULL;
up_write(&dev_replace->rwsem); goto leave;
}
ret = btrfs_commit_transaction(trans);
WARN_ON(ret);
/* the disk copy procedure reuses the scrub code */
ret = btrfs_scrub_dev(fs_info, src_device->devid, 0,
btrfs_device_get_total_bytes(src_device),
&dev_replace->scrub_progress, 0, 1);
ret = btrfs_dev_replace_finishing(fs_info, ret); if (ret == -EINPROGRESS)
ret = BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS;
/* don't allow cancel or unmount to disturb the finishing procedure */
mutex_lock(&dev_replace->lock_finishing_cancel_unmount);
down_read(&dev_replace->rwsem); /* was the operation canceled, or is it finished? */ if (dev_replace->replace_state !=
BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED) {
up_read(&dev_replace->rwsem);
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return0;
}
/* *flushalloutstandingI/Oandinodeextentmappingsbeforethe *copyoperationisdeclaredasbeingfinished
*/
ret = btrfs_start_delalloc_roots(fs_info, LONG_MAX, false); if (ret) {
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return ret;
}
btrfs_wait_ordered_roots(fs_info, U64_MAX, NULL);
/* *Wehavetousethisloopapproachbecauseatthispointsrc_device *hastobeavailablefortransactioncommittocomplete,yetnew *chunksshouldn'tbeallocatedonthedevice.
*/ while (1) {
trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) {
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return PTR_ERR(trans);
}
ret = btrfs_commit_transaction(trans);
WARN_ON(ret);
/* Prevent write_all_supers() during the finishing procedure */
mutex_lock(&fs_devices->device_list_mutex); /* Prevent new chunks being allocated on the source device */
mutex_lock(&fs_info->chunk_mutex);
switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
ret = 0; break; case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED:
ret = 1000; break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
ret = div64_u64(dev_replace->cursor_left,
div_u64(btrfs_device_get_total_bytes(
dev_replace->srcdev), 1000)); break;
}
mutex_lock(&dev_replace->lock_finishing_cancel_unmount);
down_write(&dev_replace->rwsem); switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED;
up_write(&dev_replace->rwsem); break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
tgt_device = dev_replace->tgtdev;
src_device = dev_replace->srcdev;
up_write(&dev_replace->rwsem);
ret = btrfs_scrub_cancel(fs_info); if (ret < 0) {
result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED;
} else {
result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR; /* *btrfs_dev_replace_finishing()willhandlethe *cleanuppart
*/
btrfs_info(fs_info, "dev_replace from %s (devid %llu) to %s canceled",
btrfs_dev_name(src_device), src_device->devid,
btrfs_dev_name(tgt_device));
} break; case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: /* *Scrubdoingthereplaceisn'trunningsoweneedtodothe *cleanupstepofbtrfs_dev_replace_finishing()here
*/
result = BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR;
tgt_device = dev_replace->tgtdev;
src_device = dev_replace->srcdev;
dev_replace->tgtdev = NULL;
dev_replace->srcdev = NULL;
dev_replace->replace_state =
BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED;
dev_replace->time_stopped = ktime_get_real_seconds();
dev_replace->item_needs_writeback = 1;
up_write(&dev_replace->rwsem);
/* Scrub for replace must not be running in suspended state */
btrfs_scrub_cancel(fs_info);
trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) {
mutex_unlock(&dev_replace->lock_finishing_cancel_unmount); return PTR_ERR(trans);
}
ret = btrfs_commit_transaction(trans);
WARN_ON(ret);
btrfs_info(fs_info, "suspended dev_replace from %s (devid %llu) to %s canceled",
btrfs_dev_name(src_device), src_device->devid,
btrfs_dev_name(tgt_device));
if (tgt_device)
btrfs_destroy_dev_replace_tgtdev(tgt_device); break; default:
up_write(&dev_replace->rwsem);
result = -EINVAL;
}
switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED: case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
dev_replace->replace_state =
BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED;
dev_replace->time_stopped = ktime_get_real_seconds();
dev_replace->item_needs_writeback = 1;
btrfs_info(fs_info, "suspending dev_replace for unmount"); break;
}
/* resume dev_replace procedure that was interrupted by unmount */ int btrfs_resume_dev_replace_async(struct btrfs_fs_info *fs_info)
{ struct task_struct *task; struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
down_write(&dev_replace->rwsem);
switch (dev_replace->replace_state) { case BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED: case BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED: case BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED:
up_write(&dev_replace->rwsem); return0; case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED: break; case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
dev_replace->replace_state =
BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED; break;
} if (!dev_replace->tgtdev || !dev_replace->tgtdev->bdev) {
btrfs_info(fs_info, "cannot continue dev_replace, tgtdev is missing");
btrfs_info(fs_info, "you may cancel the operation after 'mount -o degraded'");
dev_replace->replace_state =
BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED;
up_write(&dev_replace->rwsem); return0;
}
up_write(&dev_replace->rwsem);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.