From 225ffb360161b2bbe599d8db6d7a38fde6bf2bfc Mon Sep 17 00:00:00 2001
From: Jeffrey Cody <jcody@redhat.com>
Date: Wed, 21 Mar 2012 21:55:08 +0100
Subject: [PATCH 41/55] qmp: add block_stream command

RH-Author: Jeffrey Cody <jcody@redhat.com>
Message-id: <4334a90afb0c3d57ea41e3b69db6c08e73dccbd9.1332362400.git.jcody@redhat.com>
Patchwork-id: 38892
O-Subject: [RHEL6.3 qemu-kvm PATCH v8 41/54] qmp: add block_stream command
Bugzilla: 582475
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>

From: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>

Add the block_stream command, which starts copy backing file contents
into the image file.  Also add the BLOCK_JOB_COMPLETED QMP event which
is emitted when image streaming completes.  Later patches add control
over the background copy speed, cancelation, and querying running
streaming operations.

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Acked-by: Luiz Capitulino <lcapitulino@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>

(cherry picked from commit 12bd451fe0be83474910bb63b5874458141d4230)

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 QMP/qmp-events.txt |   29 ++++++++++++++++++++++
 blockdev.c         |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 blockdev.h         |    1 +
 monitor.c          |    3 ++
 monitor.h          |    1 +
 qemu-monitor.hx    |   43 ++++++++++++++++++++++++++++++++
 qerror.c           |    4 +++
 qerror.h           |    3 ++
 trace-events       |    4 +++
 9 files changed, 157 insertions(+), 0 deletions(-)

Signed-off-by: Michal Novotny <minovotn@redhat.com>
---
 QMP/qmp-events.txt |   29 ++++++++++++++++++++++
 blockdev.c         |   69 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 blockdev.h         |    1 +
 monitor.c          |    3 ++
 monitor.h          |    1 +
 qemu-monitor.hx    |   43 ++++++++++++++++++++++++++++++++
 qerror.c           |    4 +++
 qerror.h           |    3 ++
 trace-events       |    4 +++
 9 files changed, 157 insertions(+), 0 deletions(-)

diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
index 42f6992..cb43ba7 100644
--- a/QMP/qmp-events.txt
+++ b/QMP/qmp-events.txt
@@ -297,3 +297,32 @@ Example:
 
 Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
 followed respectively by the RESET, SHUTDOWN, or STOP events.
+
+
+BLOCK_JOB_COMPLETED
+-------------------
+
+Emitted when a block job has completed.
+
+Data:
+
+- "type":     Job type ("stream" for image streaming, json-string)
+- "device":   Device name (json-string)
+- "len":      Maximum progress value (json-int)
+- "offset":   Current progress value (json-int)
+              On success this is equal to len.
+              On failure this is less than len.
+- "speed":    Rate limit, bytes per second (json-int)
+- "error":    Error message (json-string, optional)
+              Only present on failure.  This field contains a human-readable
+              error message.  There are no semantics other than that streaming
+              has failed and clients should not try to interpret the error
+              string.
+
+Example:
+
+{ "event": "BLOCK_JOB_COMPLETED",
+     "data": { "type": "stream", "device": "virtio-disk0",
+               "len": 10737418240, "offset": 10737418240,
+               "speed": 0 },
+     "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
diff --git a/blockdev.c b/blockdev.c
index cdd3ea3..d60ee40 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -13,9 +13,11 @@
 #include "qerror.h"
 #include "qemu-option.h"
 #include "qemu-config.h"
+#include "qemu-objects.h"
 #include "sysemu.h"
 #include "block_int.h"
 #include "qmp-commands.h"
+#include "trace.h"
 
 struct drivelist drives = QTAILQ_HEAD_INITIALIZER(drives);
 DriveInfo *extboot_drive = NULL;
@@ -998,3 +1000,70 @@ int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
 
     return 0;
 }
+
+static QObject *qobject_from_block_job(BlockJob *job)
+{
+    return qobject_from_jsonf("{ 'type': %s,"
+                              "'device': %s,"
+                              "'len': %" PRId64 ","
+                              "'offset': %" PRId64 ","
+                              "'speed': %" PRId64 " }",
+                              job->job_type->job_type,
+                              bdrv_get_device_name(job->bs),
+                              job->len,
+                              job->offset,
+                              job->speed);
+}
+
+static void block_stream_cb(void *opaque, int ret)
+{
+    BlockDriverState *bs = opaque;
+    QObject *obj;
+
+    trace_block_stream_cb(bs, bs->job, ret);
+
+    assert(bs->job);
+    obj = qobject_from_block_job(bs->job);
+    if (ret < 0) {
+        QDict *dict = qobject_to_qdict(obj);
+        qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
+    }
+
+    monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj);
+    qobject_decref(obj);
+}
+
+int do_block_stream(Monitor *mon, const QDict *params, QObject **ret_data)
+{
+    const char *device = qdict_get_str(params, "device");
+    const char *base = qdict_get_try_str(params, "base");
+    BlockDriverState *bs;
+    int ret;
+
+    bs = bdrv_find(device);
+    if (!bs) {
+        qerror_report(QERR_DEVICE_NOT_FOUND, device);
+        return -1;
+    }
+
+    /* Base device not supported */
+    if (base) {
+        qerror_report(QERR_NOT_SUPPORTED);
+        return -1;
+    }
+
+    ret = stream_start(bs, NULL, block_stream_cb, bs);
+    if (ret < 0) {
+        switch (ret) {
+        case -EBUSY:
+            qerror_report(QERR_DEVICE_IN_USE, device);
+            return -1;
+        default:
+            qerror_report(QERR_NOT_SUPPORTED);
+            return -1;
+        }
+    }
+
+    trace_do_block_stream(bs, bs->job);
+    return 0;
+}
diff --git a/blockdev.h b/blockdev.h
index 171401c..6194bda 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -71,5 +71,6 @@ int do_change_block(Monitor *mon, const char *device,
                     const char *filename, const char *fmt);
 int simple_drive_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
 int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_block_stream(Monitor *mon, const QDict *qdict, QObject **ret_data);
 
 #endif
