diff --git a/SOURCES/libvirt-Split-qemuDomainChrInsert-into-two-parts.patch b/SOURCES/libvirt-Split-qemuDomainChrInsert-into-two-parts.patch
new file mode 100644
index 0000000..275ae32
--- /dev/null
+++ b/SOURCES/libvirt-Split-qemuDomainChrInsert-into-two-parts.patch
@@ -0,0 +1,185 @@
+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
+
diff --git a/SOURCES/libvirt-blockcopy-allow-block-device-destination.patch b/SOURCES/libvirt-blockcopy-allow-block-device-destination.patch
new file mode 100644
index 0000000..a6a25b7
--- /dev/null
+++ b/SOURCES/libvirt-blockcopy-allow-block-device-destination.patch
@@ -0,0 +1,234 @@
+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
+
diff --git a/SOURCES/libvirt-blockjob-shuffle-block-rebase-code.patch b/SOURCES/libvirt-blockjob-shuffle-block-rebase-code.patch
new file mode 100644
index 0000000..0071039
--- /dev/null
+++ b/SOURCES/libvirt-blockjob-shuffle-block-rebase-code.patch
@@ -0,0 +1,107 @@
+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
+
diff --git a/SOURCES/libvirt-hotplug-only-add-a-chardev-to-vmdef-after-monitor-call.patch b/SOURCES/libvirt-hotplug-only-add-a-chardev-to-vmdef-after-monitor-call.patch
new file mode 100644
index 0000000..4d1287f
--- /dev/null
+++ b/SOURCES/libvirt-hotplug-only-add-a-chardev-to-vmdef-after-monitor-call.patch
@@ -0,0 +1,103 @@
+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
+
diff --git a/SOURCES/libvirt-qemu-Disallow-concurrent-block-jobs-on-a-single-disk.patch b/SOURCES/libvirt-qemu-Disallow-concurrent-block-jobs-on-a-single-disk.patch
new file mode 100644
index 0000000..13a9f92
--- /dev/null
+++ b/SOURCES/libvirt-qemu-Disallow-concurrent-block-jobs-on-a-single-disk.patch
@@ -0,0 +1,185 @@
+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
+
diff --git a/SOURCES/libvirt-qemu-block-commit-Mark-disk-in-block-jobs-only-on-successful-command.patch b/SOURCES/libvirt-qemu-block-commit-Mark-disk-in-block-jobs-only-on-successful-command.patch
new file mode 100644
index 0000000..2f1d9e9
--- /dev/null
+++ b/SOURCES/libvirt-qemu-block-commit-Mark-disk-in-block-jobs-only-on-successful-command.patch
@@ -0,0 +1,37 @@
+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
+
diff --git a/SOURCES/libvirt-qemu-event-Don-t-fiddle-with-disk-backing-trees-without-a-job.patch b/SOURCES/libvirt-qemu-event-Don-t-fiddle-with-disk-backing-trees-without-a-job.patch
new file mode 100644
index 0000000..1daff8b
--- /dev/null
+++ b/SOURCES/libvirt-qemu-event-Don-t-fiddle-with-disk-backing-trees-without-a-job.patch
@@ -0,0 +1,363 @@
+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
+
diff --git a/SOURCES/libvirt-qemu-process-Export-qemuProcessFindDomainDiskByAlias.patch b/SOURCES/libvirt-qemu-process-Export-qemuProcessFindDomainDiskByAlias.patch
new file mode 100644
index 0000000..0d45d4d
--- /dev/null
+++ b/SOURCES/libvirt-qemu-process-Export-qemuProcessFindDomainDiskByAlias.patch
@@ -0,0 +1,48 @@
+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
+
diff --git a/SOURCES/libvirt-qemu-read-backing-chain-names-from-qemu.patch b/SOURCES/libvirt-qemu-read-backing-chain-names-from-qemu.patch
new file mode 100644
index 0000000..00ff877
--- /dev/null
+++ b/SOURCES/libvirt-qemu-read-backing-chain-names-from-qemu.patch
@@ -0,0 +1,329 @@
+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
+
diff --git a/SOURCES/libvirt-qemuBuildNumaArgStr-Use-memory-backend-ram-more-wisely.patch b/SOURCES/libvirt-qemuBuildNumaArgStr-Use-memory-backend-ram-more-wisely.patch
new file mode 100644
index 0000000..bf83133
--- /dev/null
+++ b/SOURCES/libvirt-qemuBuildNumaArgStr-Use-memory-backend-ram-more-wisely.patch
@@ -0,0 +1,248 @@
+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
+
diff --git a/SOURCES/libvirt-qemuProcessHandleBlockJob-Set-disk-mirrorState-more-often.patch b/SOURCES/libvirt-qemuProcessHandleBlockJob-Set-disk-mirrorState-more-often.patch
new file mode 100644
index 0000000..5ca957b
--- /dev/null
+++ b/SOURCES/libvirt-qemuProcessHandleBlockJob-Set-disk-mirrorState-more-often.patch
@@ -0,0 +1,86 @@
+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
+
diff --git a/SOURCES/libvirt-qemuProcessHandleBlockJob-Take-status-into-account.patch b/SOURCES/libvirt-qemuProcessHandleBlockJob-Take-status-into-account.patch
new file mode 100644
index 0000000..e619d2a
--- /dev/null
+++ b/SOURCES/libvirt-qemuProcessHandleBlockJob-Take-status-into-account.patch
@@ -0,0 +1,47 @@
+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
+
diff --git a/SOURCES/libvirt-qemuxml2argvtest-Fake-response-from-numad.patch b/SOURCES/libvirt-qemuxml2argvtest-Fake-response-from-numad.patch
new file mode 100644
index 0000000..400a421
--- /dev/null
+++ b/SOURCES/libvirt-qemuxml2argvtest-Fake-response-from-numad.patch
@@ -0,0 +1,66 @@
+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
+
diff --git a/SOURCES/libvirt-util-storage-Fix-parsing-of-nbd-URI-without-path.patch b/SOURCES/libvirt-util-storage-Fix-parsing-of-nbd-URI-without-path.patch
new file mode 100644
index 0000000..790adba
--- /dev/null
+++ b/SOURCES/libvirt-util-storage-Fix-parsing-of-nbd-URI-without-path.patch
@@ -0,0 +1,68 @@
+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
+
diff --git a/SOURCES/libvirt-util-storagefile-Don-t-crash-on-gluster-URIs-without-path.patch b/SOURCES/libvirt-util-storagefile-Don-t-crash-on-gluster-URIs-without-path.patch
new file mode 100644
index 0000000..f0a76cb
--- /dev/null
+++ b/SOURCES/libvirt-util-storagefile-Don-t-crash-on-gluster-URIs-without-path.patch
@@ -0,0 +1,49 @@
+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
+
diff --git a/SPECS/libvirt.spec b/SPECS/libvirt.spec
index 48dc0e5..1a70713 100644
--- a/SPECS/libvirt.spec
+++ b/SPECS/libvirt.spec
@@ -367,7 +367,7 @@
 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
@@ -711,6 +711,21 @@ Patch330: libvirt-Fix-vmdef-usage-after-domain-crash-in-monitor-on-device-attach
 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}
@@ -2579,6 +2594,25 @@ exit 0
 %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)