From 6270aec1271880bb5f12db46d40f51671c548f59 Mon Sep 17 00:00:00 2001
From: Jeff Cody <jcody@redhat.com>
Date: Thu, 7 Nov 2013 07:28:56 +0100
Subject: [PATCH 57/81] block: optionally disable live block jobs

RH-Author: Jeff Cody <jcody@redhat.com>
Message-id: <5b15ff37a1f28a76e2b66c07df996d3a7c37d6e9.1383712781.git.jcody@redhat.com>
Patchwork-id: 55478
O-Subject: [RHEL7 qemu-kvm PATCH 1/3] block: optionally disable live block jobs
Bugzilla: 987582
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Acked-by: Eric Blake <eblake@redhat.com>
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>

This disables all block job operations that are part of the virt
differentiation features.

This includes:
    * live snapshots (single and group)
    * block stream
    * block commit
    * block mirror
    * block transactions

All of these are disabled via the CONFIG_LIVE_BLOCK_OPS config option.

In RHEL6, block job differentiation grew from live snapshot disablement,
which was used to encompass many operations that were not live
snapshots.  CONFIG_LIVE_SNAPSHOTS was used for disablement of multiple
features.

For RHEL7, let's use naming that is more accurate:
CONFIG_LIVE_BLOCK_OPS, to denote live block operations.

RHEL and RHEV have different binaries, where RHEV qemu-kvm binaries
support live snapshots.

The JSON files qapi-schema-rhel.json and qapi-schema-rhev.json are
automatically generated from the qapi-schema.json, by looking for
'_rhev_only' comments in the 'master' JSON file.

For RHEL6, the JSON file had makeshift 'ifdef' statements.  This was
changed to '_rhev_only' and '_end_rhev-only' blocks, because calling
them 'ifdef' was misleading - it did not depend on the actual state
of that variable itself.

The Makefile variable RHEV_CONFIGS can be appended with additional
config items that may appear in the qapi-schema.json file.  For
instance, to have features CONFIG_LIVE_BLOCK_OPS and CONFIG_SOME_FEATURE
both be RHEV only, set the variable like so:

RHEV_CONFIGS = CONFIG_LIVE_BLOCK_OPS CONFIG_SOME_FEATURE

RHEV_CONFIGS is then turned into a regex. For the example above, the
following pattern is matched when generating the RHEL/RHEV JSON files:
^#_rhev-only +(\bCONFIG_LIVE_BLOCK_OPS\b|\bCONFIG_SOME_FEATURE\b)

Unlike the RHEL6 counterpart, this one uses original qmp-commands.h,
qapi-types.h, qapi-visit.c, etc.. filenames, simplifying the Makefile a
bit.

BZ: 987582

The Makefile changes were derived from
RHEL6 commit bb5bd75dd86a6d05c9df4c2d271f98744d6c90b7, but this was not
a cherry-pick as the actual changes differed significantly.

Signed-off-by: Jeff Cody <jcody@redhat.com>
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 Makefile            |   48 ++++++++++++++++++++++++++++++++++++++++++------
 block/Makefile.objs |    2 ++
 blockdev.c          |    7 +++++++
 configure           |   14 +++++++++++++-
 hmp-commands.hx     |    5 ++++-
 hmp.c               |    4 ++++
 qapi-schema.json    |    6 ++++++
 qmp-commands.hx     |    4 ++++
 8 files changed, 82 insertions(+), 8 deletions(-)

diff --git a/Makefile b/Makefile
index dcd31c6..45048a3 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,11 @@
 
 # Always point to the root of the build tree (needs GNU make).
 BUILD_DIR=$(CURDIR)
+# useful for passing ' ' and ',' into Makefile functional calls,
+# as these characters cannot be passed otherwise
+_empty :=  
+_space := $(_empty) $(_empty)
+_comma := ,
 
 # All following code might depend on configuration variables
 ifneq ($(wildcard config-host.mak),)
@@ -213,15 +218,45 @@ qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
 $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, "  GEN   $@")
 
