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?= +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 +Signed-off-by: Jiri Denemark +--- + 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: +From: Eric Blake +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 . 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 +(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 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 +(cherry picked from commit b7e73585a8d96677695a52bafb156f26cbd48fb5) +Signed-off-by: Jiri Denemark +--- + 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 @@ + + + +- +- ++ ++ + + +
+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 I I I [I] [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] + + Copy a disk backing image chain to I. 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 +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 +(cherry picked from commit 02d2bd7d91c200d1ea1a5b3f78c8b41720cea832) +Signed-off-by: Jiri Denemark +--- + 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?= +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 +Signed-off-by: Jiri Denemark +--- + 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 +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 +--- + 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 +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 +--- + 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 +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 +--- + 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: +From: Peter Krempa +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 +--- + 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 +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 +(cherry picked from commit f9ea3d60119e82c02c00fbf3678c3ed20634dea1) + +Conflicts: + src/qemu/qemu_driver.c - context with older monitor wrap semantics +Signed-off-by: Jiri Denemark +--- + 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: +From: Michal Privoznik +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: + + + + + +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 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 +(cherry picked from commit 7832fac84741d65e851dbdbfaf474785cbfdcf3c) +Signed-off-by: Michal Privoznik + +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 +--- + 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 @@ ++ ++ dummy2 ++ 4d92ec27-9ebf-400b-ae91-20c71c647c19 ++ 131072 ++ 65536 ++ 6 ++ 2 ++ ++ ++ ++ ++ ++ ++ ++ ++ hvm ++ ++ ++ ++ destroy ++ restart ++ destroy ++ ++ /usr/bin/qemu-system-x86_64 ++ ++
++ ++ ++ ++
++ ++ ++
++ ++ ++ ++ +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: +From: Michal Privoznik +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 +(cherry picked from commit c37943a0687a8fdb08e6eda8ae4b9f4f43f4f2ed) +Signed-off-by: Jiri Denemark +--- + 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 +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 +(cherry picked from commit 76c61cdca20c106960af033e5d0f5da70177af0f) +Signed-off-by: Jiri Denemark +--- + 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 +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 +(cherry picked from commit 38064806966c04d7cf7525cd78aa6f82bd09e6d0) +Signed-off-by: Michal Privoznik + +Conflicts: + tests/qemuxml2argvtest.c: Context, as f7afeddc is not + backported yet. +Signed-off-by: Jiri Denemark +--- + 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: +From: Peter Krempa +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 +--- + 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 +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 +--- + 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 - 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 - 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 - 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)