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 <tejus.gk@nutanix.com>
---
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
On Wed, Jan 14, 2026 at 10:42:02 +0000, Tejus GK wrote:
> 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 <tejus.gk@nutanix.com>
> ---
> 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/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
> + */
This looks identical to as long as you are about to use the FD with a VM
object:
/**
* virDomainFDAssociate:
* @domain: a domain object
* @name: name for the file descriptor group
* @nfds: number of fds in @fds
* @fds: file descriptors to associate with domain
* @flags: optional flags; bitwise-OR of supported virDomainFDAssociateFlags
*
* Associate the FDs in @fd with @domain under @name. The FDs are associated as
* long as the connection used to associated exists and are disposed of
* afterwards. FD may still be kept open by the hypervisor for as long as it's
* needed.
*
* Security labelling (e.g. via the selinux) may be applied on the passed FDs
* when required for usage by the VM. By default libvirt does not restore the
* seclabels on the FDs afterwards to avoid keeping it open unnecessarily.
*
* Restoring of the security label can be requested by passing either
* VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE for a best-effort attempt to restore
* the security label after use.
* Requesting the restore of security label will require that the file
* descriptors are kept open for the whole time they are used by the hypervisor,
* or other additional overhead.
*
* In certain cases usage of the fd group would imply read-only access. Passing
* VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE in @flags ensures that a writable
* security label is picked in case when the file represented by the fds may
* be used in write mode.
*
* Returns 0 on success, -1 on error.
*
* Since: 9.0.0
*/
int
virDomainFDAssociate(virDomainPtr domain,
const char *name,
unsigned int nfds,
int *fds,
unsigned int flags)
> + 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);
On Wed, Jan 14, 2026 at 13:18:51 +0100, Peter Krempa via Devel wrote:
> On Wed, Jan 14, 2026 at 10:42:02 +0000, Tejus GK wrote:
> > 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 <tejus.gk@nutanix.com>
> > ---
> > 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/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
> > + */
>
> This looks identical to as long as you are about to use the FD with a VM
> object:
Looking at the usage in the code it seems to be in a place which has
@domain available, so no new API should be required for this usage.
>
> /**
> * virDomainFDAssociate:
> * @domain: a domain object
> * @name: name for the file descriptor group
> * @nfds: number of fds in @fds
> * @fds: file descriptors to associate with domain
> * @flags: optional flags; bitwise-OR of supported virDomainFDAssociateFlags
> *
> * Associate the FDs in @fd with @domain under @name. The FDs are associated as
> * long as the connection used to associated exists and are disposed of
> * afterwards. FD may still be kept open by the hypervisor for as long as it's
> * needed.
> *
> * Security labelling (e.g. via the selinux) may be applied on the passed FDs
> * when required for usage by the VM. By default libvirt does not restore the
> * seclabels on the FDs afterwards to avoid keeping it open unnecessarily.
> *
> * Restoring of the security label can be requested by passing either
> * VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE for a best-effort attempt to restore
> * the security label after use.
> * Requesting the restore of security label will require that the file
> * descriptors are kept open for the whole time they are used by the hypervisor,
> * or other additional overhead.
> *
> * In certain cases usage of the fd group would imply read-only access. Passing
> * VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE in @flags ensures that a writable
> * security label is picked in case when the file represented by the fds may
> * be used in write mode.
> *
> * Returns 0 on success, -1 on error.
> *
> * Since: 9.0.0
> */
> int
> virDomainFDAssociate(virDomainPtr domain,
> const char *name,
> unsigned int nfds,
> int *fds,
> unsigned int flags)
>
>
> > + 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);
>
> On 14 Jan 2026, at 5:54 PM, Peter Krempa <pkrempa@redhat.com> wrote: > > !-------------------------------------------------------------------| > CAUTION: External Email > > |-------------------------------------------------------------------! > > On Wed, Jan 14, 2026 at 13:18:51 +0100, Peter Krempa via Devel wrote: >> On Wed, Jan 14, 2026 at 10:42:02 +0000, Tejus GK wrote: >>> 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 <tejus.gk@nutanix.com> >>> --- >>> 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/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 >>> + */ >> >> This looks identical to as long as you are about to use the FD with a VM >> object: > > Looking at the usage in the code it seems to be in a place which has > @domain available, so no new API should be required for this usage. Hi Peter, Thank you for the review! I agree that the API I’ve introduced is quite similar to virDomainFDAssociate. But it can’t be reused for the client FD-based live migration. The reason is that virDomainFDAssociate requires a @domain object, meaning a domain up and running at the time of the API call. However, in the live-migration use case, on the destination host, the domain doesn’t exist prior to the migration being triggered; hence, the client won’t be able to pass an FD to the VM, which doesn’t exist at that point. Hence, virDomainFDStore is there to store FDs associated with the “name” key, rather than the domain. Regards, Tejus
On Wed, Jan 14, 2026 at 13:46:53 +0000, Tejus GK wrote: > > > > On 14 Jan 2026, at 5:54 PM, Peter Krempa <pkrempa@redhat.com> wrote: > > > > !-------------------------------------------------------------------| > > CAUTION: External Email > > > > |-------------------------------------------------------------------! > > > > On Wed, Jan 14, 2026 at 13:18:51 +0100, Peter Krempa via Devel wrote: > >> On Wed, Jan 14, 2026 at 10:42:02 +0000, Tejus GK wrote: > >>> 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 <tejus.gk@nutanix.com> > >>> --- > >>> 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/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 > >>> + */ > >> > >> This looks identical to as long as you are about to use the FD with a VM > >> object: > > > > Looking at the usage in the code it seems to be in a place which has > > @domain available, so no new API should be required for this usage. > > Hi Peter, > > Thank you for the review! I agree that the API I’ve introduced is quite similar to virDomainFDAssociate. > But it can’t be reused for the client FD-based live migration. The reason is that virDomainFDAssociate requires a @domain object, meaning a domain up and running at the time of the API call. However, in the live-migration use case, on the destination host, the domain doesn’t exist prior to the migration being triggered; hence, the client won’t be able to pass an FD to the VM, which doesn’t exist at that point. Good point. > > Hence, virDomainFDStore is there to store FDs associated with the “name” key, rather than the domain. Okay but that will make things much more complex due to our ACL stuff. Allowing to pass any FD means that any user (even those who can't see the VM) could pass a FD and theoretically siphon data off from it. I don't think we want to have a 'global' FD pool to pass. In case of migration I'd either suggest to define the VM (with possibly a dummy definition) just to have the domain object available. Alternatively we should have a migration API allowing FD passing.
> On 14 Jan 2026, at 9:00 PM, Peter Krempa <pkrempa@redhat.com> wrote: > > !-------------------------------------------------------------------| > CAUTION: External Email > > |-------------------------------------------------------------------! > > On Wed, Jan 14, 2026 at 13:46:53 +0000, Tejus GK wrote: >> >> >>> On 14 Jan 2026, at 5:54 PM, Peter Krempa <pkrempa@redhat.com> wrote: >>> >>> !-------------------------------------------------------------------| >>> CAUTION: External Email >>> >>> |-------------------------------------------------------------------! >>> >>> On Wed, Jan 14, 2026 at 13:18:51 +0100, Peter Krempa via Devel wrote: >>>> On Wed, Jan 14, 2026 at 10:42:02 +0000, Tejus GK wrote: >>>>> 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 <tejus.gk@nutanix.com> >>>>> --- >>>>> 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/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 >>>>> + */ >>>> >>>> This looks identical to as long as you are about to use the FD with a VM >>>> object: >>> >>> Looking at the usage in the code it seems to be in a place which has >>> @domain available, so no new API should be required for this usage. >> >> Hi Peter, >> >> Thank you for the review! I agree that the API I’ve introduced is quite similar to virDomainFDAssociate. >> But it can’t be reused for the client FD-based live migration. The reason is that virDomainFDAssociate requires a @domain object, meaning a domain up and running at the time of the API call. However, in the live-migration use case, on the destination host, the domain doesn’t exist prior to the migration being triggered; hence, the client won’t be able to pass an FD to the VM, which doesn’t exist at that point. > > Good point. > >> >> Hence, virDomainFDStore is there to store FDs associated with the “name” key, rather than the domain. > > Okay but that will make things much more complex due to our ACL stuff. > > Allowing to pass any FD means that any user (even those who can't see > the VM) could pass a FD and theoretically siphon data off from it. > > I don't think we want to have a 'global' FD pool to pass. In case of > migration I'd either suggest to define the VM (with possibly a dummy > definition) just to have the domain object available. Alternatively we > should have a migration API allowing FD passing. Hi Peter, Thank you for the suggestion! I wanted to clarify that my design of maintaining a global pool of FDs, was so to seamlessly support the use case for managed P2P migrations. > I'd either suggest to define the VM (with possibly a dummy definition) Is this workflow supported in P2P migrations? Also, we would have to at some point of time, blow up the VM definition to match the one on the source, does libvirt have support for that? > Alternatively we should have a migration API allowing FD passing. Hmm, even if we go ahead and implement that, I’m wondering how to make it work for P2P managed migrations. Since, source libvirt won’t be able to pass on FDs to the destination host. Thoughts? Regards, Tejus
On Mon, Jan 19, 2026 at 14:08:29 +0000, Tejus GK wrote: > > On 14 Jan 2026, at 9:00 PM, Peter Krempa <pkrempa@redhat.com> wrote: > > > > !-------------------------------------------------------------------| > > CAUTION: External Email > > > > |-------------------------------------------------------------------! Oh no! [...] > > > >> > >> Hence, virDomainFDStore is there to store FDs associated with the “name” key, rather than the domain. > > > > Okay but that will make things much more complex due to our ACL stuff. > > > > Allowing to pass any FD means that any user (even those who can't see > > the VM) could pass a FD and theoretically siphon data off from it. > > > > I don't think we want to have a 'global' FD pool to pass. In case of > > migration I'd either suggest to define the VM (with possibly a dummy > > definition) just to have the domain object available. Alternatively we > > should have a migration API allowing FD passing. > > Hi Peter, > Thank you for the suggestion! I wanted to clarify that my design of maintaining a global pool of FDs, was so to seamlessly support the use case for managed P2P migrations. > > > I'd either suggest to define the VM (with possibly a dummy definition) > Is this workflow supported in P2P migrations? Also, we would have to at some point of time, blow up the VM definition to match the one on the source, does libvirt have support for that? Yes, ... > > Alternatively we should have a migration API allowing FD passing. > Hmm, even if we go ahead and implement that, I’m wondering how to make it work for P2P managed migrations. Since, source libvirt won’t be able to pass on FDs to the destination host. Thoughts? you need to open another new client connection on the destination and pass the FDs. In fact that's requrired since FD passing works only locally and the remote daemon is thus unable to do that. For now you can test it for FD-passed disks, where prior to migration you open a new connection and pass the FDs then kick off the migration This is a complex deployment where you do need to set up stuff on the destination manually so IMO this is a reasonable trade-off.
> On 20 Jan 2026, at 8:45 PM, Peter Krempa <pkrempa@redhat.com> wrote: > > !-------------------------------------------------------------------| > CAUTION: External Email > > |-------------------------------------------------------------------! > > On Mon, Jan 19, 2026 at 14:08:29 +0000, Tejus GK wrote: >>> On 14 Jan 2026, at 9:00 PM, Peter Krempa <pkrempa@redhat.com> wrote: >>> >>> !-------------------------------------------------------------------| >>> CAUTION: External Email >>> >>> |-------------------------------------------------------------------! > > Oh no! > Hah! > >>> >>>> >>>> Hence, virDomainFDStore is there to store FDs associated with the “name” key, rather than the domain. >>> >>> Okay but that will make things much more complex due to our ACL stuff. >>> >>> Allowing to pass any FD means that any user (even those who can't see >>> the VM) could pass a FD and theoretically siphon data off from it. >>> >>> I don't think we want to have a 'global' FD pool to pass. In case of >>> migration I'd either suggest to define the VM (with possibly a dummy >>> definition) just to have the domain object available. Alternatively we >>> should have a migration API allowing FD passing. >> >> Hi Peter, >> Thank you for the suggestion! I wanted to clarify that my design of maintaining a global pool of FDs, was so to seamlessly support the use case for managed P2P migrations. >> >>> I'd either suggest to define the VM (with possibly a dummy definition) >> Is this workflow supported in P2P migrations? Also, we would have to at some point of time, blow up the VM definition to match the one on the source, does libvirt have support for that? > > Yes, ... > >>> Alternatively we should have a migration API allowing FD passing. >> Hmm, even if we go ahead and implement that, I’m wondering how to make it work for P2P managed migrations. Since, source libvirt won’t be able to pass on FDs to the destination host. Thoughts? > > you need to open another new client connection on the destination and > pass the FDs. In fact that's requrired since FD passing works only > locally and the remote daemon is thus unable to do that. > > For now you can test it for FD-passed disks, where prior to migration > you open a new connection and pass the FDs then kick off the migration > > This is a complex deployment where you do need to set up stuff on the > destination manually so IMO this is a reasonable trade-off. > Hi Peter, thanks for the dummy VM suggestion, I gave it a spin, and it seems to work! I’ll rework the patch-series keeping the approach in mind, and share it on the list soon. Regards, Tejus
© 2016 - 2026 Red Hat, Inc.