From 278a706ea66bdf334ca728191fda19b96f15d88d Mon Sep 17 00:00:00 2001
From: Anthony Liguori <aliguori@redhat.com>
Date: Fri, 12 Aug 2011 15:38:19 +0200
Subject: [PATCH 11/15] qmp: add block_job_cancel command

RH-Author: Anthony Liguori <aliguori@redhat.com>
Message-id: <1313163503-2523-12-git-send-email-aliguori@redhat.com>
Patchwork-id: 31336
O-Subject: [RHEL6.2 qemu PATCH 11/15] qmp: add block_job_cancel command
Bugzilla: 633370
RH-Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Orit Wasserman <owasserm@redhat.com>
RH-Acked-by: Christoph Hellwig <chellwig@redhat.com>

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

Image streaming operations can be stopped using the block_job_cancel
command.  In the future other types of background operations on block
devices can be cancelled using this command.

The command synopsis is:

block_job_cancel
----------------

Stop an active block streaming operation.

This command returns once the active block streaming operation has been
stopped.  It is an error to call this command if no operation is in
progress.

The image file retains its backing file unless the streaming operation
happens to complete just as it is being cancelled.

A new block streaming operation can be started at a later time to finish
copying all data from the backing file.

Arguments:

- device: device name (json-string)

Errors:

DeviceNotActive: streaming is not active on this device
DeviceInUse:     cancellation already in progress

Examples:

-> { "execute": "block_job_cancel", "arguments":
    { "device": "virtio0" } }
<- { "return":  {} }

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@redhat.com>

Bugzilla: 633370

---
 blockdev.c      |   34 ++++++++++++++++++++++++++++++++++
 blockdev.h      |    3 +++
 qemu-monitor.hx |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 0 deletions(-)

Signed-off-by: Michal Novotny <mignov@gmail.com>
---
 blockdev.c      |   34 ++++++++++++++++++++++++++++++++++
 blockdev.h      |    3 +++
 qemu-monitor.hx |   47 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 0 deletions(-)

diff --git a/blockdev.c b/blockdev.c
index a8ee1af..133e3eb 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -54,6 +54,8 @@ static const int if_max_devs[IF_COUNT] = {
 };
 
 typedef struct StreamState {
+    MonitorCompletion *cancel_cb;
+    void *cancel_opaque;
     int64_t offset;             /* current position in block device */
     BlockDriverState *bs;
     QEMUTimer *timer;
@@ -92,6 +94,10 @@ static void stream_free(StreamState *s)
 {
     QLIST_REMOVE(s, list);
 
+    if (s->cancel_cb) {
+        s->cancel_cb(s->cancel_opaque, NULL);
+    }
+
     bdrv_set_in_use(s->bs, 0);
     qemu_del_timer(s->timer);
     qemu_free_timer(s->timer);
@@ -118,6 +124,8 @@ static void stream_cb(void *opaque, int nb_sectors)
     if (s->offset == bdrv_getlength(s->bs)) {
         bdrv_change_backing_file(s->bs, NULL, NULL);
         stream_complete(s, 0);
+    } else if (s->cancel_cb) {
+        stream_free(s);
     } else {
         qemu_mod_timer(s->timer, qemu_get_clock(rt_clock));
     }
@@ -184,6 +192,24 @@ static StreamState *stream_start(const char *device)
     return s;
 }
 
+static int stream_stop(const char *device, MonitorCompletion *cb, void *opaque)
+{
+    StreamState *s = stream_find(device);
+
+    if (!s) {
+        qerror_report(QERR_DEVICE_NOT_ACTIVE, device);
+        return -1;
+    }
+    if (s->cancel_cb) {
+        qerror_report(QERR_DEVICE_IN_USE, device);
+        return -1;
+    }
+
+    s->cancel_cb = cb;
+    s->cancel_opaque = opaque;
+    return 0;
+}
+
 /*
  * We automatically delete the drive when a device using it gets
  * unplugged.  Questionable feature, but we can't just drop it.
@@ -841,6 +867,14 @@ int do_block_stream(Monitor *mon, const QDict *params, QObject **ret_data)
     return stream_start(device) ? 0 : -1;
 }
 
+int do_block_job_cancel(Monitor *mon, const QDict *params,
+                        MonitorCompletion cb, void *opaque)
+{
+    const char *device = qdict_get_str(params, "device");
+
+    return stream_stop(device, cb, opaque);
+}
+
 static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
 {
     if (!force) {
diff --git a/blockdev.h b/blockdev.h
index dd9c64d..cd399ee 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -12,6 +12,7 @@
 
 #include "block.h"
 #include "qemu-queue.h"
+#include "monitor.h"
 
 void blockdev_mark_auto_del(BlockDriverState *bs);
 void blockdev_auto_del(BlockDriverState *bs);
@@ -73,5 +74,7 @@ int do_change_block(Monitor *mon, const char *device,
 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 *params, QObject **ret_data);
+int do_block_job_cancel(Monitor *mon, const QDict *params,
+                        MonitorCompletion cb, void *opaque);
 
 #endif
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index d7c0c6e..e846e61 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -168,6 +168,53 @@ Examples:
 EQMP
 
     {
+        .name       = "block_job_cancel",
+        .args_type  = "device:B",
+        .params     = "device",
+        .help       = "Stop an active block streaming operation",
+        .user_print = monitor_user_noop,
+        .mhandler.cmd_async = do_block_job_cancel,
+        .async      = 1,
+    },
+
+STEXI
+@item block_job_cancel
+@findex block_job_cancel
+Stop an active block streaming operation.
+ETEXI
+SQMP
+
+block_job_cancel
+----------------
+
+Stop an active block streaming operation.
+
+This command returns once the active block streaming operation has been
+stopped.  It is an error to call this command if no operation is in progress.
+
+The image file retains its backing file unless the streaming operation happens
+to complete just as it is being cancelled.
+
+A new block streaming operation can be started at a later time to finish
+copying all data from the backing file.
+
+Arguments:
+
+- device: device name (json-string)
+
+Errors:
+
+DeviceNotActive: streaming is not active on this device
+DeviceInUse:     cancellation already in progress
+
+Examples:
+
+-> { "execute": "block_job_cancel", "arguments": { "device": "virtio0" } }
+<- { "return":  {} }
+
+EQMP
+
+    {
         .name       = "q|quit",
         .args_type  = "",
         .params     = "",
-- 
1.7.4.4

