[PATCH v3] util: support PCI passthrough net device stats collection
by zhenwei pi
Collect PCI passthrough net device stats from kernel by netlink
API.
Currently, libvirt can not get PCI passthrough net device stats,
run command:
#virsh domifstat instance --interface=52:54:00:2d:b2:35
error: Failed to get interface stats instance 52:54:00:2d:b2:35
error: internal error: Interface name not provided
The PCI device(usually SR-IOV virtual function device) is detached
while it's used in PCI passthrough mode. And we can not parse this
device from /proc/net/dev any more.
In this patch, libvirt check net device is VF of not firstly, then
query virNetDevVFInterfaceStats(new API).
virNetDevVFInterfaceStats parses VFs info of all PFs, compares MAC
address until the two MAC addresses match.
'#ip -s link show' can get the same result. Instead of parsing the
output result, implement this feature by libnl API.
Notice that this feature deponds on driver of PF.
Test on Mellanox ConnectX-4 Lx, it works well.
Also test on Intel Corporation 82599ES, it works, but only get 0.
(ip-link command get the same result).
IFLA_VF_STATS is supported since Linux-4.2, suggested by Laine,
just using defined(__linux__) && WITH_LIBNL is enough.
Signed-off-by: zhenwei pi <pizhenwei(a)bytedance.com>
---
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 3 ++
src/util/virnetdev.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++
src/util/virnetdev.h | 5 ++
4 files changed, 130 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index bdbe3431b8..bcc40b8d69 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2585,6 +2585,7 @@ virNetDevSetRcvMulti;
virNetDevSetupControl;
virNetDevSysfsFile;
virNetDevValidateConfig;
+virNetDevVFInterfaceStats;
# util/virnetdevbandwidth.h
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index ae715c01d7..f554010c40 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -10196,6 +10196,9 @@ qemuDomainInterfaceStats(virDomainPtr dom,
if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
if (virNetDevOpenvswitchInterfaceStats(net->ifname, stats) < 0)
goto cleanup;
+ } else if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+ if (virNetDevVFInterfaceStats(&net->mac, stats) < 0)
+ goto cleanup;
} else {
if (virNetDevTapInterfaceStats(net->ifname, stats,
!virDomainNetTypeSharesHostView(net)) < 0)
diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c
index e1a4cc2bef..3d54d07606 100644
--- a/src/util/virnetdev.c
+++ b/src/util/virnetdev.c
@@ -1489,6 +1489,7 @@ static struct nla_policy ifla_vf_policy[IFLA_VF_MAX+1] = {
.maxlen = sizeof(struct ifla_vf_mac) },
[IFLA_VF_VLAN] = { .type = NLA_UNSPEC,
.maxlen = sizeof(struct ifla_vf_vlan) },
+ [IFLA_VF_STATS] = { .type = NLA_NESTED },
};
@@ -2265,6 +2266,116 @@ virNetDevSetNetConfig(const char *linkdev, int vf,
return 0;
}
+static struct nla_policy ifla_vfstats_policy[IFLA_VF_STATS_MAX+1] = {
+ [IFLA_VF_STATS_RX_PACKETS] = { .type = NLA_U64 },
+ [IFLA_VF_STATS_TX_PACKETS] = { .type = NLA_U64 },
+ [IFLA_VF_STATS_RX_BYTES] = { .type = NLA_U64 },
+ [IFLA_VF_STATS_TX_BYTES] = { .type = NLA_U64 },
+ [IFLA_VF_STATS_BROADCAST] = { .type = NLA_U64 },
+ [IFLA_VF_STATS_MULTICAST] = { .type = NLA_U64 },
+};
+
+static int
+virNetDevParseVfStats(struct nlattr **tb, virMacAddrPtr mac,
+ virDomainInterfaceStatsPtr stats)
+{
+ int ret = -1, len;
+ struct ifla_vf_mac *vf_lladdr;
+ struct nlattr *nla, *t[IFLA_VF_MAX+1];
+ struct nlattr *stb[IFLA_VF_STATS_MAX+1];
+
+ if (tb == NULL || mac == NULL || stats == NULL) {
+ return -1;
+ }
+
+ if (!tb[IFLA_VFINFO_LIST])
+ return -1;
+
+ len = nla_len(tb[IFLA_VFINFO_LIST]);
+
+ for (nla = nla_data(tb[IFLA_VFINFO_LIST]); nla_ok(nla, len);
+ nla = nla_next(nla, &len)) {
+ ret = nla_parse(t, IFLA_VF_MAX, nla_data(nla), nla_len(nla),
+ ifla_vf_policy);
+ if (ret < 0)
+ return -1;
+
+ if (t[IFLA_VF_MAC] == NULL) {
+ continue;
+ }
+
+ vf_lladdr = nla_data(t[IFLA_VF_MAC]);
+ if (virMacAddrCmpRaw(mac, vf_lladdr->mac)) {
+ continue;
+ }
+
+ if (t[IFLA_VF_STATS]) {
+ ret = nla_parse_nested(stb, IFLA_VF_STATS_MAX,
+ t[IFLA_VF_STATS],
+ ifla_vfstats_policy);
+ if (ret < 0)
+ return -1;
+
+ stats->rx_bytes = nla_get_u64(stb[IFLA_VF_STATS_RX_BYTES]);
+ stats->tx_bytes = nla_get_u64(stb[IFLA_VF_STATS_TX_BYTES]);
+ stats->rx_packets = nla_get_u64(stb[IFLA_VF_STATS_RX_PACKETS]);
+ stats->tx_packets = nla_get_u64(stb[IFLA_VF_STATS_TX_PACKETS]);
+ }
+ return 0;
+ }
+
+ return ret;
+}
+
+/**
+ * virNetDevVFInterfaceStats:
+ * @mac: MAC address of the VF interface
+ * @stats: returns stats of the VF interface
+ *
+ * Get the VF interface from kernel by netlink.
+ * Returns 0 on success, -1 on failure.
+ */
+int
+virNetDevVFInterfaceStats(virMacAddrPtr mac,
+ virDomainInterfaceStatsPtr stats)
+{
+ int rc = -1;
+ void *nlData = NULL;
+ struct nlattr *tb[IFLA_MAX + 1] = {NULL, };
+ char *sysfsDevicePath = NULL;
+ DIR *dirp = NULL;
+ struct dirent *dp;
+
+ if (virDirOpen(&dirp, SYSFS_NET_DIR) < 0)
+ return -1;
+
+ /* get all PCI net devices, and parse VFs list from netlink API.
+ * compare MAC address, collect device stats if matching.
+ */
+ while (virDirRead(dirp, &dp, SYSFS_NET_DIR) > 0) {
+ if (virNetDevSysfsFile(&sysfsDevicePath, dp->d_name, "device") < 0)
+ break;
+
+ if (virNetDevIsPCIDevice(sysfsDevicePath)) {
+ rc = virNetlinkDumpLink(dp->d_name, -1, &nlData, tb, 0, 0);
+ if (rc < 0) {
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = virNetDevParseVfStats(tb, mac, stats);
+ VIR_FREE(nlData);
+ if (rc == 0)
+ goto cleanup;
+ }
+ VIR_FREE(sysfsDevicePath);
+ }
+
+ cleanup:
+ VIR_FREE(sysfsDevicePath);
+ VIR_DIR_CLOSE(dirp);
+ return rc;
+}
#else /* defined(__linux__) && defined(WITH_LIBNL) && defined(IFLA_VF_MAX) */
@@ -2309,6 +2420,16 @@ virNetDevSetNetConfig(const char *linkdev G_GNUC_UNUSED,
}
+int
+virNetDevVFInterfaceStats(virMacAddrPtr mac G_GNUC_UNUSED,
+ virDomainInterfaceStatsPtr stats G_GNUC_UNUSED)
+{
+ virReportSystemError(ENOSYS, "%s",
+ _("Unable to get VF net device stats on this platform"));
+ return -1;
+}
+
+
#endif /* defined(__linux__) && defined(WITH_LIBNL) && defined(IFLA_VF_MAX) */
VIR_ENUM_IMPL(virNetDevIfState,
diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h
index 5f581323ed..ff59d9d341 100644
--- a/src/util/virnetdev.h
+++ b/src/util/virnetdev.h
@@ -312,4 +312,9 @@ int virNetDevSysfsFile(char **pf_sysfs_device_link,
int virNetDevRunEthernetScript(const char *ifname, const char *script)
G_GNUC_NO_INLINE;
+int virNetDevVFInterfaceStats(virMacAddrPtr mac,
+ virDomainInterfaceStatsPtr stats)
+ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNetDevRxFilter, virNetDevRxFilterFree);
--
2.11.0
4 years, 2 months
[PATCH v2] qemu: migration: don't open storage driver too early
by Cole Robinson
If storage migration is requested, and the destination storage does
not exist on the remote host, qemu's migration support will call
into the libvirt storage driver to precreate the destination storage.
The storage driver virConnectPtr is opened too early though, adding
an unnecessary dependency on the storage driver for several cases
that don't require it. This currently requires kubevirt to install
the storage driver even though they aren't actually using it.
Push the virGetConnectStorage calls to right before the cases they are
actually needed.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
v2:
Only open the connection once per VM via
qemuMigrationDstPrecreateStorage
src/qemu/qemu_migration.c | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 2000c86640..4e959abebf 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -169,7 +169,7 @@ qemuMigrationSrcRestoreDomainState(virQEMUDriverPtr driver, virDomainObjPtr vm)
static int
-qemuMigrationDstPrecreateDisk(virConnectPtr conn,
+qemuMigrationDstPrecreateDisk(virConnectPtr *conn,
virDomainDiskDefPtr disk,
unsigned long long capacity)
{
@@ -204,7 +204,12 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
*volName = '\0';
volName++;
- if (!(pool = virStoragePoolLookupByTargetPath(conn, basePath)))
+ if (!*conn) {
+ if (!(*conn = virGetConnectStorage()))
+ goto cleanup;
+ }
+
+ if (!(pool = virStoragePoolLookupByTargetPath(*conn, basePath)))
goto cleanup;
format = virStorageFileFormatTypeToString(disk->src->format);
if (disk->src->format == VIR_STORAGE_FILE_QCOW2)
@@ -212,7 +217,12 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
break;
case VIR_STORAGE_TYPE_VOLUME:
- if (!(pool = virStoragePoolLookupByName(conn, disk->src->srcpool->pool)))
+ if (!*conn) {
+ if (!(*conn = virGetConnectStorage()))
+ goto cleanup;
+ }
+
+ if (!(pool = virStoragePoolLookupByName(*conn, disk->src->srcpool->pool)))
goto cleanup;
format = virStorageFileFormatTypeToString(disk->src->format);
volName = disk->src->srcpool->volume;
@@ -304,14 +314,11 @@ qemuMigrationDstPrecreateStorage(virDomainObjPtr vm,
{
int ret = -1;
size_t i = 0;
- virConnectPtr conn;
+ virConnectPtr conn = NULL;
if (!nbd || !nbd->ndisks)
return 0;
- if (!(conn = virGetConnectStorage()))
- return -1;
-
for (i = 0; i < nbd->ndisks; i++) {
virDomainDiskDefPtr disk;
const char *diskSrcPath;
@@ -349,7 +356,8 @@ qemuMigrationDstPrecreateStorage(virDomainObjPtr vm,
VIR_DEBUG("Proceeding with disk source %s", NULLSTR(diskSrcPath));
- if (qemuMigrationDstPrecreateDisk(conn, disk, nbd->disks[i].capacity) < 0)
+ if (qemuMigrationDstPrecreateDisk(&conn,
+ disk, nbd->disks[i].capacity) < 0)
goto cleanup;
}
--
2.28.0
4 years, 2 months
[PATCH 0/2] docs: Fix migration.html generation and report such errors next time
by Peter Krempa
Invert the order of the patches to see the error:
FAILED: docs/migration.xslout.html
/usr/bin/meson --internal exe --capture docs/migration.xslout.html -- /bin/xsltproc --stringparam pagesrc docs/migration.html.in --stringparam builddir /home/pipo/build/libvirt/gcc --stringparam timestamp 'Mon Oct 12 14:03:50 2020 UTC' --nonet ../../../libvirt/docs/site.xsl ../../../libvirt/docs/migration.html.in
../../../libvirt/docs/migration.html.in:664: parser error : Opening and ending tag mismatch: p line 649 and body
</body>
^
../../../libvirt/docs/migration.html.in:665: parser error : Opening and ending tag mismatch: body line 649 and html
</html>
^
../../../libvirt/docs/migration.html.in:666: parser error : EndTag: '</' not found
^
unable to parse ../../../libvirt/docs/migration.html.in
[132/262] Generating 'docs/ci.xslout.html.p/ci.html.in'.
ninja: build stopped: subcommand failed.
Peter Krempa (2):
docs: migration: Fix syntax
docs: meson.build: Generate HTML files directly by meson
docs/meson.build | 39 +++++++++++++++++++++++++--------------
docs/migration.html.in | 1 +
scripts/meson-html-gen.py | 37 -------------------------------------
3 files changed, 26 insertions(+), 51 deletions(-)
delete mode 100755 scripts/meson-html-gen.py
--
2.26.2
4 years, 2 months
[PATCH] qemu: migration: don't open storage driver too early
by Cole Robinson
If storage migration is requested, and the destination storage does
not exist on the remote host, qemu's migration support will call
into the libvirt storage driver to precreate the destination storage.
The storage driver virConnectPtr is opened too early though, adding
an unnecessary dependency on the storage driver for several cases
that don't require it. This currently requires kubevirt to install
the storage driver even though they aren't actually using it.
Push the virGetConnectStorage calls to right before the cases they are
actually needed.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/qemu/qemu_migration.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 2000c86640..99a6b41483 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -169,8 +169,7 @@ qemuMigrationSrcRestoreDomainState(virQEMUDriverPtr driver, virDomainObjPtr vm)
static int
-qemuMigrationDstPrecreateDisk(virConnectPtr conn,
- virDomainDiskDefPtr disk,
+qemuMigrationDstPrecreateDisk(virDomainDiskDefPtr disk,
unsigned long long capacity)
{
int ret = -1;
@@ -181,6 +180,7 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
const char *format = NULL;
unsigned int flags = 0;
+ virConnectPtr conn = NULL;
VIR_DEBUG("Precreate disk type=%s", virStorageTypeToString(disk->src->type));
@@ -204,6 +204,9 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
*volName = '\0';
volName++;
+ if (!(conn = virGetConnectStorage()))
+ goto cleanup;
+
if (!(pool = virStoragePoolLookupByTargetPath(conn, basePath)))
goto cleanup;
format = virStorageFileFormatTypeToString(disk->src->format);
@@ -212,6 +215,9 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
break;
case VIR_STORAGE_TYPE_VOLUME:
+ if (!(conn = virGetConnectStorage()))
+ goto cleanup;
+
if (!(pool = virStoragePoolLookupByName(conn, disk->src->srcpool->pool)))
goto cleanup;
format = virStorageFileFormatTypeToString(disk->src->format);
@@ -270,6 +276,7 @@ qemuMigrationDstPrecreateDisk(virConnectPtr conn,
VIR_FREE(volStr);
virObjectUnref(vol);
virObjectUnref(pool);
+ virObjectUnref(conn);
return ret;
}
@@ -304,13 +311,10 @@ qemuMigrationDstPrecreateStorage(virDomainObjPtr vm,
{
int ret = -1;
size_t i = 0;
- virConnectPtr conn;
if (!nbd || !nbd->ndisks)
return 0;
- if (!(conn = virGetConnectStorage()))
- return -1;
for (i = 0; i < nbd->ndisks; i++) {
virDomainDiskDefPtr disk;
@@ -349,13 +353,12 @@ qemuMigrationDstPrecreateStorage(virDomainObjPtr vm,
VIR_DEBUG("Proceeding with disk source %s", NULLSTR(diskSrcPath));
- if (qemuMigrationDstPrecreateDisk(conn, disk, nbd->disks[i].capacity) < 0)
+ if (qemuMigrationDstPrecreateDisk(disk, nbd->disks[i].capacity) < 0)
goto cleanup;
}
ret = 0;
cleanup:
- virObjectUnref(conn);
return ret;
}
--
2.28.0
4 years, 2 months
[PATCH 1/3] bhyve: fix virtio-9p src/dst order
by Roman Bogorodskiy
For the virtio-9p bhyve command line argument, the proper order
is mount_tag=/path/to/host/dir, not the opposite.
Signed-off-by: Roman Bogorodskiy <bogorodskiy(a)gmail.com>
---
src/bhyve/bhyve_command.c | 2 +-
tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p-readonly.args | 2 +-
tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p.args | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/bhyve/bhyve_command.c b/src/bhyve/bhyve_command.c
index 7606840f45..acf3a5a433 100644
--- a/src/bhyve/bhyve_command.c
+++ b/src/bhyve/bhyve_command.c
@@ -607,8 +607,8 @@ bhyveBuildFSArgStr(const virDomainDef *def G_GNUC_UNUSED,
virCommandAddArgFormat(cmd, "%d:%d,virtio-9p,%s=%s%s",
fs->info.addr.pci.slot,
fs->info.addr.pci.function,
- fs->src->path,
fs->dst,
+ fs->src->path,
virBufferCurrentContent(¶ms));
return 0;
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p-readonly.args b/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p-readonly.args
index 193895574d..bfcd88e366 100644
--- a/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p-readonly.args
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p-readonly.args
@@ -7,4 +7,4 @@
-s 0:0,hostbridge \
-s 2:0,ahci,hd:/tmp/freebsd.img \
-s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \
--s 4:0,virtio-9p,/shared/dir=shared_dir,ro bhyve
+-s 4:0,virtio-9p,shared_dir=/shared/dir,ro bhyve
diff --git a/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p.args b/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p.args
index 0d27954432..e890f7400b 100644
--- a/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p.args
+++ b/tests/bhyvexml2argvdata/bhyvexml2argv-fs-9p.args
@@ -7,4 +7,4 @@
-s 0:0,hostbridge \
-s 2:0,ahci,hd:/tmp/freebsd.img \
-s 3:0,virtio-net,faketapdev,mac=52:54:00:b9:94:02 \
--s 4:0,virtio-9p,/shared/dir=shared_dir bhyve
+-s 4:0,virtio-9p,shared_dir=/shared/dir bhyve
--
2.28.0
4 years, 2 months
[PATCH] libvirt: add memory failure event
by zhenwei pi
Since QEMU 5.2 (commit-77b285f7f6), QEMU supports 'memory failure'
event, posts event to monitor if hitting a hardware memory error.
Several changes in this patch:
Add a new event 'memory failure' for libvirt domain.
Implement memory failure event handling for QEMU from QMP.
Also implement virsh command callback functions.
Test case:
~# virsh event stretch --event memory-failure
event 'memory-failure' for domain stretch:
recipient: guest
action: inject
flags:
action required: 0
recursive: 0
events received: 1
Signed-off-by: zhenwei pi <pizhenwei(a)bytedance.com>
---
examples/c/misc/event-test.c | 17 ++++++++
include/libvirt/libvirt-domain.h | 84 +++++++++++++++++++++++++++++++++++++
src/conf/domain_event.c | 82 ++++++++++++++++++++++++++++++++++++
src/conf/domain_event.h | 12 ++++++
src/libvirt_private.syms | 2 +
src/qemu/qemu_domain.c | 1 +
src/qemu/qemu_domain.h | 1 +
src/qemu/qemu_driver.c | 57 +++++++++++++++++++++++++
src/qemu/qemu_monitor.c | 21 +++++++++-
src/qemu/qemu_monitor.h | 39 +++++++++++++++++
src/qemu/qemu_monitor_json.c | 50 ++++++++++++++++++++++
src/qemu/qemu_process.c | 28 +++++++++++++
src/remote/remote_daemon_dispatch.c | 33 +++++++++++++++
src/remote/remote_driver.c | 35 ++++++++++++++++
src/remote/remote_protocol.x | 21 +++++++++-
src/remote_protocol-structs | 12 ++++++
tools/virsh-domain.c | 37 ++++++++++++++++
17 files changed, 530 insertions(+), 2 deletions(-)
diff --git a/examples/c/misc/event-test.c b/examples/c/misc/event-test.c
index 52caa8ffa8..b10946d569 100644
--- a/examples/c/misc/event-test.c
+++ b/examples/c/misc/event-test.c
@@ -964,6 +964,22 @@ myDomainEventBlockThresholdCallback(virConnectPtr conn G_GNUC_UNUSED,
static int
+myDomainEventMemoryFailureCallback(virConnectPtr conn G_GNUC_UNUSED,
+ virDomainPtr dom,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags,
+ void *opaque G_GNUC_UNUSED)
+{
+ printf("%s EVENT: Domain %s(%d) memory failure: recipient '%d', "
+ "aciont '%d', action_required '%d', recursive '%d'",
+ __func__, virDomainGetName(dom), virDomainGetID(dom), recipient,
+ action, flags->action_required, flags->recursive);
+ return 0;
+}
+
+
+static int
myDomainEventMigrationIterationCallback(virConnectPtr conn G_GNUC_UNUSED,
virDomainPtr dom,
int iteration,
@@ -1093,6 +1109,7 @@ struct domainEventData domainEvents[] = {
DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback),
DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, myDomainEventMetadataChangeCallback),
DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback),
+ DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE, myDomainEventMemoryFailureCallback),
};
struct storagePoolEventData {
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 77f9116675..a9170d9a7e 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -3196,6 +3196,66 @@ typedef enum {
} virDomainEventCrashedDetailType;
/**
+ * virDomainMemoryFailureRecipientType:
+ *
+ * Recipient of a memory failure event.
+ */
+typedef enum {
+ /* memory failure at hypersivor memory address space */
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_HYPERVISOR = 0,
+
+ /* memory failure at guest memory address space */
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_GUEST = 1,
+
+# ifdef VIR_ENUM_SENTINELS
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST
+# endif
+} virDomainMemoryFailureRecipientType;
+
+
+/**
+ * virDomainMemoryFailureActionType:
+ *
+ * Action of a memory failure event.
+ */
+typedef enum {
+ /* the memory failure could be ignored. This will only be the case for
+ * action-optional failures. */
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_IGNORE = 0,
+
+ /* memory failure occurred in guest memory, the guest enabled MCE handling
+ * mechanism, and hypervisor could inject the MCE into the guest
+ * successfully. */
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_INJECT = 1,
+
+ /* the failure is unrecoverable. This occurs for action-required failures
+ * if the recipient is the hypervisor; hypervisor will exit. */
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_FATAL = 2,
+
+ /* the failure is unrecoverable but confined to the guest. This occurs if
+ * the recipient is a guest which is not ready to handle memory failures. */
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_RESET = 3,
+
+# ifdef VIR_ENUM_SENTINELS
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST
+# endif
+} virDomainMemoryFailureActionType;
+
+
+typedef struct _virDomainMemoryFailureFlags virDomainMemoryFailureFlags;
+typedef virDomainMemoryFailureFlags *virDomainMemoryFailureFlagsPtr;
+struct _virDomainMemoryFailureFlags {
+ /* whether a memory failure event is action-required or action-optional
+ * (e.g. a failure during memory scrub). */
+ int action_required;
+
+ /* whether the failure occurred while the previous failure was still in
+ * progress. */
+ int recursive;
+};
+
+
+/**
* virConnectDomainEventCallback:
* @conn: virConnect connection
* @dom: The domain on which the event occurred
@@ -4565,6 +4625,29 @@ typedef void (*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn,
void *opaque);
/**
+ * virConnectDomainEventMemoryFailureCallback:
+ * @conn: connection object
+ * @dom: domain on which the event occurred
+ * @recipient: the recipient of hardware memory failure
+ * @action: the action of hardware memory failure
+ * @flags: the flags of hardware memory failure
+ * @opaque: application specified data
+ *
+ * The callback occurs when the hypervisor handles the hardware memory
+ * corrupted event.
+ *
+ * The callback signature to use when registering for an event of type
+ * VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE with virConnectDomainEventRegisterAny()
+ */
+typedef void (*virConnectDomainEventMemoryFailureCallback)(virConnectPtr conn,
+ virDomainPtr dom,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags,
+ void *opaque);
+
+
+/**
* VIR_DOMAIN_EVENT_CALLBACK:
*
* Used to cast the event specific callback into the generic one
@@ -4606,6 +4689,7 @@ typedef enum {
VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED = 22, /* virConnectDomainEventDeviceRemovalFailedCallback */
VIR_DOMAIN_EVENT_ID_METADATA_CHANGE = 23, /* virConnectDomainEventMetadataChangeCallback */
VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD = 24, /* virConnectDomainEventBlockThresholdCallback */
+ VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE = 25, /* virConnectDomainEventMemoryFailureCallback */
# ifdef VIR_ENUM_SENTINELS
VIR_DOMAIN_EVENT_ID_LAST
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c
index a8bd9f1595..20c5590835 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -57,6 +57,7 @@ static virClassPtr virDomainEventJobCompletedClass;
static virClassPtr virDomainEventDeviceRemovalFailedClass;
static virClassPtr virDomainEventMetadataChangeClass;
static virClassPtr virDomainEventBlockThresholdClass;
+static virClassPtr virDomainEventMemoryFailureClass;
static void virDomainEventDispose(void *obj);
static void virDomainEventLifecycleDispose(void *obj);
@@ -79,6 +80,7 @@ static void virDomainEventJobCompletedDispose(void *obj);
static void virDomainEventDeviceRemovalFailedDispose(void *obj);
static void virDomainEventMetadataChangeDispose(void *obj);
static void virDomainEventBlockThresholdDispose(void *obj);
+static void virDomainEventMemoryFailureDispose(void *obj);
static void
virDomainEventDispatchDefaultFunc(virConnectPtr conn,
@@ -287,6 +289,16 @@ struct _virDomainEventBlockThreshold {
typedef struct _virDomainEventBlockThreshold virDomainEventBlockThreshold;
typedef virDomainEventBlockThreshold *virDomainEventBlockThresholdPtr;
+struct _virDomainEventMemoryFailure {
+ virDomainEvent parent;
+
+ virDomainMemoryFailureRecipientType recipient;
+ virDomainMemoryFailureActionType action;
+ virDomainMemoryFailureFlags flags;
+};
+typedef struct _virDomainEventMemoryFailure virDomainEventMemoryFailure;
+typedef virDomainEventMemoryFailure *virDomainEventMemoryFailurePtr;
+
static int
virDomainEventsOnceInit(void)
@@ -333,6 +345,8 @@ virDomainEventsOnceInit(void)
return -1;
if (!VIR_CLASS_NEW(virDomainEventBlockThreshold, virDomainEventClass))
return -1;
+ if (!VIR_CLASS_NEW(virDomainEventMemoryFailure, virDomainEventClass))
+ return -1;
return 0;
}
@@ -542,6 +556,14 @@ virDomainEventBlockThresholdDispose(void *obj)
}
+static void
+virDomainEventMemoryFailureDispose(void *obj)
+{
+ virDomainEventMemoryFailurePtr event = obj;
+ VIR_DEBUG("obj=%p", event);
+}
+
+
static void *
virDomainEventNew(virClassPtr klass,
int eventID,
@@ -1619,6 +1641,53 @@ virDomainEventBlockThresholdNewFromDom(virDomainPtr dom,
}
+static virObjectEventPtr
+virDomainEventMemoryFailureNew(int id,
+ const char *name,
+ unsigned char *uuid,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags)
+{
+ virDomainEventMemoryFailurePtr ev;
+
+ if (virDomainEventsInitialize() < 0)
+ return NULL;
+
+ if (!(ev = virDomainEventNew(virDomainEventMemoryFailureClass,
+ VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE,
+ id, name, uuid)))
+ return NULL;
+
+ ev->recipient = recipient;
+ ev->action = action;
+ ev->flags.action_required = flags->action_required;
+ ev->flags.recursive = flags->recursive;
+
+ return (virObjectEventPtr)ev;
+}
+
+virObjectEventPtr
+virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags)
+{
+ return virDomainEventMemoryFailureNew(obj->def->id, obj->def->name,
+ obj->def->uuid, recipient, action,
+ flags);
+}
+
+virObjectEventPtr
+virDomainEventMemoryFailureNewFromDom(virDomainPtr dom,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags)
+{
+ return virDomainEventMemoryFailureNew(dom->id, dom->name, dom->uuid,
+ recipient, action, flags);
+}
+
static void
virDomainEventDispatchDefaultFunc(virConnectPtr conn,
virObjectEventPtr event,
@@ -1902,6 +1971,19 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn,
cbopaque);
goto cleanup;
}
+ case VIR_DOMAIN_EVENT_ID_MEMORY_FAILURE:
+ {
+ virDomainEventMemoryFailurePtr memoryFailureEvent;
+
+ memoryFailureEvent = (virDomainEventMemoryFailurePtr)event;
+ ((virConnectDomainEventMemoryFailureCallback)cb)(conn, dom,
+ memoryFailureEvent->recipient,
+ memoryFailureEvent->action,
+ &memoryFailureEvent->flags,
+ cbopaque);
+ goto cleanup;
+ }
+
case VIR_DOMAIN_EVENT_ID_LAST:
break;
}
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h
index d1cfb81d62..5b317e8d30 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -255,6 +255,18 @@ virDomainEventBlockThresholdNewFromDom(virDomainPtr dom,
unsigned long long threshold,
unsigned long long excess);
+virObjectEventPtr
+virDomainEventMemoryFailureNewFromObj(virDomainObjPtr obj,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags);
+
+virObjectEventPtr
+virDomainEventMemoryFailureNewFromDom(virDomainPtr dom,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags);
+
int
virDomainEventStateRegister(virConnectPtr conn,
virObjectEventStatePtr state,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 152083d220..927de5001a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -704,6 +704,8 @@ virDomainEventLifecycleNew;
virDomainEventLifecycleNewFromDef;
virDomainEventLifecycleNewFromDom;
virDomainEventLifecycleNewFromObj;
+virDomainEventMemoryFailureNewFromDom;
+virDomainEventMemoryFailureNewFromObj;
virDomainEventMetadataChangeNewFromDom;
virDomainEventMetadataChangeNewFromObj;
virDomainEventMigrationIterationNewFromDom;
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 9623123d3c..5b5316fadd 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -10551,6 +10551,7 @@ qemuProcessEventFree(struct qemuProcessEvent *event)
case QEMU_PROCESS_EVENT_BLOCK_JOB:
case QEMU_PROCESS_EVENT_MONITOR_EOF:
case QEMU_PROCESS_EVENT_GUEST_CRASHLOADED:
+ case QEMU_PROCESS_EVENT_MEMORY_FAILURE:
VIR_FREE(event->data);
break;
case QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE:
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 9bf32e16c9..51d5963f25 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -441,6 +441,7 @@ typedef enum {
QEMU_PROCESS_EVENT_PR_DISCONNECT,
QEMU_PROCESS_EVENT_RDMA_GID_STATUS_CHANGED,
QEMU_PROCESS_EVENT_GUEST_CRASHLOADED,
+ QEMU_PROCESS_EVENT_MEMORY_FAILURE,
QEMU_PROCESS_EVENT_LAST
} qemuProcessEventType;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 8ef812cd94..aecd947836 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4292,6 +4292,59 @@ processGuestCrashloadedEvent(virQEMUDriverPtr driver,
}
+static void
+processMemoryFailureEvent(virQEMUDriverPtr driver,
+ virDomainObjPtr vm,
+ qemuMonitorEventMemoryFailurePtr mfp)
+{
+ virObjectEventPtr event = NULL;
+ virDomainMemoryFailureRecipientType recipient;
+ virDomainMemoryFailureActionType action;
+ virDomainMemoryFailureFlags flags;
+
+ switch (mfp->recipient) {
+ case QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_HYPERVISOR:
+ recipient = VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_HYPERVISOR;
+ break;
+ case QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_GUEST:
+ recipient = VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_GUEST;
+ break;
+ case QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_LAST:
+ default:
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("requested unknown memory failure recipient"));
+ return;
+ }
+
+ switch (mfp->action) {
+ case QEMU_MONITOR_MEMORY_FAILURE_ACTION_IGNORE:
+ action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_IGNORE;
+ break;
+ case QEMU_MONITOR_MEMORY_FAILURE_ACTION_INJECT:
+ action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_INJECT;
+ break;
+ case QEMU_MONITOR_MEMORY_FAILURE_ACTION_FATAL:
+ action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_FATAL;
+ break;
+ case QEMU_MONITOR_MEMORY_FAILURE_ACTION_RESET:
+ action = VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_RESET;
+ break;
+ case QEMU_MONITOR_MEMORY_FAILURE_ACTION_LAST:
+ default:
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("requested unknown memory failure action"));
+ return;
+ }
+
+ flags.action_required = mfp->action_required;
+ flags.recursive = mfp->recursive;
+ event = virDomainEventMemoryFailureNewFromObj(vm, recipient, action,
+ &flags);
+
+ virObjectEventStateQueue(driver->domainEventState, event);
+}
+
+
static void qemuProcessEventHandler(void *data, void *opaque)
{
struct qemuProcessEvent *processEvent = data;
@@ -4341,6 +4394,10 @@ static void qemuProcessEventHandler(void *data, void *opaque)
case QEMU_PROCESS_EVENT_GUEST_CRASHLOADED:
processGuestCrashloadedEvent(driver, vm);
break;
+ case QEMU_PROCESS_EVENT_MEMORY_FAILURE:
+ processMemoryFailureEvent(driver, vm, processEvent->data);
+ break;
+
case QEMU_PROCESS_EVENT_LAST:
break;
}
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 8c991fefbb..189b789bb8 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -159,7 +159,6 @@ static int qemuMonitorOnceInit(void)
VIR_ONCE_GLOBAL_INIT(qemuMonitor);
-
VIR_ENUM_IMPL(qemuMonitorMigrationStatus,
QEMU_MONITOR_MIGRATION_STATUS_LAST,
"inactive", "setup",
@@ -197,6 +196,14 @@ VIR_ENUM_IMPL(qemuMonitorDumpStatus,
"none", "active", "completed", "failed",
);
+VIR_ENUM_IMPL(qemuMonitorMemoryFailureRecipient,
+ QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_LAST,
+ "hypervisor", "guest");
+
+VIR_ENUM_IMPL(qemuMonitorMemoryFailureAction,
+ QEMU_MONITOR_MEMORY_FAILURE_ACTION_LAST,
+ "ignore", "inject",
+ "fatal", "reset");
#if DEBUG_RAW_IO
static char *
@@ -1428,6 +1435,18 @@ qemuMonitorEmitSpiceMigrated(qemuMonitorPtr mon)
int
+qemuMonitorEmitMemoryFailure(qemuMonitorPtr mon,
+ qemuMonitorEventMemoryFailurePtr mfp)
+{
+ int ret = -1;
+
+ QEMU_MONITOR_CALLBACK(mon, ret, domainMemoryFailure, mon->vm, mfp);
+
+ return ret;
+}
+
+
+int
qemuMonitorEmitMigrationStatus(qemuMonitorPtr mon,
int status)
{
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index a744c8975b..17ba006a2f 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -340,6 +340,40 @@ typedef int (*qemuMonitorDomainGuestCrashloadedCallback)(qemuMonitorPtr mon,
virDomainObjPtr vm,
void *opaque);
+typedef enum {
+ QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_HYPERVISOR,
+ QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_GUEST,
+
+ QEMU_MONITOR_MEMORY_FAILURE_RECIPIENT_LAST
+} qemuMonitorMemoryFailureRecipient;
+
+VIR_ENUM_DECL(qemuMonitorMemoryFailureRecipient);
+
+typedef enum {
+ QEMU_MONITOR_MEMORY_FAILURE_ACTION_IGNORE,
+ QEMU_MONITOR_MEMORY_FAILURE_ACTION_INJECT,
+ QEMU_MONITOR_MEMORY_FAILURE_ACTION_FATAL,
+ QEMU_MONITOR_MEMORY_FAILURE_ACTION_RESET,
+
+ QEMU_MONITOR_MEMORY_FAILURE_ACTION_LAST
+} qemuMonitorMemoryFailureAction;
+
+VIR_ENUM_DECL(qemuMonitorMemoryFailureAction);
+
+typedef struct _qemuMonitorEventMemoryFailure qemuMonitorEventMemoryFailure;
+typedef qemuMonitorEventMemoryFailure *qemuMonitorEventMemoryFailurePtr;
+struct _qemuMonitorEventMemoryFailure {
+ qemuMonitorMemoryFailureRecipient recipient;
+ qemuMonitorMemoryFailureAction action;
+ bool action_required;
+ bool recursive;
+};
+
+typedef int (*qemuMonitorDomainMemoryFailureCallback)(qemuMonitorPtr mon,
+ virDomainObjPtr vm,
+ qemuMonitorEventMemoryFailurePtr mfp,
+ void *opaque);
+
typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks;
typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr;
struct _qemuMonitorCallbacks {
@@ -376,6 +410,7 @@ struct _qemuMonitorCallbacks {
qemuMonitorDomainPRManagerStatusChangedCallback domainPRManagerStatusChanged;
qemuMonitorDomainRdmaGidStatusChangedCallback domainRdmaGidStatusChanged;
qemuMonitorDomainGuestCrashloadedCallback domainGuestCrashloaded;
+ qemuMonitorDomainMemoryFailureCallback domainMemoryFailure;
};
qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm,
@@ -475,6 +510,10 @@ int qemuMonitorEmitSerialChange(qemuMonitorPtr mon,
const char *devAlias,
bool connected);
int qemuMonitorEmitSpiceMigrated(qemuMonitorPtr mon);
+
+int qemuMonitorEmitMemoryFailure(qemuMonitorPtr mon,
+ qemuMonitorEventMemoryFailurePtr mfp);
+
int qemuMonitorEmitMigrationStatus(qemuMonitorPtr mon,
int status);
int qemuMonitorEmitMigrationPass(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 26ac499fc5..8e2659dc21 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -112,6 +112,7 @@ static void qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValue
static void qemuMonitorJSONHandleDumpCompleted(qemuMonitorPtr mon, virJSONValuePtr data);
static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data);
static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleMemoryFailure(qemuMonitorPtr mon, virJSONValuePtr data);
typedef struct {
const char *type;
@@ -132,6 +133,7 @@ static qemuEventHandler eventHandlers[] = {
{ "GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded, },
{ "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
{ "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, },
+ { "MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure, },
{ "MIGRATION", qemuMonitorJSONHandleMigrationStatus, },
{ "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, },
{ "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
@@ -1336,6 +1338,54 @@ qemuMonitorJSONHandleSpiceMigrated(qemuMonitorPtr mon,
static void
+qemuMonitorJSONHandleMemoryFailure(qemuMonitorPtr mon,
+ virJSONValuePtr data)
+{
+ virJSONValuePtr flagsjson = virJSONValueObjectGetObject(data, "flags");
+ const char *str;
+ int recipient;
+ int action;
+ bool ar = false;
+ bool recursive = false;
+ qemuMonitorEventMemoryFailurePtr mfp;
+
+ if (!(str = virJSONValueObjectGetString(data, "recipient"))) {
+ VIR_WARN("missing recipient in memory failure event");
+ return;
+ }
+
+ recipient = qemuMonitorMemoryFailureRecipientTypeFromString(str);
+ if (recipient == -1) {
+ VIR_WARN("unknown recipient '%s' in memory_failure event", str);
+ return;
+ }
+
+ if (!(str = virJSONValueObjectGetString(data, "action"))) {
+ VIR_WARN("missing action in memory failure event");
+ return;
+ }
+
+ action = qemuMonitorMemoryFailureActionTypeFromString(str);
+ if (action == -1) {
+ VIR_WARN("unknown action '%s' in memory_failure event", str);
+ return;
+ }
+
+ if (flagsjson) {
+ virJSONValueObjectGetBoolean(flagsjson, "action-required", &ar);
+ virJSONValueObjectGetBoolean(flagsjson, "recursive", &recursive);
+ }
+
+ mfp = g_new0(qemuMonitorEventMemoryFailure, 1);
+ mfp->recipient = recipient;
+ mfp->action = action;
+ mfp->action_required = ar;
+ mfp->recursive = recursive;
+ qemuMonitorEmitMemoryFailure(mon, mfp);
+}
+
+
+static void
qemuMonitorJSONHandleMigrationStatus(qemuMonitorPtr mon,
virJSONValuePtr data)
{
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 6b5de29fdb..abcbab0f06 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -1878,6 +1878,33 @@ qemuProcessHandleGuestCrashloaded(qemuMonitorPtr mon G_GNUC_UNUSED,
}
+static int
+qemuProcessHandleMemoryFailure(qemuMonitorPtr mon G_GNUC_UNUSED,
+ virDomainObjPtr vm,
+ qemuMonitorEventMemoryFailurePtr mfp,
+ void *opaque)
+{
+ virQEMUDriverPtr driver = opaque;
+ struct qemuProcessEvent *processEvent;
+
+ virObjectLock(vm);
+ processEvent = g_new0(struct qemuProcessEvent, 1);
+
+ processEvent->eventType = QEMU_PROCESS_EVENT_MEMORY_FAILURE;
+ processEvent->data = mfp;
+ processEvent->vm = virObjectRef(vm);
+
+ if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) {
+ virObjectUnref(vm);
+ qemuProcessEventFree(processEvent);
+ }
+
+ virObjectUnlock(vm);
+
+ return 0;
+}
+
+
static qemuMonitorCallbacks monitorCallbacks = {
.eofNotify = qemuProcessHandleMonitorEOF,
.errorNotify = qemuProcessHandleMonitorError,
@@ -1910,6 +1937,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
.domainPRManagerStatusChanged = qemuProcessHandlePRManagerStatusChanged,
.domainRdmaGidStatusChanged = qemuProcessHandleRdmaGidStatusChanged,
.domainGuestCrashloaded = qemuProcessHandleGuestCrashloaded,
+ .domainMemoryFailure = qemuProcessHandleMemoryFailure,
};
static void
diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c
index 32ebcd8f36..45a8ab3c05 100644
--- a/src/remote/remote_daemon_dispatch.c
+++ b/src/remote/remote_daemon_dispatch.c
@@ -1302,6 +1302,38 @@ remoteRelayDomainEventBlockThreshold(virConnectPtr conn,
}
+static int
+remoteRelayDomainEventMemoryFailure(virConnectPtr conn,
+ virDomainPtr dom,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags,
+ void *opaque)
+{
+ daemonClientEventCallbackPtr callback = opaque;
+ remote_domain_event_memory_failure_msg data;
+
+ if (callback->callbackID < 0 ||
+ !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+ return -1;
+
+ /* build return data */
+ memset(&data, 0, sizeof(data));
+ data.callbackID = callback->callbackID;
+ data.recipient = recipient;
+ data.action = action;
+ data.flags.action_required = flags->action_required;
+ data.flags.recursive = flags->recursive;
+ make_nonnull_domain(&data.dom, dom);
+
+ remoteDispatchObjectEventSend(callback->client, remoteProgram,
+ REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE,
+ (xdrproc_t)xdr_remote_domain_event_memory_failure_msg, &data);
+
+ return 0;
+}
+
+
static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot),
@@ -1328,6 +1360,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold),
+ VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMemoryFailure),
};
G_STATIC_ASSERT(G_N_ELEMENTS(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index d318224605..5b29161a22 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -405,6 +405,11 @@ remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog,
void *evdata, void *opaque);
static void
+remoteDomainBuildEventMemoryFailure(virNetClientProgramPtr prog,
+ virNetClientPtr client,
+ void *evdata, void *opaque);
+
+static void
remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog G_GNUC_UNUSED,
virNetClientPtr client G_GNUC_UNUSED,
void *evdata, void *opaque);
@@ -615,6 +620,10 @@ static virNetClientProgramEvent remoteEvents[] = {
remoteDomainBuildEventBlockThreshold,
sizeof(remote_domain_event_block_threshold_msg),
(xdrproc_t)xdr_remote_domain_event_block_threshold_msg },
+ { REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE,
+ remoteDomainBuildEventMemoryFailure,
+ sizeof(remote_domain_event_memory_failure_msg),
+ (xdrproc_t)xdr_remote_domain_event_memory_failure_msg },
};
static void
@@ -5440,6 +5449,32 @@ remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog G_GNUC_UNUSED,
}
+static void
+remoteDomainBuildEventMemoryFailure(virNetClientProgramPtr prog G_GNUC_UNUSED,
+ virNetClientPtr client G_GNUC_UNUSED,
+ void *evdata, void *opaque)
+{
+ virConnectPtr conn = opaque;
+ remote_domain_event_memory_failure_msg *msg = evdata;
+ struct private_data *priv = conn->privateData;
+ virDomainPtr dom;
+ virDomainMemoryFailureFlags flags;
+ virObjectEventPtr event = NULL;
+
+ if (!(dom = get_nonnull_domain(conn, msg->dom)))
+ return;
+
+ flags.action_required = msg->flags.action_required;
+ flags.recursive = msg->flags.recursive;
+ event = virDomainEventMemoryFailureNewFromDom(dom, msg->recipient,
+ msg->action, &flags);
+
+ virObjectUnref(dom);
+
+ virObjectEventStateQueueRemote(priv->eventState, event, msg->callbackID);
+}
+
+
static int
remoteStreamSend(virStreamPtr st,
const char *data,
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index f4d6147676..a3fda24807 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3469,6 +3469,19 @@ struct remote_domain_event_callback_metadata_change_msg {
remote_string nsuri;
};
+struct remote_domain_event_memory_failure_flags {
+ int action_required;
+ int recursive;
+};
+
+struct remote_domain_event_memory_failure_msg {
+ int callbackID;
+ remote_nonnull_domain dom;
+ int recipient;
+ int action;
+ remote_domain_event_memory_failure_flags flags;
+};
+
struct remote_connect_secret_event_register_any_args {
int eventID;
remote_secret secret;
@@ -6668,5 +6681,11 @@ enum remote_procedure {
* @priority: high
* @acl: domain:read
*/
- REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422
+ REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422,
+
+ /**
+ * @generate: both
+ * @acl: none
+ */
+ REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE = 423
};
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index bae0f0b545..1b74fb330d 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -2862,6 +2862,17 @@ struct remote_domain_event_callback_metadata_change_msg {
int type;
remote_string nsuri;
};
+struct remote_domain_event_memory_failure_flags {
+ int action_required;
+ int recursive;
+};
+struct remote_domain_event_memory_failure_msg {
+ int callbackID;
+ remote_nonnull_domain dom;
+ int recipient;
+ int action;
+ remote_domain_event_memory_failure_flags flags;
+};
struct remote_connect_secret_event_register_any_args {
int eventID;
remote_secret secret;
@@ -3558,4 +3569,5 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_AGENT_SET_RESPONSE_TIMEOUT = 420,
REMOTE_PROC_DOMAIN_BACKUP_BEGIN = 421,
REMOTE_PROC_DOMAIN_BACKUP_GET_XML_DESC = 422,
+ REMOTE_PROC_DOMAIN_EVENT_MEMORY_FAILURE = 423,
};
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index 8f11393197..7c6b19a54b 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -13590,6 +13590,41 @@ virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED,
}
+VIR_ENUM_DECL(virshEventMemoryFailureRecipientType);
+VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType,
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST,
+ N_("hypervisor"),
+ N_("guest"));
+
+VIR_ENUM_DECL(virshEventMemoryFailureActionType);
+VIR_ENUM_IMPL(virshEventMemoryFailureActionType,
+ VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST,
+ N_("ignore"),
+ N_("inject"),
+ N_("fatal"),
+ N_("reset"));
+
+static void
+virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED,
+ virDomainPtr dom,
+ virDomainMemoryFailureRecipientType recipient,
+ virDomainMemoryFailureActionType action,
+ virDomainMemoryFailureFlagsPtr flags,
+ void *opaque)
+{
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+ virBufferAsprintf(&buf, _("event 'memory-failure' for domain %s:\n"
+ "recipient: %s\naction: %s\nflags:\n"
+ "\taction required: %d\n\trecursive: %d\n"),
+ virDomainGetName(dom),
+ UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)),
+ UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action)),
+ !!(flags->action_required), !!(flags->recursive));
+ virshEventPrint(opaque, &buf);
+}
+
+
virshDomainEventCallback virshDomainEventCallbacks[] = {
{ "lifecycle",
VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
@@ -13639,6 +13674,8 @@ virshDomainEventCallback virshDomainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
{ "block-threshold",
VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
+ { "memory-failure",
+ VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), },
};
G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks));
--
2.11.0
4 years, 2 months
[PATCH] virsocketaddr: Zero @netmask in virSocketAddrPrefixToNetmask()
by Michal Privoznik
The aim of virSocketAddrPrefixToNetmask() is to initialize passed
virSocketAddr structure based on prefix length and family.
However, it doesn't set all members in the struct which may lead
to reads of uninitialized values:
==15421== Use of uninitialised value of size 8
==15421== at 0x50F297A: _itoa_word (in /lib64/libc-2.31.so)
==15421== by 0x510C8FE: __vfprintf_internal (in /lib64/libc-2.31.so)
==15421== by 0x5120295: __vsnprintf_internal (in /lib64/libc-2.31.so)
==15421== by 0x50F8969: snprintf (in /lib64/libc-2.31.so)
==15421== by 0x51BB602: getnameinfo (in /lib64/libc-2.31.so)
==15421== by 0x496DEE0: virSocketAddrFormatFull (virsocketaddr.c:486)
==15421== by 0x496DD9F: virSocketAddrFormat (virsocketaddr.c:444)
==15421== by 0x11871F: networkDnsmasqConfContents (bridge_driver.c:1404)
==15421== by 0x1118F5: testCompareXMLToConfFiles (networkxml2conftest.c:48)
==15421== by 0x111BAF: testCompareXMLToConfHelper (networkxml2conftest.c:112)
==15421== by 0x112679: virTestRun (testutils.c:142)
==15421== by 0x111D09: mymain (networkxml2conftest.c:144)
==15421== Uninitialised value was created by a stack allocation
==15421== at 0x1175D2: networkDnsmasqConfContents (bridge_driver.c:1056)
All callers expect the function to initialize the structure
fully.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/util/virsocketaddr.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/util/virsocketaddr.c b/src/util/virsocketaddr.c
index e0eb76ded3..65aaa632c7 100644
--- a/src/util/virsocketaddr.c
+++ b/src/util/virsocketaddr.c
@@ -1097,6 +1097,8 @@ virSocketAddrPrefixToNetmask(unsigned int prefix,
virSocketAddrPtr netmask,
int family)
{
+ memset(netmask, 0, sizeof(*netmask));
+
netmask->data.stor.ss_family = AF_UNSPEC; /* assume failure */
if (family == AF_INET) {
@@ -1135,7 +1137,7 @@ virSocketAddrPrefixToNetmask(unsigned int prefix,
}
return 0;
- }
+}
/**
* virSocketAddrGetIPPrefix:
--
2.26.2
4 years, 2 months
[libvirt PATCH 0/4] Some documentation fixes
by Tim Wiederhake
I encountered some references to a cpu_map.xml file which was
moved to a subdirectory in commit 3ecbac95cd and split up in to
different files in 2c127947ae and e6d7be38b9. This series removes
all remaining references to this file and fixes an unrelated minor
issue in the description of the "feature" element in the section
about "cpu".
Tim Wiederhake (4):
tests: Remove references to "cpu_map.xml" in the code
qemu: Remove references to "cpu_map.xml" in the code
docs: Remove references to "cpu_map.xml" in the documentation
doc: Fix element name in description of "feature"
docs/formatdomain.rst | 14 +++++++-------
src/qemu/qemu_capabilities.c | 2 +-
tests/cputest.c | 4 ++--
3 files changed, 10 insertions(+), 10 deletions(-)
--
2.26.2
4 years, 2 months
[libvirt PATCH 0/3] ci: Start building RPMs
by Andrea Bolognani
See it in action:
https://gitlab.com/abologna/libvirt/-/pipelines/199936179
Andrea Bolognani (3):
ci: Refresh Dockerfiles
ci: Allow skipping dist
ci: Start building RPMs
.gitlab-ci.yml | 41 ++++++++++---------
ci/containers/libvirt-centos-7.Dockerfile | 1 +
ci/containers/libvirt-centos-8.Dockerfile | 1 +
.../libvirt-centos-stream.Dockerfile | 1 +
ci/containers/libvirt-fedora-31.Dockerfile | 1 +
ci/containers/libvirt-fedora-32.Dockerfile | 1 +
...rt-fedora-rawhide-cross-mingw32.Dockerfile | 1 +
...rt-fedora-rawhide-cross-mingw64.Dockerfile | 1 +
.../libvirt-fedora-rawhide.Dockerfile | 1 +
9 files changed, 30 insertions(+), 19 deletions(-)
--
2.26.2
4 years, 2 months
[libvirt PATCH 00/10] use g_autoptr with cgroups (glib chronicles)
by Pavel Hrdina
Pavel Hrdina (10):
qemu_cgroup: introduce qemuRestoreCgroupThread helper
util: vircgroup: use GLib alloc functions
util: vircgroup: change virCgroupFree to take only virCgroupPtr
util: vircgroup: introduce g_autoptr() for virCgroup
libvirt-lxc: use g_autoptr for virCgroup
lxc: use g_autoptr for virCgroup
qemu: use g_autoptr for virCgroup
util: use g_autoptr for virCgroup
tests: use g_autoptr for virCgroup
tools: use g_autoptr for virCgroup
src/libvirt-lxc.c | 5 +-
src/lxc/lxc_cgroup.c | 20 +--
src/lxc/lxc_container.c | 37 ++---
src/lxc/lxc_controller.c | 2 +-
src/lxc/lxc_domain.c | 2 +-
src/lxc/lxc_process.c | 9 +-
src/qemu/qemu_cgroup.c | 87 ++++++-----
src/qemu/qemu_domain.c | 3 +-
src/qemu/qemu_driver.c | 139 ++++++------------
src/qemu/qemu_process.c | 2 +-
src/util/vircgroup.c | 198 +++++++++++--------------
src/util/vircgroup.h | 4 +-
src/util/vircgroupv1.c | 9 +-
tests/vircgrouptest.c | 236 +++++++++++-------------------
tools/virt-host-validate-common.c | 4 +-
15 files changed, 305 insertions(+), 452 deletions(-)
--
2.26.2
4 years, 2 months