diff --git a/monitor.c b/monitor.c
index b714e99..ff688a0 100644
--- a/monitor.c
+++ b/monitor.c
@@ -478,6 +478,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
         case QEVENT_DEVICE_TRAY_MOVED:
              event_name = "DEVICE_TRAY_MOVED";
             break;
+        case QEVENT_BLOCK_JOB_COMPLETED:
+            event_name = "BLOCK_JOB_COMPLETED";
+            break;
         case QEVENT_RH_SPICE_INITIALIZED:
             event_name = RFQDN_REDHAT "SPICE_INITIALIZED";
             break;
diff --git a/monitor.h b/monitor.h
index b4481cf..f315a92 100644
--- a/monitor.h
+++ b/monitor.h
@@ -39,6 +39,7 @@ typedef enum MonitorEvent {
     QEVENT_SPICE_INITIALIZED,
     QEVENT_SPICE_DISCONNECTED,
     QEVENT_DEVICE_TRAY_MOVED,
+    QEVENT_BLOCK_JOB_COMPLETED,
     QEVENT_RH_SPICE_INITIALIZED,
     QEVENT_RH_SPICE_DISCONNECTED,
     QEVENT_SUSPEND,
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 95b4635..839e77b 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -2036,6 +2036,49 @@ Example:
 
 EQMP
 
+    {
+        .name       = "block_stream",
+        .args_type  = "device:B,base:s?",
+        .params     = "device [base]",
+        .help       = "background copy backing file into a block device",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_new = do_block_stream,
+    },
+
+SQMP
+block_stream
+------------
+
+Copy data from a backing file into a block device.
+
+The block streaming operation is performed in the background until the entire
+backing file has been copied.  This command returns immediately once streaming
+has started.  The status of ongoing block streaming operations can be checked
+with query-block-jobs.  The operation can be stopped before it has completed
+using the block_job_cancel command.
+
+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
+file chain instead of flattening the entire image.
+
+On successful completion the image file is updated to drop the backing file
+and the BLOCK_JOB_COMPLETED event is emitted.
+
+Arguments:
+
+- device: the device name (json-string)
+- base:   the common backing file name (json-string, optional)
+
+Returns:
+
+Nothing on success.
+If streaming is already active on this device, DeviceInUse.
+If device does not exist, DeviceNotFound.
+If image streaming is not supported by this device, NotSupported.
+
+EQMP
+
 HXCOMM Keep the 'info' command at the end!
 HXCOMM This is required for the QMP documentation layout.
 
diff --git a/qerror.c b/qerror.c
index 29d6577..724a7bf 100644
--- a/qerror.c
+++ b/qerror.c
@@ -174,6 +174,10 @@ static const QErrorStringTable qerror_table[] = {
         .desc      = "No '%(bus)' bus found for device '%(device)'",
     },
     {
+        .error_fmt = QERR_NOT_SUPPORTED,
+        .desc      = "Not supported",
+    },
+    {
         .error_fmt = QERR_OPEN_FILE_FAILED,
         .desc      = "Could not open '%(filename)'",
     },
diff --git a/qerror.h b/qerror.h
index 27de1da..18e4b95 100644
--- a/qerror.h
+++ b/qerror.h
@@ -152,6 +152,9 @@ QError *qobject_to_qerror(const QObject *obj);
 #define QERR_NO_BUS_FOR_DEVICE \
     "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }"
 
+#define QERR_NOT_SUPPORTED \
+    "{ 'class': 'NotSupported', 'data': {} }"
+
 #define QERR_OPEN_FILE_FAILED \
     "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }"
 
diff --git a/trace-events b/trace-events
index bf83d7a..f226c1c 100644
--- a/trace-events
+++ b/trace-events
@@ -66,6 +66,10 @@ disable bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, i
 disable stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
 disable stream_start(void *bs, void *base, void *s, void *co, void *opaque) "bs %p base %p s %p co %p opaque %p"
 
+# blockdev.c
+disable block_stream_cb(void *bs, void *job, int ret) "bs %p job %p ret %d"
+disable do_block_stream(void *bs, void *job) "bs %p job %p"
+
 # hw/virtio-blk.c
 disable virtio_blk_req_complete(void *req, int status) "req %p status %d"
 disable virtio_blk_rw_complete(void *req, int ret) "req %p ret %d"
-- 
1.7.7.6

