|
|
|
@ -1559,6 +1559,79 @@ static void drive_backup_clean(BlkTransactionState *common) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
typedef struct BlockdevBackupState { |
|
|
|
BlkTransactionState common; |
|
|
|
BlockDriverState *bs; |
|
|
|
BlockJob *job; |
|
|
|
AioContext *aio_context; |
|
|
|
} BlockdevBackupState; |
|
|
|
|
|
|
|
static void blockdev_backup_prepare(BlkTransactionState *common, Error **errp) |
|
|
|
{ |
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); |
|
|
|
BlockdevBackup *backup; |
|
|
|
BlockDriverState *bs, *target; |
|
|
|
Error *local_err = NULL; |
|
|
|
|
|
|
|
assert(common->action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); |
|
|
|
backup = common->action->blockdev_backup; |
|
|
|
|
|
|
|
bs = bdrv_find(backup->device); |
|
|
|
if (!bs) { |
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
target = bdrv_find(backup->target); |
|
|
|
if (!target) { |
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->target); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* AioContext is released in .clean() */ |
|
|
|
state->aio_context = bdrv_get_aio_context(bs); |
|
|
|
if (state->aio_context != bdrv_get_aio_context(target)) { |
|
|
|
state->aio_context = NULL; |
|
|
|
error_setg(errp, "Backup between two IO threads is not implemented"); |
|
|
|
return; |
|
|
|
} |
|
|
|
aio_context_acquire(state->aio_context); |
|
|
|
|
|
|
|
qmp_blockdev_backup(backup->device, backup->target, |
|
|
|
backup->sync, |
|
|
|
backup->has_speed, backup->speed, |
|
|
|
backup->has_on_source_error, backup->on_source_error, |
|
|
|
backup->has_on_target_error, backup->on_target_error, |
|
|
|
&local_err); |
|
|
|
if (local_err) { |
|
|
|
error_propagate(errp, local_err); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
state->bs = bs; |
|
|
|
state->job = state->bs->job; |
|
|
|
} |
|
|
|
|
|
|
|
static void blockdev_backup_abort(BlkTransactionState *common) |
|
|
|
{ |
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); |
|
|
|
BlockDriverState *bs = state->bs; |
|
|
|
|
|
|
|
/* Only cancel if it's the job we started */ |
|
|
|
if (bs && bs->job && bs->job == state->job) { |
|
|
|
block_job_cancel_sync(bs->job); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void blockdev_backup_clean(BlkTransactionState *common) |
|
|
|
{ |
|
|
|
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); |
|
|
|
|
|
|
|
if (state->aio_context) { |
|
|
|
aio_context_release(state->aio_context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void abort_prepare(BlkTransactionState *common, Error **errp) |
|
|
|
{ |
|
|
|
error_setg(errp, "Transaction aborted using Abort action"); |
|
|
|
@ -1582,6 +1655,12 @@ static const BdrvActionOps actions[] = { |
|
|
|
.abort = drive_backup_abort, |
|
|
|
.clean = drive_backup_clean, |
|
|
|
}, |
|
|
|
[TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = { |
|
|
|
.instance_size = sizeof(BlockdevBackupState), |
|
|
|
.prepare = blockdev_backup_prepare, |
|
|
|
.abort = blockdev_backup_abort, |
|
|
|
.clean = blockdev_backup_clean, |
|
|
|
}, |
|
|
|
[TRANSACTION_ACTION_KIND_ABORT] = { |
|
|
|
.instance_size = sizeof(BlkTransactionState), |
|
|
|
.prepare = abort_prepare, |
|
|
|
|