render / rpms / libvirt

Forked from rpms/libvirt 4 months ago
Clone
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