This API allows callers to pass and store file descriptors inside libvirt using a hash table, with a provided "name" serving as the key. The stored file descriptors can be queried and reused later, enabling use cases such as FD-based live migration.
Signed-off-by: Tejus GK <[email protected]> --- include/libvirt/libvirt-domain.h | 9 +++++ src/driver-hypervisor.h | 7 ++++ src/libvirt-domain.c | 62 +++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/qemu/qemu_conf.h | 4 ++ src/qemu/qemu_driver.c | 41 +++++++++++++++++++ src/remote/remote_daemon_dispatch.c | 36 +++++++++++++++++ src/remote/remote_driver.c | 22 ++++++++++ src/remote/remote_protocol.x | 12 +++++- 9 files changed, 197 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index b0acd0083b..be40a127d6 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -8751,5 +8751,14 @@ virDomainDelThrottleGroup(virDomainPtr dom, const char *group, unsigned int flags); +/** +* virDomainFDStore: +* +* since: 12.0.0 +*/ +int virDomainFDStore(virConnectPtr conn, + const char *name, + unsigned int nfds, + int *fds); #endif /* LIBVIRT_DOMAIN_H */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 6a43688b0c..0c2a9041fe 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1473,6 +1473,12 @@ typedef int const char *groupname, unsigned int flags); +typedef int +(*virDrvDomainFDStore)(virConnectPtr conn, + const char *name, + unsigned int nfds, + int *fds); + typedef struct _virHypervisorDriver virHypervisorDriver; /** @@ -1750,4 +1756,5 @@ struct _virHypervisorDriver { virDrvDomainGraphicsReload domainGraphicsReload; virDrvDomainSetThrottleGroup domainSetThrottleGroup; virDrvDomainDelThrottleGroup domainDelThrottleGroup; + virDrvDomainFDStore domainFDStore; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 74c70a0a43..7e194f7193 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -14269,3 +14269,65 @@ virDomainDelThrottleGroup(virDomainPtr dom, virDispatchError(dom->conn); return -1; } + + +/** + * virDomainFDStore: + * @conn: a connection object + * @name: name for the file descriptor group + * @nfds: number of fds in @fds + * @fds: file descriptors to store + * + * Store the FDs in @fds with @conn under @name. The FDs persist unitl + * explicitly consumed (e.g. during a live migration) or until libvirt + * shuts down/ restarts. + * + * Unlike virDomainFDAssociate, this does not require a domain to exist + * at the time of storing the FDs. + * + * Returns 0 on success, -1 on error. + * + * Since: 12.0.0 + */ + int + virDomainFDStore(virConnectPtr conn, + const char *name, + unsigned int nfds, + int *fds) + { + int rc; + + VIR_DEBUG("conn=%p, name='%s', nfds=%u, fds=%p", + conn, name, nfds, fds); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + virCheckNonNullArgGoto(name, error); + virCheckNonZeroArgGoto(nfds, error); + virCheckNonNullArgGoto(fds, error); + + if ((rc = VIR_DRV_SUPPORTS_FEATURE(conn->driver, conn, + VIR_DRV_FEATURE_FD_PASSING)) < 0) + goto error; + + if (rc == 0) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("fd passing is not supported by this connection")); + goto error; + } + + if (!conn->driver->domainFDStore) { + virReportUnsupportedError(); + goto error; + } + + if ((rc = conn->driver->domainFDStore(conn, name, nfds, fds)) < 0) + goto error; + + return rc; + + error: + virDispatchError(conn); + return -1; + } diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index c506acd2ed..3b3a4bb4e1 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -956,4 +956,9 @@ LIBVIRT_11.2.0 { virDomainDelThrottleGroup; } LIBVIRT_10.2.0; +LIBVIRT_12.0.0 { + global: + virDomainFDStore; +} LIBVIRT_11.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index c284e108a1..cc655811f7 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -349,6 +349,10 @@ struct _virQEMUDriver { /* Immutable pointer, self-locking APIs */ virFileCache *nbdkitCapsCache; + + /* Hash table mapping FD names (string) to qemuFDTuple objects */ + GHashTable *domainFDs; + }; virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 3f154969b8..94a7b17090 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -35,6 +35,7 @@ #include "qemu_alias.h" #include "qemu_block.h" #include "qemu_conf.h" +#include "qemu_fd.h" #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_hostdev.h" @@ -558,6 +559,11 @@ qemuStateInitialize(bool privileged, if (!(qemu_driver->domains = virDomainObjListNew())) goto error; + qemu_driver->domainFDs = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) qemuFDTupleFree); + /* Init domain events */ qemu_driver->domainEventState = virObjectEventStateNew(); if (!qemu_driver->domainEventState) @@ -1033,6 +1039,7 @@ qemuStateCleanup(void) virObjectUnref(qemu_driver->domains); virObjectUnref(qemu_driver->nbdkitCapsCache); virInhibitorFree(qemu_driver->inhibitor); + g_clear_pointer(&qemu_driver->domainFDs, g_hash_table_unref); if (qemu_driver->lockFD != -1) virPidFileRelease(qemu_driver->config->stateDir, "driver", qemu_driver->lockFD); @@ -20259,6 +20266,39 @@ qemuDomainFDAssociate(virDomainPtr domain, return ret; } +static int +qemuDomainFDStore(virConnectPtr conn, + const char *name, + unsigned int nfds, + int *fds) +{ + virQEMUDriver *driver = conn->privateData; + g_autoptr(qemuFDTuple) new = NULL; + size_t i; + + if (nfds == 0) + return 0; + + new = qemuFDTupleNew(); + new->nfds = nfds; + new->fds = g_new0(int, new->nfds); + + for (i = 0; i < new->nfds; i++) { + if ((new->fds[i] = dup(fds[i])) < 0) { + virReportSystemError(errno, + _("failed to duplicate passed fd with index '%1$zu'"), + i); + return -1; + } + } + + VIR_WITH_MUTEX_LOCK_GUARD(&driver->lock) { + g_hash_table_insert(driver->domainFDs, g_strdup(name), g_steal_pointer(&new)); + } + + return 0; +} + static int qemuDomainGraphicsReload(virDomainPtr domain, unsigned int type, @@ -20804,6 +20844,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainSetAutostartOnce = qemuDomainSetAutostartOnce, /* 11.2.0 */ .domainSetThrottleGroup = qemuDomainSetThrottleGroup, /* 11.2.0 */ .domainDelThrottleGroup = qemuDomainDelThrottleGroup, /* 11.2.0 */ + .domainFDStore = qemuDomainFDStore, /* 12.0.0 */ }; diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index 7e74ff063f..bbf39be748 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -7163,6 +7163,42 @@ remoteDispatchNetworkPortGetParameters(virNetServer *server G_GNUC_UNUSED, return rv; } +static int +remoteDispatchDomainFdStore(virNetServer *server G_GNUC_UNUSED, + virNetServerClient *client, + virNetMessage *msg G_GNUC_UNUSED, + struct virNetMessageError *rerr, + remote_domain_fd_store_args *args) +{ + int *fds = NULL; + unsigned int nfds = 0; + int rv = -1; + virConnectPtr conn = remoteGetHypervisorConn(client); + size_t i; + + if (!conn) + goto cleanup; + + fds = g_new0(int, msg->nfds); + for (i = 0; i < msg->nfds; i++) { + if ((fds[i] = virNetMessageDupFD(msg, i)) < 0) + goto cleanup; + nfds++; + } + + if (virDomainFDStore(conn, args->name, nfds, fds) < 0) + goto cleanup; + + rv = 0; + + cleanup: + for (i = 0; i < nfds; i++) + VIR_FORCE_CLOSE(fds[i]); + g_free(fds); + if (rv < 0) + virNetMessageSaveError(rerr); + return rv; +} /*----- Helpers. -----*/ diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed87..db255ac125 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7488,6 +7488,27 @@ remoteDomainFDAssociate(virDomainPtr domain, return 0; } +static int +remoteDomainFDStore(virConnectPtr conn, + const char *name, + unsigned int nfds, + int *fds) +{ + remote_domain_fd_store_args args = {0}; + struct private_data *priv = conn->privateData; + VIR_LOCK_GUARD lock = remoteDriverLock(priv); + + args.name = (char *)name; + + if (callFull(conn, priv, 0, fds, nfds, NULL, NULL, + REMOTE_PROC_DOMAIN_FD_STORE, + (xdrproc_t) xdr_remote_domain_fd_store_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. @@ -7935,6 +7956,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGraphicsReload = remoteDomainGraphicsReload, /* 10.2.0 */ .domainSetThrottleGroup = remoteDomainSetThrottleGroup, /* 11.2.0 */ .domainDelThrottleGroup = remoteDomainDelThrottleGroup, /* 11.2.0 */ + .domainFDStore = remoteDomainFDStore, /* 12.0.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 3c93203210..3ddfdd8bec 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -4008,6 +4008,10 @@ struct remote_domain_event_nic_mac_change_msg { remote_nonnull_string newMAC; }; +struct remote_domain_fd_store_args { + remote_nonnull_string name; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -7119,5 +7123,11 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453 + REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453, + + /** + * @generate: none + * @acl: none + */ + REMOTE_PROC_DOMAIN_FD_STORE = 454 }; -- 2.43.7
