I do meet libvirtd crash sometime when test this patch(I also
met it when test v1 yesterday, but can not reproduce it 100%.)
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffe9d39700 (LWP 25413)]
virJSONValueObjectGetString (object=0x0, key=key@entry=0x7fffe4f72429
"filename") at util/virjson.c:1074
1074 if (object->type != VIR_JSON_TYPE_OBJECT)
(gdb) t a a bt
Thread 6 (Thread 0x7fffe9d39700 (LWP 25413)):
#0 virJSONValueObjectGetString (object=0x0,
key=key@entry=0x7fffe4f72429 "filename") at util/virjson.c:1074
#1 0x00007fffe4f2a1f4 in qemuMonitorJSONDiskNameLookupOne
(image=<optimized out>, top=0x7fffd40013b0,
target=target@entry=0x7fffd40013b0)
at qemu/qemu_monitor_json.c:3901
#2 0x00007fffe4f2a1bc in qemuMonitorJSONDiskNameLookupOne
(image=<optimized out>, top=top@entry=0x7fffdc0fc940,
target=target@entry=0x7fffd40013b0)
at qemu/qemu_monitor_json.c:3898
#3 0x00007fffe4f31800 in qemuMonitorJSONDiskNameLookup (mon=<optimized
out>, device=0x7fffd429cee0 "drive-virtio-disk0", top=0x7fffdc0fc940,
target=target@entry=0x7fffd40013b0) at qemu/qemu_monitor_json.c:3963
#4 0x00007fffe4f1f87e in qemuMonitorDiskNameLookup (mon=<optimized
out>, device=<optimized out>, top=<optimized out>,
target=target@entry=0x7fffd40013b0)
at qemu/qemu_monitor.c:3475
#5 0x00007fffe4f55775 in qemuDomainBlockCommit (dom=<optimized out>,
path=<optimized out>, base=<optimized out>, top=<optimized out>,
bandwidth=<optimized out>,
flags=<optimized out>) at qemu/qemu_driver.c:16937
#6 0x00007ffff75ff933 in virDomainBlockCommit
(dom=dom@entry=0x7fffd429d630, disk=0x7fffd40010a0 "vda", base=0x0,
top=0x0, bandwidth=0, flags=5)
at libvirt-domain.c:10218
#7 0x00005555555736fe in remoteDispatchDomainBlockCommit
(server=<optimized out>, msg=<optimized out>, args=0x7fffd429d9c0,
rerr=0x7fffe9d38cb0,
client=<optimized out>) at remote_dispatch.h:2594
#8 remoteDispatchDomainBlockCommitHelper (server=<optimized out>,
client=<optimized out>, msg=<optimized out>, rerr=0x7fffe9d38cb0,
args=0x7fffd429d9c0,
ret=<optimized out>) at remote_dispatch.h:2564
#9 0x00007ffff7653db9 in virNetServerProgramDispatchCall
(msg=0x5555557d8240, client=0x5555557ce4a0, server=0x5555557cc820,
prog=0x5555557d4a40)
at rpc/virnetserverprogram.c:437
#10 virNetServerProgramDispatch (prog=0x5555557d4a40,
server=server@entry=0x5555557cc820, client=0x5555557ce4a0,
msg=0x5555557d8240) at rpc/virnetserverprogram.c:307
#11 0x00005555555989d8 in virNetServerProcessMsg (msg=<optimized out>,
prog=<optimized out>, client=<optimized out>, srv=0x5555557cc820) at
rpc/virnetserver.c:172
#12 virNetServerHandleJob (jobOpaque=<optimized out>,
opaque=0x5555557cc820) at rpc/virnetserver.c:193
#13 0x00007ffff755ed8e in virThreadPoolWorker
(opaque=opaque@entry=0x5555557d8370) at util/virthreadpool.c:144
#14 0x00007ffff755e72e in virThreadHelper (data=<optimized out>) at
util/virthread.c:197
#15 0x00007ffff5de252a in start_thread (arg=0x7fffe9d39700) at
pthread_create.c:310
#16 0x00007ffff5b1e22d in clone () at
../sysdeps/unix/sysv/linux/x86_64/clone.S:109
On 03/13/2015 04:23 AM, Eric Blake wrote:
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(a)redhat.com>
---
v2: as suggested by Dan, add a sanity checking valve to ensure we
don't use qemu's string until vetting that it resolves to the same
local name we are already tracking
src/qemu/qemu_driver.c | 28 ++++++-------
src/qemu/qemu_monitor.c | 20 ++++++++-
src/qemu/qemu_monitor.h | 8 +++-
src/qemu/qemu_monitor_json.c | 97 +++++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_monitor_json.h | 9 +++-
5 files changed, 144 insertions(+), 18 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index b3263ac..f0e530d 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -16132,9 +16132,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",
@@ -16172,8 +16169,12 @@ qemuDomainBlockJobImpl(virDomainObjPtr vm,
}
qemuDomainObjEnterMonitor(driver, vm);
- ret = qemuMonitorBlockJob(priv->mon, device, basePath, backingPath,
- speed, mode, async);
+ if (baseSource)
+ basePath = qemuMonitorDiskNameLookup(priv->mon, device, disk->src,
+ baseSource);
+ if (!baseSource || basePath)
+ ret = qemuMonitorBlockJob(priv->mon, device, basePath, backingPath,
+ speed, mode, async);
if (qemuDomainObjExitMonitor(driver, vm) < 0)
ret = -1;
if (ret < 0) {
@@ -16903,12 +16904,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)) {
@@ -16939,9 +16934,14 @@ qemuDomainBlockCommit(virDomainPtr dom,
disk->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT;
}
qemuDomainObjEnterMonitor(driver, vm);
- ret = qemuMonitorBlockCommit(priv->mon, device,
- topPath, basePath, backingPath,
- speed);
+ 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,
+ speed);
if (qemuDomainObjExitMonitor(driver, vm) < 0) {
ret = -1;
goto endjob;
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index d869a72..cf7dc5e 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1,7 +1,7 @@
/*
* qemu_monitor.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
@@ -3458,6 +3458,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 b30da34..e67d800 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
@@ -734,6 +734,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 c16f3ca..522fd17 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
@@ -3883,6 +3883,101 @@ qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char
*device,
}
+static char *
+qemuMonitorJSONDiskNameLookupOne(virJSONValuePtr image,
+ virStorageSourcePtr top,
+ virStorageSourcePtr target)
+{
+ virJSONValuePtr backing;
+ char *ret;
+
+ if (!top)
+ 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")) == NULL) {
If dev=NULL, it will cased crash in virJSONValueObjectGetString?
const char *
virJSONValueObjectGetString(virJSONValuePtr object,
const char *key)
{
virJSONValuePtr val;
if (object->type != VIR_JSON_TYPE_OBJECT)
return NULL;
+ 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;
+ }
+ }
+ 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 8ceea8a..49392b6 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
@@ -277,6 +277,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,
--
Regards
shyu