+# if there are multiple config items to be RHEV-only, simply add it to
+# RHEV_CONFIGS, like so: RHEV_CONFIGS = CONFIG_LIVE_BLOCK_OPS CONFIG_SOME_FEATURE
+RHEV_CONFIGS = CONFIG_LIVE_BLOCK_OPS
+# Turn $(RHEV_CONFIGS) into a regex with logical OR, and whole word matching
+RHEV_ONLY_CONFIG_ITEMS = (\b$(subst $(_space),\b|\b,$(strip $(RHEV_CONFIGS)))\b)
+
+GENERATED_JSON_FILES = $(addprefix $(SRC_PATH)/, qapi-schema-rhel.json qapi-schema-rhev.json)
+
+$(SRC_PATH)/qapi-schema-rhev.json: $(SRC_PATH)/qapi-schema.json
+	-@echo "# THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY" > $@
+	-@echo "#" >> $@
+	$(call quiet-command,sed -r "/^#_rhev-only +$(RHEV_ONLY_CONFIG_ITEMS)/d;/^#_end-rhev-only/d" $< >> $@, "  GEN   $@")
+
+$(SRC_PATH)/qapi-schema-rhel.json: $(SRC_PATH)/qapi-schema.json
+	-@echo "# THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY" > $@
+	-@echo "#" >> $@
+	$(call quiet-command,sed -r "/^#_rhev-only +$(RHEV_ONLY_CONFIG_ITEMS)/$(_comma)/^#_end-rhev-only/d" $< >> $@, "  GEN   $@")
+
+ifeq ($(CONFIG_LIVE_BLOCK_OPS),y)
 qapi-types.c qapi-types.h :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." < $<, "  GEN   $@")
+$(SRC_PATH)/qapi-schema-rhev.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "."  < $<, "  GEN   $@")
 qapi-visit.c qapi-visit.h :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "."  < $<, "  GEN   $@")
+$(SRC_PATH)/qapi-schema-rhev.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "."  < $<, "  GEN   $@")
 qmp-commands.h qmp-marshal.c :\
-$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
-	$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, "  GEN   $@")
+$(SRC_PATH)/qapi-schema-rhev.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "."  < $<, "  GEN   $@")
+else
+qapi-types.c qapi-types.h :\
+$(SRC_PATH)/qapi-schema-rhel.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "."  < $<, "  GEN   $@")
+qapi-visit.c qapi-visit.h :\
+$(SRC_PATH)/qapi-schema-rhel.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py)
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "."  < $<, "  GEN   $@")
+qmp-commands.h qmp-marshal.c :\
+$(SRC_PATH)/qapi-schema-rhel.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
+	$(call quiet-command,python $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "."  < $<, "  GEN   $@")
+endif
 
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
 $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
@@ -243,6 +278,7 @@ clean:
 	rm -f trace/generated-tracers-dtrace.h*
 	rm -f $(foreach f,$(GENERATED_HEADERS),$(f) $(f)-timestamp)
 	rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp)
+	rm -f $(foreach f,$(GENERATED_JSON_FILES),$(f) $(f)-timestamp)
 	rm -rf qapi-generated
 	rm -rf qga/qapi-generated
 	$(MAKE) -C tests/tcg clean
diff --git a/block/Makefile.objs b/block/Makefile.objs
index 6b8d5ec..f355271 100644
--- a/block/Makefile.objs
+++ b/block/Makefile.objs
@@ -17,8 +17,10 @@ block-obj-$(CONFIG_GLUSTERFS) += gluster.o
 block-obj-$(CONFIG_LIBSSH2) += ssh.o
 endif
 
+ifeq ($(CONFIG_LIVE_BLOCK_OPS),y)
 common-obj-y += stream.o
 common-obj-y += commit.o
 common-obj-y += mirror.o
+endif
 
 $(obj)/curl.o: QEMU_CFLAGS+=$(CURL_CFLAGS)
diff --git a/blockdev.c b/blockdev.c
index f65aff4..6710f61 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -237,6 +237,8 @@ typedef struct {
     DriveInfo *dinfo;
 } DrivePutRefBH;
 
