--- /dev/null
+From 8518fbb314727cbb3f747c20ef194df7cd1bdac3 Mon Sep 17 00:00:00 2001
+Message-Id: <8518fbb314727cbb3f747c20ef194df7cd1bdac3@dist-git>
+From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
+Date: Wed, 28 Jan 2015 12:25:11 +0100
+Subject: [PATCH] Split qemuDomainChrInsert into two parts
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1195155
+
+Do the allocation first, then add the actual device.
+The second part should never fail. This is good
+for live hotplug where we don't want to remove the device
+on OOM after the monitor command succeeded.
+
+The only change in behavior is that on failure, the
+vmdef->consoles array is freed, not just the first console.
+
+(cherry picked from commit daf51be5f1b0f7b41c0813d43d6b66edfbe4f6d9)
+Signed-off-by: Ján Tomko <jtomko@redhat.com>
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/conf/domain_conf.c | 18 +++++++++++++---
+ src/conf/domain_conf.h | 7 +++++--
+ src/libvirt_private.syms | 3 ++-
+ src/qemu/qemu_hotplug.c | 54 +++++++++++++++++++++++++++++++++++++++++-------
+ 4 files changed, 68 insertions(+), 14 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index fed87f0..9bfffd0 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -11529,15 +11529,27 @@ virDomainChrGetDomainPtrs(const virDomainDef *vmdef,
+
+
+ int
+-virDomainChrInsert(virDomainDefPtr vmdef,
+- virDomainChrDefPtr chr)
++virDomainChrPreAlloc(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr)
+ {
+ virDomainChrDefPtr **arrPtr = NULL;
+ size_t *cntPtr = NULL;
+
+ virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr);
+
+- return VIR_APPEND_ELEMENT(*arrPtr, *cntPtr, chr);
++ return VIR_REALLOC_N(*arrPtr, *cntPtr + 1);
++}
++
++void
++virDomainChrInsertPreAlloced(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr)
++{
++ virDomainChrDefPtr **arrPtr = NULL;
++ size_t *cntPtr = NULL;
++
++ virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr);
++
++ ignore_value(VIR_APPEND_ELEMENT_INPLACE(*arrPtr, *cntPtr, chr));
+ }
+
+ virDomainChrDefPtr
+diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
+index b912845..1436eb8 100644
+--- a/src/conf/domain_conf.h
++++ b/src/conf/domain_conf.h
+@@ -2543,8 +2543,11 @@ bool
+ virDomainChrEquals(virDomainChrDefPtr src,
+ virDomainChrDefPtr tgt);
+ int
+-virDomainChrInsert(virDomainDefPtr vmdef,
+- virDomainChrDefPtr chr);
++virDomainChrPreAlloc(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr);
++void
++virDomainChrInsertPreAlloced(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr);
+ virDomainChrDefPtr
+ virDomainChrRemove(virDomainDefPtr vmdef,
+ virDomainChrDefPtr chr);
+diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
+index 325a912..18c715a 100644
+--- a/src/libvirt_private.syms
++++ b/src/libvirt_private.syms
+@@ -153,7 +153,8 @@ virDomainChrDefNew;
+ virDomainChrEquals;
+ virDomainChrFind;
+ virDomainChrGetDomainPtrs;
+-virDomainChrInsert;
++virDomainChrInsertPreAlloced;
++virDomainChrPreAlloc;
+ virDomainChrRemove;
+ virDomainChrSerialTargetTypeFromString;
+ virDomainChrSerialTargetTypeToString;
+diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
+index 8a3eb27..00ce77f 100644
+--- a/src/qemu/qemu_hotplug.c
++++ b/src/qemu/qemu_hotplug.c
+@@ -1390,9 +1390,9 @@ int qemuDomainAttachRedirdevDevice(virQEMUDriverPtr driver,
+
+ }
+
+-int
+-qemuDomainChrInsert(virDomainDefPtr vmdef,
+- virDomainChrDefPtr chr)
++static int
++qemuDomainChrPreInsert(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr)
+ {
+ if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
+ chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL) {
+@@ -1407,25 +1407,63 @@ qemuDomainChrInsert(virDomainDefPtr vmdef,
+ return -1;
+ }
+
+- if (virDomainChrInsert(vmdef, chr) < 0)
++ if (virDomainChrPreAlloc(vmdef, chr) < 0)
+ return -1;
+
+ /* Due to some crazy backcompat stuff, the first serial device is an alias
+ * to the first console too. If this is the case, the definition must be
+ * duplicated as first console device. */
+- if (vmdef->nserials == 1 && vmdef->nconsoles == 0) {
+- if ((!vmdef->consoles && VIR_ALLOC(vmdef->consoles) < 0) ||
+- VIR_ALLOC(vmdef->consoles[0]) < 0) {
+- virDomainChrRemove(vmdef, chr);
++ if (vmdef->nserials == 0 && vmdef->nconsoles == 0 &&
++ chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL) {
++ if (!vmdef->consoles && VIR_ALLOC(vmdef->consoles) < 0)
++ return -1;
++
++ if (VIR_ALLOC(vmdef->consoles[0]) < 0) {
++ VIR_FREE(vmdef->consoles);
+ return -1;
+ }
++ vmdef->nconsoles++;
++ }
++ return 0;
++}
++
++static void
++qemuDomainChrInsertPreAlloced(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr)
++{
++ virDomainChrInsertPreAlloced(vmdef, chr);
++ if (vmdef->nserials == 1 && vmdef->nconsoles == 0 &&
++ chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL) {
+ vmdef->nconsoles = 1;
+
+ /* Create an console alias for the serial port */
+ vmdef->consoles[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
+ vmdef->consoles[0]->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
+ }
++}
+
++static void
++qemuDomainChrInsertPreAllocCleanup(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr)
++{
++ /* Remove the stub console added by qemuDomainChrPreInsert */
++ if (vmdef->nserials == 0 && vmdef->nconsoles == 1 &&
++ chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL) {
++ VIR_FREE(vmdef->consoles[0]);
++ VIR_FREE(vmdef->consoles);
++ vmdef->nconsoles = 0;
++ }
++}
++
++int
++qemuDomainChrInsert(virDomainDefPtr vmdef,
++ virDomainChrDefPtr chr)
++{
++ if (qemuDomainChrPreInsert(vmdef, chr) < 0) {
++ qemuDomainChrInsertPreAllocCleanup(vmdef, chr);
++ return -1;
++ }
++ qemuDomainChrInsertPreAlloced(vmdef, chr);
+ return 0;
+ }
+
+--
+2.3.0
+
--- /dev/null
+From babfc1d48c3a0f83592fa501b609fd839ff1a51b Mon Sep 17 00:00:00 2001
+Message-Id: <babfc1d48c3a0f83592fa501b609fd839ff1a51b@dist-git>
+From: Eric Blake <eblake@redhat.com>
+Date: Tue, 24 Feb 2015 11:59:52 +0100
+Subject: [PATCH] blockcopy: allow block device destination
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1196066
+
+To date, anyone performing a block copy and pivot ends up with
+the destination being treated as <disk type='file'>. While this
+works for data access for a block device, it has at least one
+noticeable shortcoming: virDomainGetBlockInfo() reports allocation
+differently for block devices visited as files (the size of the
+device) than for block devices visited as <disk type='block'>
+(the maximum sector used, as reported by qemu); and this difference
+is significant when trying to manage qcow2 format on block devices
+that can be grown as needed.
+
+Of course, the more powerful virDomainBlockCopy() API can already
+express the ability to set the <disk> type. But a new API can't
+be backported, while a new flag to an existing API can; and it is
+also rather inconvenient to have to resort to the full power of
+generating XML when just adding a flag to the older call will do
+the trick. So this patch enhances blockcopy to let the user flag
+when the resulting XML after the copy must list the device as
+type='block'.
+
+* include/libvirt/libvirt.h.in (VIR_DOMAIN_BLOCK_REBASE_COPY_DEV):
+New flag.
+* src/libvirt.c (virDomainBlockRebase): Document it.
+* tools/virsh-domain.c (opts_block_copy, blockJobImpl): Add
+--blockdev option.
+* tools/virsh.pod (blockcopy): Document it.
+* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Allow new flag.
+(qemuDomainBlockCopy): Remember the flag, and make sure it is only
+used on actual block devices.
+* tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml: Test it.
+
+Signed-off-by: Eric Blake <eblake@redhat.com>
+(cherry picked from commit b7e73585a8d96677695a52bafb156f26cbd48fb5)
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ include/libvirt/libvirt.h.in | 2 ++
+ src/libvirt.c | 8 +++--
+ src/qemu/qemu_driver.c | 36 ++++++++++++++--------
+ .../qemuxml2argvdata/qemuxml2argv-disk-mirror.xml | 4 +--
+ tools/virsh-domain.c | 6 ++++
+ tools/virsh.pod | 7 +++--
+ 6 files changed, 45 insertions(+), 18 deletions(-)
+
+diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
+index 94de8a6..e086c8f 100644
+--- a/include/libvirt/libvirt.h.in
++++ b/include/libvirt/libvirt.h.in
+@@ -2638,6 +2638,8 @@ typedef enum {
+ VIR_DOMAIN_BLOCK_REBASE_RELATIVE = 1 << 4, /* Keep backing chain
+ referenced using relative
+ names */
++ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV = 1 << 5, /* Treat destination as block
++ device instead of file */
+ } virDomainBlockRebaseFlags;
+
+ int virDomainBlockRebase(virDomainPtr dom, const char *disk,
+diff --git a/src/libvirt.c b/src/libvirt.c
+index 5315881..e1c02dc 100644
+--- a/src/libvirt.c
++++ b/src/libvirt.c
+@@ -19891,7 +19891,10 @@ virDomainBlockPull(virDomainPtr dom, const char *disk,
+ * pre-create files with relative backing file names, rather than the default
+ * of absolute backing file names; as a security precaution, you should
+ * generally only use reuse_ext with the shallow flag and a non-raw
+- * destination file.
++ * destination file. By default, the copy destination will be treated as
++ * type='file', but using VIR_DOMAIN_BLOCK_REBASE_COPY_DEV treats the
++ * destination as type='block' (affecting how virDomainGetBlockInfo() will
++ * report allocation after pivoting).
+ *
+ * A copy job has two parts; in the first phase, the @bandwidth parameter
+ * affects how fast the source is pulled into the destination, and the job
+@@ -19966,7 +19969,8 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk,
+ virCheckNonNullArgGoto(base, error);
+ } else if (flags & (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
+ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
+- VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)) {
++ VIR_DOMAIN_BLOCK_REBASE_COPY_RAW |
++ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)) {
+ virReportInvalidArg(flags,
+ _("use of flags in %s requires a copy job"),
+ __FUNCTION__);
+diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
+index 162e039..c25c5ac 100644
+--- a/src/qemu/qemu_driver.c
++++ b/src/qemu/qemu_driver.c
+@@ -15781,7 +15781,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
+
+ /* Preliminaries: find the disk we are editing, sanity checks */
+ virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
+- VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT, -1);
++ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
++ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV, -1);
+
+ priv = vm->privateData;
+ cfg = virQEMUDriverGetConfig(driver);
+@@ -15842,25 +15843,34 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
+ virReportSystemError(errno, _("unable to stat for disk %s: %s"),
+ disk->dst, dest);
+ goto endjob;
+- } else if (flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) {
++ } else if (flags & (VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
++ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)) {
+ virReportSystemError(errno,
+ _("missing destination file for disk %s: %s"),
+ disk->dst, dest);
+ goto endjob;
+ }
+- } else if (!S_ISBLK(st.st_mode) && st.st_size &&
+- !(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) {
+- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+- _("external destination file for disk %s already "
+- "exists and is not a block device: %s"),
+- disk->dst, dest);
+- goto endjob;
++ } else if (!S_ISBLK(st.st_mode)) {
++ if (st.st_size && !(flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT)) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
++ _("external destination file for disk %s already "
++ "exists and is not a block device: %s"),
++ disk->dst, dest);
++ goto endjob;
++ }
++ if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV) {
++ virReportError(VIR_ERR_INVALID_ARG,
++ _("blockdev flag requested for disk %s, but file "
++ "'%s' is not a block device"), disk->dst, dest);
++ goto endjob;
++ }
+ }
+
+ if (VIR_ALLOC(mirror) < 0)
+ goto endjob;
+ /* XXX Allow non-file mirror destinations */
+- mirror->type = VIR_STORAGE_TYPE_FILE;
++ mirror->type = flags & VIR_DOMAIN_BLOCK_REBASE_COPY_DEV ?
++ VIR_STORAGE_TYPE_BLOCK : VIR_STORAGE_TYPE_FILE;
+
+ if (format) {
+ if ((mirror->format = virStorageFileFormatTypeFromString(format)) <= 0) {
+@@ -15954,7 +15964,8 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
+ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
+ VIR_DOMAIN_BLOCK_REBASE_COPY |
+ VIR_DOMAIN_BLOCK_REBASE_COPY_RAW |
+- VIR_DOMAIN_BLOCK_REBASE_RELATIVE, -1);
++ VIR_DOMAIN_BLOCK_REBASE_RELATIVE |
++ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV, -1);
+
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ return -1;
+@@ -15982,7 +15993,8 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
+ }
+
+ flags &= (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
+- VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT);
++ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
++ VIR_DOMAIN_BLOCK_REBASE_COPY_DEV);
+ ret = qemuDomainBlockCopy(vm, dom->conn, path, base, format,
+ bandwidth, flags);
+ vm = NULL;
+diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
+index 46f2a3e..7495a45 100644
+--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
++++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-mirror.xml
+@@ -17,8 +17,8 @@
+ <disk type='block' device='disk'>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <backingStore/>
+- <mirror type='file' file='/dev/HostVG/QEMUGuest1Copy' job='copy' ready='yes'>
+- <source file='/dev/HostVG/QEMUGuest1Copy'/>
++ <mirror type='block' job='copy' ready='yes'>
++ <source dev='/dev/HostVG/QEMUGuest1Copy'/>
+ </mirror>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
+index 28f5319..8f79b55 100644
+--- a/tools/virsh-domain.c
++++ b/tools/virsh-domain.c
+@@ -1550,6 +1550,8 @@ blockJobImpl(vshControl *ctl, const vshCmd *cmd,
+ flags |= VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT;
+ if (vshCommandOptBool(cmd, "raw"))
+ flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_RAW;
++ if (vshCommandOptBool(cmd, "blockdev"))
++ flags |= VIR_DOMAIN_BLOCK_REBASE_COPY_DEV;
+ if (vshCommandOptStringReq(ctl, cmd, "dest", &base) < 0)
+ goto cleanup;
+ ret = virDomainBlockRebase(dom, path, base, bandwidth, flags);
+@@ -1857,6 +1859,10 @@ static const vshCmdOptDef opts_block_copy[] = {
+ .type = VSH_OT_BOOL,
+ .help = N_("use raw destination file")
+ },
++ {.name = "blockdev",
++ .type = VSH_OT_BOOL,
++ .help = N_("copy destination is block device instead of regular file")
++ },
+ {.name = "wait",
+ .type = VSH_OT_BOOL,
+ .help = N_("wait for job to reach mirroring phase")
+diff --git a/tools/virsh.pod b/tools/virsh.pod
+index c5b176a..46ef01d 100644
+--- a/tools/virsh.pod
++++ b/tools/virsh.pod
+@@ -959,7 +959,8 @@ unlimited. The hypervisor can choose whether to reject the value or
+ convert it to the maximum value allowed.
+
+ =item B<blockcopy> I<domain> I<path> I<dest> [I<bandwidth>] [I<--shallow>]
+-[I<--reuse-external>] [I<--raw>] [I<--wait> [I<--async>] [I<--verbose>]]
++[I<--reuse-external>] [I<--raw>] [I<--blockdev>]
++[I<--wait> [I<--async>] [I<--verbose>]]
+ [{I<--pivot> | I<--finish>}] [I<--timeout> B<seconds>]
+
+ Copy a disk backing image chain to I<dest>. By default, this command
+@@ -977,7 +978,9 @@ The format of the destination is determined by the first match in the
+ following list: if I<--raw> is specified, it will be raw; if
+ I<--reuse-external> is specified, the existing destination is probed
+ for a format; and in all other cases, the destination format will
+-match the source format.
++match the source format. The destination is treated as a regular
++file unless I<--blockdev> is used to signal that it is a block
++device.
+
+ By default, the copy job runs in the background, and consists of two
+ phases. Initially, the job must copy all data from the source, and
+--
+2.3.0
+
--- /dev/null
+From 61fbb57d74cc44594b5bcb184c350ab18b963291 Mon Sep 17 00:00:00 2001
+Message-Id: <61fbb57d74cc44594b5bcb184c350ab18b963291@dist-git>
+From: Eric Blake <eblake@redhat.com>
+Date: Tue, 24 Feb 2015 11:59:51 +0100
+Subject: [PATCH] blockjob: shuffle block rebase code
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1196066
+
+The existing virDomainBlockRebase code rejected the combination of
+_RELATIVE and _COPY flags, but only by accident. It makes sense
+to add support for the combination someday, at least for the case
+of _SHALLOW and not _REUSE_EXT; but to implement it, libvirt would
+have to pre-create the file with a relative backing name, and I'm
+not ready to code that in yet.
+
+Meanwhile, the code to forward on to the block copy code is getting
+longer, and reorganizing the function to have the block pull done
+early makes it easier to add even more block copy prep code.
+
+This patch should have no semantic difference other than the quality
+of the error message on the unsupported flag combination. Pre-patch:
+
+error: unsupported flags (0x10) in function qemuDomainBlockCopy
+
+Post-patch:
+
+error: argument unsupported: Relative backing during copy not supported yet
+
+* src/qemu/qemu_driver.c (qemuDomainBlockRebase): Reorder code,
+and improve error message of relative copy.
+
+Signed-off-by: Eric Blake <eblake@redhat.com>
+(cherry picked from commit 02d2bd7d91c200d1ea1a5b3f78c8b41720cea832)
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_driver.c | 47 ++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 32 insertions(+), 15 deletions(-)
+
+diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
+index f3b909f..162e039 100644
+--- a/src/qemu/qemu_driver.c
++++ b/src/qemu/qemu_driver.c
+@@ -15947,6 +15947,8 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
+ unsigned long bandwidth, unsigned int flags)
+ {
+ virDomainObjPtr vm;
++ const char *format = NULL;
++ int ret = -1;
+
+ virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
+ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
+@@ -15957,22 +15959,37 @@ qemuDomainBlockRebase(virDomainPtr dom, const char *path, const char *base,
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ return -1;
+
+- if (virDomainBlockRebaseEnsureACL(dom->conn, vm->def) < 0) {
++ if (virDomainBlockRebaseEnsureACL(dom->conn, vm->def) < 0)
++ goto cleanup;
++
++ /* For normal rebase (enhanced blockpull), the common code handles
++ * everything, including vm cleanup. */
++ if (!(flags & VIR_DOMAIN_BLOCK_REBASE_COPY))
++ return qemuDomainBlockJobImpl(vm, dom->conn, path, base, bandwidth,
++ NULL, BLOCK_JOB_PULL, flags);
++
++ /* If we got here, we are doing a block copy rebase. */
++ if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
++ format = "raw";
++
++ /* XXX: If we are doing a shallow copy but not reusing an external
++ * file, we should attempt to pre-create the destination with a
++ * relative backing chain instead of qemu's default of absolute */
++ if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE) {
++ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
++ _("Relative backing during copy not supported yet"));
++ goto cleanup;
++ }
++
++ flags &= (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
++ VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT);
++ ret = qemuDomainBlockCopy(vm, dom->conn, path, base, format,
++ bandwidth, flags);
++ vm = NULL;
++ cleanup:
++ if (vm)
+ virObjectUnlock(vm);
+- return -1;
+- }
+-
+- if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY) {
+- const char *format = NULL;
+- if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY_RAW)
+- format = "raw";
+- flags &= ~(VIR_DOMAIN_BLOCK_REBASE_COPY |
+- VIR_DOMAIN_BLOCK_REBASE_COPY_RAW);
+- return qemuDomainBlockCopy(vm, dom->conn, path, base, format, bandwidth, flags);
+- }
+-
+- return qemuDomainBlockJobImpl(vm, dom->conn, path, base, bandwidth, NULL,
+- BLOCK_JOB_PULL, flags);
++ return ret;
+ }
+
+ static int
+--
+2.3.0
+
--- /dev/null
+From 7854f0d28b2bd526ae27777aa6c97f0ab3443523 Mon Sep 17 00:00:00 2001
+Message-Id: <7854f0d28b2bd526ae27777aa6c97f0ab3443523@dist-git>
+From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com>
+Date: Wed, 28 Jan 2015 12:25:12 +0100
+Subject: [PATCH] hotplug: only add a chardev to vmdef after monitor call
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1195155
+
+This way the device is in vmdef only if ret = 0 and the caller
+(qemuDomainAttachDeviceFlags) does not free it.
+
+Otherwise it might get double freed by qemuProcessStop
+and qemuDomainAttachDeviceFlags if the domain crashed
+in monitor after we've added it to vm->def.
+
+(cherry picked from commit 21e0e8866e341da74e296ca3cf2d97812e847a66)
+Signed-off-by: Ján Tomko <jtomko@redhat.com>
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_hotplug.c | 34 +++++++++++-----------------------
+ 1 file changed, 11 insertions(+), 23 deletions(-)
+
+diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
+index 00ce77f..89757bc 100644
+--- a/src/qemu/qemu_hotplug.c
++++ b/src/qemu/qemu_hotplug.c
+@@ -1510,59 +1510,47 @@ int qemuDomainAttachChrDevice(virQEMUDriverPtr driver,
+ virDomainDefPtr vmdef = vm->def;
+ char *devstr = NULL;
+ char *charAlias = NULL;
+- bool need_remove = false;
+
+ if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("qemu does not support -device"));
+- return ret;
++ goto cleanup;
+ }
+
+ if (qemuAssignDeviceChrAlias(vmdef, chr, -1) < 0)
+- return ret;
++ goto cleanup;
+
+ if (qemuBuildChrDeviceStr(&devstr, vm->def, chr, priv->qemuCaps) < 0)
+- return ret;
++ goto cleanup;
+
+ if (virAsprintf(&charAlias, "char%s", chr->info.alias) < 0)
+ goto cleanup;
+
+- if (qemuDomainChrInsert(vmdef, chr) < 0)
++ if (qemuDomainChrPreInsert(vmdef, chr) < 0)
+ goto cleanup;
+- need_remove = true;
+
+ qemuDomainObjEnterMonitor(driver, vm);
+ if (qemuMonitorAttachCharDev(priv->mon, charAlias, &chr->source) < 0) {
+- if (qemuDomainObjExitMonitor(driver, vm) < 0) {
+- need_remove = false;
+- ret = -1;
+- goto cleanup;
+- }
++ ignore_value(qemuDomainObjExitMonitor(driver, vm));
+ goto audit;
+ }
+
+ if (devstr && qemuMonitorAddDevice(priv->mon, devstr) < 0) {
+ /* detach associated chardev on error */
+ qemuMonitorDetachCharDev(priv->mon, charAlias);
+- if (qemuDomainObjExitMonitor(driver, vm) < 0) {
+- need_remove = false;
+- ret = -1;
+- goto cleanup;
+- }
++ ignore_value(qemuDomainObjExitMonitor(driver, vm));
+ goto audit;
+ }
+- if (qemuDomainObjExitMonitor(driver, vm) < 0) {
+- need_remove = false;
+- ret = -1;
+- goto cleanup;
+- }
++ if (qemuDomainObjExitMonitor(driver, vm) < 0)
++ goto audit;
+
++ qemuDomainChrInsertPreAlloced(vm->def, chr);
+ ret = 0;
+ audit:
+ virDomainAuditChardev(vm, NULL, chr, "attach", ret == 0);
+ cleanup:
+- if (ret < 0 && need_remove)
+- qemuDomainChrRemove(vmdef, chr);
++ if (ret < 0 && virDomainObjIsActive(vm))
++ qemuDomainChrInsertPreAllocCleanup(vm->def, chr);
+ VIR_FREE(charAlias);
+ VIR_FREE(devstr);
+ return ret;
+--
+2.3.0
+
--- /dev/null
+From 4e7b21b4138e011c05ae72ed8b92f0bd2b888744 Mon Sep 17 00:00:00 2001
+Message-Id: <4e7b21b4138e011c05ae72ed8b92f0bd2b888744@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 17 Mar 2015 13:13:53 +0100
+Subject: [PATCH] qemu: Disallow concurrent block jobs on a single disk
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1202719
+
+While qemu may be prepared to do this libvirt is not. Forbid the block
+ops until we fix our code.
+
+(cherry picked from commit 51f9f03a4ca50b070c0fbfb29748d49f583e15e1)
+
+Conflicts:
+ src/qemu/qemu_domain.h - context with upstream changes
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/conf/domain_conf.h | 4 ++++
+ src/qemu/qemu_domain.c | 23 +++++++++++++++++++++++
+ src/qemu/qemu_domain.h | 2 ++
+ src/qemu/qemu_driver.c | 28 +++++++++++++---------------
+ 4 files changed, 42 insertions(+), 15 deletions(-)
+
+diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
+index 1436eb8..654c27d 100644
+--- a/src/conf/domain_conf.h
++++ b/src/conf/domain_conf.h
+@@ -636,6 +636,10 @@ struct _virDomainDiskDef {
+ int tray_status; /* enum virDomainDiskTray */
+ int removable; /* enum virTristateSwitch */
+
++ /* ideally we want a smarter way to interlock block jobs on single qemu disk
++ * in the future, but for now we just disallow any concurrent job on a
++ * single disk */
++ bool blockjob;
+ virStorageSourcePtr mirror;
+ int mirrorState; /* enum virDomainDiskMirrorState */
+ int mirrorJob; /* virDomainBlockJobType */
+diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
+index 0c3d21f..b9bf3eb 100644
+--- a/src/qemu/qemu_domain.c
++++ b/src/qemu/qemu_domain.c
+@@ -2771,6 +2771,29 @@ qemuDomainDetermineDiskChain(virQEMUDriverPtr driver,
+ return ret;
+ }
+
++
++bool
++qemuDomainDiskBlockJobIsActive(virDomainDiskDefPtr disk)
++{
++ if (disk->mirror) {
++ virReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
++ _("disk '%s' already in active block job"),
++ disk->dst);
++
++ return true;
++ }
++
++ if (disk->blockjob) {
++ virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
++ _("disk '%s' already in active block job"),
++ disk->dst);
++ return true;
++ }
++
++ return false;
++}
++
++
+ int
+ qemuDomainUpdateDeviceList(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
+index 76054ec..63d1261 100644
+--- a/src/qemu/qemu_domain.h
++++ b/src/qemu/qemu_domain.h
+@@ -416,4 +416,6 @@ int qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo,
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2)
+ ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
+
++bool qemuDomainDiskBlockJobIsActive(virDomainDiskDefPtr disk);
++
+ #endif /* __QEMU_DOMAIN_H__ */
+diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
+index a19281d..2bd4a1d 100644
+--- a/src/qemu/qemu_driver.c
++++ b/src/qemu/qemu_driver.c
+@@ -4490,6 +4490,7 @@ processBlockJobEvent(virQEMUDriverPtr driver,
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+ ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk,
+ true, true));
++ disk->blockjob = false;
+ break;
+
+ case VIR_DOMAIN_BLOCK_JOB_READY:
+@@ -4505,6 +4506,7 @@ processBlockJobEvent(virQEMUDriverPtr driver,
+ VIR_DOMAIN_DISK_MIRROR_STATE_ABORT : VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+ save = true;
++ disk->blockjob = false;
+ break;
+
+ case VIR_DOMAIN_BLOCK_JOB_LAST:
+@@ -15583,6 +15585,7 @@ qemuDomainBlockPivot(virConnectPtr conn,
+ disk->mirror = NULL;
+ disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
++ disk->blockjob = false;
+ }
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ ret = -1;
+@@ -15679,12 +15682,9 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
+ goto endjob;
+ disk = vm->def->disks[idx];
+
+- if (mode == BLOCK_JOB_PULL && disk->mirror) {
+- virReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+- _("disk '%s' already in active block job"),
+- disk->dst);
++ if (mode == BLOCK_JOB_PULL && qemuDomainDiskBlockJobIsActive(disk))
+ goto endjob;
+- }
++
+ if (mode == BLOCK_JOB_ABORT) {
+ if ((flags & VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT) &&
+ !(async && disk->mirror)) {
+@@ -15756,6 +15756,8 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
+ if (mode == BLOCK_JOB_ABORT && disk->mirror)
+ disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+ goto endjob;
++ } else if (mode == BLOCK_JOB_PULL) {
++ disk->blockjob = true;
+ }
+
+ /* Snoop block copy operations, so future cancel operations can
+@@ -15943,12 +15945,8 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
+ goto endjob;
+ }
+ disk = vm->def->disks[idx];
+- if (disk->mirror) {
+- virReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+- _("disk '%s' already in active block job"),
+- disk->dst);
++ if (qemuDomainDiskBlockJobIsActive(disk))
+ goto endjob;
+- }
+
+ if (!(virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DRIVE_MIRROR) &&
+ virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKJOB_ASYNC))) {
+@@ -16074,6 +16072,7 @@ qemuDomainBlockCopy(virDomainObjPtr vm,
+ disk->mirror = mirror;
+ mirror = NULL;
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY;
++ disk->blockjob = true;
+
+ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+ VIR_WARN("Unable to save status on vm %s after state change",
+@@ -16232,12 +16231,9 @@ qemuDomainBlockCommit(virDomainPtr dom,
+ disk->dst);
+ goto endjob;
+ }
+- if (disk->mirror) {
+- virReportError(VIR_ERR_BLOCK_COPY_ACTIVE,
+- _("disk '%s' already in active block job"),
+- disk->dst);
++
++ if (qemuDomainDiskBlockJobIsActive(disk))
+ goto endjob;
+- }
+ if (qemuDomainDetermineDiskChain(driver, vm, disk, false, true) < 0)
+ goto endjob;
+
+@@ -16358,6 +16354,8 @@ qemuDomainBlockCommit(virDomainPtr dom,
+ bandwidth);
+ qemuDomainObjExitMonitor(driver, vm);
+
++ disk->blockjob = true;
++
+ if (mirror) {
+ if (ret == 0) {
+ virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+--
+2.3.3
+
--- /dev/null
+From 826bf4c001e1190982e500c6d88c0513d3e507ce Mon Sep 17 00:00:00 2001
+Message-Id: <826bf4c001e1190982e500c6d88c0513d3e507ce@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 17 Mar 2015 13:13:54 +0100
+Subject: [PATCH] qemu: block-commit: Mark disk in block jobs only on
+ successful command
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1202719
+
+Patch 51f9f03a4ca50b070c0fbfb29748d49f583e15e1 introduces a regression
+where if a blockCommit operation fails the disk is still marked as being
+part of a block job but can't be unmarked later.
+
+(cherry picked from commit ee744b5b387b5123ee40683c52ab40783ffc3020)
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_driver.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
+index 2bd4a1d..9dc243a 100644
+--- a/src/qemu/qemu_driver.c
++++ b/src/qemu/qemu_driver.c
+@@ -16354,7 +16354,8 @@ qemuDomainBlockCommit(virDomainPtr dom,
+ bandwidth);
+ qemuDomainObjExitMonitor(driver, vm);
+
+- disk->blockjob = true;
++ if (ret == 0)
++ disk->blockjob = true;
+
+ if (mirror) {
+ if (ret == 0) {
+--
+2.3.3
+
--- /dev/null
+From 12fdae1ebb74296a4db3b191f16dfda757024b8f Mon Sep 17 00:00:00 2001
+Message-Id: <12fdae1ebb74296a4db3b191f16dfda757024b8f@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 17 Mar 2015 13:13:52 +0100
+Subject: [PATCH] qemu: event: Don't fiddle with disk backing trees without a
+ job
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1202719
+
+Surprisingly we did not grab a VM job when a block job finished and we'd
+happily rewrite the backing chain data. This made it possible to crash
+libvirt when queueing two backing chains tightly and other badness.
+
+To fix it, add yet another handler to the helper thread that handles
+monitor events that require a job.
+
+(cherry picked from commit 1a92c719101e5bfa6fe2b78006ad04c7f075ea28)
+
+ Changes: src/qemu/qemu_driver.c: qemuDomainObjEndJob() needs it's
+ return value to be ignored as the locking was not refactored yet.
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_domain.h | 2 +
+ src/qemu/qemu_driver.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++
+ src/qemu/qemu_process.c | 129 ++++++++-----------------------------------
+ 3 files changed, 168 insertions(+), 105 deletions(-)
+
+diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
+index bf37e26..76054ec 100644
+--- a/src/qemu/qemu_domain.h
++++ b/src/qemu/qemu_domain.h
+@@ -196,6 +196,7 @@ typedef enum {
+ QEMU_PROCESS_EVENT_DEVICE_DELETED,
+ QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED,
+ QEMU_PROCESS_EVENT_SERIAL_CHANGED,
++ QEMU_PROCESS_EVENT_BLOCK_JOB,
+
+ QEMU_PROCESS_EVENT_LAST
+ } qemuProcessEventType;
+@@ -204,6 +205,7 @@ struct qemuProcessEvent {
+ virDomainObjPtr vm;
+ qemuProcessEventType eventType;
+ int action;
++ int status;
+ void *data;
+ };
+
+diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
+index c25c5ac..a19281d 100644
+--- a/src/qemu/qemu_driver.c
++++ b/src/qemu/qemu_driver.c
+@@ -4401,6 +4401,141 @@ processSerialChangedEvent(virQEMUDriverPtr driver,
+ }
+
+
++static void
++processBlockJobEvent(virQEMUDriverPtr driver,
++ virDomainObjPtr vm,
++ char *diskAlias,
++ int type,
++ int status)
++{
++ virObjectEventPtr event = NULL;
++ virObjectEventPtr event2 = NULL;
++ const char *path;
++ virDomainDiskDefPtr disk;
++ virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
++ virDomainDiskDefPtr persistDisk = NULL;
++ bool save = false;
++
++ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
++ goto cleanup;
++
++ if (!virDomainObjIsActive(vm)) {
++ VIR_DEBUG("Domain is not running");
++ goto endjob;
++ }
++
++ disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias);
++
++ if (disk) {
++ /* Have to generate two variants of the event for old vs. new
++ * client callbacks */
++ if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT &&
++ disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT)
++ type = disk->mirrorJob;
++ path = virDomainDiskGetSource(disk);
++ event = virDomainEventBlockJobNewFromObj(vm, path, type, status);
++ event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type,
++ status);
++
++ /* If we completed a block pull or commit, then update the XML
++ * to match. */
++ switch ((virConnectDomainEventBlockJobStatus) status) {
++ case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
++ if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) {
++ if (vm->newDef) {
++ int indx = virDomainDiskIndexByName(vm->newDef, disk->dst,
++ false);
++ virStorageSourcePtr copy = NULL;
++
++ if (indx >= 0) {
++ persistDisk = vm->newDef->disks[indx];
++ copy = virStorageSourceCopy(disk->mirror, false);
++ if (virStorageSourceInitChainElement(copy,
++ persistDisk->src,
++ true) < 0) {
++ VIR_WARN("Unable to update persistent definition "
++ "on vm %s after block job",
++ vm->def->name);
++ virStorageSourceFree(copy);
++ copy = NULL;
++ persistDisk = NULL;
++ }
++ }
++ if (copy) {
++ virStorageSourceFree(persistDisk->src);
++ persistDisk->src = copy;
++ }
++ }
++
++ /* XXX We want to revoke security labels and disk
++ * lease, as well as audit that revocation, before
++ * dropping the original source. But it gets tricky
++ * if both source and mirror share common backing
++ * files (we want to only revoke the non-shared
++ * portion of the chain); so for now, we leak the
++ * access to the original. */
++ virStorageSourceFree(disk->src);
++ disk->src = disk->mirror;
++ } else {
++ virStorageSourceFree(disk->mirror);
++ }
++
++ /* Recompute the cached backing chain to match our
++ * updates. Better would be storing the chain ourselves
++ * rather than reprobing, but we haven't quite completed
++ * that conversion to use our XML tracking. */
++ disk->mirror = NULL;
++ save = disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
++ disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
++ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
++ ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk,
++ true, true));
++ break;
++
++ case VIR_DOMAIN_BLOCK_JOB_READY:
++ disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_READY;
++ save = true;
++ break;
++
++ case VIR_DOMAIN_BLOCK_JOB_FAILED:
++ case VIR_DOMAIN_BLOCK_JOB_CANCELED:
++ virStorageSourceFree(disk->mirror);
++ disk->mirror = NULL;
++ disk->mirrorState = status == VIR_DOMAIN_BLOCK_JOB_FAILED ?
++ VIR_DOMAIN_DISK_MIRROR_STATE_ABORT : VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
++ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
++ save = true;
++ break;
++
++ case VIR_DOMAIN_BLOCK_JOB_LAST:
++ break;
++ }
++ }
++
++ if (save) {
++ if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
++ VIR_WARN("Unable to save status on vm %s after block job",
++ vm->def->name);
++ if (persistDisk && virDomainSaveConfig(cfg->configDir,
++ vm->newDef) < 0)
++ VIR_WARN("Unable to update persistent definition on vm %s "
++ "after block job", vm->def->name);
++ }
++ virObjectUnlock(vm);
++ virObjectUnref(cfg);
++
++ if (event)
++ qemuDomainEventQueue(driver, event);
++ if (event2)
++ qemuDomainEventQueue(driver, event2);
++
++ endjob:
++ ignore_value(qemuDomainObjEndJob(driver, vm));
++ cleanup:
++ VIR_FREE(diskAlias);
++}
++
++
+ static void qemuProcessEventHandler(void *data, void *opaque)
+ {
+ struct qemuProcessEvent *processEvent = data;
+@@ -4427,6 +4562,13 @@ static void qemuProcessEventHandler(void *data, void *opaque)
+ case QEMU_PROCESS_EVENT_SERIAL_CHANGED:
+ processSerialChangedEvent(driver, vm, processEvent->data,
+ processEvent->action);
++ break;
++ case QEMU_PROCESS_EVENT_BLOCK_JOB:
++ processBlockJobEvent(driver, vm,
++ processEvent->data,
++ processEvent->action,
++ processEvent->status);
++ break;
+ case QEMU_PROCESS_EVENT_LAST:
+ break;
+ }
+diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
+index ffba29d..83a59a1 100644
+--- a/src/qemu/qemu_process.c
++++ b/src/qemu/qemu_process.c
+@@ -1024,123 +1024,42 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ void *opaque)
+ {
+ virQEMUDriverPtr driver = opaque;
+- virObjectEventPtr event = NULL;
+- virObjectEventPtr event2 = NULL;
+- const char *path;
+- virDomainDiskDefPtr disk;
+- virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+- virDomainDiskDefPtr persistDisk = NULL;
+- bool save = false;
++ struct qemuProcessEvent *processEvent = NULL;
++ char *data;
+
+ virObjectLock(vm);
+- disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias);
+
+- if (disk) {
+- /* Have to generate two variants of the event for old vs. new
+- * client callbacks */
+- if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT &&
+- disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT)
+- type = disk->mirrorJob;
+- path = virDomainDiskGetSource(disk);
+- event = virDomainEventBlockJobNewFromObj(vm, path, type, status);
+- event2 = virDomainEventBlockJob2NewFromObj(vm, disk->dst, type,
+- status);
++ VIR_DEBUG("Block job for device %s (domain: %p,%s) type %d status %d",
++ diskAlias, vm, vm->def->name, type, status);
+
+- /* If we completed a block pull or commit, then update the XML
+- * to match. */
+- switch ((virConnectDomainEventBlockJobStatus) status) {
+- case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
+- if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) {
+- if (vm->newDef) {
+- int indx = virDomainDiskIndexByName(vm->newDef, disk->dst,
+- false);
+- virStorageSourcePtr copy = NULL;
++ if (VIR_ALLOC(processEvent) < 0)
++ goto error;
+
+- if (indx >= 0) {
+- persistDisk = vm->newDef->disks[indx];
+- copy = virStorageSourceCopy(disk->mirror, false);
+- if (virStorageSourceInitChainElement(copy,
+- persistDisk->src,
+- true) < 0) {
+- VIR_WARN("Unable to update persistent definition "
+- "on vm %s after block job",
+- vm->def->name);
+- virStorageSourceFree(copy);
+- copy = NULL;
+- persistDisk = NULL;
+- }
+- }
+- if (copy) {
+- virStorageSourceFree(persistDisk->src);
+- persistDisk->src = copy;
+- }
+- }
++ processEvent->eventType = QEMU_PROCESS_EVENT_BLOCK_JOB;
++ if (VIR_STRDUP(data, diskAlias) < 0)
++ goto error;
++ processEvent->data = data;
++ processEvent->vm = vm;
++ processEvent->action = type;
++ processEvent->status = status;
+
+- /* XXX We want to revoke security labels and disk
+- * lease, as well as audit that revocation, before
+- * dropping the original source. But it gets tricky
+- * if both source and mirror share common backing
+- * files (we want to only revoke the non-shared
+- * portion of the chain); so for now, we leak the
+- * access to the original. */
+- virStorageSourceFree(disk->src);
+- disk->src = disk->mirror;
+- } else {
+- virStorageSourceFree(disk->mirror);
+- }
+-
+- /* Recompute the cached backing chain to match our
+- * updates. Better would be storing the chain ourselves
+- * rather than reprobing, but we haven't quite completed
+- * that conversion to use our XML tracking. */
+- disk->mirror = NULL;
+- save = disk->mirrorState != VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+- disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+- disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+- ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk,
+- true, true));
+- break;
+-
+- case VIR_DOMAIN_BLOCK_JOB_READY:
+- disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_READY;
+- save = true;
+- break;
+-
+- case VIR_DOMAIN_BLOCK_JOB_FAILED:
+- case VIR_DOMAIN_BLOCK_JOB_CANCELED:
+- virStorageSourceFree(disk->mirror);
+- disk->mirror = NULL;
+- disk->mirrorState = status == VIR_DOMAIN_BLOCK_JOB_FAILED ?
+- VIR_DOMAIN_DISK_MIRROR_STATE_ABORT : VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+- disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+- save = true;
+- break;
+-
+- case VIR_DOMAIN_BLOCK_JOB_LAST:
+- break;
+- }
++ virObjectRef(vm);
++ if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
++ ignore_value(virObjectUnref(vm));
++ goto error;
+ }
+
+- if (save) {
+- if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
+- VIR_WARN("Unable to save status on vm %s after block job",
+- vm->def->name);
+- if (persistDisk && virDomainSaveConfig(cfg->configDir,
+- vm->newDef) < 0)
+- VIR_WARN("Unable to update persistent definition on vm %s "
+- "after block job", vm->def->name);
+- }
++ cleanup:
+ virObjectUnlock(vm);
+- virObjectUnref(cfg);
+-
+- if (event)
+- qemuDomainEventQueue(driver, event);
+- if (event2)
+- qemuDomainEventQueue(driver, event2);
+-
+ return 0;
++ error:
++ if (processEvent)
++ VIR_FREE(processEvent->data);
++ VIR_FREE(processEvent);
++ goto cleanup;
+ }
+
++
+ static int
+ qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ virDomainObjPtr vm,
+--
+2.3.3
+
--- /dev/null
+From b7cd7b195f024f75b5dbc033c49a6da4005aa665 Mon Sep 17 00:00:00 2001
+Message-Id: <b7cd7b195f024f75b5dbc033c49a6da4005aa665@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 17 Mar 2015 13:13:51 +0100
+Subject: [PATCH] qemu: process: Export qemuProcessFindDomainDiskByAlias
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1202719
+
+(cherry picked from commit 5c634730b99b53afd6e2cea4b7d2fc2dfc2ee630)
+
+Conflicts:
+ src/qemu/qemu_process.h - context with new declarations added
+ upstream
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_process.c | 2 +-
+ src/qemu/qemu_process.h | 3 +++
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
+index b9e7280..ffba29d 100644
+--- a/src/qemu/qemu_process.c
++++ b/src/qemu/qemu_process.c
+@@ -389,7 +389,7 @@ qemuProcessFindDomainDiskByPath(virDomainObjPtr vm,
+ return NULL;
+ }
+
+-static virDomainDiskDefPtr
++virDomainDiskDefPtr
+ qemuProcessFindDomainDiskByAlias(virDomainObjPtr vm,
+ const char *alias)
+ {
+diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h
+index 5948ea4..9fbf7ba 100644
+--- a/src/qemu/qemu_process.h
++++ b/src/qemu/qemu_process.h
+@@ -104,4 +104,7 @@ virBitmapPtr qemuPrepareCpumap(virQEMUDriverPtr driver,
+
+ int qemuProcessReadLog(int fd, char *buf, int buflen, int off, bool skipchar);
+
++virDomainDiskDefPtr qemuProcessFindDomainDiskByAlias(virDomainObjPtr vm,
++ const char *alias);
++
+ #endif /* __QEMU_PROCESS_H__ */
+--
+2.3.3
+
--- /dev/null
+From 1aefc4d0bab87ed9b1a531fad029cb0c24db220f Mon Sep 17 00:00:00 2001
+Message-Id: <1aefc4d0bab87ed9b1a531fad029cb0c24db220f@dist-git>
+From: Eric Blake <eblake@redhat.com>
+Date: Tue, 17 Mar 2015 15:12:49 -0600
+Subject: [PATCH] qemu: read backing chain names from qemu
+
+7.1.z: https://bugzilla.redhat.com/show_bug.cgi?id=1203119
+7.2: https://bugzilla.redhat.com/show_bug.cgi?id=1199182
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1199182 documents that
+after a series of disk snapshots into existing destination images,
+followed by active commits of the top image, it is possible for
+qemu 2.2 and earlier to end up tracking a different name for the
+image than what it would have had when opening the chain afresh.
+That is, when starting with the chain 'a <- b <- c', the name
+associated with 'b' is how it was spelled in the metadata of 'c',
+but when starting with 'a', taking two snapshots into 'a <- b <- c',
+then committing 'c' back into 'b', the name associated with 'b' is
+now the name used when taking the first snapshot.
+
+Sadly, older qemu doesn't know how to treat different spellings of
+the same filename as identical files (it uses strcmp() instead of
+checking for the same inode), which means libvirt's attempt to
+commit an image using solely the names learned from qcow2 metadata
+fails with a cryptic:
+
+error: internal error: unable to execute QEMU command 'block-commit': Top image file /tmp/images/c/../b/b not found
+
+even though the file exists. Trying to teach libvirt the rules on
+which name qemu will expect is not worth the effort (besides, we'd
+have to remember it across libvirtd restarts, and track whether a
+file was opened via metadata or via snapshot creation for a given
+qemu process); it is easier to just always directly ask qemu what
+string it expects to see in the first place.
+
+As a safety valve, we validate that any name returned by qemu
+still maps to the same local file as we have tracked it, so that
+a compromised qemu cannot accidentally cause us to act on an
+incorrect file.
+
+* src/qemu/qemu_monitor.h (qemuMonitorDiskNameLookup): New
+prototype.
+* src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskNameLookup):
+Likewise.
+* src/qemu/qemu_monitor.c (qemuMonitorDiskNameLookup): New function.
+* src/qemu/qemu_monitor_json.c (qemuMonitorJSONDiskNameLookup)
+(qemuMonitorJSONDiskNameLookupOne): Likewise.
+* src/qemu/qemu_driver.c (qemuDomainBlockCommit)
+(qemuDomainBlockJobImpl): Use it.
+
+Signed-off-by: Eric Blake <eblake@redhat.com>
+(cherry picked from commit f9ea3d60119e82c02c00fbf3678c3ed20634dea1)
+
+Conflicts:
+ src/qemu/qemu_driver.c - context with older monitor wrap semantics
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_driver.c | 28 ++++++------
+ src/qemu/qemu_monitor.c | 18 ++++++++
+ src/qemu/qemu_monitor.h | 8 +++-
+ src/qemu/qemu_monitor_json.c | 102 ++++++++++++++++++++++++++++++++++++++++++-
+ src/qemu/qemu_monitor_json.h | 9 +++-
+ 5 files changed, 148 insertions(+), 17 deletions(-)
+
+diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
+index 9dc243a..4293817 100644
+--- a/src/qemu/qemu_driver.c
++++ b/src/qemu/qemu_driver.c
+@@ -15720,9 +15720,6 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
+ goto endjob;
+
+ if (baseSource) {
+- if (qemuGetDriveSourceString(baseSource, NULL, &basePath) < 0)
+- goto endjob;
+-
+ if (flags & VIR_DOMAIN_BLOCK_REBASE_RELATIVE) {
+ if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_CHANGE_BACKING_FILE)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+@@ -15746,8 +15743,12 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
+ }
+
+ qemuDomainObjEnterMonitor(driver, vm);
+- ret = qemuMonitorBlockJob(priv->mon, device, basePath, backingPath,
+- bandwidth, info, mode, async);
++ if (baseSource)
++ basePath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src,
++ baseSource);
++ if (!baseSource || basePath)
++ ret = qemuMonitorBlockJob(priv->mon, device, basePath, backingPath,
++ bandwidth, info, mode, async);
+ qemuDomainObjExitMonitor(driver, vm);
+ if (info && info->type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT &&
+ disk->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT)
+@@ -16313,12 +16314,6 @@ qemuDomainBlockCommit(virDomainPtr dom,
+ VIR_DISK_CHAIN_READ_WRITE) < 0))
+ goto endjob;
+
+- if (qemuGetDriveSourceString(topSource, NULL, &topPath) < 0)
+- goto endjob;
+-
+- if (qemuGetDriveSourceString(baseSource, NULL, &basePath) < 0)
+- goto endjob;
+-
+ if (flags & VIR_DOMAIN_BLOCK_COMMIT_RELATIVE &&
+ topSource != disk->src) {
+ if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_CHANGE_BACKING_FILE)) {
+@@ -16349,9 +16344,14 @@ qemuDomainBlockCommit(virDomainPtr dom,
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT;
+ }
+ qemuDomainObjEnterMonitor(driver, vm);
+- ret = qemuMonitorBlockCommit(priv->mon, device,
+- topPath, basePath, backingPath,
+- bandwidth);
++ basePath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src,
++ baseSource);
++ topPath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src,
++ topSource);
++ if (basePath && topPath)
++ ret = qemuMonitorBlockCommit(priv->mon, device,
++ topPath, basePath, backingPath,
++ bandwidth);
+ qemuDomainObjExitMonitor(driver, vm);
+
+ if (ret == 0)
+diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
+index 0b1b80e..2bb6fdb 100644
+--- a/src/qemu/qemu_monitor.c
++++ b/src/qemu/qemu_monitor.c
+@@ -3464,6 +3464,24 @@ qemuMonitorSupportsActiveCommit(qemuMonitorPtr mon)
+ }
+
+
++/* Determine the name that qemu is using for tracking the backing
++ * element TARGET within the chain starting at TOP. */
++char *
++qemuMonitorDiskNameLookup(qemuMonitorPtr mon,
++ const char *device,
++ virStorageSourcePtr top,
++ virStorageSourcePtr target)
++{
++ if (!mon->json) {
++ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
++ _("JSON monitor is required"));
++ return NULL;
++ }
++
++ return qemuMonitorJSONDiskNameLookup(mon, device, top, target);
++}
++
++
+ /* Use the block-job-complete monitor command to pivot a block copy
+ * job. */
+ int
+diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
+index 8930744..df6a8c0 100644
+--- a/src/qemu/qemu_monitor.h
++++ b/src/qemu/qemu_monitor.h
+@@ -1,7 +1,7 @@
+ /*
+ * qemu_monitor.h: interaction with QEMU monitor console
+ *
+- * Copyright (C) 2006-2014 Red Hat, Inc.
++ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+@@ -739,6 +739,12 @@ int qemuMonitorBlockCommit(qemuMonitorPtr mon,
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
+ ATTRIBUTE_NONNULL(4);
+ bool qemuMonitorSupportsActiveCommit(qemuMonitorPtr mon);
++char *qemuMonitorDiskNameLookup(qemuMonitorPtr mon,
++ const char *device,
++ virStorageSourcePtr top,
++ virStorageSourcePtr target)
++ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
++ ATTRIBUTE_NONNULL(4);
+
+ int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
+ const char *cmd,
+diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
+index 9b3d17a..e58c88b 100644
+--- a/src/qemu/qemu_monitor_json.c
++++ b/src/qemu/qemu_monitor_json.c
+@@ -1,7 +1,7 @@
+ /*
+ * qemu_monitor_json.c: interaction with QEMU monitor console
+ *
+- * Copyright (C) 2006-2014 Red Hat, Inc.
++ * Copyright (C) 2006-2015 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+@@ -4221,6 +4221,106 @@ qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char *device,
+ }
+
+
++static char *
++qemuMonitorJSONDiskNameLookupOne(virJSONValuePtr image,
++ virStorageSourcePtr top,
++ virStorageSourcePtr target)
++{
++ virJSONValuePtr backing;
++ char *ret;
++
++ /* The caller will report a generic message if we return NULL
++ * without an error; but in some cases we can improve by reporting
++ * a more specific message. */
++ if (!top || !image)
++ return NULL;
++ if (top != target) {
++ backing = virJSONValueObjectGet(image, "backing-image");
++ return qemuMonitorJSONDiskNameLookupOne(backing, top->backingStore,
++ target);
++ }
++ if (VIR_STRDUP(ret, virJSONValueObjectGetString(image, "filename")) < 0)
++ return NULL;
++ /* Sanity check - the name qemu gave us should resolve to the same
++ file tracked by our target description. */
++ if (virStorageSourceIsLocalStorage(target) &&
++ STRNEQ(ret, target->path) &&
++ !virFileLinkPointsTo(ret, target->path)) {
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("qemu block name '%s' doesn't match expected '%s'"),
++ ret, target->path);
++ VIR_FREE(ret);
++ }
++ return ret;
++}
++
++
++char *
++qemuMonitorJSONDiskNameLookup(qemuMonitorPtr mon,
++ const char *device,
++ virStorageSourcePtr top,
++ virStorageSourcePtr target)
++{
++ char *ret = NULL;
++ virJSONValuePtr cmd = NULL;
++ virJSONValuePtr reply = NULL;
++ virJSONValuePtr devices;
++ size_t i;
++
++ cmd = qemuMonitorJSONMakeCommand("query-block", NULL);
++ if (!cmd)
++ return NULL;
++ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
++ goto cleanup;
++
++ devices = virJSONValueObjectGet(reply, "return");
++ if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++ _("block info reply was missing device list"));
++ goto cleanup;
++ }
++
++ for (i = 0; i < virJSONValueArraySize(devices); i++) {
++ virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
++ virJSONValuePtr inserted;
++ virJSONValuePtr image;
++ const char *thisdev;
++
++ if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++ _("block info device entry was not in expected format"));
++ goto cleanup;
++ }
++
++ if (!(thisdev = virJSONValueObjectGetString(dev, "device"))) {
++ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++ _("block info device entry was not in expected format"));
++ goto cleanup;
++ }
++
++ if (STREQ(thisdev, device)) {
++ if ((inserted = virJSONValueObjectGet(dev, "inserted")) &&
++ (image = virJSONValueObjectGet(inserted, "image"))) {
++ ret = qemuMonitorJSONDiskNameLookupOne(image, top, target);
++ }
++ break;
++ }
++ }
++ /* Guarantee an error when returning NULL, but don't override a
++ * more specific error if one was already generated. */
++ if (!ret && !virGetLastError())
++ virReportError(VIR_ERR_INTERNAL_ERROR,
++ _("unable to find backing name for device %s"),
++ device);
++
++ cleanup:
++ virJSONValueFree(cmd);
++ virJSONValueFree(reply);
++
++ return ret;
++}
++
++
+ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
+ const char *cmd_str,
+ char **reply_str,
+diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
+index ff20029..1028802 100644
+--- a/src/qemu/qemu_monitor_json.h
++++ b/src/qemu/qemu_monitor_json.h
+@@ -1,7 +1,7 @@
+ /*
+ * qemu_monitor_json.h: interaction with QEMU monitor console
+ *
+- * Copyright (C) 2006-2009, 2011-2014 Red Hat, Inc.
++ * Copyright (C) 2006-2009, 2011-2015 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+@@ -280,6 +280,13 @@ int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon,
+ unsigned long long bandwidth)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
++char *qemuMonitorJSONDiskNameLookup(qemuMonitorPtr mon,
++ const char *device,
++ virStorageSourcePtr top,
++ virStorageSourcePtr target)
++ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
++ ATTRIBUTE_NONNULL(4);
++
+ int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
+ const char *cmd_str,
+ char **reply_str,
+--
+2.3.3
+
--- /dev/null
+From c0af84c532e72d42a66059998ef7f03dcb0d6bd1 Mon Sep 17 00:00:00 2001
+Message-Id: <c0af84c532e72d42a66059998ef7f03dcb0d6bd1@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Mon, 23 Feb 2015 08:20:03 +0100
+Subject: [PATCH] qemuBuildNumaArgStr: Use memory-backend-ram more wisely
+
+RHEL-7.2: https://bugzilla.redhat.com/show_bug.cgi?id=1191567
+RHEL-7.1.z: https://bugzilla.redhat.com/show_bug.cgi?id=1194982
+
+So, when building the '-numa' command line, the qemuBuildNumaArgStr()
+function does quite a lot of checks to chose the best backend, or to
+check if one is in fact needed. However, it returned that backend is
+needed even for this little fella:
+
+ <numatune>
+ <memory mode="strict" nodeset="0,2"/>
+ </numatune>
+
+This can be guaranteed via CGroups entirely, there's no need to use
+memory-backend-ram to let qemu know where to get memory from. Well, as
+long as there's no <memnode/> element, which explicitly requires the
+backend. Long story short, we wouldn't have to care, as qemu works
+either way. However, the problem is migration (as always). Previously,
+libvirt would have started qemu with:
+
+ -numa node,memory=X
+
+in this case and restricted memory placement in CGroups. Today, libvirt
+creates more complicated command line:
+
+ -object memory-backend-ram,id=ram-node0,size=X
+ -numa node,memdev=ram-node0
+
+Again, one wouldn't find anything wrong with these two approaches.
+Both work just fine. Unless you try to migrated from the older libvirt
+into the newer one. These two approaches are, unfortunately, not
+compatible. My suggestion is, in order to allow users to migrate, lets
+use the older approach for as long as the newer one is not needed.
+
+Moreover, this partly cherry-picks
+b92a0037103efc15639dee9562866dbaffe302fb too. Especially the tests,
+which need to be fixed too. We can't mix the bare -numa and
+memory-backend-ram on the command line.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+(cherry picked from commit 7832fac84741d65e851dbdbfaf474785cbfdcf3c)
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+
+Conflicts:
+ src/qemu/qemu_command.c: The code is totally rewritten
+ upstream (esp. after memory hotplug).
+ tests/qemuxml2argvtest.c: Context, some tests were not
+ introduced yet.
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_command.c | 35 +++++++++++---------
+ .../qemuxml2argv-cputune-numatune.args | 5 +++
+ .../qemuxml2argv-cputune-numatune.xml | 37 ++++++++++++++++++++++
+ .../qemuxml2argv-hugepages-pages3.args | 3 +-
+ .../qemuxml2argv-numatune-auto-prefer.args | 5 +++
+ .../qemuxml2argv-numatune-memnode-no-memory.args | 3 +-
+ tests/qemuxml2argvtest.c | 6 ++++
+ 7 files changed, 77 insertions(+), 17 deletions(-)
+ create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.args
+ create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.xml
+ create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.args
+
+diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
+index db0c324..a9cb7a3 100644
+--- a/src/qemu/qemu_command.c
++++ b/src/qemu/qemu_command.c
+@@ -6540,22 +6540,27 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
+ char *nodemask = NULL;
+ char *mem_path = NULL;
+ int ret = -1;
++ bool useNodemask = false;
+
+- if (virDomainNumatuneHasPerNodeBinding(def->numatune) &&
+- !(virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
+- virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE))) {
+- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+- _("Per-node memory binding is not supported "
+- "with this QEMU"));
+- goto cleanup;
++ if (virDomainNumatuneHasPerNodeBinding(def->numatune)) {
++ if (!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
++ virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE))) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
++ _("Per-node memory binding is not supported "
++ "with this QEMU"));
++ goto cleanup;
++ }
++ useNodemask = true;
+ }
+
+- if (def->mem.nhugepages && def->mem.hugepages[0].size &&
+- !virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
+- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+- _("huge pages per NUMA node are not "
+- "supported with this QEMU"));
+- goto cleanup;
++ if (def->mem.nhugepages && def->mem.hugepages[0].size) {
++ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
++ _("huge pages per NUMA node are not "
++ "supported with this QEMU"));
++ goto cleanup;
++ }
++ useNodemask = true;
+ }
+
+ for (i = 0; i < def->mem.nhugepages; i++) {
+@@ -6714,7 +6719,7 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
+ virBufferAsprintf(&buf, ",policy=%s", policy);
+ }
+
+- if (hugepage || nodemask) {
++ if (useNodemask) {
+ virCommandAddArg(cmd, "-object");
+ virCommandAddArgBuffer(cmd, &buf);
+ } else {
+@@ -6739,7 +6744,7 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
+ virBufferAdd(&buf, tmpmask, -1);
+ }
+
+- if (hugepage || nodemask)
++ if (useNodemask)
+ virBufferAsprintf(&buf, ",memdev=ram-node%zu", i);
+ else
+ virBufferAsprintf(&buf, ",mem=%d", cellmem);
+diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.args b/tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.args
+new file mode 100644
+index 0000000..481f72f
+--- /dev/null
++++ b/tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.args
+@@ -0,0 +1,5 @@
++LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
++/usr/bin/qemu-system-x86_64 -S -M pc-q35-2.3 -m 128 \
++-smp 2,maxcpus=6,sockets=6,cores=1,threads=1 \
++-nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi \
++-boot c -net none -serial none -parallel none
+diff --git a/tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.xml b/tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.xml
+new file mode 100644
+index 0000000..01bbb3d
+--- /dev/null
++++ b/tests/qemuxml2argvdata/qemuxml2argv-cputune-numatune.xml
+@@ -0,0 +1,37 @@
++<domain type='kvm'>
++ <name>dummy2</name>
++ <uuid>4d92ec27-9ebf-400b-ae91-20c71c647c19</uuid>
++ <memory unit='KiB'>131072</memory>
++ <currentMemory unit='KiB'>65536</currentMemory>
++ <vcpu placement='auto' current='2'>6</vcpu>
++ <iothreads>2</iothreads>
++ <cputune>
++ <emulatorpin cpuset='1-3'/>
++ <iothreadpin iothread='1' cpuset='2'/>
++ </cputune>
++ <numatune>
++ <memory mode='strict' placement='auto'/>
++ </numatune>
++ <os>
++ <type arch='x86_64' machine='pc-q35-2.3'>hvm</type>
++ <boot dev='hd'/>
++ </os>
++ <clock offset='utc'/>
++ <on_poweroff>destroy</on_poweroff>
++ <on_reboot>restart</on_reboot>
++ <on_crash>destroy</on_crash>
++ <devices>
++ <emulator>/usr/bin/qemu-system-x86_64</emulator>
++ <controller type='sata' index='0'>
++ <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
++ </controller>
++ <controller type='pci' index='0' model='pcie-root'/>
++ <controller type='pci' index='1' model='dmi-to-pci-bridge'>
++ <address type='pci' domain='0x0000' bus='0x00' slot='0x1e' function='0x0'/>
++ </controller>
++ <controller type='pci' index='2' model='pci-bridge'>
++ <address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x0'/>
++ </controller>
++ <memballoon model='none'/>
++ </devices>
++</domain>
+diff --git a/tests/qemuxml2argvdata/qemuxml2argv-hugepages-pages3.args b/tests/qemuxml2argvdata/qemuxml2argv-hugepages-pages3.args
+index f81947e..27b3f8e 100644
+--- a/tests/qemuxml2argvdata/qemuxml2argv-hugepages-pages3.args
++++ b/tests/qemuxml2argvdata/qemuxml2argv-hugepages-pages3.args
+@@ -1,6 +1,7 @@
+ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+ /usr/bin/qemu -S -M pc -m 1024 -smp 2 \
+--numa node,nodeid=0,cpus=0,mem=256 \
++-object memory-backend-ram,size=256M,id=ram-node0 \
++-numa node,nodeid=0,cpus=0,memdev=ram-node0 \
+ -object memory-backend-file,prealloc=yes,\
+ mem-path=/dev/hugepages1G/libvirt/qemu,size=768M,id=ram-node1 \
+ -numa node,nodeid=1,cpus=1,memdev=ram-node1 \
+diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.args b/tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.args
+new file mode 100644
+index 0000000..0b1b0f5
+--- /dev/null
++++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.args
+@@ -0,0 +1,5 @@
++LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
++/usr/bin/kvm -S -M pc -m 64 -smp 1 \
++-numa node,nodeid=0,cpus=0,mem=64 \
++-nographic -monitor unix:/tmp/test-monitor,server,nowait \
++-no-acpi -boot c -usb -net none -serial none -parallel none
+diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.args b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.args
+index 2addf97..b0e274c 100644
+--- a/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.args
++++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-memnode-no-memory.args
+@@ -2,6 +2,7 @@ LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+ /usr/bin/kvm -S -M pc -m 64 -smp 2 \
+ -object memory-backend-ram,size=32M,id=ram-node0,host-nodes=3,policy=preferred \
+ -numa node,nodeid=0,cpus=0,memdev=ram-node0 \
+--numa node,nodeid=1,cpus=1,mem=32 \
++-object memory-backend-ram,size=32M,id=ram-node1 \
++-numa node,nodeid=1,cpus=1,memdev=ram-node1 \
+ -nographic -monitor unix:/tmp/test-monitor,server,nowait \
+ -no-acpi -boot c -usb -net none -serial none -parallel none
+diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
+index 98bb9ad..ee7397a 100644
+--- a/tests/qemuxml2argvtest.c
++++ b/tests/qemuxml2argvtest.c
+@@ -1246,6 +1246,10 @@ mymain(void)
+ DO_TEST("blkiotune-device", QEMU_CAPS_NAME);
+ DO_TEST("cputune", QEMU_CAPS_NAME);
+ DO_TEST("cputune-zero-shares", QEMU_CAPS_NAME);
++ DO_TEST("cputune-numatune", QEMU_CAPS_SMP_TOPOLOGY,
++ QEMU_CAPS_KVM,
++ QEMU_CAPS_OBJECT_MEMORY_RAM,
++ QEMU_CAPS_OBJECT_MEMORY_FILE);
+
+ DO_TEST("numatune-memory", NONE);
+ DO_TEST_PARSE_ERROR("numatune-memory-invalid-nodeset", NONE);
+@@ -1256,6 +1260,8 @@ mymain(void)
+ DO_TEST_FAILURE("numatune-memnode-no-memory", NONE);
+
+ DO_TEST("numatune-auto-nodeset-invalid", NONE);
++ DO_TEST("numatune-auto-prefer", QEMU_CAPS_OBJECT_MEMORY_RAM,
++ QEMU_CAPS_OBJECT_MEMORY_FILE);
+ DO_TEST_PARSE_ERROR("numatune-memnode-nocpu", NONE);
+ DO_TEST_PARSE_ERROR("numatune-memnodes-problematic", NONE);
+ DO_TEST("numad", NONE);
+--
+2.3.0
+
--- /dev/null
+From cafefa3ed050be8e81f29e4b6fa4ea30597ab8a3 Mon Sep 17 00:00:00 2001
+Message-Id: <cafefa3ed050be8e81f29e4b6fa4ea30597ab8a3@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Tue, 17 Mar 2015 13:13:48 +0100
+Subject: [PATCH] qemuProcessHandleBlockJob: Set disk->mirrorState more often
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1202719
+
+Currently, upon BLOCK_JOB_* event, disk->mirrorState is not updated
+each time. The callback code handling the events checks if a blockjob
+was started via our public APIs prior to setting the mirrorState.
+However, some block jobs may be started internally (e.g. during
+storage migration), in which case we don't bother with setting
+disk->mirror (there's nothing we can set it to anyway), or other
+fields. But it will come handy if we update the mirrorState in these
+cases too. The event wasn't delivered just for fun - we've started the
+job after all.
+
+So, in this commit, the mirrorState is set to whatever job status
+we've obtained. Of course, there are some actions on some statuses
+that we want to perform. But instead of if {} else if {} else {} ...
+enumeration, let's move to switch().
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+(cherry picked from commit c37943a0687a8fdb08e6eda8ae4b9f4f43f4f2ed)
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_process.c | 35 ++++++++++++++++++++---------------
+ 1 file changed, 20 insertions(+), 15 deletions(-)
+
+diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
+index 45bcf76..63cb198 100644
+--- a/src/qemu/qemu_process.c
++++ b/src/qemu/qemu_process.c
+@@ -1048,7 +1048,8 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+
+ /* If we completed a block pull or commit, then update the XML
+ * to match. */
+- if (status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) {
++ switch ((virConnectDomainEventBlockJobStatus) status) {
++ case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
+ if (disk->mirrorState == VIR_DOMAIN_DISK_MIRROR_STATE_PIVOT) {
+ if (vm->newDef) {
+ int indx = virDomainDiskIndexByName(vm->newDef, disk->dst,
+@@ -1098,20 +1099,24 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+ ignore_value(qemuDomainDetermineDiskChain(driver, vm, disk,
+ true, true));
+- } else if (disk->mirror &&
+- (type == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY ||
+- type == VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT)) {
+- if (status == VIR_DOMAIN_BLOCK_JOB_READY) {
+- disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_READY;
+- save = true;
+- } else if (status == VIR_DOMAIN_BLOCK_JOB_FAILED ||
+- status == VIR_DOMAIN_BLOCK_JOB_CANCELED) {
+- virStorageSourceFree(disk->mirror);
+- disk->mirror = NULL;
+- disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+- disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+- save = true;
+- }
++ break;
++
++ case VIR_DOMAIN_BLOCK_JOB_READY:
++ disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_READY;
++ save = true;
++ break;
++
++ case VIR_DOMAIN_BLOCK_JOB_FAILED:
++ case VIR_DOMAIN_BLOCK_JOB_CANCELED:
++ virStorageSourceFree(disk->mirror);
++ disk->mirror = NULL;
++ disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
++ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
++ save = true;
++ break;
++
++ case VIR_DOMAIN_BLOCK_JOB_LAST:
++ break;
+ }
+ }
+
+--
+2.3.3
+
--- /dev/null
+From 137ba6bfafefe50b85f7c099dd1472f2fe69c127 Mon Sep 17 00:00:00 2001
+Message-Id: <137ba6bfafefe50b85f7c099dd1472f2fe69c127@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Tue, 17 Mar 2015 13:13:50 +0100
+Subject: [PATCH] qemuProcessHandleBlockJob: Take status into account
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1202719
+
+Upon BLOCK_JOB_COMPLETED event delivery, we check if the job has
+completed (in qemuMonitorJSONHandleBlockJobImpl()). For better image,
+the event looks something like this:
+
+"timestamp": {"seconds": 1423582694, "microseconds": 372666}, "event":
+"BLOCK_JOB_COMPLETED", "data": {"device": "drive-virtio-disk0", "len":
+8412790784, "offset": 409993216, "speed": 8796093022207, "type":
+"mirror", "error": "No space left on device"}}
+
+If "len" does not equal "offset" it's considered an error, and we can
+clearly see "error" field filled in. However, later in the event
+processing this case was handled no differently to case of job being
+aborted via separate API. It's time that we start differentiate these
+two because of the future work.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+(cherry picked from commit 76c61cdca20c106960af033e5d0f5da70177af0f)
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/qemu/qemu_process.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
+index 63cb198..b9e7280 100644
+--- a/src/qemu/qemu_process.c
++++ b/src/qemu/qemu_process.c
+@@ -1110,7 +1110,8 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+ case VIR_DOMAIN_BLOCK_JOB_CANCELED:
+ virStorageSourceFree(disk->mirror);
+ disk->mirror = NULL;
+- disk->mirrorState = VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
++ disk->mirrorState = status == VIR_DOMAIN_BLOCK_JOB_FAILED ?
++ VIR_DOMAIN_DISK_MIRROR_STATE_ABORT : VIR_DOMAIN_DISK_MIRROR_STATE_NONE;
+ disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
+ save = true;
+ break;
+--
+2.3.3
+
--- /dev/null
+From 4841f815fcefb0a2e8715808bb4038c89e3b3889 Mon Sep 17 00:00:00 2001
+Message-Id: <4841f815fcefb0a2e8715808bb4038c89e3b3889@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Mon, 23 Feb 2015 08:20:02 +0100
+Subject: [PATCH] qemuxml2argvtest: Fake response from numad
+
+RHEL-7.2: https://bugzilla.redhat.com/show_bug.cgi?id=1191567
+RHEL-7.1.z: https://bugzilla.redhat.com/show_bug.cgi?id=1194982
+
+Well, we can pretend that we've asked numad for its suggestion and let
+qemu command line be built with that respect. Again, this alone has no
+big value, but see later commits which build on the top of this.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+(cherry picked from commit 38064806966c04d7cf7525cd78aa6f82bd09e6d0)
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+
+Conflicts:
+ tests/qemuxml2argvtest.c: Context, as f7afeddc is not
+ backported yet.
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ tests/qemuxml2argvtest.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
+index 595b658..98bb9ad 100644
+--- a/tests/qemuxml2argvtest.c
++++ b/tests/qemuxml2argvtest.c
+@@ -279,12 +279,16 @@ static int testCompareXMLToArgvFiles(const char *xml,
+ char *log = NULL;
+ virCommandPtr cmd = NULL;
+ size_t i;
++ virBitmapPtr nodeset = NULL;
+
+ if (!(conn = virGetConnect()))
+ goto out;
+ conn->secretDriver = &fakeSecretDriver;
+ conn->storageDriver = &fakeStorageDriver;
+
++ if (virBitmapParse("0-3", '\0', &nodeset, 4) < 0)
++ goto out;
++
+ if (!(vmdef = virDomainDefParseFile(xml, driver.caps, driver.xmlopt,
+ QEMU_EXPECTED_VIRT_TYPES,
+ VIR_DOMAIN_XML_INACTIVE))) {
+@@ -363,7 +367,7 @@ static int testCompareXMLToArgvFiles(const char *xml,
+ VIR_NETDEV_VPORT_PROFILE_OP_NO_OP,
+ &testCallbacks, false,
+ (flags & FLAG_FIPS),
+- NULL))) {
++ nodeset))) {
+ if (!virtTestOOMActive() &&
+ (flags & FLAG_EXPECT_FAILURE)) {
+ ret = 0;
+@@ -416,6 +420,7 @@ static int testCompareXMLToArgvFiles(const char *xml,
+ virCommandFree(cmd);
+ virDomainDefFree(vmdef);
+ virObjectUnref(conn);
++ virBitmapFree(nodeset);
+ return ret;
+ }
+
+--
+2.3.0
+
--- /dev/null
+From c0e6c7fa7cd8fb4bb4ee26552c7ab35352c47ed0 Mon Sep 17 00:00:00 2001
+Message-Id: <c0e6c7fa7cd8fb4bb4ee26552c7ab35352c47ed0@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Tue, 24 Feb 2015 10:32:21 +0100
+Subject: [PATCH] util: storage: Fix parsing of nbd:// URI without path
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1195156
+
+If a storage file would be backed with a NBD device without path
+(nbd://localhost) libvirt would crash when parsing the backing path for
+the disk as the URI structure's path element is NULL in such case but
+the NBD parser would access it shamelessly.
+
+(cherry picked from commit fdb80ed4f6563928b9942a0d1450e0c725aa6c06)
+
+ Conflicts: tests/virstoragetest.c - test case numbering refactor was
+ not backported and thus I
+ manually numbered this test as
+ 12.5
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/virstoragefile.c | 3 ++-
+ tests/virstoragetest.c | 14 ++++++++++++++
+ 2 files changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
+index 580ad20..3ab20a4 100644
+--- a/src/util/virstoragefile.c
++++ b/src/util/virstoragefile.c
+@@ -2150,7 +2150,8 @@ virStorageSourceParseBackingURI(virStorageSourcePtr src,
+ /* XXX We currently don't support auth, so don't bother parsing it */
+
+ /* possibly skip the leading slash */
+- if (VIR_STRDUP(src->path,
++ if (uri->path &&
++ VIR_STRDUP(src->path,
+ *uri->path == '/' ? uri->path + 1 : uri->path) < 0)
+ goto cleanup;
+
+diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c
+index 2601edc..fc37489 100644
+--- a/tests/virstoragetest.c
++++ b/tests/virstoragetest.c
+@@ -870,6 +870,20 @@ mymain(void)
+ (&qcow2, &nbd2), EXP_PASS,
+ (&qcow2, &nbd2), ALLOW_PROBE | EXP_PASS);
+
++ /* Rewrite qcow2 to use an nbd: protocol without path as backend */
++ virCommandFree(cmd);
++ cmd = virCommandNewArgList(qemuimg, "rebase", "-u", "-f", "qcow2",
++ "-F", "raw", "-b", "nbd://example.org",
++ "qcow2", NULL);
++ if (virCommandRun(cmd, NULL) < 0)
++ ret = -1;
++ qcow2.expBackingStoreRaw = "nbd://example.org";
++
++ nbd2.path = NULL;
++ TEST_CHAIN(12.5, absqcow2, VIR_STORAGE_FILE_QCOW2,
++ (&qcow2, &nbd2), EXP_PASS,
++ (&qcow2, &nbd2), ALLOW_PROBE | EXP_PASS);
++
+ /* qed file */
+ testFileData qed = {
+ .expBackingStoreRaw = absraw,
+--
+2.3.0
+
--- /dev/null
+From 84fd99c804b4835bc459c0dd6195c9ab4b8129f4 Mon Sep 17 00:00:00 2001
+Message-Id: <84fd99c804b4835bc459c0dd6195c9ab4b8129f4@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Wed, 4 Mar 2015 18:13:42 +0100
+Subject: [PATCH] util: storagefile: Don't crash on gluster URIs without path
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1198720
+
+Similar to commit fdb80ed4f6563928b9942a0d1450e0c725aa6c06 libvirtd
+would crash if a gluster URI without path would be used in the backing
+chain of a volume. The crash happens in the gluster specific part of the
+parser that extracts the gluster volume name from the path.
+
+Fix the crash by checking that the PATH is NULL.
+
+This patch does not contain a test case as it's not possible to test it
+with the current infrastructure as the test suite would attempt to
+contact the gluster server in the URI. I'm working on the test suite
+addition but that will be post-release material.
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1196528
+(cherry picked from commit fc56ecd73520d3a3680d6f7500944298a99f254d)
+
+Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/virstoragefile.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
+index 3ab20a4..8fa1e69 100644
+--- a/src/util/virstoragefile.c
++++ b/src/util/virstoragefile.c
+@@ -2157,6 +2157,13 @@ virStorageSourceParseBackingURI(virStorageSourcePtr src,
+
+ if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) {
+ char *tmp;
++
++ if (!src->path) {
++ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
++ _("missing volume name and path for gluster volume"));
++ goto cleanup;
++ }
++
+ if (!(tmp = strchr(src->path, '/')) ||
+ tmp == src->path) {
+ virReportError(VIR_ERR_XML_ERROR,
+--
+2.3.3
+
Summary: Library providing a simple virtualization API
Name: libvirt
Version: 1.2.8
-Release: 16%{?dist}%{?extra_release}
+Release: 16%{?dist}.2%{?extra_release}
License: LGPLv2+
Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Patch331: libvirt-qemu-Add-missing-goto-error-in-qemuRestoreCgroupState.patch
Patch332: libvirt-qemu-don-t-setup-cpuset.mems-if-memory-mode-in-numatune-is-not-strict.patch
Patch333: libvirt-lxc-don-t-setup-cpuset.mems-if-memory-mode-in-numatune-is-not-strict.patch
+Patch334: libvirt-qemuxml2argvtest-Fake-response-from-numad.patch
+Patch335: libvirt-qemuBuildNumaArgStr-Use-memory-backend-ram-more-wisely.patch
+Patch336: libvirt-util-storage-Fix-parsing-of-nbd-URI-without-path.patch
+Patch337: libvirt-Split-qemuDomainChrInsert-into-two-parts.patch
+Patch338: libvirt-hotplug-only-add-a-chardev-to-vmdef-after-monitor-call.patch
+Patch339: libvirt-blockjob-shuffle-block-rebase-code.patch
+Patch340: libvirt-blockcopy-allow-block-device-destination.patch
+Patch341: libvirt-util-storagefile-Don-t-crash-on-gluster-URIs-without-path.patch
+Patch342: libvirt-qemuProcessHandleBlockJob-Set-disk-mirrorState-more-often.patch
+Patch343: libvirt-qemuProcessHandleBlockJob-Take-status-into-account.patch
+Patch344: libvirt-qemu-process-Export-qemuProcessFindDomainDiskByAlias.patch
+Patch345: libvirt-qemu-event-Don-t-fiddle-with-disk-backing-trees-without-a-job.patch
+Patch346: libvirt-qemu-Disallow-concurrent-block-jobs-on-a-single-disk.patch
+Patch347: libvirt-qemu-block-commit-Mark-disk-in-block-jobs-only-on-successful-command.patch
+Patch348: libvirt-qemu-read-backing-chain-names-from-qemu.patch
%if %{with_libvirtd}
%doc examples/systemtap
%changelog
+* Wed Mar 18 2015 Jiri Denemark <jdenemar@redhat.com> - 1.2.8-16.el7_1.2
+- util: storagefile: Don't crash on gluster URIs without path (rhbz#1198720)
+- qemuProcessHandleBlockJob: Set disk->mirrorState more often (rhbz#1202719)
+- qemuProcessHandleBlockJob: Take status into account (rhbz#1202719)
+- qemu: process: Export qemuProcessFindDomainDiskByAlias (rhbz#1202719)
+- qemu: event: Don't fiddle with disk backing trees without a job (rhbz#1202719)
+- qemu: Disallow concurrent block jobs on a single disk (rhbz#1202719)
+- qemu: block-commit: Mark disk in block jobs only on successful command (rhbz#1202719)
+- qemu: read backing chain names from qemu (rhbz#1203119)
+
+* Wed Feb 25 2015 Jiri Denemark <jdenemar@redhat.com> - 1.2.8-16.el7_1.1
+- qemuxml2argvtest: Fake response from numad (rhbz#1194982)
+- qemuBuildNumaArgStr: Use memory-backend-ram more wisely (rhbz#1194982)
+- util: storage: Fix parsing of nbd:// URI without path (rhbz#1195156)
+- Split qemuDomainChrInsert into two parts (rhbz#1195155)
+- hotplug: only add a chardev to vmdef after monitor call (rhbz#1195155)
+- blockjob: shuffle block rebase code (rhbz#1196066)
+- blockcopy: allow block device destination (rhbz#1196066)
+
* Wed Jan 28 2015 Jiri Denemark <jdenemar@redhat.com> - 1.2.8-16
- qemu: don't setup cpuset.mems if memory mode in numatune is not 'strict' (rhbz#1186094)
- lxc: don't setup cpuset.mems if memory mode in numatune is not 'strict' (rhbz#1186094)