* [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer
@ 2015-03-23 15:12 Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node Alberto Garcia
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Alberto Garcia @ 2015-03-23 15:12 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, Markus Armbruster, Max Reitz,
Stefan Hajnoczi
This is a new version of the patches that add support for streaming
to any intermediate layer. You can check the previous version here:
https://lists.gnu.org/archive/html/qemu-devel/2015-02/msg04116.html
I followed Kevin's idea to put the ownership of the block job directly
on the node that receives the data instead of the root node.
There's a few things that I'm not completely sure of and will
certainly generate some debate, but I decided to send the code anyway
so the actual changes can be seen. Note that this depends on the "Add
bdrv_get_device_or_node_name()" patchset I sent last week.
Here are the changes:
1) The 'top' parameter of block-stream disappears. 'device' also
accepts a node name now, which is used to specify the node where
the data will be written.
2) Block jobs can now be owned by any node. This implies:
- The block-job-* commands now also accept a node name in the
'device' parameter. I decided not to add a separate 'node-name'
parameter, following what we agreed with the 'block-stream'
command.
- The BlockJobInfo type and BLOCK_JOB_* events will report the node
name in the 'device' field if the node does not have a device
name. It seems that we had an agreement for the BlockJobInfo case
so I decided to follow the same approach for these events.
However, in the BLOCK_IMAGE_CORRUPTED case the preferred solution
was to add a new 'node-name' field, so I guess we might want to
do the same here?
- query-block-jobs now searches the whole tree, not just the root
nodes.
3) Operations are blocked in all intermediate nodes during the job. If
we have a chain [A] -> [B] -> [C] -> [D] -> [E] and we stream from
[B] to [E], then [C] and [D] will also be blocked during the job.
Since [C] and [D] will be removed from the chain after the job is
finished I understand that we don't want to perform any operation
with them.
4) As a consequence of 3), op blockers are also checked in all
intermediate nodes (not just in the topmost one) before starting a
streaming operation. I'm currently checking BLOCK_OP_TYPE_STREAM,
but maybe I should use a different op for the intermediate nodes
since what I'm going to do there is removing them from the chain?
I think those are the most important changes. Any feedback is welcome!
Berto
v2:
- The 'block-stream' command does not have a 'node-name' parameter
anymore and reuses 'device' for that purpose.
- Block jobs can now be owned by any intermediate node, and not just
by the ones at the root. query-block-jobs is updated to reflect that
change.
- The 'device' parameter of all 'block-job-*' commands can now take a
node name.
- The BlockJobInfo type and all BLOCK_JOB_* events report the node
name in the 'device' field if the node does not have a device name.
- All intermediate nodes are blocked (and checked for blockers) during
the streaming operation.
Alberto Garcia (5):
block: allow block jobs in any arbitrary node
block: never cancel a streaming job without running stream_complete()
block: Support streaming to an intermediate layer
block: Add QMP support for streaming to an intermediate layer
docs: Document how to stream to an intermediate layer
block.c | 4 +++-
block/mirror.c | 5 +++--
block/stream.c | 47 ++++++++++++++++++++++++++++++++++++++++++-----
blockdev.c | 47 +++++++++++++++++++++++++----------------------
blockjob.c | 17 +++++++++--------
docs/live-block-ops.txt | 32 ++++++++++++++++++++------------
docs/qmp/qmp-events.txt | 8 ++++----
include/qapi/qmp/qerror.h | 3 ---
qapi/block-core.json | 28 ++++++++++++++++------------
9 files changed, 122 insertions(+), 69 deletions(-)
--
2.1.4
^ permalink raw reply [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node
2015-03-23 15:12 [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer Alberto Garcia
@ 2015-03-23 15:12 ` Alberto Garcia
2015-03-24 22:46 ` Eric Blake
2015-03-23 15:12 ` [Qemu-devel] [PATCH 2/5] block: never cancel a streaming job without running stream_complete() Alberto Garcia
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Alberto Garcia @ 2015-03-23 15:12 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, Markus Armbruster, Max Reitz,
Stefan Hajnoczi
Currently, block jobs can only be owned by root nodes. This patch
allows block jobs to be in any arbitrary node, by making the following
changes:
- Block jobs can now be identified by the node name of their
BlockDriverState in addition to the device name. Since both device
and node names live in the same namespace there's no ambiguity.
- The "device" parameter used by all commands that operate on block
jobs can also be a node name now.
- The node name is used as a fallback to fill in the BlockJobInfo
structure and all BLOCK_JOB_* events if there is no device name for
that job.
- When querying the list of existing block jobs, all nodes are
searched, not just the ones at the root.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/mirror.c | 5 +++--
blockdev.c | 27 +++++++++++++++------------
blockjob.c | 17 +++++++++--------
docs/qmp/qmp-events.txt | 8 ++++----
include/qapi/qmp/qerror.h | 3 ---
qapi/block-core.json | 18 +++++++++---------
6 files changed, 40 insertions(+), 38 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
index 4056164..189e8f8 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -607,8 +607,9 @@ static void mirror_complete(BlockJob *job, Error **errp)
return;
}
if (!s->synced) {
- error_set(errp, QERR_BLOCK_JOB_NOT_READY,
- bdrv_get_device_name(job->bs));
+ error_setg(errp,
+ "The active block job for device '%s' cannot be completed",
+ bdrv_get_device_name(job->bs));
return;
}
diff --git a/blockdev.c b/blockdev.c
index 30dc9d2..932cf60 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2643,18 +2643,18 @@ out:
aio_context_release(aio_context);
}
-/* Get the block job for a given device name and acquire its AioContext */
-static BlockJob *find_block_job(const char *device, AioContext **aio_context,
+/* Get the block job for a given device or node name
+ * and acquire its AioContext */
+static BlockJob *find_block_job(const char *device_or_node,
+ AioContext **aio_context,
Error **errp)
{
- BlockBackend *blk;
BlockDriverState *bs;
- blk = blk_by_name(device);
- if (!blk) {
+ bs = bdrv_lookup_bs(device_or_node, device_or_node, NULL);
+ if (!bs) {
goto notfound;
}
- bs = blk_bs(blk);
*aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(*aio_context);
@@ -2668,7 +2668,7 @@ static BlockJob *find_block_job(const char *device, AioContext **aio_context,
notfound:
error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
- "No active block job on device '%s'", device);
+ "No active block job on node '%s'", device_or_node);
*aio_context = NULL;
return NULL;
}
@@ -2905,14 +2905,17 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
for (bs = bdrv_next(NULL); bs; bs = bdrv_next(bs)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
+ BlockDriverState *node;
aio_context_acquire(aio_context);
- if (bs->job) {
- BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
- elem->value = block_job_query(bs->job);
- *p_next = elem;
- p_next = &elem->next;
+ for (node = bs; node; node = node->backing_hd) {
+ if (node->job) {
+ BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
+ elem->value = block_job_query(node->job);
+ *p_next = elem;
+ p_next = &elem->next;
+ }
}
aio_context_release(aio_context);
diff --git a/blockjob.c b/blockjob.c
index ba2255d..e25fac2 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -42,7 +42,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
BlockJob *job;
if (bs->job) {
- error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
+ error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_or_node_name(bs));
return NULL;
}
bdrv_ref(bs);
@@ -108,8 +108,9 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
void block_job_complete(BlockJob *job, Error **errp)
{
if (job->paused || job->cancelled || !job->driver->complete) {
- error_set(errp, QERR_BLOCK_JOB_NOT_READY,
- bdrv_get_device_name(job->bs));
+ error_setg(errp,
+ "The active block job for node '%s' cannot be completed",
+ bdrv_get_device_or_node_name(job->bs));
return;
}
@@ -255,7 +256,7 @@ BlockJobInfo *block_job_query(BlockJob *job)
{
BlockJobInfo *info = g_new0(BlockJobInfo, 1);
info->type = g_strdup(BlockJobType_lookup[job->driver->job_type]);
- info->device = g_strdup(bdrv_get_device_name(job->bs));
+ info->device = g_strdup(bdrv_get_device_or_node_name(job->bs));
info->len = job->len;
info->busy = job->busy;
info->paused = job->paused;
@@ -277,7 +278,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
void block_job_event_cancelled(BlockJob *job)
{
qapi_event_send_block_job_cancelled(job->driver->job_type,
- bdrv_get_device_name(job->bs),
+ bdrv_get_device_or_node_name(job->bs),
job->len,
job->offset,
job->speed,
@@ -287,7 +288,7 @@ void block_job_event_cancelled(BlockJob *job)
void block_job_event_completed(BlockJob *job, const char *msg)
{
qapi_event_send_block_job_completed(job->driver->job_type,
- bdrv_get_device_name(job->bs),
+ bdrv_get_device_or_node_name(job->bs),
job->len,
job->offset,
job->speed,
@@ -301,7 +302,7 @@ void block_job_event_ready(BlockJob *job)
job->ready = true;
qapi_event_send_block_job_ready(job->driver->job_type,
- bdrv_get_device_name(job->bs),
+ bdrv_get_device_or_node_name(job->bs),
job->len,
job->offset,
job->speed, &error_abort);
@@ -330,7 +331,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
default:
abort();
}
- qapi_event_send_block_job_error(bdrv_get_device_name(job->bs),
+ qapi_event_send_block_job_error(bdrv_get_device_or_node_name(job->bs),
is_read ? IO_OPERATION_TYPE_READ :
IO_OPERATION_TYPE_WRITE,
action, &error_abort);
diff --git a/docs/qmp/qmp-events.txt b/docs/qmp/qmp-events.txt
index ed1d0a5..dce0f7a 100644
--- a/docs/qmp/qmp-events.txt
+++ b/docs/qmp/qmp-events.txt
@@ -86,7 +86,7 @@ Data:
- "type": Job type (json-string; "stream" for image streaming
"commit" for block commit)
-- "device": Device name (json-string)
+- "device": Device name, or node name if not present (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
@@ -110,7 +110,7 @@ Data:
- "type": Job type (json-string; "stream" for image streaming
"commit" for block commit)
-- "device": Device name (json-string)
+- "device": Device name, or node name if not present (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
@@ -137,7 +137,7 @@ Emitted when a block job encounters an error.
Data:
-- "device": device name (json-string)
+- "device": device name, or node name if not present (json-string)
- "operation": I/O operation (json-string, "read" or "write")
- "action": action that has been taken, it's one of the following (json-string):
"ignore": error has been ignored, the job may fail later
@@ -161,7 +161,7 @@ Data:
- "type": Job type (json-string; "stream" for image streaming
"commit" for block commit)
-- "device": Device name (json-string)
+- "device": Device name, or node name if not present (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h
index e567339..4ba442e 100644
--- a/include/qapi/qmp/qerror.h
+++ b/include/qapi/qmp/qerror.h
@@ -37,9 +37,6 @@ void qerror_report_err(Error *err);
#define QERR_BASE_NOT_FOUND \
ERROR_CLASS_GENERIC_ERROR, "Base '%s' not found"
-#define QERR_BLOCK_JOB_NOT_READY \
- ERROR_CLASS_GENERIC_ERROR, "The active block job for device '%s' cannot be completed"
-
#define QERR_BUS_NO_HOTPLUG \
ERROR_CLASS_GENERIC_ERROR, "Bus '%s' does not support hotplugging"
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 2a40b73..bdb9db0 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1065,7 +1065,7 @@
#
# Throttling can be disabled by setting the speed to 0.
#
-# @device: the device name
+# @device: the device or node name of the owner of the block job.
#
# @speed: the maximum speed, in bytes per second, or 0 for unlimited.
# Defaults to 0.
@@ -1096,7 +1096,7 @@
# operation can be started at a later time to finish copying all data from the
# backing file.
#
-# @device: the device name
+# @device: the device or node name of the owner of the block job.
#
# @force: #optional whether to allow cancellation of a paused job (default
# false). Since 1.3.
@@ -1122,7 +1122,7 @@
# the operation is actually paused. Cancelling a paused job automatically
# resumes it.
#
-# @device: the device name
+# @device: the device or node name of the owner of the block job.
#
# Returns: Nothing on success
# If no background operation is active on this device, DeviceNotActive
@@ -1142,7 +1142,7 @@
#
# This command also clears the error status of the job.
#
-# @device: the device name
+# @device: the device or node name of the owner of the block job.
#
# Returns: Nothing on success
# If no background operation is active on this device, DeviceNotActive
@@ -1168,7 +1168,7 @@
#
# A cancelled or paused job cannot be completed.
#
-# @device: the device name
+# @device: the device or node name of the owner of the block job.
#
# Returns: Nothing on success
# If no background operation is active on this device, DeviceNotActive
@@ -1815,7 +1815,7 @@
#
# @type: job type
#
-# @device: device name
+# @device: device name, or node name if not present
#
# @len: maximum progress value
#
@@ -1846,7 +1846,7 @@
#
# @type: job type
#
-# @device: device name
+# @device: device name, or node name if not present
#
# @len: maximum progress value
#
@@ -1869,7 +1869,7 @@
#
# Emitted when a block job encounters an error
#
-# @device: device name
+# @device: device name, or node name if not present
#
# @operation: I/O operation
#
@@ -1889,7 +1889,7 @@
#
# @type: job type
#
-# @device: device name
+# @device: device name, or node name if not present
#
# @len: maximum progress value
#
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 2/5] block: never cancel a streaming job without running stream_complete()
2015-03-23 15:12 [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node Alberto Garcia
@ 2015-03-23 15:12 ` Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 3/5] block: Support streaming to an intermediate layer Alberto Garcia
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Alberto Garcia @ 2015-03-23 15:12 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, Markus Armbruster, Max Reitz,
Stefan Hajnoczi
We need to call stream_complete() in order to do all the necessary
clean-ups, even if there's an early failure. At the moment it's only
useful to make sure that s->backing_file_str is not leaked, but it
will become more important as we introduce support for streaming to
any intermediate node.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block/stream.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/block/stream.c b/block/stream.c
index a628901..37bfd8b 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -114,21 +114,21 @@ static void coroutine_fn stream_run(void *opaque)
StreamCompleteData *data;
BlockDriverState *bs = s->common.bs;
BlockDriverState *base = s->base;
- int64_t sector_num, end;
+ int64_t sector_num = 0;
+ int64_t end = -1;
int error = 0;
int ret = 0;
int n = 0;
void *buf;
if (!bs->backing_hd) {
- block_job_completed(&s->common, 0);
- return;
+ goto out;
}
s->common.len = bdrv_getlength(bs);
if (s->common.len < 0) {
- block_job_completed(&s->common, s->common.len);
- return;
+ ret = s->common.len;
+ goto out;
}
end = s->common.len >> BDRV_SECTOR_BITS;
@@ -215,6 +215,7 @@ wait:
qemu_vfree(buf);
+out:
/* Modify backing chain and close BDSes in main loop */
data = g_malloc(sizeof(*data));
data->ret = ret;
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 3/5] block: Support streaming to an intermediate layer
2015-03-23 15:12 [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 2/5] block: never cancel a streaming job without running stream_complete() Alberto Garcia
@ 2015-03-23 15:12 ` Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 4/5] block: Add QMP support for " Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 5/5] docs: Document how to stream " Alberto Garcia
4 siblings, 0 replies; 7+ messages in thread
From: Alberto Garcia @ 2015-03-23 15:12 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, Markus Armbruster, Max Reitz,
Stefan Hajnoczi
This makes sure that the image we are steaming into is open in
read-write mode during the operation.
Operation blockers are also set in all intermediate nodes, since they
will be removed from the chain afterwards.
Finally, this also unblocks the stream operation in backing files.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
block.c | 4 +++-
block/stream.c | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/block.c b/block.c
index 737ab68..6d9cfca 100644
--- a/block.c
+++ b/block.c
@@ -1240,9 +1240,11 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
backing_hd->drv ? backing_hd->drv->format_name : "");
bdrv_op_block_all(bs->backing_hd, bs->backing_blocker);
- /* Otherwise we won't be able to commit due to check in bdrv_commit */
+ /* Otherwise we won't be able to commit or stream */
bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET,
bs->backing_blocker);
+ bdrv_op_unblock(bs->backing_hd, BLOCK_OP_TYPE_STREAM,
+ bs->backing_blocker);
out:
bdrv_refresh_limits(bs, NULL);
}
diff --git a/block/stream.c b/block/stream.c
index 37bfd8b..327d964 100644
--- a/block/stream.c
+++ b/block/stream.c
@@ -33,6 +33,8 @@ typedef struct StreamBlockJob {
BlockDriverState *base;
BlockdevOnError on_error;
char *backing_file_str;
+ int bs_flags;
+ Error *blocker;
} StreamBlockJob;
static int coroutine_fn stream_populate(BlockDriverState *bs,
@@ -88,8 +90,15 @@ static void stream_complete(BlockJob *job, void *opaque)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
StreamCompleteData *data = opaque;
+ BlockDriverState *i;
BlockDriverState *base = s->base;
+ /* Remove all blockers set in stream_start() */
+ for (i = job->bs->backing_hd; i && i != s->base; i = i->backing_hd) {
+ bdrv_op_unblock_all(i, s->blocker);
+ }
+ error_free(s->blocker);
+
if (!block_job_is_cancelled(&s->common) && data->reached_end &&
data->ret == 0) {
const char *base_id = NULL, *base_fmt = NULL;
@@ -103,6 +112,11 @@ static void stream_complete(BlockJob *job, void *opaque)
close_unused_images(job->bs, base, base_id);
}
+ /* Reopen the image back in read-only mode if necessary */
+ if (s->bs_flags != bdrv_get_flags(job->bs)) {
+ bdrv_reopen(job->bs, s->bs_flags, NULL);
+ }
+
g_free(s->backing_file_str);
block_job_completed(&s->common, data->ret);
g_free(data);
@@ -246,7 +260,9 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
BlockCompletionFunc *cb,
void *opaque, Error **errp)
{
+ BlockDriverState *i;
StreamBlockJob *s;
+ int orig_bs_flags;
if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
on_error == BLOCKDEV_ON_ERROR_ENOSPC) &&
@@ -255,13 +271,33 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
return;
}
+ /* Make sure that the image in opened in read-write mode */
+ orig_bs_flags = bdrv_get_flags(bs);
+ if (!(orig_bs_flags & BDRV_O_RDWR)) {
+ Error *local_err = NULL;
+ bdrv_reopen(bs, orig_bs_flags | BDRV_O_RDWR, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
+ /* Block all intermediate nodes between bs and base, because they
+ * will disappear from the chain after this operation */
+ error_setg(&s->blocker, "blocked by the block-stream operation in '%s'",
+ bdrv_get_node_name(bs));
+ for (i = bs->backing_hd; i != base && i != NULL; i = i->backing_hd) {
+ bdrv_op_block_all(i, s->blocker);
+ }
+
s->base = base;
s->backing_file_str = g_strdup(backing_file_str);
+ s->bs_flags = orig_bs_flags;
s->on_error = on_error;
s->common.co = qemu_coroutine_create(stream_run);
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 4/5] block: Add QMP support for streaming to an intermediate layer
2015-03-23 15:12 [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer Alberto Garcia
` (2 preceding siblings ...)
2015-03-23 15:12 ` [Qemu-devel] [PATCH 3/5] block: Support streaming to an intermediate layer Alberto Garcia
@ 2015-03-23 15:12 ` Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 5/5] docs: Document how to stream " Alberto Garcia
4 siblings, 0 replies; 7+ messages in thread
From: Alberto Garcia @ 2015-03-23 15:12 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, Markus Armbruster, Max Reitz,
Stefan Hajnoczi
This patch makes the 'device' paramater of the 'block-stream' command
allow a node name as well as a device name.
In addition to that, operation blockers will be checked in all
intermediate nodes between the top and the base node.
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
blockdev.c | 20 ++++++++++----------
qapi/block-core.json | 10 +++++++---
2 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/blockdev.c b/blockdev.c
index 932cf60..1ff8f2b 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2104,8 +2104,7 @@ void qmp_block_stream(const char *device,
bool has_on_error, BlockdevOnError on_error,
Error **errp)
{
- BlockBackend *blk;
- BlockDriverState *bs;
+ BlockDriverState *bs, *i;
BlockDriverState *base_bs = NULL;
AioContext *aio_context;
Error *local_err = NULL;
@@ -2115,20 +2114,14 @@ void qmp_block_stream(const char *device,
on_error = BLOCKDEV_ON_ERROR_REPORT;
}
- blk = blk_by_name(device);
- if (!blk) {
- error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ bs = bdrv_lookup_bs(device, device, errp);
+ if (!bs) {
return;
}
- bs = blk_bs(blk);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
- goto out;
- }
-
if (has_base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
@@ -2139,6 +2132,13 @@ void qmp_block_stream(const char *device,
base_name = base;
}
+ /* Check for op blockers in the whole chain between bs and base */
+ for (i = bs; i != NULL && i != base_bs; i = i->backing_hd) {
+ if (bdrv_op_is_blocked(i, BLOCK_OP_TYPE_STREAM, errp)) {
+ goto out;
+ }
+ }
+
/* if we are streaming the entire chain, the result will have no backing
* file, and specifying one is therefore an error */
if (base_bs == NULL && has_backing_file) {
diff --git a/qapi/block-core.json b/qapi/block-core.json
index bdb9db0..b548aec 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1014,6 +1014,10 @@
# with query-block-jobs. The operation can be stopped before it has completed
# using the block-job-cancel command.
#
+# The node that receives the data is called the top image, can be located
+# in any part of the whole chain and can be specified using its device
+# or node name.
+#
# If a base file is specified then sectors are not copied from that base file and
# its backing chain. When streaming completes the image file will have the base
# file as its backing file. This can be used to stream a subset of the backing
@@ -1022,12 +1026,12 @@
# On successful completion the image file is updated to drop the backing file
# and the BLOCK_JOB_COMPLETED event is emitted.
#
-# @device: the device name
+# @device: the device or node name of the top image
#
# @base: #optional the common backing file name
#
-# @backing-file: #optional The backing file string to write into the active
-# layer. This filename is not validated.
+# @backing-file: #optional The backing file string to write into the top
+# image. This filename is not validated.
#
# If a pathname string is such that it cannot be
# resolved by QEMU, that means that subsequent QMP or
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [Qemu-devel] [PATCH 5/5] docs: Document how to stream to an intermediate layer
2015-03-23 15:12 [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer Alberto Garcia
` (3 preceding siblings ...)
2015-03-23 15:12 ` [Qemu-devel] [PATCH 4/5] block: Add QMP support for " Alberto Garcia
@ 2015-03-23 15:12 ` Alberto Garcia
4 siblings, 0 replies; 7+ messages in thread
From: Alberto Garcia @ 2015-03-23 15:12 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Alberto Garcia, Markus Armbruster, Max Reitz,
Stefan Hajnoczi
Signed-off-by: Alberto Garcia <berto@igalia.com>
---
docs/live-block-ops.txt | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/docs/live-block-ops.txt b/docs/live-block-ops.txt
index a257087..5e969fd 100644
--- a/docs/live-block-ops.txt
+++ b/docs/live-block-ops.txt
@@ -10,9 +10,9 @@ Snapshot live merge
Given a snapshot chain, described in this document in the following
format:
-[A] -> [B] -> [C] -> [D]
+[A] -> [B] -> [C] -> [D] -> [E]
-Where the rightmost object ([D] in the example) described is the current
+Where the rightmost object ([E] in the example) described is the current
image which the guest OS has write access to. To the left of it is its base
image, and so on accordingly until the leftmost image, which has no
base.
@@ -21,28 +21,36 @@ The snapshot live merge operation transforms such a chain into a
smaller one with fewer elements, such as this transformation relative
to the first example:
-[A] -> [D]
+[A] -> [E]
-Currently only forward merge with target being the active image is
-supported, that is, data copy is performed in the right direction with
-destination being the rightmost image.
+Data is copied in the right direction with destination being the
+rightmost image, but any other intermediate image can be specified
+instead, [D] in this example:
+
+[A] -> [B] -> [D] -> [E]
The operation is implemented in QEMU through image streaming facilities.
The basic idea is to execute 'block_stream virtio0' while the guest is
running. Progress can be monitored using 'info block-jobs'. When the
streaming operation completes it raises a QMP event. 'block_stream'
-copies data from the backing file(s) into the active image. When finished,
-it adjusts the backing file pointer.
+copies data from the backing file(s) into the destination image.
+When finished, it adjusts the backing file pointer.
-The 'base' parameter specifies an image which data need not be streamed from.
-This image will be used as the backing file for the active image when the
-operation is finished.
+The 'base' parameter specifies an image which data need not be
+streamed from. This image will be used as the backing file for the
+destination image when the operation is finished.
-In the example above, the command would be:
+In the first example above, the command would be:
(qemu) block_stream virtio0 A
+In order to specify a destination image different from the active
+(rightmost) one we can use its (previously set) node name instead.
+
+In the second example above, the command would be:
+
+(qemu) block_stream node-D A
Live block copy
===============
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node
2015-03-23 15:12 ` [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node Alberto Garcia
@ 2015-03-24 22:46 ` Eric Blake
0 siblings, 0 replies; 7+ messages in thread
From: Eric Blake @ 2015-03-24 22:46 UTC (permalink / raw)
To: Alberto Garcia, qemu-devel
Cc: Kevin Wolf, Markus Armbruster, Stefan Hajnoczi, Max Reitz
[-- Attachment #1: Type: text/plain, Size: 1023 bytes --]
On 03/23/2015 09:12 AM, Alberto Garcia wrote:
> Currently, block jobs can only be owned by root nodes. This patch
> allows block jobs to be in any arbitrary node, by making the following
> changes:
>
> - Block jobs can now be identified by the node name of their
> BlockDriverState in addition to the device name. Since both device
> and node names live in the same namespace there's no ambiguity.
>
> - The "device" parameter used by all commands that operate on block
> jobs can also be a node name now.
>
> - The node name is used as a fallback to fill in the BlockJobInfo
> structure and all BLOCK_JOB_* events if there is no device name for
> that job.
>
> - When querying the list of existing block jobs, all nodes are
> searched, not just the ones at the root.
>
> Signed-off-by: Alberto Garcia <berto@igalia.com>
> ---
Reviewed-by: Eric Blake <eblake@redhat.com>
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-03-24 22:46 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-03-23 15:12 [Qemu-devel] [PATCH v2 0/5] Support streaming to an intermediate layer Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 1/5] block: allow block jobs in any arbitrary node Alberto Garcia
2015-03-24 22:46 ` Eric Blake
2015-03-23 15:12 ` [Qemu-devel] [PATCH 2/5] block: never cancel a streaming job without running stream_complete() Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 3/5] block: Support streaming to an intermediate layer Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 4/5] block: Add QMP support for " Alberto Garcia
2015-03-23 15:12 ` [Qemu-devel] [PATCH 5/5] docs: Document how to stream " Alberto Garcia
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).