Merge remote-tracking branch 'kwolf/for-anthony' into staging

# By Stefan Hajnoczi (11) and others
# Via Kevin Wolf
* kwolf/for-anthony:
  cmd646: fix build when DEBUG_IDE is enabled.
  block: change default of .has_zero_init to 0
  vpc: Implement .bdrv_has_zero_init
  vmdk: remove wrong calculation of relative path
  gluster: Return bdrv_has_zero_init = 0
  block/ssh: Set bdrv_has_zero_init according to the file type.
  block: Make BlockJobTypes const
  qemu-iotests: add 055 drive-backup test case
  qemu-iotests: extract wait_until_completed() into iotests.py
  blockdev: add Abort transaction
  blockdev: add DriveBackup transaction
  blockdev: allow BdrvActionOps->commit() to be NULL
  blockdev: rename BlkTransactionStates to singular
  block: add drive-backup QMP command
  blockdev: use bdrv_getlength() in qmp_drive_mirror()
  blockdev: drop redundant proto_drv check
  block: add basic backup support to block driver
  block: add bdrv_add_before_write_notifier()
  notify: add NotiferWithReturn so notifier list can abort
  raw-posix: Fix /dev/cdrom magic on OS X

Message-id: 1372429509-29642-1-git-send-email-kwolf@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
This commit is contained in:
Anthony Liguori
2013-06-28 10:37:34 -05:00
33 changed files with 1198 additions and 163 deletions
+19 -12
View File
@@ -305,6 +305,7 @@ BlockDriverState *bdrv_new(const char *device_name)
}
bdrv_iostatus_disable(bs);
notifier_list_init(&bs->close_notifiers);
notifier_with_return_list_init(&bs->before_write_notifiers);
return bs;
}
@@ -1840,16 +1841,6 @@ int bdrv_commit_all(void)
return 0;
}
struct BdrvTrackedRequest {
BlockDriverState *bs;
int64_t sector_num;
int nb_sectors;
bool is_write;
QLIST_ENTRY(BdrvTrackedRequest) list;
Coroutine *co; /* owner, used for deadlock detection */
CoQueue wait_queue; /* coroutines blocked on this request */
};
/**
* Remove an active request from the tracked requests list
*
@@ -2620,7 +2611,11 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
tracked_request_begin(&req, bs, sector_num, nb_sectors, true);
if (flags & BDRV_REQ_ZERO_WRITE) {
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
if (ret < 0) {
/* Do nothing, write notifier decided to fail this request */
} else if (flags & BDRV_REQ_ZERO_WRITE) {
ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors);
} else {
ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
@@ -2916,6 +2911,11 @@ void bdrv_flush_all(void)
}
}
int bdrv_has_zero_init_1(BlockDriverState *bs)
{
return 1;
}
int bdrv_has_zero_init(BlockDriverState *bs)
{
assert(bs->drv);
@@ -2924,7 +2924,8 @@ int bdrv_has_zero_init(BlockDriverState *bs)
return bs->drv->bdrv_has_zero_init(bs);
}
return 1;
/* safe default */
return 0;
}
typedef struct BdrvCoIsAllocatedData {
@@ -4581,3 +4582,9 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs)
/* Currently BlockDriverState always uses the main loop AioContext */
return qemu_get_aio_context();
}
void bdrv_add_before_write_notifier(BlockDriverState *bs,
NotifierWithReturn *notifier)
{
notifier_with_return_list_add(&bs->before_write_notifiers, notifier);
}
+1
View File
@@ -21,5 +21,6 @@ endif
common-obj-y += stream.o
common-obj-y += commit.o
common-obj-y += mirror.o
common-obj-y += backup.o
$(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS)
+341
View File
@@ -0,0 +1,341 @@
/*
* QEMU backup
*
* Copyright (C) 2013 Proxmox Server Solutions
*
* Authors:
* Dietmar Maurer (dietmar@proxmox.com)
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include "trace.h"
#include "block/block.h"
#include "block/block_int.h"
#include "block/blockjob.h"
#include "qemu/ratelimit.h"
#define BACKUP_CLUSTER_BITS 16
#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS)
#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
#define SLICE_TIME 100000000ULL /* ns */
typedef struct CowRequest {
int64_t start;
int64_t end;
QLIST_ENTRY(CowRequest) list;
CoQueue wait_queue; /* coroutines blocked on this request */
} CowRequest;
typedef struct BackupBlockJob {
BlockJob common;
BlockDriverState *target;
RateLimit limit;
BlockdevOnError on_source_error;
BlockdevOnError on_target_error;
CoRwlock flush_rwlock;
uint64_t sectors_read;
HBitmap *bitmap;
QLIST_HEAD(, CowRequest) inflight_reqs;
} BackupBlockJob;
/* See if in-flight requests overlap and wait for them to complete */
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
int64_t start,
int64_t end)
{
CowRequest *req;
bool retry;
do {
retry = false;
QLIST_FOREACH(req, &job->inflight_reqs, list) {
if (end > req->start && start < req->end) {
qemu_co_queue_wait(&req->wait_queue);
retry = true;
break;
}
}
} while (retry);
}
/* Keep track of an in-flight request */
static void cow_request_begin(CowRequest *req, BackupBlockJob *job,
int64_t start, int64_t end)
{
req->start = start;
req->end = end;
qemu_co_queue_init(&req->wait_queue);
QLIST_INSERT_HEAD(&job->inflight_reqs, req, list);
}
/* Forget about a completed request */
static void cow_request_end(CowRequest *req)
{
QLIST_REMOVE(req, list);
qemu_co_queue_restart_all(&req->wait_queue);
}
static int coroutine_fn backup_do_cow(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
bool *error_is_read)
{
BackupBlockJob *job = (BackupBlockJob *)bs->job;
CowRequest cow_request;
struct iovec iov;
QEMUIOVector bounce_qiov;
void *bounce_buffer = NULL;
int ret = 0;
int64_t start, end;
int n;
qemu_co_rwlock_rdlock(&job->flush_rwlock);
start = sector_num / BACKUP_SECTORS_PER_CLUSTER;
end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER);
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end);
for (; start < end; start++) {
if (hbitmap_get(job->bitmap, start)) {
trace_backup_do_cow_skip(job, start);
continue; /* already copied */
}
trace_backup_do_cow_process(job, start);
n = MIN(BACKUP_SECTORS_PER_CLUSTER,
job->common.len / BDRV_SECTOR_SIZE -
start * BACKUP_SECTORS_PER_CLUSTER);
if (!bounce_buffer) {
bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE);
}
iov.iov_base = bounce_buffer;
iov.iov_len = n * BDRV_SECTOR_SIZE;
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
&bounce_qiov);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
}
goto out;
}
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
ret = bdrv_co_write_zeroes(job->target,
start * BACKUP_SECTORS_PER_CLUSTER, n);
} else {
ret = bdrv_co_writev(job->target,
start * BACKUP_SECTORS_PER_CLUSTER, n,
&bounce_qiov);
}
if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret);
if (error_is_read) {
*error_is_read = false;
}
goto out;
}
hbitmap_set(job->bitmap, start, 1);
/* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset.
*/
job->sectors_read += n;
job->common.offset += n * BDRV_SECTOR_SIZE;
}
out:
if (bounce_buffer) {
qemu_vfree(bounce_buffer);
}
cow_request_end(&cow_request);
trace_backup_do_cow_return(job, sector_num, nb_sectors, ret);
qemu_co_rwlock_unlock(&job->flush_rwlock);
return ret;
}
static int coroutine_fn backup_before_write_notify(
NotifierWithReturn *notifier,
void *opaque)
{
BdrvTrackedRequest *req = opaque;
return backup_do_cow(req->bs, req->sector_num, req->nb_sectors, NULL);
}
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (speed < 0) {
error_set(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
}
static void backup_iostatus_reset(BlockJob *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
bdrv_iostatus_reset(s->target);
}
static const BlockJobType backup_job_type = {
.instance_size = sizeof(BackupBlockJob),
.job_type = "backup",
.set_speed = backup_set_speed,
.iostatus_reset = backup_iostatus_reset,
};
static BlockErrorAction backup_error_action(BackupBlockJob *job,
bool read, int error)
{
if (read) {
return block_job_error_action(&job->common, job->common.bs,
job->on_source_error, true, error);
} else {
return block_job_error_action(&job->common, job->target,
job->on_target_error, false, error);
}
}
static void coroutine_fn backup_run(void *opaque)
{
BackupBlockJob *job = opaque;
BlockDriverState *bs = job->common.bs;
BlockDriverState *target = job->target;
BlockdevOnError on_target_error = job->on_target_error;
NotifierWithReturn before_write = {
.notify = backup_before_write_notify,
};
int64_t start, end;
int ret = 0;
QLIST_INIT(&job->inflight_reqs);
qemu_co_rwlock_init(&job->flush_rwlock);
start = 0;
end = DIV_ROUND_UP(job->common.len / BDRV_SECTOR_SIZE,
BACKUP_SECTORS_PER_CLUSTER);
job->bitmap = hbitmap_alloc(end, 0);
bdrv_set_enable_write_cache(target, true);
bdrv_set_on_error(target, on_target_error, on_target_error);
bdrv_iostatus_enable(target);
bdrv_add_before_write_notifier(bs, &before_write);
for (; start < end; start++) {
bool error_is_read;
if (block_job_is_cancelled(&job->common)) {
break;
}
/* we need to yield so that qemu_aio_flush() returns.
* (without, VM does not reboot)
*/
if (job->common.speed) {
uint64_t delay_ns = ratelimit_calculate_delay(
&job->limit, job->sectors_read);
job->sectors_read = 0;
block_job_sleep_ns(&job->common, rt_clock, delay_ns);
} else {
block_job_sleep_ns(&job->common, rt_clock, 0);
}
if (block_job_is_cancelled(&job->common)) {
break;
}
ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
BACKUP_SECTORS_PER_CLUSTER, &error_is_read);
if (ret < 0) {
/* Depending on error action, fail now or retry cluster */
BlockErrorAction action =
backup_error_action(job, error_is_read, -ret);
if (action == BDRV_ACTION_REPORT) {
break;
} else {
start--;
continue;
}
}
}
notifier_with_return_remove(&before_write);
/* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&job->flush_rwlock);
qemu_co_rwlock_unlock(&job->flush_rwlock);
hbitmap_free(job->bitmap);
bdrv_iostatus_disable(target);
bdrv_delete(target);
block_job_completed(&job->common, ret);
}
void backup_start(BlockDriverState *bs, BlockDriverState *target,
int64_t speed,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
BlockDriverCompletionFunc *cb, void *opaque,
Error **errp)
{
int64_t len;
assert(bs);
assert(target);
assert(cb);
if ((on_source_error == BLOCKDEV_ON_ERROR_STOP ||
on_source_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
!bdrv_iostatus_is_enabled(bs)) {
error_set(errp, QERR_INVALID_PARAMETER, "on-source-error");
return;
}
len = bdrv_getlength(bs);
if (len < 0) {
error_setg_errno(errp, -len, "unable to get length for '%s'",
bdrv_get_device_name(bs));
return;
}
BackupBlockJob *job = block_job_create(&backup_job_type, bs, speed,
cb, opaque, errp);
if (!job) {
return;
}
job->on_source_error = on_source_error;
job->on_target_error = on_target_error;
job->target = target;
job->common.len = len;
job->common.co = qemu_coroutine_create(backup_run);
qemu_coroutine_enter(job->common.co, job);
}
+1 -1
View File
@@ -173,7 +173,7 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
}
static BlockJobType commit_job_type = {
static const BlockJobType commit_job_type = {
.instance_size = sizeof(CommitBlockJob),
.job_type = "commit",
.set_speed = commit_set_speed,
+1
View File
@@ -340,6 +340,7 @@ static BlockDriver bdrv_cow = {
.bdrv_open = cow_open,
.bdrv_close = cow_close,
.bdrv_create = cow_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_read = cow_co_read,
.bdrv_write = cow_co_write,
+10
View File
@@ -574,6 +574,12 @@ static void qemu_gluster_close(BlockDriverState *bs)
glfs_fini(s->glfs);
}
static int qemu_gluster_has_zero_init(BlockDriverState *bs)
{
/* GlusterFS volume could be backed by a block device */
return 0;
}
static QEMUOptionParameter qemu_gluster_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -595,6 +601,7 @@ static BlockDriver bdrv_gluster = {
.bdrv_aio_readv = qemu_gluster_aio_readv,
.bdrv_aio_writev = qemu_gluster_aio_writev,
.bdrv_aio_flush = qemu_gluster_aio_flush,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
.create_options = qemu_gluster_create_options,
};
@@ -610,6 +617,7 @@ static BlockDriver bdrv_gluster_tcp = {
.bdrv_aio_readv = qemu_gluster_aio_readv,
.bdrv_aio_writev = qemu_gluster_aio_writev,
.bdrv_aio_flush = qemu_gluster_aio_flush,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
.create_options = qemu_gluster_create_options,
};
@@ -625,6 +633,7 @@ static BlockDriver bdrv_gluster_unix = {
.bdrv_aio_readv = qemu_gluster_aio_readv,
.bdrv_aio_writev = qemu_gluster_aio_writev,
.bdrv_aio_flush = qemu_gluster_aio_flush,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
.create_options = qemu_gluster_create_options,
};
@@ -640,6 +649,7 @@ static BlockDriver bdrv_gluster_rdma = {
.bdrv_aio_readv = qemu_gluster_aio_readv,
.bdrv_aio_writev = qemu_gluster_aio_writev,
.bdrv_aio_flush = qemu_gluster_aio_flush,
.bdrv_has_zero_init = qemu_gluster_has_zero_init,
.create_options = qemu_gluster_create_options,
};
+1 -1
View File
@@ -524,7 +524,7 @@ static void mirror_complete(BlockJob *job, Error **errp)
block_job_resume(job);
}
static BlockJobType mirror_job_type = {
static const BlockJobType mirror_job_type = {
.instance_size = sizeof(MirrorBlockJob),
.job_type = "mirror",
.set_speed = mirror_set_speed,
+1
View File
@@ -892,6 +892,7 @@ static BlockDriver bdrv_qcow = {
.bdrv_close = qcow_close,
.bdrv_reopen_prepare = qcow_reopen_prepare,
.bdrv_create = qcow_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_readv = qcow_co_readv,
.bdrv_co_writev = qcow_co_writev,
+1
View File
@@ -1785,6 +1785,7 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_close = qcow2_close,
.bdrv_reopen_prepare = qcow2_reopen_prepare,
.bdrv_create = qcow2_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_is_allocated = qcow2_co_is_allocated,
.bdrv_set_key = qcow2_set_key,
.bdrv_make_empty = qcow2_make_empty,
+1
View File
@@ -1574,6 +1574,7 @@ static BlockDriver bdrv_qed = {
.bdrv_close = bdrv_qed_close,
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
.bdrv_create = bdrv_qed_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_is_allocated = bdrv_qed_co_is_allocated,
.bdrv_make_empty = bdrv_qed_make_empty,
.bdrv_aio_readv = bdrv_qed_aio_readv,
+2 -9
View File
@@ -1199,6 +1199,7 @@ static BlockDriver bdrv_file = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_close = raw_close,
.bdrv_create = raw_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_is_allocated = raw_co_is_allocated,
.bdrv_aio_readv = raw_aio_readv,
@@ -1350,6 +1351,7 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
qemu_close(fd);
}
filename = bsdPath;
qdict_put(options, "filename", qstring_from_str(filename));
}
if ( mediaIterator )
@@ -1526,11 +1528,6 @@ static int hdev_create(const char *filename, QEMUOptionParameter *options)
return ret;
}
static int hdev_has_zero_init(BlockDriverState *bs)
{
return 0;
}
static BlockDriver bdrv_host_device = {
.format_name = "host_device",
.protocol_name = "host_device",
@@ -1543,7 +1540,6 @@ static BlockDriver bdrv_host_device = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
@@ -1668,7 +1664,6 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
@@ -1770,7 +1765,6 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
@@ -1892,7 +1886,6 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_reopen_abort = raw_reopen_abort,
.bdrv_create = hdev_create,
.create_options = raw_create_options,
.bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
+1 -6
View File
@@ -459,6 +459,7 @@ static BlockDriver bdrv_file = {
.bdrv_file_open = raw_open,
.bdrv_close = raw_close,
.bdrv_create = raw_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
@@ -570,11 +571,6 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
return 0;
}
static int hdev_has_zero_init(BlockDriverState *bs)
{
return 0;
}
static BlockDriver bdrv_host_device = {
.format_name = "host_device",
.protocol_name = "host_device",
@@ -582,7 +578,6 @@ static BlockDriver bdrv_host_device = {
.bdrv_probe_device = hdev_probe_device,
.bdrv_file_open = hdev_open,
.bdrv_close = raw_close,
.bdrv_has_zero_init = hdev_has_zero_init,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
+1
View File
@@ -996,6 +996,7 @@ static BlockDriver bdrv_rbd = {
.bdrv_file_open = qemu_rbd_open,
.bdrv_close = qemu_rbd_close,
.bdrv_create = qemu_rbd_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_get_info = qemu_rbd_getinfo,
.create_options = qemu_rbd_create_options,
.bdrv_getlength = qemu_rbd_getlength,
+1
View File
@@ -2401,6 +2401,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_file_open = sd_open,
.bdrv_close = sd_close,
.bdrv_create = sd_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength,
.bdrv_truncate = sd_truncate,
+16
View File
@@ -716,6 +716,21 @@ static void ssh_close(BlockDriverState *bs)
ssh_state_free(s);
}
static int ssh_has_zero_init(BlockDriverState *bs)
{
BDRVSSHState *s = bs->opaque;
/* Assume false, unless we can positively prove it's true. */
int has_zero_init = 0;
if (s->attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
if (s->attrs.permissions & LIBSSH2_SFTP_S_IFREG) {
has_zero_init = 1;
}
}
return has_zero_init;
}
static void restart_coroutine(void *opaque)
{
Coroutine *co = opaque;
@@ -1037,6 +1052,7 @@ static BlockDriver bdrv_ssh = {
.bdrv_file_open = ssh_file_open,
.bdrv_create = ssh_create,
.bdrv_close = ssh_close,
.bdrv_has_zero_init = ssh_has_zero_init,
.bdrv_co_readv = ssh_co_readv,
.bdrv_co_writev = ssh_co_writev,
.bdrv_getlength = ssh_getlength,
+1 -1
View File
@@ -198,7 +198,7 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
}
static BlockJobType stream_job_type = {
static const BlockJobType stream_job_type = {
.instance_size = sizeof(StreamBlockJob),
.job_type = "stream",
.set_speed = stream_set_speed,
+1
View File
@@ -779,6 +779,7 @@ static BlockDriver bdrv_vdi = {
.bdrv_close = vdi_close,
.bdrv_reopen_prepare = vdi_reopen_prepare,
.bdrv_create = vdi_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_co_is_allocated = vdi_co_is_allocated,
.bdrv_make_empty = vdi_make_empty,
+1 -43
View File
@@ -1487,45 +1487,6 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
return VMDK_OK;
}
static int relative_path(char *dest, int dest_size,
const char *base, const char *target)
{
int i = 0;
int n = 0;
const char *p, *q;
#ifdef _WIN32
const char *sep = "\\";
#else
const char *sep = "/";
#endif
if (!(dest && base && target)) {
return VMDK_ERROR;
}
if (path_is_absolute(target)) {
pstrcpy(dest, dest_size, target);
return VMDK_OK;
}
while (base[i] == target[i]) {
i++;
}
p = &base[i];
q = &target[i];
while (*p) {
if (*p == *sep) {
n++;
}
p++;
}
dest[0] = '\0';
for (; n; n--) {
pstrcat(dest, dest_size, "..");
pstrcat(dest, dest_size, sep);
}
pstrcat(dest, dest_size, q);
return VMDK_OK;
}
static int vmdk_create(const char *filename, QEMUOptionParameter *options)
{
int fd, idx = 0;
@@ -1625,7 +1586,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
return -ENOTSUP;
}
if (backing_file) {
char parent_filename[PATH_MAX];
BlockDriverState *bs = bdrv_new("");
ret = bdrv_open(bs, backing_file, NULL, 0, NULL);
if (ret != 0) {
@@ -1638,10 +1598,8 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
}
parent_cid = vmdk_read_cid(bs, 0);
bdrv_delete(bs);
relative_path(parent_filename, sizeof(parent_filename),
filename, backing_file);
snprintf(parent_desc_line, sizeof(parent_desc_line),
"parentFileNameHint=\"%s\"", parent_filename);
"parentFileNameHint=\"%s\"", backing_file);
}
/* Create extents */
+19 -6
View File
@@ -786,6 +786,18 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
return ret;
}
static int vpc_has_zero_init(BlockDriverState *bs)
{
BDRVVPCState *s = bs->opaque;
struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf;
if (cpu_to_be32(footer->type) == VHD_FIXED) {
return bdrv_has_zero_init(bs->file);
} else {
return 1;
}
}
static void vpc_close(BlockDriverState *bs)
{
BDRVVPCState *s = bs->opaque;
@@ -818,16 +830,17 @@ static BlockDriver bdrv_vpc = {
.format_name = "vpc",
.instance_size = sizeof(BDRVVPCState),
.bdrv_probe = vpc_probe,
.bdrv_open = vpc_open,
.bdrv_close = vpc_close,
.bdrv_reopen_prepare = vpc_reopen_prepare,
.bdrv_create = vpc_create,
.bdrv_probe = vpc_probe,
.bdrv_open = vpc_open,
.bdrv_close = vpc_close,
.bdrv_reopen_prepare = vpc_reopen_prepare,
.bdrv_create = vpc_create,
.bdrv_read = vpc_co_read,
.bdrv_write = vpc_co_write,
.create_options = vpc_create_options,
.create_options = vpc_create_options,
.bdrv_has_zero_init = vpc_has_zero_init,
};
static void bdrv_vpc_init(void)
+220 -68
View File
@@ -780,7 +780,7 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
/* New and old BlockDriverState structs for group snapshots */
typedef struct BlkTransactionStates BlkTransactionStates;
typedef struct BlkTransactionState BlkTransactionState;
/* Only prepare() may fail. In a single transaction, only one of commit() or
abort() will be called, clean() will always be called if it present. */
@@ -788,13 +788,13 @@ typedef struct BdrvActionOps {
/* Size of state struct, in bytes. */
size_t instance_size;
/* Prepare the work, must NOT be NULL. */
void (*prepare)(BlkTransactionStates *common, Error **errp);
/* Commit the changes, must NOT be NULL. */
void (*commit)(BlkTransactionStates *common);
void (*prepare)(BlkTransactionState *common, Error **errp);
/* Commit the changes, can be NULL. */
void (*commit)(BlkTransactionState *common);
/* Abort the changes on fail, can be NULL. */
void (*abort)(BlkTransactionStates *common);
void (*abort)(BlkTransactionState *common);
/* Clean up resource in the end, can be NULL. */
void (*clean)(BlkTransactionStates *common);
void (*clean)(BlkTransactionState *common);
} BdrvActionOps;
/*
@@ -802,23 +802,22 @@ typedef struct BdrvActionOps {
* that compiler will also arrange it to the same address with parent instance.
* Later it will be used in free().
*/
struct BlkTransactionStates {
struct BlkTransactionState {
TransactionAction *action;
const BdrvActionOps *ops;
QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
QSIMPLEQ_ENTRY(BlkTransactionState) entry;
};
/* external snapshot private data */
typedef struct ExternalSnapshotStates {
BlkTransactionStates common;
typedef struct ExternalSnapshotState {
BlkTransactionState common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
} ExternalSnapshotStates;
} ExternalSnapshotState;
static void external_snapshot_prepare(BlkTransactionStates *common,
static void external_snapshot_prepare(BlkTransactionState *common,
Error **errp)
{
BlockDriver *proto_drv;
BlockDriver *drv;
int flags, ret;
Error *local_err = NULL;
@@ -826,8 +825,8 @@ static void external_snapshot_prepare(BlkTransactionStates *common,
const char *new_image_file;
const char *format = "qcow2";
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
ExternalSnapshotStates *states =
DO_UPCAST(ExternalSnapshotStates, common, common);
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action;
/* get parameters */
@@ -849,42 +848,36 @@ static void external_snapshot_prepare(BlkTransactionStates *common,
return;
}
states->old_bs = bdrv_find(device);
if (!states->old_bs) {
state->old_bs = bdrv_find(device);
if (!state->old_bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (!bdrv_is_inserted(states->old_bs)) {
if (!bdrv_is_inserted(state->old_bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
}
if (bdrv_in_use(states->old_bs)) {
if (bdrv_in_use(state->old_bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
return;
}
if (!bdrv_is_read_only(states->old_bs)) {
if (bdrv_flush(states->old_bs)) {
if (!bdrv_is_read_only(state->old_bs)) {
if (bdrv_flush(state->old_bs)) {
error_set(errp, QERR_IO_ERROR);
return;
}
}
flags = states->old_bs->open_flags;
proto_drv = bdrv_find_protocol(new_image_file);
if (!proto_drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
flags = state->old_bs->open_flags;
/* create new image w/backing file */
if (mode != NEW_IMAGE_MODE_EXISTING) {
bdrv_img_create(new_image_file, format,
states->old_bs->filename,
states->old_bs->drv->format_name,
state->old_bs->filename,
state->old_bs->drv->format_name,
NULL, -1, flags, &local_err, false);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
@@ -893,46 +886,110 @@ static void external_snapshot_prepare(BlkTransactionStates *common,
}
/* We will manually add the backing_hd field to the bs later */
states->new_bs = bdrv_new("");
state->new_bs = bdrv_new("");
/* TODO Inherit bs->options or only take explicit options with an
* extended QMP command? */
ret = bdrv_open(states->new_bs, new_image_file, NULL,
ret = bdrv_open(state->new_bs, new_image_file, NULL,
flags | BDRV_O_NO_BACKING, drv);
if (ret != 0) {
error_setg_file_open(errp, -ret, new_image_file);
}
}
static void external_snapshot_commit(BlkTransactionStates *common)
static void external_snapshot_commit(BlkTransactionState *common)
{
ExternalSnapshotStates *states =
DO_UPCAST(ExternalSnapshotStates, common, common);
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
/* This removes our old bs from the bdrv_states, and adds the new bs */
bdrv_append(states->new_bs, states->old_bs);
/* This removes our old bs and adds the new bs */
bdrv_append(state->new_bs, state->old_bs);
/* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we
* don't want to abort all of them if one of them fails the reopen */
bdrv_reopen(states->new_bs, states->new_bs->open_flags & ~BDRV_O_RDWR,
bdrv_reopen(state->new_bs, state->new_bs->open_flags & ~BDRV_O_RDWR,
NULL);
}
static void external_snapshot_abort(BlkTransactionStates *common)
static void external_snapshot_abort(BlkTransactionState *common)
{
ExternalSnapshotStates *states =
DO_UPCAST(ExternalSnapshotStates, common, common);
if (states->new_bs) {
bdrv_delete(states->new_bs);
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);
if (state->new_bs) {
bdrv_delete(state->new_bs);
}
}
typedef struct DriveBackupState {
BlkTransactionState common;
BlockDriverState *bs;
BlockJob *job;
} DriveBackupState;
static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
DriveBackup *backup;
Error *local_err = NULL;
assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->drive_backup;
qmp_drive_backup(backup->device, backup->target,
backup->has_format, backup->format,
backup->has_mode, backup->mode,
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 (error_is_set(&local_err)) {
error_propagate(errp, local_err);
state->bs = NULL;
state->job = NULL;
return;
}
state->bs = bdrv_find(backup->device);
state->job = state->bs->job;
}
static void drive_backup_abort(BlkTransactionState *common)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, 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 abort_prepare(BlkTransactionState *common, Error **errp)
{
error_setg(errp, "Transaction aborted using Abort action");
}
static void abort_commit(BlkTransactionState *common)
{
assert(false); /* this action never succeeds */
}
static const BdrvActionOps actions[] = {
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
.instance_size = sizeof(ExternalSnapshotStates),
.instance_size = sizeof(ExternalSnapshotState),
.prepare = external_snapshot_prepare,
.commit = external_snapshot_commit,
.abort = external_snapshot_abort,
},
[TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
.instance_size = sizeof(DriveBackupState),
.prepare = drive_backup_prepare,
.abort = drive_backup_abort,
},
[TRANSACTION_ACTION_KIND_ABORT] = {
.instance_size = sizeof(BlkTransactionState),
.prepare = abort_prepare,
.commit = abort_commit,
},
};
/*
@@ -943,10 +1000,10 @@ static const BdrvActionOps actions[] = {
void qmp_transaction(TransactionActionList *dev_list, Error **errp)
{
TransactionActionList *dev_entry = dev_list;
BlkTransactionStates *states, *next;
BlkTransactionState *state, *next;
Error *local_err = NULL;
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionState) snap_bdrv_states;
QSIMPLEQ_INIT(&snap_bdrv_states);
/* drain all i/o before any snapshots */
@@ -963,20 +1020,22 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp)
assert(dev_info->kind < ARRAY_SIZE(actions));
ops = &actions[dev_info->kind];
states = g_malloc0(ops->instance_size);
states->ops = ops;
states->action = dev_info;
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
state = g_malloc0(ops->instance_size);
state->ops = ops;
state->action = dev_info;
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
states->ops->prepare(states, &local_err);
state->ops->prepare(state, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto delete_and_fail;
}
}
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
states->ops->commit(states);
QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
if (state->ops->commit) {
state->ops->commit(state);
}
}
/* success */
@@ -987,17 +1046,17 @@ delete_and_fail:
* failure, and it is all-or-none; abandon each new bs, and keep using
* the original bs for all images
*/
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
if (states->ops->abort) {
states->ops->abort(states);
QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) {
if (state->ops->abort) {
state->ops->abort(state);
}
}
exit:
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
if (states->ops->clean) {
states->ops->clean(states);
QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
if (state->ops->clean) {
state->ops->clean(state);
}
g_free(states);
g_free(state);
}
}
@@ -1360,6 +1419,103 @@ void qmp_block_commit(const char *device,
drive_get_ref(drive_get_by_blockdev(bs));
}
void qmp_drive_backup(const char *device, const char *target,
bool has_format, const char *format,
bool has_mode, enum NewImageMode mode,
bool has_speed, int64_t speed,
bool has_on_source_error, BlockdevOnError on_source_error,
bool has_on_target_error, BlockdevOnError on_target_error,
Error **errp)
{
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriver *drv = NULL;
Error *local_err = NULL;
int flags;
int64_t size;
int ret;
if (!has_speed) {
speed = 0;
}
if (!has_on_source_error) {
on_source_error = BLOCKDEV_ON_ERROR_REPORT;
}
if (!has_on_target_error) {
on_target_error = BLOCKDEV_ON_ERROR_REPORT;
}
if (!has_mode) {
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
}
if (!has_format) {
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
}
if (format) {
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
}
}
if (bdrv_in_use(bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
return;
}
flags = bs->open_flags | BDRV_O_RDWR;
size = bdrv_getlength(bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
return;
}
if (mode != NEW_IMAGE_MODE_EXISTING) {
assert(format && drv);
bdrv_img_create(target, format,
NULL, NULL, NULL, size, flags, &local_err, false);
}
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return;
}
target_bs = bdrv_new("");
ret = bdrv_open(target_bs, target, NULL, flags, drv);
if (ret < 0) {
bdrv_delete(target_bs);
error_setg_file_open(errp, -ret, target);
return;
}
backup_start(bs, target_bs, speed, on_source_error, on_target_error,
block_job_cb, bs, &local_err);
if (local_err != NULL) {
bdrv_delete(target_bs);
error_propagate(errp, local_err);
return;
}
/* Grab a reference so hotplug does not delete the BlockDriverState from
* underneath us.
*/
drive_get_ref(drive_get_by_blockdev(bs));
}
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
void qmp_drive_mirror(const char *device, const char *target,
@@ -1375,11 +1531,10 @@ void qmp_drive_mirror(const char *device, const char *target,
{
BlockDriverState *bs;
BlockDriverState *source, *target_bs;
BlockDriver *proto_drv;
BlockDriver *drv = NULL;
Error *local_err = NULL;
int flags;
uint64_t size;
int64_t size;
int ret;
if (!has_speed) {
@@ -1443,14 +1598,12 @@ void qmp_drive_mirror(const char *device, const char *target,
sync = MIRROR_SYNC_MODE_FULL;
}
proto_drv = bdrv_find_protocol(target);
if (!proto_drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
size = bdrv_getlength(bs);
if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed");
return;
}
bdrv_get_geometry(bs, &size);
size *= 512;
if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) {
/* create new image w/o backing file */
assert(format && drv);
@@ -1483,7 +1636,6 @@ void qmp_drive_mirror(const char *device, const char *target,
*/
target_bs = bdrv_new("");
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv);
if (ret < 0) {
bdrv_delete(target_bs);
error_setg_file_open(errp, -ret, target);

Some files were not shown because too many files have changed in this diff Show More