[PATCH 0/2] Fix problems with local pkg-config files.
by Daniel P. Berrangé
Daniel P. Berrangé (2):
src: add missing libvirt-admin.pc.in for local usage
src: fix package name in local pkg-config files
src/libvirt-admin.pc.in | 19 +++++++++++++++++++
src/libvirt-lxc.pc.in | 2 +-
src/libvirt-qemu.pc.in | 2 +-
src/meson.build | 1 +
4 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 src/libvirt-admin.pc.in
--
2.49.0
2 weeks, 1 day
[PATCH] virdevmapper: Always use device name for finding targets
by Bhavin
From: Bhavin Gandhi <bhavin192(a)geeksocket.in>
DM_TABLE_DEPS expects a device name in dm_ioctl.name. In one of the
cases, full path of the device was getting returned causing the ioctl
call to fail with `ENXIO (No such device or address)`.
Also rename the function and variable names to better reflect that we
are dealing with DM device names and not paths.
This got introduced in 22494556542c676d1b9e7f1c1f2ea13ac17e1e3e
Resolves: https://gitlab.com/libvirt/libvirt/-/issues/790
Signed-off-by: Bhavin Gandhi <bhavin192(a)geeksocket.in>
---
src/util/virdevmapper.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c
index d0eae671ab..42c86d89cf 100644
--- a/src/util/virdevmapper.c
+++ b/src/util/virdevmapper.c
@@ -164,7 +164,7 @@ virDMOpen(void)
static char *
-virDMSanitizepath(const char *path)
+virDMGetDeviceName(const char *path)
{
g_autofree char *dmDirPath = NULL;
struct dirent *ent = NULL;
@@ -205,7 +205,7 @@ virDMSanitizepath(const char *path)
if (stat(tmp, &sb[1]) == 0 &&
sb[0].st_rdev == sb[1].st_rdev) {
- return g_steal_pointer(&tmp);
+ return g_strdup(ent->d_name);
}
}
@@ -219,7 +219,7 @@ virDevMapperGetTargetsImpl(int controlFD,
GSList **devPaths,
unsigned int ttl)
{
- g_autofree char *sanitizedPath = NULL;
+ g_autofree char *deviceName = NULL;
g_autofree char *buf = NULL;
struct dm_ioctl dm = { 0 };
struct dm_target_deps *deps = NULL;
@@ -233,10 +233,10 @@ virDevMapperGetTargetsImpl(int controlFD,
if (!virIsDevMapperDevice(path))
return 0;
- if (!(sanitizedPath = virDMSanitizepath(path)))
+ if (!(deviceName = virDMGetDeviceName(path)))
return 0;
- if (virStrcpy(dm.name, sanitizedPath, DM_NAME_LEN) < 0) {
+ if (virStrcpy(dm.name, deviceName, DM_NAME_LEN) < 0) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("Resolved device mapper name too long"));
return -1;
--
2.49.0
2 weeks, 1 day
[PATCH v2] qemu: passt: add support for custom command line arguments
by Enrique Llorente
This adds support for custom command line arguments for the passt
backend, similar to qemu:commandline. The feature allows passing
additional arguments to the passt process for development and testing
purposes.
The implementation:
- Adds a passt XML namespace for custom arguments
- Properly taints the domain when custom args are used
- Includes comprehensive test coverage
- Adds documentation for the new feature
Usage example:
<interface type='user'>
<backend type='passt' xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
<passt:commandline>
<passt:arg value='--debug'/>
<passt:arg value='--verbose'/>
</passt:commandline>
</backend>
</interface>
Signed-off-by: Enrique Llorente <ellorent(a)redhat.com>
---
docs/formatdomain.rst | 40 +++++++++
src/conf/domain_conf.c | 84 ++++++++++++++++++-
src/conf/domain_conf.h | 6 ++
src/qemu/qemu_passt.c | 9 ++
...-user-passt-custom-args.x86_64-latest.args | 39 +++++++++
...t-user-passt-custom-args.x86_64-latest.xml | 57 +++++++++++++
.../net-user-passt-custom-args.xml | 52 ++++++++++++
tests/qemuxmlconftest.c | 1 +
8 files changed, 287 insertions(+), 1 deletion(-)
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 9a2f065590..59899aaa4a 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -5464,6 +5464,46 @@ ports **with the exception of some subset**.
</devices>
...
+Custom passt commandline arguments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+:since:`Since 11.7.0` For development and testing purposes, it is
+sometimes useful to be able to pass additional command-line arguments
+directly to the passt process. This can be accomplished using a
+special passt namespace in the domain XML that is similar to the qemu
+commandline namespace:
+
+::
+
+ ...
+ <devices>
+ ...
+ <interface type='user'>
+ <backend type='passt' xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:commandline>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ </passt:commandline>
+ </backend>
+ </interface>
+ </devices>
+ ...
+
+Any arguments provided using this method will be appended to the passt
+command line, and will therefore override any default options set by
+libvirt in the case of conflicts.
+
+**This feature is intended for development and testing only.**
+Arguments are used as specified in the XML, and no validation beyond
+basic libvirt XML schema validation is performed. It is expected that
+this feature will primarily be used for testing new passt options, so
+that they can be evaluated for inclusion in libvirt's schema in a
+future release. It should not be used as a substitute for configuring
+passt with the proper libvirt XML elements and attributes. **When a
+domain uses this feature, it will be marked as "tainted" in the logs and
+the ``virsh dominfo`` output will include "custom-argv" in the list
+of reasons.**
+
Generic ethernet connection
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 1e24e41a48..9d39bb39bd 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2918,6 +2918,10 @@ virDomainNetDefFree(virDomainNetDef *def)
g_free(def->backend.tap);
g_free(def->backend.vhost);
g_free(def->backend.logFile);
+ if (def->backend.passtCommandline) {
+ g_strfreev(def->backend.passtCommandline->args);
+ g_free(def->backend.passtCommandline);
+ }
virDomainNetTeamingInfoFree(def->teaming);
g_free(def->virtPortProfile);
g_free(def->script);
@@ -9772,6 +9776,7 @@ virDomainNetBackendParseXML(xmlNodePtr node,
{
g_autofree char *tap = virXMLPropString(node, "tap");
g_autofree char *vhost = virXMLPropString(node, "vhost");
+ xmlNodePtr cur;
/* In the case of NET_TYPE_USER, backend type can be unspecified
* (i.e. VIR_DOMAIN_NET_BACKEND_DEFAULT) and that means 'use
@@ -9808,6 +9813,40 @@ virDomainNetBackendParseXML(xmlNodePtr node,
def->backend.vhost = virFileSanitizePath(vhost);
}
+ /* Parse passt namespace commandline */
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (cur->ns &&
+ STREQ((const char *)cur->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") &&
+ STREQ((const char *)cur->name, "commandline")) {
+ xmlNodePtr arg_node = cur->children;
+ GPtrArray *args = g_ptr_array_new();
+
+ while (arg_node != NULL) {
+ if (arg_node->type == XML_ELEMENT_NODE &&
+ arg_node->ns &&
+ STREQ((const char *)arg_node->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") &&
+ STREQ((const char *)arg_node->name, "arg")) {
+ g_autofree char *value = virXMLPropString(arg_node, "value");
+ if (value)
+ g_ptr_array_add(args, g_strdup(value));
+ }
+ arg_node = arg_node->next;
+ }
+
+ if (args->len > 0) {
+ def->backend.passtCommandline = g_new0(virDomainNetBackendPasstCommandline, 1);
+ g_ptr_array_add(args, NULL); /* NULL-terminate */
+ def->backend.passtCommandline->args = (char **)g_ptr_array_free(args, FALSE);
+ } else {
+ g_ptr_array_unref(args);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+
return 0;
}
@@ -20802,6 +20841,33 @@ virDomainNetBackendIsEqual(virDomainNetBackend *src,
STRNEQ_NULLABLE(src->logFile, dst->logFile)) {
return false;
}
+
+ /* Compare passt commandline */
+ if ((src->passtCommandline && dst->passtCommandline) ||
+ (!src->passtCommandline && !dst->passtCommandline)) {
+ if (src->passtCommandline && dst->passtCommandline) {
+ /* Compare argument arrays */
+ char **srcArgs = src->passtCommandline->args;
+ char **dstArgs = dst->passtCommandline->args;
+
+ if (!srcArgs && !dstArgs) {
+ /* Both are NULL, continue */
+ } else if (!srcArgs || !dstArgs) {
+ return false; /* One is NULL, other is not */
+ } else {
+ /* Compare each argument */
+ int i = 0;
+ while (srcArgs[i] || dstArgs[i]) {
+ if (!srcArgs[i] || !dstArgs[i] || STRNEQ(srcArgs[i], dstArgs[i]))
+ return false;
+ i++;
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
return true;
}
@@ -24926,6 +24992,7 @@ virDomainNetBackendFormat(virBuffer *buf,
virDomainNetBackend *backend)
{
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
if (backend->type) {
virBufferAsprintf(&attrBuf, " type='%s'",
@@ -24934,7 +25001,22 @@ virDomainNetBackendFormat(virBuffer *buf,
virBufferEscapeString(&attrBuf, " tap='%s'", backend->tap);
virBufferEscapeString(&attrBuf, " vhost='%s'", backend->vhost);
virBufferEscapeString(&attrBuf, " logFile='%s'", backend->logFile);
- virXMLFormatElement(buf, "backend", &attrBuf, NULL);
+
+ /* Format passt commandline with namespace */
+ if (backend->passtCommandline && backend->passtCommandline->args) {
+ virBufferAddLit(&childBuf, "<passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>\n");
+ virBufferAdjustIndent(&childBuf, 2);
+
+ for (int i = 0; backend->passtCommandline->args[i]; i++) {
+ virBufferAsprintf(&childBuf, "<passt:arg value='%s'/>\n",
+ backend->passtCommandline->args[i]);
+ }
+
+ virBufferAdjustIndent(&childBuf, -2);
+ virBufferAddLit(&childBuf, "</passt:commandline>\n");
+ }
+
+ virXMLFormatElement(buf, "backend", &attrBuf, &childBuf);
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6997cf7c09..1f51bad546 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1070,12 +1070,18 @@ struct _virDomainActualNetDef {
unsigned int class_id; /* class ID for bandwidth 'floor' */
};
+typedef struct _virDomainNetBackendPasstCommandline virDomainNetBackendPasstCommandline;
+struct _virDomainNetBackendPasstCommandline {
+ char **args; /* NULL-terminated array of arguments */
+};
+
struct _virDomainNetBackend {
virDomainNetBackendType type;
char *tap;
char *vhost;
/* The following are currently only valid/used when backend type='passt' */
char *logFile; /* path to logfile used by passt process */
+ virDomainNetBackendPasstCommandline *passtCommandline; /* for passt overrides */
};
struct _virDomainNetPortForwardRange {
diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c
index fcc34de384..e64ccfa5aa 100644
--- a/src/qemu/qemu_passt.c
+++ b/src/qemu/qemu_passt.c
@@ -317,6 +317,15 @@ qemuPasstStart(virDomainObj *vm,
virCommandAddArg(cmd, virBufferCurrentContent(&buf));
}
+ /* Add custom passt arguments from namespace */
+ if (net->backend.passtCommandline && net->backend.passtCommandline->args) {
+ for (i = 0; net->backend.passtCommandline->args[i]; i++) {
+ virCommandAddArg(cmd, net->backend.passtCommandline->args[i]);
+ }
+
+ /* Taint the domain when using custom passt arguments */
+ qemuDomainObjTaint(driver, vm, VIR_DOMAIN_TAINT_CUSTOM_ARGV, NULL);
+ }
if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0)
return -1;
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
new file mode 100644
index 0000000000..1715d4253c
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
@@ -0,0 +1,39 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-i386 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
+-accel tcg \
+-cpu qemu32 \
+-m size=219136k \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \
+-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"drive-ide0-0-0","id":"ide0-0-0","bootindex":1}' \
+-netdev '{"type":"stream","addr":{"type":"unix","path":"/var/run/libvirt/qemu/passt/-1-QEMUGuest1-net0.socket"},"server":false,"reconnect-ms":5000,"id":"hostnet0"}' \
+-device '{"driver":"rtl8139","netdev":"hostnet0","id":"net0","mac":"52:54:00:12:34:56","bus":"pci.0","addr":"0x3"}' \
+-vnc 127.0.0.1:0 \
+-device '{"driver":"cirrus-vga","id":"video0","bus":"pci.0","addr":"0x2"}' \
+-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x4"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
new file mode 100644
index 0000000000..310ff6c39e
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
@@ -0,0 +1,57 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <cpu mode='custom' match='exact' check='none'>
+ <model fallback='forbid'>qemu32</model>
+ </cpu>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-i386</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='piix3-uhci'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <mac address='52:54:00:12:34:56'/>
+ <model type='rtl8139'/>
+ <backend type='passt' logFile='/tmp/passt.log'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--no-dhcp'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <video>
+ <model type='cirrus' vram='16384' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </memballoon>
+ <audio id='1' type='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
new file mode 100644
index 0000000000..15eca80b5e
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
@@ -0,0 +1,52 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-i386</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='piix3-uhci'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <mac address='52:54:00:12:34:56'/>
+ <backend type='passt' logFile='/tmp/passt.log'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--no-dhcp'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <video>
+ <model type='cirrus' vram='16384' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
\ No newline at end of file
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
index 9fba984290..839ae49ed4 100644
--- a/tests/qemuxmlconftest.c
+++ b/tests/qemuxmlconftest.c
@@ -1805,6 +1805,7 @@ mymain(void)
DO_TEST_CAPS_LATEST("net-user-addr");
DO_TEST_CAPS_LATEST("net-user-passt");
DO_TEST_CAPS_VER("net-user-passt", "7.2.0");
+ DO_TEST_CAPS_LATEST("net-user-passt-custom-args");
DO_TEST_CAPS_LATEST_PARSE_ERROR("net-user-slirp-portforward");
DO_TEST_CAPS_LATEST("net-vhostuser-passt");
DO_TEST_CAPS_LATEST_PARSE_ERROR("net-vhostuser-passt-no-shmem");
--
2.50.0
2 weeks, 1 day
[PATCH v3 00/21] LIBVIRT: X86: TDX support
by Zhenzhong Duan
Hi,
This series brings libvirt the x86 TDX support.
* What's TDX?
TDX stands for Trust Domain Extensions which isolates VMs from
the virtual-machine manager (VMM)/hypervisor and any other software on
the platform.
This patchset extends libvirt to support TDX, with which one can start a TDX
guest from high level rather than running qemu directly.
* Misc
As QEMU use a software emulated way to reset guest which isn't supported by TDX
guest for security reason. We simulate reboot for TDX guest by kill and create a
new one in FakeReboot framework.
Complete code can be found at [1].
* Test
Tested with upstream qemu v10.0.0-1724-gf9a3def17b
shutdown/reboot/reset with virsh
shutdown/reboot trigger in guest
shutdown with on_poweroff=destroy/restart
reboot with on_reboot=destroy/restart
* Patch organization
- patch 1-4: Some preparing work
- patch 5-6: Support query of TDX capabilities
- patch 7-13: Add TDX type to launchsecurity framework
- patch 14-19: Add reboot/reset support to TDX guest
- patch 20: Add conf test dump/cases for '+inteltdx' variant
- patch 21: Add docs
TODO:
- add reconnect logic in virsh command
[1] https://github.com/intel/libvirt-tdx/commits/tdx_for_upstream_v3
Thanks
Zhenzhong
Changelog:
v3:
- fix a hiden failure in qemuBuildTDXQGSCommandLine() (Peter Krempa)
- avoid the use of the ternary operator (Peter Krempa)
- add capability test dump before capability introduced (Peter Krempa)
- change tests version number from 11.0.0 to 10.1.0 (Peter Krempa)
v2:
- add capability and xmlconf test (Peter Krempa)
v1:
- s/virQEMUCapsKVMSupportsSecureGuestINTEL/virQEMUCapsKVMSupportsSecureGuestTDX (Daniel)
- make policy element optional and expose to QEMU directly (Daniel)
- s/qemuProcessSecFakeReboot/qemuProcessFakeRebootViaRecreate (Daniel)
- simplify QGS element schema by supporting only UNIX socket (Daniel)
- add new events VIR_DOMAIN_EVENT_[STOPPED|STARTED] for control plane (Daniel)
- s/quoteGenerationService/quoteGenerationSocket as QEMU
- add virsh reset support
rfcv4:
- add a check to tools/virt-host-validate-qemu.c (Daniel)
- remove check of q35 (Daniel)
- model 'SocktetAddress' QAPI in xml schema (Daniel)
- s/Quote-Generation-Service/quoteGenerationService/ (Daniel)
- define bits in tdx->policy and add validating logic (Daniel)
- presume QEMU choose split kernel irqchip for TDX guest by default (Daniel)
- utilize existing FakeReboot framework to do reboot for TDX guest (Daniel)
- drop patch11 'conf: Add support to keep same domid for hard reboot' (Daniel)
- add test in tests/ to validate parsing and formatting logic (Daniel)
- add doc in docs/formatdomain.rst (Daniel)
- add R-B
rfcv3:
- Change to generate qemu cmdline with -bios
- drop firmware auto match as -bios is used
- add a hard reboot method to reboot TDX guest
rfcv3: https://www.mail-archive.com/devel@lists.libvirt.org/msg00385.html
rfcv2:
- give up using qmp cmd and check TDX directly on host for TDX capabilities.
- use launchsecurity framework to support TDX
- use <os>.<loader> for general loader
- add auto firmware match feature for TDX
A example TDVF fimware description file 70-edk2-x86_64-tdx.json:
{
"description": "UEFI firmware for x86_64, supporting Intel TDX",
"interface-types": [
"uefi"
],
"mapping": {
"device": "generic",
"filename": "/usr/share/OVMF/OVMF_CODE-tdx.fd"
},
"targets": [
{
"architecture": "x86_64",
"machines": [
"pc-q35-*"
]
}
],
"features": [
"intel-tdx",
"verbose-dynamic"
],
"tags": [
]
}
rfcv2: https://www.mail-archive.com/libvir-list@redhat.com/msg219378.html
Zhenzhong Duan (21):
tools: Secure guest check for Intel in virt-host-validate
qemu: Check if INTEL Trust Domain Extention support is enabled
qemucapabilitiesdata: Document '+inteltdx' variant
qemucapabilitiestest: Add data for the qemu-10.1.0 dev cycle on x86_64
for the '+inteltdx' variant
qemu: Add TDX capability
conf: Expose TDX feature in domain capabilities
conf: Add tdx as launch security type
conf: Validate TDX launchSecurity element
mrConfigId/mrOwner/mrOwnerConfig
qemu: Add command line and validation for TDX type
conf: Expose TDX type in domain launch security capability
qemu: Force special parameters enabled for TDX guest
conf: Add Intel TDX Quote Generation Service(QGS) support
qemu: Add command line for TDX Quote Generation Service(QGS)
qemu: Add FakeReboot support for TDX guest
qemu: Support reboot command in guest
qemu: Avoid duplicate FakeReboot for secure guest
qemu: Send event VIR_DOMAIN_EVENT_[STOPPED|STARTED] during recreation
qemu: Bypass sending VIR_DOMAIN_EVENT_RESUMED event when TD VM reboot
qemu: Support domain reset command for TDX guest
qemuxmlconftest: Add latest version of 'launch-security-tdx*' test
data
docs: domain: Add documentation for Intel TDX guest
docs/formatdomain.rst | 63 +
docs/formatdomaincaps.rst | 1 +
examples/c/misc/event-test.c | 6 +
include/libvirt/libvirt-domain.h | 2 +
src/conf/domain_capabilities.c | 1 +
src/conf/domain_capabilities.h | 1 +
src/conf/domain_conf.c | 82 +
src/conf/domain_conf.h | 21 +
src/conf/domain_validate.c | 11 +
src/conf/schemas/domaincaps.rng | 9 +
src/conf/schemas/domaincommon.rng | 41 +
src/conf/virconftypes.h | 2 +
src/qemu/qemu_capabilities.c | 38 +-
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_cgroup.c | 1 +
src/qemu/qemu_command.c | 43 +
src/qemu/qemu_domain.h | 1 +
src/qemu/qemu_driver.c | 11 +-
src/qemu/qemu_firmware.c | 1 +
src/qemu/qemu_monitor.c | 34 +-
src/qemu/qemu_monitor.h | 2 +-
src/qemu/qemu_monitor_json.c | 6 +-
src/qemu/qemu_namespace.c | 1 +
src/qemu/qemu_process.c | 104 +-
src/qemu/qemu_process.h | 2 +
src/qemu/qemu_validate.c | 45 +
src/security/security_dac.c | 2 +
.../qemu_10.1.0-q35.x86_64+inteltdx.xml | 783 +
.../qemu_10.1.0-tcg.x86_64+inteltdx.xml | 1830 +
.../qemu_10.1.0.x86_64+inteltdx.xml | 783 +
tests/domaincapsmock.c | 3 +-
tests/qemucapabilitiesdata/README.rst | 5 +
.../caps_10.1.0_x86_64+inteltdx.replies | 44552 ++++++++++++++++
.../caps_10.1.0_x86_64+inteltdx.xml | 3585 ++
.../caps.x86_64+inteltdx.xml | 29 +
...h-security-tdx.x86_64-latest+inteltdx.args | 44 +
...ch-security-tdx.x86_64-latest+inteltdx.xml | 74 +
tests/qemuxmlconfdata/launch-security-tdx.xml | 27 +
tests/qemuxmlconftest.c | 3 +
tools/virsh-domain-event.c | 6 +-
tools/virt-host-validate-common.c | 31 +-
tools/virt-host-validate-common.h | 1 +
42 files changed, 52273 insertions(+), 15 deletions(-)
create mode 100644 tests/domaincapsdata/qemu_10.1.0-q35.x86_64+inteltdx.xml
create mode 100644 tests/domaincapsdata/qemu_10.1.0-tcg.x86_64+inteltdx.xml
create mode 100644 tests/domaincapsdata/qemu_10.1.0.x86_64+inteltdx.xml
create mode 100644 tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.replies
create mode 100644 tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml
create mode 100644 tests/qemucaps2xmloutdata/caps.x86_64+inteltdx.xml
create mode 100644 tests/qemuxmlconfdata/launch-security-tdx.x86_64-latest+inteltdx.args
create mode 100644 tests/qemuxmlconfdata/launch-security-tdx.x86_64-latest+inteltdx.xml
create mode 100644 tests/qemuxmlconfdata/launch-security-tdx.xml
--
2.34.1
2 weeks, 2 days
[PATCH] nwfilter: Avoid firewall hole during VM startup by checking rule presence
by Dion Bosschieter
Upon VM bootstrapping (start,restore,incoming migration)
iptablesCreateBaseChainsFW is called and unconditionally deletes and
reinserts top-level firewall chain jumps (e.g. INPUT, FORWARD rules).
This briefly opens a hole in the firewall, allowing packets through
until the insertions complete.
This commit ensures that the base chains are only created once per layer
(IPV4/IPV6) and checks whether the expected rules already exist using
`iptables -C`. If they do, no delete/insert operations are performed.
This eliminates the short window where packets could bypass filters during
VM lifecycle operations.
Signed-off-by: Dion Bosschieter <dionbosschieter(a)gmail.com>
---
src/nwfilter/nwfilter_ebiptables_driver.c | 79 ++++++++++++++---------
1 file changed, 47 insertions(+), 32 deletions(-)
diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c
index 067df6e612..42a0133159 100644
--- a/src/nwfilter/nwfilter_ebiptables_driver.c
+++ b/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -131,6 +131,14 @@ static char chainprefixes_host_temp[3] = {
0
};
+typedef struct {
+ const char *chain;
+ const char *position;
+ const char *targetChain;
+} iptablesBaseChainFW;
+
+static bool baseChainFWDefined[VIR_FIREWALL_LAYER_LAST] = { false };
+
static int
printVar(virNWFilterVarCombIter *vars,
char *buf, int bufsize,
@@ -403,38 +411,45 @@ static void
iptablesCreateBaseChainsFW(virFirewall *fw,
virFirewallLayer layer)
{
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", VIRT_IN_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", VIRT_OUT_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", VIRT_IN_POST_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", HOST_IN_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "FORWARD", "-j", VIRT_IN_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "FORWARD", "-j", VIRT_OUT_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "FORWARD", "-j", VIRT_IN_POST_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "INPUT", "-j", HOST_IN_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "FORWARD", "1", "-j", VIRT_IN_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "FORWARD", "2", "-j", VIRT_OUT_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "FORWARD", "3", "-j", VIRT_IN_POST_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "INPUT", "1", "-j", HOST_IN_CHAIN, NULL);
+ iptablesBaseChainFW fw_chains[] = {
+ {"FORWARD", "1", VIRT_IN_CHAIN},
+ {"FORWARD", "2", VIRT_OUT_CHAIN},
+ {"FORWARD", "3", VIRT_IN_POST_CHAIN},
+ {"INPUT", "1", HOST_IN_CHAIN},
+ };
+ size_t i;
+
+ // iptablesCreateBaseChainsFW already ran once for this layer,
+ // we don't have to recreate the base chains on every firewall update
+ if (baseChainFWDefined[layer])
+ return;
+
+ // set defined state so we skip the following logic next run
+ baseChainFWDefined[layer] = true;
+
+ virFirewallStartTransaction(fw, 0);
+
+ for (i = 0; i < G_N_ELEMENTS(fw_chains); i++)
+ virFirewallAddCmd(fw, layer,
+ "-C", fw_chains[i].chain,
+ "-j", fw_chains[i].targetChain, NULL);
+
+ if (virFirewallApply(fw) == 0)
+ // rules already in place
+ return;
+
+ for (i = 0; i < G_N_ELEMENTS(fw_chains); i++) {
+ virFirewallAddCmdFull(fw, layer,
+ true, NULL, NULL,
+ "-N", fw_chains[i].targetChain, NULL);
+ virFirewallAddCmdFull(fw, layer,
+ true, NULL, NULL,
+ "-D", fw_chains[i].chain, "-j",
+ fw_chains[i].targetChain, NULL);
+ virFirewallAddCmd(fw, layer,
+ "-I", fw_chains[i].chain, fw_chains[i].position,
+ "-j", fw_chains[i].targetChain, NULL);
+ }
}
--
2.39.3 (Apple Git-146)
2 weeks, 3 days
[PATCH 0/4] Allow xml-configured coredump format on VM crash
by Nikolai Barybin
When libvirt processes VM crash event it always dumps core in raw
format.
This series makes it possible to configure dump format via domain xml.
This would be especcialy helpful for Windows guests, because it requires
a lot effort to convert raw dump into wingdb.
Nikolai Barybin (4):
conf: schemas: add coredump_format element to events section
src: conf: add parsing/formatting for 'coredump_format' value
qemu: use configurable dump format in doCoreDumpToAutoDumpPath()
docs: formatdomain: document 'coredump_format' element
docs/formatdomain.rst | 9 +++++
src/conf/domain_conf.c | 64 +++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 2 +
src/conf/schemas/domaincommon.rng | 19 +++++++++
src/libvirt_private.syms | 2 +
src/qemu/qemu_driver.c | 2 +-
6 files changed, 97 insertions(+), 1 deletion(-)
--
2.43.5
2 weeks, 3 days
[PATCH] qemu: Switch to virtio-scsi on ARM
by Jim Fehlig
From: Jim Fehlig <jfehlig(a)suse.com>
Similar to x86, the default SCSI controller model for ARM is lsilogic.
But unlike x86, the ARM virt machine type prefers virtio devices. Switch
the default controller model for ARM from lsilogic to virtio-scsi.
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
IMO, the lsilogic SCSI controller is a poor default for the ARM virt machine
type. One could argue modern operating systems are more likely to contain a
functional virtio-scsi driver than an LSI one. However, I do understand this
change could break existing ARM VM configurations containing a SCSI
controller without a model specification. One could also argue the pain
inflicted is tolerable :-).
The test churn is interesting. I haven't yet investigated if there's an
underlying bug, or if it's a consequence of libvirt's processing of
controllers. Much appreciated if anyone has an explanation handy :-).
src/qemu/qemu_domain.c | 3 ++-
...ault-models.aarch64-latest.abi-update.args | 13 +++++------
...fault-models.aarch64-latest.abi-update.xml | 22 ++++++++-----------
...64-virt-default-models.aarch64-latest.args | 13 +++++------
...h64-virt-default-models.aarch64-latest.xml | 22 ++++++++-----------
5 files changed, 32 insertions(+), 41 deletions(-)
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 0d2548d8d4..499db0ad78 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -4252,7 +4252,8 @@ qemuDomainGetSCSIControllerModel(const virDomainDef *def,
if (qemuDomainIsPSeries(def))
return VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI;
- if (ARCH_IS_S390(def->os.arch) || qemuDomainIsLoongArchVirt(def))
+ if (ARCH_IS_ARM(def->os.arch) || ARCH_IS_S390(def->os.arch) ||
+ qemuDomainIsLoongArchVirt(def))
return VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI))
return VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC;
diff --git a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.args b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.args
index 96fb251d80..ff86567c59 100644
--- a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.args
+++ b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.args
@@ -29,20 +29,19 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \
-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \
-device '{"driver":"pcie-root-port","port":9,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x1.0x1"}' \
-device '{"driver":"pcie-root-port","port":10,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x2"}' \
--device '{"driver":"pcie-pci-bridge","id":"pci.4","bus":"pci.1","addr":"0x0"}' \
--device '{"driver":"pcie-root-port","port":11,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x1.0x3"}' \
--device '{"driver":"pcie-root-port","port":12,"chassis":6,"id":"pci.6","bus":"pcie.0","addr":"0x1.0x4"}' \
--device '{"driver":"qemu-xhci","id":"usb","bus":"pci.3","addr":"0x0"}' \
--device '{"driver":"lsi","id":"scsi0","bus":"pci.4","addr":"0x1"}' \
+-device '{"driver":"pcie-root-port","port":11,"chassis":4,"id":"pci.4","bus":"pcie.0","addr":"0x1.0x3"}' \
+-device '{"driver":"pcie-root-port","port":12,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x1.0x4"}' \
+-device '{"driver":"qemu-xhci","id":"usb","bus":"pci.2","addr":"0x0"}' \
+-device '{"driver":"virtio-scsi-pci","id":"scsi0","bus":"pci.3","addr":"0x0"}' \
-netdev '{"type":"user","id":"hostnet0"}' \
--device '{"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:09:a4:37","bus":"pci.2","addr":"0x0"}' \
+-device '{"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:09:a4:37","bus":"pci.1","addr":"0x0"}' \
-chardev pty,id=charserial0 \
-serial chardev:charserial0 \
-chardev socket,id=chrtpm,path=/dev/test \
-tpmdev emulator,id=tpm-tpm0,chardev=chrtpm \
-device '{"driver":"tpm-tis-device","tpmdev":"tpm-tpm0","id":"tpm0"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
--device '{"driver":"virtio-gpu-pci","id":"video0","max_outputs":1,"bus":"pci.5","addr":"0x0"}' \
+-device '{"driver":"virtio-gpu-pci","id":"video0","max_outputs":1,"bus":"pci.4","addr":"0x0"}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-device '{"driver":"pvpanic-pci","bus":"pcie.0","addr":"0x2"}' \
-msg timestamp=on
diff --git a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.xml b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.xml
index f27e7e1522..5abf55cf36 100644
--- a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.xml
+++ b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.abi-update.xml
@@ -21,11 +21,11 @@
<devices>
<emulator>/usr/bin/qemu-system-aarch64</emulator>
<controller type='usb' index='0' model='qemu-xhci'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='scsi' index='0' model='virtio-scsi'>
<address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
</controller>
- <controller type='scsi' index='0' model='lsilogic'>
- <address type='pci' domain='0x0000' bus='0x04' slot='0x01' function='0x0'/>
- </controller>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
@@ -42,24 +42,20 @@
<target chassis='3' port='0xa'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
- <controller type='pci' index='4' model='pcie-to-pci-bridge'>
- <model name='pcie-pci-bridge'/>
- <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
- </controller>
- <controller type='pci' index='5' model='pcie-root-port'>
+ <controller type='pci' index='4' model='pcie-root-port'>
<model name='pcie-root-port'/>
- <target chassis='5' port='0xb'/>
+ <target chassis='4' port='0xb'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x3'/>
</controller>
- <controller type='pci' index='6' model='pcie-root-port'>
+ <controller type='pci' index='5' model='pcie-root-port'>
<model name='pcie-root-port'/>
- <target chassis='6' port='0xc'/>
+ <target chassis='5' port='0xc'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x4'/>
</controller>
<interface type='user'>
<mac address='52:54:00:09:a4:37'/>
<model type='virtio'/>
- <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
<serial type='pty'>
<target type='system-serial' port='0'>
@@ -75,7 +71,7 @@
<audio id='1' type='none'/>
<video>
<model type='virtio' heads='1' primary='yes'/>
- <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</video>
<memballoon model='none'/>
<panic model='pvpanic'>
diff --git a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.args b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.args
index 96fb251d80..ff86567c59 100644
--- a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.args
+++ b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.args
@@ -29,20 +29,19 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \
-device '{"driver":"pcie-root-port","port":8,"chassis":1,"id":"pci.1","bus":"pcie.0","multifunction":true,"addr":"0x1"}' \
-device '{"driver":"pcie-root-port","port":9,"chassis":2,"id":"pci.2","bus":"pcie.0","addr":"0x1.0x1"}' \
-device '{"driver":"pcie-root-port","port":10,"chassis":3,"id":"pci.3","bus":"pcie.0","addr":"0x1.0x2"}' \
--device '{"driver":"pcie-pci-bridge","id":"pci.4","bus":"pci.1","addr":"0x0"}' \
--device '{"driver":"pcie-root-port","port":11,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x1.0x3"}' \
--device '{"driver":"pcie-root-port","port":12,"chassis":6,"id":"pci.6","bus":"pcie.0","addr":"0x1.0x4"}' \
--device '{"driver":"qemu-xhci","id":"usb","bus":"pci.3","addr":"0x0"}' \
--device '{"driver":"lsi","id":"scsi0","bus":"pci.4","addr":"0x1"}' \
+-device '{"driver":"pcie-root-port","port":11,"chassis":4,"id":"pci.4","bus":"pcie.0","addr":"0x1.0x3"}' \
+-device '{"driver":"pcie-root-port","port":12,"chassis":5,"id":"pci.5","bus":"pcie.0","addr":"0x1.0x4"}' \
+-device '{"driver":"qemu-xhci","id":"usb","bus":"pci.2","addr":"0x0"}' \
+-device '{"driver":"virtio-scsi-pci","id":"scsi0","bus":"pci.3","addr":"0x0"}' \
-netdev '{"type":"user","id":"hostnet0"}' \
--device '{"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:09:a4:37","bus":"pci.2","addr":"0x0"}' \
+-device '{"driver":"virtio-net-pci","netdev":"hostnet0","id":"net0","mac":"52:54:00:09:a4:37","bus":"pci.1","addr":"0x0"}' \
-chardev pty,id=charserial0 \
-serial chardev:charserial0 \
-chardev socket,id=chrtpm,path=/dev/test \
-tpmdev emulator,id=tpm-tpm0,chardev=chrtpm \
-device '{"driver":"tpm-tis-device","tpmdev":"tpm-tpm0","id":"tpm0"}' \
-audiodev '{"id":"audio1","driver":"none"}' \
--device '{"driver":"virtio-gpu-pci","id":"video0","max_outputs":1,"bus":"pci.5","addr":"0x0"}' \
+-device '{"driver":"virtio-gpu-pci","id":"video0","max_outputs":1,"bus":"pci.4","addr":"0x0"}' \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-device '{"driver":"pvpanic-pci","bus":"pcie.0","addr":"0x2"}' \
-msg timestamp=on
diff --git a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.xml b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.xml
index f27e7e1522..5abf55cf36 100644
--- a/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.xml
+++ b/tests/qemuxmlconfdata/aarch64-virt-default-models.aarch64-latest.xml
@@ -21,11 +21,11 @@
<devices>
<emulator>/usr/bin/qemu-system-aarch64</emulator>
<controller type='usb' index='0' model='qemu-xhci'>
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ </controller>
+ <controller type='scsi' index='0' model='virtio-scsi'>
<address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
</controller>
- <controller type='scsi' index='0' model='lsilogic'>
- <address type='pci' domain='0x0000' bus='0x04' slot='0x01' function='0x0'/>
- </controller>
<controller type='pci' index='0' model='pcie-root'/>
<controller type='pci' index='1' model='pcie-root-port'>
<model name='pcie-root-port'/>
@@ -42,24 +42,20 @@
<target chassis='3' port='0xa'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
</controller>
- <controller type='pci' index='4' model='pcie-to-pci-bridge'>
- <model name='pcie-pci-bridge'/>
- <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
- </controller>
- <controller type='pci' index='5' model='pcie-root-port'>
+ <controller type='pci' index='4' model='pcie-root-port'>
<model name='pcie-root-port'/>
- <target chassis='5' port='0xb'/>
+ <target chassis='4' port='0xb'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x3'/>
</controller>
- <controller type='pci' index='6' model='pcie-root-port'>
+ <controller type='pci' index='5' model='pcie-root-port'>
<model name='pcie-root-port'/>
- <target chassis='6' port='0xc'/>
+ <target chassis='5' port='0xc'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x4'/>
</controller>
<interface type='user'>
<mac address='52:54:00:09:a4:37'/>
<model type='virtio'/>
- <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
<serial type='pty'>
<target type='system-serial' port='0'>
@@ -75,7 +71,7 @@
<audio id='1' type='none'/>
<video>
<model type='virtio' heads='1' primary='yes'/>
- <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
</video>
<memballoon model='none'/>
<panic model='pvpanic'>
--
2.43.0
2 weeks, 3 days
Re: [PATCH V2 1/5] qom: qom-tree-get
by Markus Armbruster
Steve Sistare <steven.sistare(a)oracle.com> writes:
> Define the qom-tree-get QAPI command, which fetches an entire tree of
> properties and values with a single QAPI call. This is much faster
> than using qom-list plus qom-get for every node and property of the
> tree. See qom.json for details.
>
> Signed-off-by: Steve Sistare <steven.sistare(a)oracle.com>
> ---
> qapi/qom.json | 56 ++++++++++++++++++++++++++++++++++++++++++
> qom/qom-qmp-cmds.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 128 insertions(+)
>
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 28ce24c..94662ad 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -46,6 +46,38 @@
> '*default-value': 'any' } }
>
> ##
> +# @ObjectPropertyValue:
> +#
> +# @name: the name of the property
> +#
> +# @type: the type of the property, as described in @ObjectPropertyInfo
That description is crap. In part because what it tries to describe is
crap. Neither is this patch's problem.
> +#
> +# @value: the value of the property. Omitted if cannot be read.
Suggest "Absent when the property cannot be read."
> +#
> +# Since 10.1
> +##
> +{ 'struct': 'ObjectPropertyValue',
> + 'data': { 'name': 'str',
> + 'type': 'str',
> + '*value': 'any' } }
ObjectPropertyValue suggests this describes a property's value. It does
not. It includes the name, i.e. it describes the *property*.
So does ObjectPropertyInfo.
The two overlap: both habe name and type. Only ObjectPropertyValue has
the current value. Only ObjectPropertyInfo has the default value and
description (I suspect the latter is useless in practice).
ObjectPropertyInfo is used with qom-list and qom-list-properties.
qom-list takes a QOM path, like your qom-tree-get and qom-list-getv.
I'd expect your commands to supersede qom-list in practice.
qom-list-properties is unlike your qom-tree-get and qom-list-getv: it
takes a type name. It's unreliable for non-abstract types: it can miss
dynamically created properties.
Let's ignore all this for now.
> +
> +##
> +# @ObjectNode:
> +#
> +# @name: the name of the node
> +#
> +# @children: child nodes
> +#
> +# @properties: properties of the node
> +#
> +# Since 10.1
> +##
> +{ 'struct': 'ObjectNode',
> + 'data': { 'name': 'str',
> + 'children': [ 'ObjectNode' ],
> + 'properties': [ 'ObjectPropertyValue' ] }}
> +
> +##
> # @qom-list:
> #
> # This command will list any properties of a object given a path in
> @@ -126,6 +158,30 @@
> 'allow-preconfig': true }
>
> ##
> +# @qom-tree-get:
> +#
> +# This command returns a tree of objects and their properties,
> +# rooted at the specified path.
> +#
> +# @path: The absolute or partial path within the object model, as
> +# described in @qom-get
> +#
> +# Errors:
> +# - If path is not valid or is ambiguous, returns an error.
By convention, we use "If <condition>, <error>, where <error> is a
member of QapiErrorClass.
What are the possible error classes? As far as I can tell:
- If path is ambiguous, GenericError
- If path cannot be resolved, DeviceNotFound
However, use of error classes other than GenericError is strongly
discouraged (see error_set() in qapi/error.h).
Is the ability to distinguish between these two errors useful?
Existing related commands such as qom-get also use DeviceNotFound.
Entirely undocumented, exact error conditions unclear. Awesome.
Libvirt seems to rely on this undocumented behavior: I can see code
checking for DeviceNotFound. Hyrum's law strikes.
qom-get fails with DeviceNotFound in both of the above cases. It fails
with GenericError when @property doesn't exist or cannot be read. Your
qom-tree-get fails differently. Awesome again.
Choices:
1. Leave errors undocumented and inconsistent.
2. Document errors for all related commands. Make the new ones as
consistent as we can.
> +# - If a property cannot be read, the value field is omitted in
> +# the corresponding @ObjectPropertyValue.
This is not an error, and therefore doesn't belong here.
ObjectPropertyValue's documentation also mentions it. Good enough?
> +#
> +# Returns: A tree of @ObjectNode. Each node contains its name, list
> +# of properties, and list of child nodes.
Hmm.
A struct Object has no name. Only properties have a name.
An ObjectNode has a name, and an ObjectPropertyValue has a name.
I may get back to this in a later message.
> +#
> +# Since 10.1
> +##
> +{ 'command': 'qom-tree-get',
> + 'data': { 'path': 'str' },
> + 'returns': 'ObjectNode',
> + 'allow-preconfig': true }
> +
> +##
> # @qom-set:
> #
> # This command will set a property from a object model path.
> diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> index 293755f..b876681 100644
> --- a/qom/qom-qmp-cmds.c
> +++ b/qom/qom-qmp-cmds.c
> @@ -69,6 +69,78 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp)
> return props;
> }
>
> +static void qom_list_add_property_value(Object *obj, ObjectProperty *prop,
> + ObjectPropertyValueList **props)
> +{
> + ObjectPropertyValue *item = g_new0(ObjectPropertyValue, 1);
> + Error *err = NULL;
> +
> + QAPI_LIST_PREPEND(*props, item);
List elements are in reverse iteration order. Not wrong. I would've
reached for QAPI_LIST_APPEND(), though.
Wait! Existing command code uses QAPI_LIST_PREPEND(). Nevermind, carry
on!
> +
> + item->name = g_strdup(prop->name);
> + item->type = g_strdup(prop->type);
> + item->value = object_property_get_qobject(obj, prop->name, &err);
> +
> + if (!item->value) {
> + /*
> + * For bulk get, the error message is dropped, but the value field
> + * is omitted so the caller knows this property could not be read.
> + */
> + error_free(err);
Simpler: pass NULL to object_property_get_qobject().
> + }
> +}
> +
> +static ObjectNode *qom_tree_get(const char *path, Error **errp)
> +{
> + Object *obj;
> + ObjectProperty *prop;
> + ObjectNode *result, *child;
> + ObjectPropertyIterator iter;
> +
> + obj = qom_resolve_path(path, errp);
> + if (obj == NULL) {
> + return NULL;
> + }
> +
> + result = g_new0(ObjectNode, 1);
> +
> + object_property_iter_init(&iter, obj);
> + while ((prop = object_property_iter_next(&iter))) {
> + if (strstart(prop->type, "child<", NULL)) {
> + g_autofree char *child_path = g_strdup_printf("%s/%s",
> + path, prop->name);
> + child = qom_tree_get(child_path, errp);
> + if (!child) {
> + qapi_free_ObjectNode(result);
> + return NULL;
> + }
> + child->name = g_strdup(prop->name);
WAT?
> + QAPI_LIST_PREPEND(result->children, child);
> + } else {
> + qom_list_add_property_value(obj, prop, &result->properties);
> + }
> + }
> +
Oh, result->name remains unset, and the caller is expected to fill it
in. Two callers, "WAT" above, and ...
> + return result;
> +}
> +
> +ObjectNode *qmp_qom_tree_get(const char *path, Error **errp)
> +{
> + ObjectNode *result = qom_tree_get(path, errp);
> +
> + if (result) {
> + /* Strip the path prefix if any */
> + const char *basename = strrchr(path, '/');
> +
> + if (!basename || !basename[1]) {
> + result->name = g_strdup(path);
> + } else {
> + result->name = g_strdup(basename + 1);
> + }
> + }
... this one.
Not a fan. But it works.
> + return result;
> +}
> +
> void qmp_qom_set(const char *path, const char *property, QObject *value,
> Error **errp)
> {
2 weeks, 4 days
Re: [PATCH V2 4/5] qom: qom-list-getv
by Markus Armbruster
Steve Sistare <steven.sistare(a)oracle.com> writes:
> Define the qom-list-getv command, which fetches all the properties and
> values for a list of paths. This is faster than qom-tree-get when
> fetching a subset of the QOM tree. See qom.json for details.
>
> Signed-off-by: Steve Sistare <steven.sistare(a)oracle.com>
> ---
> qapi/qom.json | 34 ++++++++++++++++++++++++++++++++++
> qom/qom-qmp-cmds.c | 40 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 74 insertions(+)
>
> diff --git a/qapi/qom.json b/qapi/qom.json
> index 94662ad..dc710d6 100644
> --- a/qapi/qom.json
> +++ b/qapi/qom.json
> @@ -62,6 +62,16 @@
> '*value': 'any' } }
>
> ##
> +# @ObjectPropertiesValues:
> +#
> +# @properties: a list of properties.
> +#
> +# Since 10.1
> +##
> +{ 'struct': 'ObjectPropertiesValues',
> + 'data': { 'properties': [ 'ObjectPropertyValue' ] }}
> +
> +##
> # @ObjectNode:
> #
> # @name: the name of the node
> @@ -158,6 +168,30 @@
> 'allow-preconfig': true }
>
> ##
> +# @qom-list-getv:
> +#
> +# This command returns a list of properties and their values for
> +# each object path in the input list.
Imperative mood, please: "Return a list of ..."
> +#
> +# @paths: The absolute or partial path for each object, as described
> +# in @qom-get
> +#
> +# Errors:
> +# - If any path is not valid or is ambiguous, returns an error.
> +# - If a property cannot be read, the value field is omitted in
> +# the corresponding @ObjectPropertyValue.
My comment on qom-tree-get's Errors: section applies.
> +#
> +# Returns: A list of @ObjectPropertiesValues. Each element contains
> +# the properties of the corresponding element in @paths.
Again, ObjectPropertiesValues is an unfortunate name.
> +#
> +# Since 10.1
> +##
> +{ 'command': 'qom-list-getv',
> + 'data': { 'paths': [ 'str' ] },
> + 'returns': [ 'ObjectPropertiesValues' ],
> + 'allow-preconfig': true }
> +
> +##
> # @qom-tree-get:
> #
> # This command returns a tree of objects and their properties,
I find this command *much* simpler than qom-tree-get.
qom-list-getv treats all properties the same. References, whether they
are children and links, are the same: a QOM path.
qom-tree-get separates properties into children and non-children.
Children become nested ObjectNodes, links remain QOM paths.
> diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c
> index b876681..1f05956 100644
> --- a/qom/qom-qmp-cmds.c
> +++ b/qom/qom-qmp-cmds.c
> @@ -90,6 +90,46 @@ static void qom_list_add_property_value(Object *obj, ObjectProperty *prop,
> }
> }
>
> +static ObjectPropertyValueList *qom_get_property_value_list(const char *path,
> + Error **errp)
> +{
> + Object *obj;
> + ObjectProperty *prop;
> + ObjectPropertyIterator iter;
> + ObjectPropertyValueList *props = NULL;
> +
> + obj = qom_resolve_path(path, errp);
> + if (obj == NULL) {
> + return NULL;
> + }
> +
> + object_property_iter_init(&iter, obj);
> + while ((prop = object_property_iter_next(&iter))) {
> + qom_list_add_property_value(obj, prop, &props);
> + }
> +
> + return props;
> +}
> +
> +ObjectPropertiesValuesList *qmp_qom_list_getv(strList *paths, Error **errp)
> +{
> + ObjectPropertiesValuesList *head = NULL, **tail = &head;
> +
> + for ( ; paths ; paths = paths->next) {
I'd prefer a separate variable:
for (tail = paths; tail; tail = tail->next) {
> + ObjectPropertiesValues *item = g_new0(ObjectPropertiesValues, 1);
> +
> + QAPI_LIST_APPEND(tail, item);
> +
> + item->properties = qom_get_property_value_list(paths->value, errp);
> + if (!item->properties) {
> + qapi_free_ObjectPropertiesValuesList(head);
> + return NULL;
> + }
> + }
> +
> + return head;
> +}
> +
> static ObjectNode *qom_tree_get(const char *path, Error **errp)
> {
> Object *obj;
The implementation is simpler than qom-tree's, too.
2 weeks, 4 days
Re: [PATCH V2 0/5] fast qom tree get
by Markus Armbruster
Steve Sistare <steven.sistare(a)oracle.com> writes:
> Using qom-list and qom-get to get all the nodes and property values in a
> QOM tree can take multiple seconds because it requires 1000's of individual
> QOM requests. Some managers fetch the entire tree or a large subset
> of it when starting a new VM, and this cost is a substantial fraction of
> start up time.
>
> To reduce this cost, consider QAPI calls that fetch more information in
> each call:
> * qom-list-get: given a path, return a list of properties and values.
> * qom-list-getv: given a list of paths, return a list of properties and
> values for each path.
> * qom-tree-get: given a path, return all descendant nodes rooted at that
> path, with properties and values for each.
>
> In all cases, a returned property is represented by ObjectPropertyValue,
> with fields name, type, and value. If an error occurs when reading a value
> the value field is omitted. Thus an error for one property will not cause a
> bulk fetch operation to fail.
>
> To evaluate each method, I modified scripts/qmp/qom-tree to use the method,
> verified all methods produce the same output, and timed each using:
>
> qemu-system-x86_64 -display none \
> -chardev socket,id=monitor0,path=/tmp/vm1.sock,server=on,wait=off \
> -mon monitor0,mode=control &
>
> time qom-tree -s /tmp/vm1.sock > /dev/null
>
> I only measured once per method, but the variation is low after a warm up run.
> The 'real - user - sys' column is a proxy for QEMU CPU time.
>
> method real(s) user(s) sys(s) (real - user - sys)(s)
> qom-list / qom-get 2.048 0.932 0.057 1.059
> qom-list-get 0.402 0.230 0.029 0.143
> qom-list-getv 0.200 0.132 0.015 0.053
> qom-tree-get 0.143 0.123 0.012 0.008
>
> qom-tree-get is the clear winner, reducing elapsed time by a factor of 14X,
> and reducing QEMU CPU time by 132X.
>
> qom-list-getv is slower when fetching the entire tree, but can beat
> qom-tree-get when only a subset of the tree needs to be fetched (not shown).
> qom-list-get is shown for comparison only, and is not included in this series.
How badly do you need the additional performance qom-tree-get can give
you in certain cases?
I'm asking because I find qom-list-getv *much* simpler.
2 weeks, 4 days