|
|
acda74 |
From 8a8d77f47e838413c829ee6202eb1f64613d12e1 Mon Sep 17 00:00:00 2001
|
|
|
acda74 |
Message-Id: <8a8d77f47e838413c829ee6202eb1f64613d12e1@dist-git>
|
|
|
acda74 |
From: Laine Stump <laine@redhat.com>
|
|
|
acda74 |
Date: Tue, 21 Feb 2023 01:16:04 -0500
|
|
|
acda74 |
Subject: [PATCH] qemu: respond to NETDEV_STREAM_DISCONNECTED event
|
|
|
acda74 |
|
|
|
acda74 |
When a QEMU netdev is of type "stream", if the socket it uses for
|
|
|
acda74 |
connectivity to the host network gets closed, then QEMU will send a
|
|
|
acda74 |
NETDEV_STREAM_DISCONNECTED event. We know that any stream netdev we've
|
|
|
acda74 |
created is backed by a passt process, and if the socket was closed,
|
|
|
acda74 |
that means the passt process has disappeared.
|
|
|
acda74 |
|
|
|
acda74 |
When we receive this event, we can respond by starting a new passt
|
|
|
acda74 |
process with the same options (including socket path) we originally
|
|
|
acda74 |
used. If we have previously created the stream netdev device with a
|
|
|
acda74 |
"reconnect" option, then QEMU will automatically reconnect to this new
|
|
|
acda74 |
passt process. (If we hadn't used "reconnect", then QEMU will never
|
|
|
acda74 |
try to reconnect to the new passt process, so there's no point in
|
|
|
acda74 |
starting it.)
|
|
|
acda74 |
|
|
|
acda74 |
Note that NETDEV_STREAM_DISCONNECTED is an event sent for the netdev
|
|
|
acda74 |
(ie "host side") of the network device, and so it sends the
|
|
|
acda74 |
"netdev-id" to specify which device was disconnected. But libvirt's
|
|
|
acda74 |
virDomainNetDef (the object used to keep track of network devices) is
|
|
|
acda74 |
the internal representation of both the host-side "netdev", and the
|
|
|
acda74 |
guest side device, and virDomainNetDef doesn't directly keep track of
|
|
|
acda74 |
the netdev-id, only of the device's "alias" (which is the "id"
|
|
|
acda74 |
parameter of the *guest* side of the device). Fortunately, by convention
|
|
|
acda74 |
libvirt always names the host-side of devices as "host" + alias, so in
|
|
|
acda74 |
order to search for the affected NetDef, all we need to do is trim the
|
|
|
acda74 |
1st 4 characters from the netdev-id and look for the NetDef having
|
|
|
acda74 |
that resulting trimmed string as its alias. (Contrast this to
|
|
|
acda74 |
NIC_RX_FILTER_CHANGED, which is an event received for the guest side
|
|
|
acda74 |
of the device, and so directly contains the device alias.)
|
|
|
acda74 |
|
|
|
acda74 |
Resolves: https://bugzilla.redhat.com/2172098
|
|
|
acda74 |
Signed-off-by: Laine Stump <laine@redhat.com>
|
|
|
acda74 |
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
|
|
|
acda74 |
(cherry picked from commit f62ce81b8a57d8033be4c661e071cbd12b83bf7b)
|
|
|
acda74 |
Signed-off-by: Laine Stump <laine@redhat.com>
|
|
|
acda74 |
---
|
|
|
acda74 |
src/qemu/qemu_domain.c | 1 +
|
|
|
acda74 |
src/qemu/qemu_domain.h | 1 +
|
|
|
acda74 |
src/qemu/qemu_driver.c | 76 ++++++++++++++++++++++++++++++++++++
|
|
|
acda74 |
src/qemu/qemu_monitor.c | 11 ++++++
|
|
|
acda74 |
src/qemu/qemu_monitor.h | 6 +++
|
|
|
acda74 |
src/qemu/qemu_monitor_json.c | 16 ++++++++
|
|
|
acda74 |
src/qemu/qemu_process.c | 18 +++++++++
|
|
|
acda74 |
7 files changed, 129 insertions(+)
|
|
|
acda74 |
|
|
|
acda74 |
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
|
|
|
acda74 |
index 247134672b..26408b90a2 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_domain.c
|
|
|
acda74 |
+++ b/src/qemu/qemu_domain.c
|
|
|
acda74 |
@@ -11165,6 +11165,7 @@ qemuProcessEventFree(struct qemuProcessEvent *event)
|
|
|
acda74 |
break;
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_WATCHDOG:
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_DEVICE_DELETED:
|
|
|
acda74 |
+ case QEMU_PROCESS_EVENT_NETDEV_STREAM_DISCONNECTED:
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED:
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_SERIAL_CHANGED:
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_MONITOR_EOF:
|
|
|
acda74 |
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
|
|
|
acda74 |
index eca5404cdc..fb9ab4c5ed 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_domain.h
|
|
|
acda74 |
+++ b/src/qemu/qemu_domain.h
|
|
|
acda74 |
@@ -442,6 +442,7 @@ typedef enum {
|
|
|
acda74 |
QEMU_PROCESS_EVENT_WATCHDOG = 0,
|
|
|
acda74 |
QEMU_PROCESS_EVENT_GUESTPANIC,
|
|
|
acda74 |
QEMU_PROCESS_EVENT_DEVICE_DELETED,
|
|
|
acda74 |
+ QEMU_PROCESS_EVENT_NETDEV_STREAM_DISCONNECTED,
|
|
|
acda74 |
QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED,
|
|
|
acda74 |
QEMU_PROCESS_EVENT_SERIAL_CHANGED,
|
|
|
acda74 |
QEMU_PROCESS_EVENT_JOB_STATUS_CHANGE,
|
|
|
acda74 |
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
|
|
|
acda74 |
index 0603af6a35..d00b91fe0b 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_driver.c
|
|
|
acda74 |
+++ b/src/qemu/qemu_driver.c
|
|
|
acda74 |
@@ -40,6 +40,7 @@
|
|
|
acda74 |
#include "qemu_hostdev.h"
|
|
|
acda74 |
#include "qemu_hotplug.h"
|
|
|
acda74 |
#include "qemu_monitor.h"
|
|
|
acda74 |
+#include "qemu_passt.h"
|
|
|
acda74 |
#include "qemu_process.h"
|
|
|
acda74 |
#include "qemu_migration.h"
|
|
|
acda74 |
#include "qemu_migration_params.h"
|
|
|
acda74 |
@@ -3622,6 +3623,78 @@ processDeviceDeletedEvent(virQEMUDriver *driver,
|
|
|
acda74 |
}
|
|
|
acda74 |
|
|
|
acda74 |
|
|
|
acda74 |
+static void
|
|
|
acda74 |
+processNetdevStreamDisconnectedEvent(virDomainObj *vm,
|
|
|
acda74 |
+ const char *netdevId)
|
|
|
acda74 |
+{
|
|
|
acda74 |
+ virDomainDeviceDef dev;
|
|
|
acda74 |
+ virDomainNetDef *def;
|
|
|
acda74 |
+ virQEMUCaps *qemuCaps = QEMU_DOMAIN_PRIVATE(vm)->qemuCaps;
|
|
|
acda74 |
+ const char *devAlias = STRSKIP(netdevId, "host");
|
|
|
acda74 |
+
|
|
|
acda74 |
+ /* The event sends us the "netdev-id", but we don't store the
|
|
|
acda74 |
+ * netdev-id in the NetDef and thus can't use it to find the
|
|
|
acda74 |
+ * correct NetDef. We *do* keep the device alias in the NetDef,
|
|
|
acda74 |
+ * and by convention the netdev-id is always "host" + devAlias, so
|
|
|
acda74 |
+ * we just need to remove "host" from the front of netdev-id to
|
|
|
acda74 |
+ * get the alias, which we can then use to find the proper NetDef.
|
|
|
acda74 |
+ */
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (!devAlias) {
|
|
|
acda74 |
+ VIR_WARN("Received NETDEV_STREAM_DISCONNECTED event for unrecognized netdev %s from domain %p %s",
|
|
|
acda74 |
+ netdevId, vm, vm->def->name);
|
|
|
acda74 |
+ return;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+
|
|
|
acda74 |
+ VIR_DEBUG("Received NETDEV_STREAM_DISCONNECTED event for device %s from domain %p %s",
|
|
|
acda74 |
+ devAlias, vm, vm->def->name);
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (virDomainObjBeginJob(vm, VIR_JOB_QUERY) < 0)
|
|
|
acda74 |
+ return;
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (!virDomainObjIsActive(vm)) {
|
|
|
acda74 |
+ VIR_DEBUG("Domain is not running");
|
|
|
acda74 |
+ goto endjob;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (virDomainDefFindDevice(vm->def, devAlias, &dev, true) < 0) {
|
|
|
acda74 |
+ VIR_WARN("NETDEV_STREAM_DISCONNECTED event received for non-existent device %s in domain %s",
|
|
|
acda74 |
+ devAlias, vm->def->name);
|
|
|
acda74 |
+ goto endjob;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+ if (dev.type != VIR_DOMAIN_DEVICE_NET) {
|
|
|
acda74 |
+ VIR_WARN("NETDEV_STREAM_DISCONNECTED event received for non-network device %s in domain %s",
|
|
|
acda74 |
+ devAlias, vm->def->name);
|
|
|
acda74 |
+ goto endjob;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+ def = dev.data.net;
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (def->backend.type != VIR_DOMAIN_NET_BACKEND_PASST) {
|
|
|
acda74 |
+ VIR_DEBUG("ignore NETDEV_STREAM_DISCONNECTED event for non-passt network device %s in domain %s",
|
|
|
acda74 |
+ def->info.alias, vm->def->name);
|
|
|
acda74 |
+ goto endjob;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_STREAM_RECONNECT)) {
|
|
|
acda74 |
+ VIR_WARN("ignore NETDEV_STREAM_DISCONNECTED event for passt network device %s in domain %s - QEMU binary does not support reconnect",
|
|
|
acda74 |
+ def->info.alias, vm->def->name);
|
|
|
acda74 |
+ goto endjob;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+
|
|
|
acda74 |
+ /* handle the event - restart the passt process with its original
|
|
|
acda74 |
+ * parameters
|
|
|
acda74 |
+ */
|
|
|
acda74 |
+ VIR_DEBUG("process NETDEV_STREAM_DISCONNECTED event for network device %s in domain %s",
|
|
|
acda74 |
+ def->info.alias, vm->def->name);
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (qemuPasstStart(vm, def) < 0)
|
|
|
acda74 |
+ goto endjob;
|
|
|
acda74 |
+
|
|
|
acda74 |
+ endjob:
|
|
|
acda74 |
+ virDomainObjEndJob(vm);
|
|
|
acda74 |
+}
|
|
|
acda74 |
+
|
|
|
acda74 |
+
|
|
|
acda74 |
static void
|
|
|
acda74 |
processNicRxFilterChangedEvent(virDomainObj *vm,
|
|
|
acda74 |
const char *devAlias)
|
|
|
acda74 |
@@ -3971,6 +4044,9 @@ static void qemuProcessEventHandler(void *data, void *opaque)
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_DEVICE_DELETED:
|
|
|
acda74 |
processDeviceDeletedEvent(driver, vm, processEvent->data);
|
|
|
acda74 |
break;
|
|
|
acda74 |
+ case QEMU_PROCESS_EVENT_NETDEV_STREAM_DISCONNECTED:
|
|
|
acda74 |
+ processNetdevStreamDisconnectedEvent(vm, processEvent->data);
|
|
|
acda74 |
+ break;
|
|
|
acda74 |
case QEMU_PROCESS_EVENT_NIC_RX_FILTER_CHANGED:
|
|
|
acda74 |
processNicRxFilterChangedEvent(vm, processEvent->data);
|
|
|
acda74 |
break;
|
|
|
acda74 |
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
|
|
|
acda74 |
index 38f89167e0..1fa35f03cc 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_monitor.c
|
|
|
acda74 |
+++ b/src/qemu/qemu_monitor.c
|
|
|
acda74 |
@@ -1265,6 +1265,17 @@ qemuMonitorEmitNicRxFilterChanged(qemuMonitor *mon,
|
|
|
acda74 |
}
|
|
|
acda74 |
|
|
|
acda74 |
|
|
|
acda74 |
+void
|
|
|
acda74 |
+qemuMonitorEmitNetdevStreamDisconnected(qemuMonitor *mon,
|
|
|
acda74 |
+ const char *devAlias)
|
|
|
acda74 |
+{
|
|
|
acda74 |
+ VIR_DEBUG("mon=%p", mon);
|
|
|
acda74 |
+
|
|
|
acda74 |
+ QEMU_MONITOR_CALLBACK(mon, domainNetdevStreamDisconnected,
|
|
|
acda74 |
+ mon->vm, devAlias);
|
|
|
acda74 |
+}
|
|
|
acda74 |
+
|
|
|
acda74 |
+
|
|
|
acda74 |
void
|
|
|
acda74 |
qemuMonitorEmitSerialChange(qemuMonitor *mon,
|
|
|
acda74 |
const char *devAlias,
|
|
|
acda74 |
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
|
|
|
acda74 |
index 2d16214ba2..2fa06b99a3 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_monitor.h
|
|
|
acda74 |
+++ b/src/qemu/qemu_monitor.h
|
|
|
acda74 |
@@ -250,6 +250,9 @@ typedef void (*qemuMonitorDomainDeviceUnplugErrCallback)(qemuMonitor *mon,
|
|
|
acda74 |
virDomainObj *vm,
|
|
|
acda74 |
const char *devPath,
|
|
|
acda74 |
const char *devAlias);
|
|
|
acda74 |
+typedef void (*qemuMonitorDomainNetdevStreamDisconnectedCallback)(qemuMonitor *mon,
|
|
|
acda74 |
+ virDomainObj *vm,
|
|
|
acda74 |
+ const char *devAlias);
|
|
|
acda74 |
typedef void (*qemuMonitorDomainNicRxFilterChangedCallback)(qemuMonitor *mon,
|
|
|
acda74 |
virDomainObj *vm,
|
|
|
acda74 |
const char *devAlias);
|
|
|
acda74 |
@@ -397,6 +400,7 @@ struct _qemuMonitorCallbacks {
|
|
|
acda74 |
qemuMonitorDomainMemoryFailureCallback domainMemoryFailure;
|
|
|
acda74 |
qemuMonitorDomainMemoryDeviceSizeChange domainMemoryDeviceSizeChange;
|
|
|
acda74 |
qemuMonitorDomainDeviceUnplugErrCallback domainDeviceUnplugError;
|
|
|
acda74 |
+ qemuMonitorDomainNetdevStreamDisconnectedCallback domainNetdevStreamDisconnected;
|
|
|
acda74 |
};
|
|
|
acda74 |
|
|
|
acda74 |
qemuMonitor *qemuMonitorOpen(virDomainObj *vm,
|
|
|
acda74 |
@@ -480,6 +484,8 @@ void qemuMonitorEmitDeviceDeleted(qemuMonitor *mon,
|
|
|
acda74 |
void qemuMonitorEmitDeviceUnplugErr(qemuMonitor *mon,
|
|
|
acda74 |
const char *devPath,
|
|
|
acda74 |
const char *devAlias);
|
|
|
acda74 |
+void qemuMonitorEmitNetdevStreamDisconnected(qemuMonitor *mon,
|
|
|
acda74 |
+ const char *devAlias);
|
|
|
acda74 |
void qemuMonitorEmitNicRxFilterChanged(qemuMonitor *mon,
|
|
|
acda74 |
const char *devAlias);
|
|
|
acda74 |
void qemuMonitorEmitSerialChange(qemuMonitor *mon,
|
|
|
acda74 |
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
|
|
|
acda74 |
index db99017555..4510d0d3c9 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_monitor_json.c
|
|
|
acda74 |
+++ b/src/qemu/qemu_monitor_json.c
|
|
|
acda74 |
@@ -84,6 +84,7 @@ static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon, virJSONV
|
|
|
acda74 |
static void qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon, virJSONValue *data);
|
|
|
acda74 |
static void qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon, virJSONValue *data);
|
|
|
acda74 |
static void qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data);
|
|
|
acda74 |
+static void qemuMonitorJSONHandleNetdevStreamDisconnected(qemuMonitor *mon, virJSONValue *data);
|
|
|
acda74 |
|
|
|
acda74 |
typedef struct {
|
|
|
acda74 |
const char *type;
|
|
|
acda74 |
@@ -106,6 +107,7 @@ static qemuEventHandler eventHandlers[] = {
|
|
|
acda74 |
{ "MEMORY_FAILURE", qemuMonitorJSONHandleMemoryFailure, },
|
|
|
acda74 |
{ "MIGRATION", qemuMonitorJSONHandleMigrationStatus, },
|
|
|
acda74 |
{ "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, },
|
|
|
acda74 |
+ { "NETDEV_STREAM_DISCONNECTED", qemuMonitorJSONHandleNetdevStreamDisconnected, },
|
|
|
acda74 |
{ "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
|
|
|
acda74 |
{ "PR_MANAGER_STATUS_CHANGED", qemuMonitorJSONHandlePRManagerStatusChanged, },
|
|
|
acda74 |
{ "RDMA_GID_STATUS_CHANGED", qemuMonitorJSONHandleRdmaGidStatusChanged, },
|
|
|
acda74 |
@@ -1021,6 +1023,20 @@ qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data)
|
|
|
acda74 |
}
|
|
|
acda74 |
|
|
|
acda74 |
|
|
|
acda74 |
+static void
|
|
|
acda74 |
+qemuMonitorJSONHandleNetdevStreamDisconnected(qemuMonitor *mon, virJSONValue *data)
|
|
|
acda74 |
+{
|
|
|
acda74 |
+ const char *name;
|
|
|
acda74 |
+
|
|
|
acda74 |
+ if (!(name = virJSONValueObjectGetString(data, "netdev-id"))) {
|
|
|
acda74 |
+ VIR_WARN("missing device in NETDEV_STREAM_DISCONNECTED event");
|
|
|
acda74 |
+ return;
|
|
|
acda74 |
+ }
|
|
|
acda74 |
+
|
|
|
acda74 |
+ qemuMonitorEmitNetdevStreamDisconnected(mon, name);
|
|
|
acda74 |
+}
|
|
|
acda74 |
+
|
|
|
acda74 |
+
|
|
|
acda74 |
static void
|
|
|
acda74 |
qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data)
|
|
|
acda74 |
{
|
|
|
acda74 |
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
|
|
|
acda74 |
index 7ae859d68f..298904fe2e 100644
|
|
|
acda74 |
--- a/src/qemu/qemu_process.c
|
|
|
acda74 |
+++ b/src/qemu/qemu_process.c
|
|
|
acda74 |
@@ -1360,6 +1360,23 @@ qemuProcessHandleBlockThreshold(qemuMonitor *mon G_GNUC_UNUSED,
|
|
|
acda74 |
}
|
|
|
acda74 |
|
|
|
acda74 |
|
|
|
acda74 |
+static void
|
|
|
acda74 |
+qemuProcessHandleNetdevStreamDisconnected(qemuMonitor *mon G_GNUC_UNUSED,
|
|
|
acda74 |
+ virDomainObj *vm,
|
|
|
acda74 |
+ const char *devAlias)
|
|
|
acda74 |
+{
|
|
|
acda74 |
+ virObjectLock(vm);
|
|
|
acda74 |
+
|
|
|
acda74 |
+ VIR_DEBUG("Device %s Netdev Stream Disconnected in domain %p %s",
|
|
|
acda74 |
+ devAlias, vm, vm->def->name);
|
|
|
acda74 |
+
|
|
|
acda74 |
+ qemuProcessEventSubmit(vm, QEMU_PROCESS_EVENT_NETDEV_STREAM_DISCONNECTED,
|
|
|
acda74 |
+ 0, 0, g_strdup(devAlias));
|
|
|
acda74 |
+
|
|
|
acda74 |
+ virObjectUnlock(vm);
|
|
|
acda74 |
+}
|
|
|
acda74 |
+
|
|
|
acda74 |
+
|
|
|
acda74 |
static void
|
|
|
acda74 |
qemuProcessHandleNicRxFilterChanged(qemuMonitor *mon G_GNUC_UNUSED,
|
|
|
acda74 |
virDomainObj *vm,
|
|
|
acda74 |
@@ -1801,6 +1818,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
|
|
|
acda74 |
.domainMemoryFailure = qemuProcessHandleMemoryFailure,
|
|
|
acda74 |
.domainMemoryDeviceSizeChange = qemuProcessHandleMemoryDeviceSizeChange,
|
|
|
acda74 |
.domainDeviceUnplugError = qemuProcessHandleDeviceUnplugErr,
|
|
|
acda74 |
+ .domainNetdevStreamDisconnected = qemuProcessHandleNetdevStreamDisconnected,
|
|
|
acda74 |
};
|
|
|
acda74 |
|
|
|
acda74 |
static void
|
|
|
acda74 |
--
|
|
|
acda74 |
2.39.2
|
|
|
acda74 |
|