+/* right now, this is only used from block_job_cb() */
+#ifdef CONFIG_LIVE_BLOCK_OPS
 static void drive_put_ref_bh(void *opaque)
 {
     DrivePutRefBH *s = opaque;
@@ -262,6 +264,7 @@ static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
     s->dinfo = dinfo;
     qemu_bh_schedule(s->bh);
 }
+#endif
 
 static int parse_block_error_action(const char *buf, bool is_read)
 {
@@ -806,6 +809,7 @@ void do_commit(Monitor *mon, const QDict *qdict)
     }
 }
 
+#ifdef CONFIG_LIVE_BLOCK_OPS
 static void blockdev_do_action(int kind, void *data, Error **errp)
 {
     BlockdevAction action;
@@ -1051,6 +1055,7 @@ exit:
         g_free(states);
     }
 }
+#endif
 
 
 static void eject_device(BlockDriverState *bs, int force, Error **errp)
@@ -1286,6 +1291,7 @@ void qmp_block_resize(const char *device, int64_t size, Error **errp)
     }
 }
 
+#ifdef CONFIG_LIVE_BLOCK_OPS
 static void block_job_cb(void *opaque, int ret)
 {
     BlockDriverState *bs = opaque;
@@ -1547,6 +1553,7 @@ void qmp_drive_mirror(const char *device, const char *target,
      */
     drive_get_ref(drive_get_by_blockdev(bs));
 }
+#endif
 
 static BlockJob *find_block_job(const char *device)
 {
diff --git a/configure b/configure
index 4830f7e..9260d3c 100755
--- a/configure
+++ b/configure
@@ -240,6 +240,7 @@ gtk=""
 gtkabi="2.0"
 tpm="no"
 libssh2=""
+live_block_ops="yes"
 
 # parse CC options first
 for opt do
@@ -927,7 +928,11 @@ for opt do
   ;;
   --enable-libssh2) libssh2="yes"
   ;;
-  *) echo "ERROR: unknown option $opt"; show_help="yes"
+  --disable-live-block-ops) live_block_ops="no"
+  ;;
+  --enable-live-block-ops) live_block_ops="yes"
+  ;;
+*) echo "ERROR: unknown option $opt"; show_help="yes"
   ;;
   esac
 done
@@ -1195,6 +1200,8 @@ echo "  --gcov=GCOV              use specified gcov [$gcov_tool]"
 echo "  --enable-tpm             enable TPM support"
 echo "  --disable-libssh2        disable ssh block device support"
 echo "  --enable-libssh2         enable ssh block device support"
+echo "  --disable-live-block-ops disable live block operations support"
+echo "  --enable-live-block-ops  enable live block operations support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -3556,6 +3563,7 @@ echo "TPM support       $tpm"
 echo "libssh2 support   $libssh2"
 echo "TPM passthrough   $tpm_passthrough"
 echo "QOM debugging     $qom_cast_debug"
+echo "Live block operations $live_block_ops"
 
 if test "$sdl_too_old" = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -3940,6 +3948,10 @@ if test "$virtio_blk_data_plane" = "yes" ; then
   echo 'CONFIG_VIRTIO_BLK_DATA_PLANE=$(CONFIG_VIRTIO)' >> $config_host_mak
 fi
 
+if test "$live_block_ops" = "yes" ; then
+  echo "CONFIG_LIVE_BLOCK_OPS=y" >> $config_host_mak
+fi
+
 # USB host support
 if test "$libusb" = "yes"; then
   echo "HOST_USB=libusb legacy" >> $config_host_mak
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 5cd6368..2fc2c0b 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -68,7 +68,7 @@ action to see the updated size.  Resize to a lower size is supported,
 but should be used with extreme caution.  Note that this command only
 resizes image files, it can not resize block devices like LVM volumes.
 ETEXI
-
+#ifdef CONFIG_LIVE_BLOCK_OPS
     {
         .name       = "block_stream",
         .args_type  = "device:B,speed:o?,base:s?",
@@ -76,6 +76,7 @@ ETEXI
         .help       = "copy data from a backing file into a block device",
         .mhandler.cmd = hmp_block_stream,
     },
+#endif
 
 STEXI
 @item block_stream
