On Thu, Jul 25, 2024 at 12:04:47PM -0500, Praveen K Paladugu wrote: > > > 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@xxxxxxxxxxxxxxxxxxx> > > --- > > 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. chProcessAddNetworkDevices is already refactored and the common code is moved to new functions. Since the action performed inside the loop are different here, this can not be refactored further. > > + 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. This is taken care in the cleanup section. > > /* 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