On 7/4/2024 6:13 AM, Purna Pavan Chandra wrote:
Cloud-hypervisor now supports restoring with new net fds.
Ref:
https://github.com/cloud-hypervisor/cloud-hypervisor/pull/6402
So, pass new tap fds via SCM_RIGHTS to CH's restore api.
Signed-off-by: Purna Pavan Chandra <paekkaladevi(a)linux.microsoft.com>
---
src/ch/ch_capabilities.c | 6 +++
src/ch/ch_capabilities.h | 1 +
src/ch/ch_driver.c | 29 +++++++-----
src/ch/ch_monitor.c | 21 ++++++++-
src/ch/ch_monitor.h | 2 +-
src/ch/ch_process.c | 98 +++++++++++++++++++++++++++++++++-------
6 files changed, 126 insertions(+), 31 deletions(-)
diff --git a/src/ch/ch_capabilities.c b/src/ch/ch_capabilities.c
index 5941851500..f3765a8095 100644
--- a/src/ch/ch_capabilities.c
+++ b/src/ch/ch_capabilities.c
@@ -65,6 +65,12 @@ virCHCapsInitCHVersionCaps(int version)
if (version >= 36000000)
virCHCapsSet(chCaps, CH_SOCKET_BACKEND_SERIAL_PORT);
+ /* Starting v40, Cloud-Hypervisor supports restore with new net fds.
+ * This is required to be able to restore a guest with network config define.
+ *
https://github.com/cloud-hypervisor/cloud-hypervisor/releases/tag/v40.0 */
+ if (version >= 40000000)
+ virCHCapsSet(chCaps, CH_RESTORE_WITH_NEW_TAPFDS);
+
return g_steal_pointer(&chCaps);
}
diff --git a/src/ch/ch_capabilities.h b/src/ch/ch_capabilities.h
index 03932511f6..e5efb14b68 100644
--- a/src/ch/ch_capabilities.h
+++ b/src/ch/ch_capabilities.h
@@ -28,6 +28,7 @@ typedef enum {
CH_SERIAL_CONSOLE_IN_PARALLEL, /* Serial and Console ports can work in parallel */
CH_MULTIFD_IN_ADDNET, /* Cloud-hypervisor can accept multiple FDs in add-net api
*/
CH_SOCKET_BACKEND_SERIAL_PORT, /* Support Unix socket as a backend for a serial
port */
+ CH_RESTORE_WITH_NEW_TAPFDS, /* Cloud-hypervisor support for restore with new net fds
*/
CH_CAPS_LAST /* this must always be the last item */
} virCHCapsFlags;
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
index fbeac60825..24aed471c5 100644
--- a/src/ch/ch_driver.c
+++ b/src/ch/ch_driver.c
@@ -680,22 +680,24 @@ chDomainDestroy(virDomainPtr dom)
}
static int
-chDomainSaveAdditionalValidation(virDomainDef *vmdef)
+chDomainSaveRestoreAdditionalValidation(virCHDriver *driver, virDomainDef *vmdef)
{
- /*
- SAVE and RESTORE are functional only without any networking and
- device passthrough configuration
- */
- if (vmdef->nnets > 0) {
- virReportError(VIR_ERR_OPERATION_INVALID, "%s",
- _("cannot save domain with network interfaces"));
- return -1;
- }
+ /* SAVE and RESTORE are functional only without any host device
+ * passthrough configuration */
if (vmdef->nhostdevs > 0) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
- _("cannot save domain with host devices"));
+ _("cannot save/restore domain with host devices"));
return -1;
}
+
+ if (vmdef->nnets > 0) {
+ if (!virBitmapIsBitSet(driver->chCaps, CH_RESTORE_WITH_NEW_TAPFDS)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("cannot save/restore domain with network
devices"));
+ return -1;
+ }
+ }
+
return 0;
}
@@ -728,7 +730,7 @@ chDoDomainSave(virCHDriver *driver,
VIR_AUTOCLOSE fd = -1;
int ret = -1;
- if (chDomainSaveAdditionalValidation(vm->def) < 0)
+ if (chDomainSaveRestoreAdditionalValidation(driver, vm->def) < 0)
goto end;
domainState = virDomainObjGetState(vm, NULL);
@@ -1087,6 +1089,9 @@ chDomainRestoreFlags(virConnectPtr conn,
if (virDomainRestoreFlagsEnsureACL(conn, def) < 0)
goto cleanup;
+ if (chDomainSaveRestoreAdditionalValidation(driver, def) < 0)
+ goto cleanup;
+
if (!(vm = virDomainObjListAdd(driver->domains, &def,
driver->xmlopt,
VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
index 8d8be1f46e..3af5e7f2d1 100644
--- a/src/ch/ch_monitor.c
+++ b/src/ch/ch_monitor.c
@@ -978,14 +978,31 @@ virCHMonitorSaveVM(virCHMonitor *mon, const char *to)
}
int
-virCHMonitorBuildRestoreJson(const char *from, char **jsonstr)
+virCHMonitorBuildRestoreJson(virDomainDef *vmdef, const char *from, char **jsonstr)
{
+ size_t i;
g_autoptr(virJSONValue) restore_json = virJSONValueNewObject();
-
g_autofree char *path_url = g_strdup_printf("file://%s", from);
+
if (virJSONValueObjectAppendString(restore_json, "source_url",
path_url))
return -1;
+ /* Pass the netconfig needed to restore with new netfds */
+ if (vmdef->nnets) {
+ g_autoptr(virJSONValue) nets = virJSONValueNewArray();
+ for (i = 0; i < vmdef->nnets; i++) {
+ g_autoptr(virJSONValue) net_json = virJSONValueNewObject();
+ g_autofree char *id = g_strdup_printf("%s_%ld", CH_NET_ID_PREFIX,
i);
+ if (virJSONValueObjectAppendString(net_json, "id", id) < 0)
+ return -1;
+ if (virJSONValueObjectAppendNumberInt(net_json, "num_fds",
vmdef->nets[i]->driver.virtio.queues))
+ return -1;
+ virJSONValueArrayAppend(nets, &net_json);
+ }
+ if (virJSONValueObjectAppend(restore_json, "net_fds", &nets))
+ return -1;
+ }
+
if (!(*jsonstr = virJSONValueToString(restore_json, false)))
return -1;
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
index 375b7a49a4..02a6685d58 100644
--- a/src/ch/ch_monitor.h
+++ b/src/ch/ch_monitor.h
@@ -127,4 +127,4 @@ int virCHMonitorGetIOThreads(virCHMonitor *mon,
virDomainIOThreadInfo ***iothreads);
int
virCHMonitorBuildNetJson(virDomainNetDef *netdef, int netindex, char **jsonstr);
-int virCHMonitorBuildRestoreJson(const char *from, char **jsonstr);
+int virCHMonitorBuildRestoreJson(virDomainDef *vmdef, const char *from, char
**jsonstr);
diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c
index 0ac0a5c9a2..540bf6801d 100644
--- a/src/ch/ch_process.c
+++ b/src/ch/ch_process.c
@@ -29,6 +29,7 @@
#include "ch_process.h"
#include "domain_cgroup.h"
#include "domain_interface.h"
+#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "virjson.h"
@@ -716,6 +717,56 @@ chProcessAddNetworkDevices(virCHDriver *driver,
return 0;
}
+/**
+ * virCHRestoreCreateNetworkDevices:
+ * @driver: pointer to driver structure
+ * @vmdef: pointer to domain definition
+ * @vmtapfds: returned array of FDs of guest interfaces
+ * @nvmtapfds: returned number of network indexes
+ * @nicindexes: returned array of network indexes
+ * @nnicindexes: returned number of network indexes
+ *
+ * Create network devices for the domain. This function is called during
+ * domain restore.
+ *
+ * Returns 0 on success or -1 in case of error
+*/
+static int
+virCHRestoreCreateNetworkDevices(virCHDriver *driver,
+ virDomainDef *vmdef,
+ int **vmtapfds,
+ size_t *nvmtapfds,
+ int **nicindexes,
+ size_t *nnicindexes)
+{
+ size_t i, j;
+ size_t tapfd_len;
+ size_t index_vmtapfds;
Instead of duplicating these steps from
chProcessAddNetworkDevices,
please consider refactoring them into a different to be used here and
in chProcessAddNetworkDevices.
+ for (i = 0; i < vmdef->nnets; i++) {
+ g_autofree int *tapfds = NULL;
+ tapfd_len = vmdef->nets[i]->driver.virtio.queues;
+ if (virCHDomainValidateActualNetDef(vmdef->nets[i]) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("net definition failed validation"));
+ return -1;
+ }
+ tapfds = g_new0(int, tapfd_len);
+ memset(tapfds, -1, (tapfd_len) * sizeof(int));
+
+ /* Connect Guest interfaces */
+ if (virCHConnetNetworkInterfaces(driver, vmdef, vmdef->nets[i], tapfds,
+ nicindexes, nnicindexes) < 0)
+ return -1;
+
+ index_vmtapfds = *nvmtapfds;
+ VIR_EXPAND_N(*vmtapfds, *nvmtapfds, tapfd_len);
+ for (j = 0; j < tapfd_len; j++) {
+ VIR_APPEND_ELEMENT_INPLACE(*vmtapfds, index_vmtapfds, tapfds[j]);
+ }
+ }
+ return 0;
+}
+
/**
* virCHProcessStartValidate:
* @driver: pointer to driver structure
@@ -917,7 +968,12 @@ virCHProcessStartRestore(virCHDriver *driver, virDomainObj *vm,
const char *from
g_autofree char *payload = NULL;
g_autofree char *response = NULL;
VIR_AUTOCLOSE mon_sockfd = -1;
+ g_autofree int *tapfds = NULL;
+ g_autofree int *nicindexes = NULL;
size_t payload_len;
+ size_t ntapfds = 0;
+ size_t nnicindexes = 0;
+ int ret = -1;
if (!priv->monitor) {
/* Get the first monitor connection if not already */
@@ -932,17 +988,7 @@ virCHProcessStartRestore(virCHDriver *driver, virDomainObj *vm,
const char *from
vm->def->id = vm->pid;
priv->machineName = virCHDomainGetMachineName(vm);
- /* Pass 0, NULL as restore only works without networking support */
- if (virDomainCgroupSetupCgroup("ch", vm,
- 0, NULL, /* nnicindexes, nicindexes */
- &priv->cgroup,
- cfg->cgroupControllers,
- 0, /*maxThreadsPerProc*/
- priv->driver->privileged,
- priv->machineName) < 0)
- return -1;
-
- if (virCHMonitorBuildRestoreJson(from, &payload) < 0) {
+ if (virCHMonitorBuildRestoreJson(vm->def, from, &payload) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to restore domain"));
return -1;
@@ -960,20 +1006,40 @@ virCHProcessStartRestore(virCHDriver *driver, virDomainObj *vm,
const char *from
if ((mon_sockfd = chMonitorSocketConnect(priv->monitor)) < 0)
return -1;
- if (virSocketSendMsgWithFDs(mon_sockfd, payload, payload_len, NULL, 0) < 0) {
+ if (virCHRestoreCreateNetworkDevices(driver, vm->def, &tapfds, &ntapfds,
&nicindexes, &nnicindexes) < 0)
+ goto cleanup;
+
+ if (virDomainCgroupSetupCgroup("ch", vm,
+ nnicindexes, nicindexes,
+ &priv->cgroup,
+ cfg->cgroupControllers,
+ 0, /*maxThreadsPerProc*/
+ priv->driver->privileged,
+ priv->machineName) < 0)
+ goto cleanup;
+
+ /* Bring up netdevs before restoring vm */
+ if (virDomainInterfaceStartDevices(vm->def) < 0)
+ goto cleanup;
+
+ if (virSocketSendMsgWithFDs(mon_sockfd, payload, payload_len, tapfds, ntapfds) <
0) {
virReportSystemError(errno, "%s",
_("Failed to send restore request to CH"));
- return -1;
+ goto cleanup;
}
After sending the FDs, they have to closed on Libvirt side, even if the
send was successful. Please check chProcessAddNetworkDevices for details
on it.
/* Restore is a syncronous operationin CH. so, pass false to
wait until there's a reponse */
if (chSocketProcessHttpResponse(mon_sockfd, false) < 0)
- return -1;
+ goto cleanup;
if (virCHProcessSetup(vm) < 0)
- return -1;
+ goto cleanup;
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_FROM_SNAPSHOT);
+ ret = 0;
- return 0;
+ cleanup:
+ if (tapfds)
+ chCloseFDs(tapfds, ntapfds);
+ return ret;
}
--
Regards,
Praveen