@@ -1024,6 +1025,7 @@ gdb.
 ETEXI
 #endif
 
+#ifdef CONFIG_LIVE_BLOCK_OPS
     {
         .name       = "snapshot_blkdev",
         .args_type  = "reuse:-n,device:B,snapshot-file:s?,format:s?",
@@ -1066,6 +1068,7 @@ STEXI
 Start mirroring a block device's writes to a new destination,
 using the specified target.
 ETEXI
+#endif
 
     {
         .name       = "drive_add",
diff --git a/hmp.c b/hmp.c
index 3b3e7c7..29990d2 100644
--- a/hmp.c
+++ b/hmp.c
@@ -838,6 +838,7 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)
     hmp_handle_error(mon, &errp);
 }
 
+#ifdef CONFIG_LIVE_BLOCK_OPS
 void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
 {
     const char *device = qdict_get_str(qdict, "device");
@@ -889,6 +890,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
                                true, mode, &errp);
     hmp_handle_error(mon, &errp);
 }
+#endif
 
 void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
 {
@@ -1030,6 +1032,7 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
     hmp_handle_error(mon, &err);
 }
 
+#ifdef CONFIG_LIVE_BLOCK_OPS
 void hmp_block_stream(Monitor *mon, const QDict *qdict)
 {
     Error *error = NULL;
@@ -1043,6 +1046,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
 
     hmp_handle_error(mon, &error);
 }
+#endif
 
 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict)
 {
diff --git a/qapi-schema.json b/qapi-schema.json
index b779458..12a360a 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1617,6 +1617,7 @@
 ##
 { 'command': 'block_resize', 'data': { 'device': 'str', 'size': 'int' }}
 
+#_rhev-only CONFIG_LIVE_BLOCK_OPS
 ##
 # @NewImageMode
 #
@@ -1697,6 +1698,7 @@
 ##
 { 'command': 'blockdev-snapshot-sync',
   'data': 'BlockdevSnapshot' }
+#_end-rhev-only
 
 ##
 # @human-monitor-command:
@@ -1726,6 +1728,7 @@
   'data': {'command-line': 'str', '*cpu-index': 'int'},
   'returns': 'str' }
 
+#_rhev-only CONFIG_LIVE_BLOCK_OPS
 ##
 # @block-commit
 #
@@ -1811,6 +1814,7 @@
             '*speed': 'int', '*granularity': 'uint32',
             '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
             '*on-target-error': 'BlockdevOnError' } }
+#_end-rhev-only
 
 ##
 # @migrate_cancel
@@ -2123,6 +2127,7 @@
   'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
             'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int' } }
 
+#_rhev-only CONFIG_LIVE_BLOCK_OPS
 ##
 # @block-stream:
 #
@@ -2160,6 +2165,7 @@
 { 'command': 'block-stream',
   'data': { 'device': 'str', '*base': 'str', '*speed': 'int',
             '*on-error': 'BlockdevOnError' } }
+#_end-rhev-only
 
 ##
 # @block-job-set-speed:
diff --git a/qmp-commands.hx b/qmp-commands.hx
index de5f394..e40d54d 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -904,6 +904,7 @@ Example:
 
 EQMP
 
+#ifdef CONFIG_LIVE_BLOCK_OPS
     {
         .name       = "block-stream",
         .args_type  = "device:B,base:s?,speed:o?,on-error:s?",
@@ -915,6 +916,7 @@ EQMP
         .args_type  = "device:B,base:s?,top:s,speed:o?",
         .mhandler.cmd_new = qmp_marshal_input_block_commit,
     },
+#endif
 
     {
         .name       = "block-job-set-speed",
@@ -942,6 +944,7 @@ EQMP
         .args_type  = "device:B",
         .mhandler.cmd_new = qmp_marshal_input_block_job_complete,
     },
+#ifdef CONFIG_LIVE_BLOCK_OPS
     {
         .name       = "transaction",
         .args_type  = "actions:q",
@@ -1085,6 +1088,7 @@ Example:
 <- { "return": {} }
 
 EQMP
+#endif
 
     {
         .name       = "balloon",
-- 
1.7.1