[PATCH v2 0/2] Introduce virDomainBlockRebase2 API

During first series discussion we agreed on having API that do both - block pull and block copy was a mistake. Thus, I present 'block-stream-only' API. Changes since last revision: - Dropped block-copy functionality - Enabled bandwidth units as params, not as a flag - Improved documentation P.S. Not sure we need virsh support at this stage, because current params can be covered with 'virsh blockpull'. But if you propose more params or we want account for future changes, I can add virsh support. Nikolai Barybin (2): api: remote: introduce virDomainBlockRebase2 API qemu: implement driver support for domainBlockRebase2 API include/libvirt/libvirt-domain.h | 43 +++++++++++++++++++++ src/driver-hypervisor.h | 8 ++++ src/libvirt-domain.c | 65 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/qemu/qemu_driver.c | 48 +++++++++++++++++++++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 ++++++++- src/remote_protocol-structs | 10 +++++ 8 files changed, 196 insertions(+), 1 deletion(-) -- 2.43.5

This new API is a combination of virDomainBlockPull and virDomainBlockRebase (without copy mode) which supports extendable list of virTypedParameterPtr params. It is designed to allow more configurable use of underlying 'block-stream' QMP command. If "base" == NULL and flags == 0 it is equivalent to virDomainBlockPull. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- include/libvirt/libvirt-domain.h | 43 +++++++++++++++++++++ src/driver-hypervisor.h | 8 ++++ src/libvirt-domain.c | 65 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 ++++++++- src/remote_protocol-structs | 10 +++++ 7 files changed, 148 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 71bb49fe6c..1124c9d58f 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4459,6 +4459,49 @@ int virDomainBlockRebase(virDomainPtr dom, const char *disk, const char *base, unsigned long bandwidth, unsigned int flags); +/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE: + * Macro for the virDomainBlockRebase2 parameter 'base'. + * Expects string specifying new base block device. + * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE "base" + +/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_MIB: + * Macro for the virDomainBlockRebase2 parameter 'bandwidth-mib'. + * Expects desired bandwidth in MiB/s. + * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_MIB "bandwidth-mib" + +/** + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_BYTES: + * Macro for the virDomainBlockRebase2 parameter 'bandwidth-bytes'. + * Expects desired bandwidth in Bytes/s. + * + * Since: 11.8.0 + */ +#define VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_BYTES "bandwidth-bytes" + +/** + * virDomainBlockRebase2Flags: + * + * Flags available for virDomainBlockRebase2(). + * + * Since: 11.8.0 + */ +typedef enum { + /* Keep backing chain referenced using relative names (Since: 11.8.0) */ + VIR_DOMAIN_BLOCK_REBASE_2_RELATIVE = 1 << 4, +} virDomainBlockRebase2Flags; + +int virDomainBlockRebase2(virDomainPtr dom, const char *disk, + virTypedParameterPtr params, int nparams, + unsigned int flags); + /** * virDomainBlockCopyFlags: * diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 6a43688b0c..e64e00674c 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1089,6 +1089,13 @@ typedef int unsigned long bandwidth, unsigned int flags); +typedef int +(*virDrvDomainBlockRebase2)(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags); + typedef int (*virDrvDomainBlockCopy)(virDomainPtr dom, const char *path, @@ -1681,6 +1688,7 @@ struct _virHypervisorDriver { virDrvDomainBlockJobSetSpeed domainBlockJobSetSpeed; virDrvDomainBlockPull domainBlockPull; virDrvDomainBlockRebase domainBlockRebase; + virDrvDomainBlockRebase2 domainBlockRebase2; virDrvDomainBlockCopy domainBlockCopy; virDrvDomainBlockCommit domainBlockCommit; virDrvConnectSetKeepAlive connectSetKeepAlive; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index ca110bdf85..58e3746a09 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11197,6 +11197,71 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk, } +/** + * virDomainBlockRebase2: + * @dom: pointer to domain object + * @disk: path to the block device, or device shorthand + * @params: pointer to block rebase parameter + * @nparams: number of block rebase parameters + * @flags: bitwise-OR of virDomainBlockRebase2Flags + * + * Generelized version of virDomainBlockPull with more precise params setting + * for underlying QMP 'block-stream'. + * + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE in @params specifies intermidiate block device + * in a disk backing chain which will result in a new base for a given disk. If ommitted + * or NULL it is equavivalent to normal block pull, when whole backing chain is pulled + * into top image. + * + * VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_MIB and VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_BYTES + * in @params specify bandwidth in MiB/s or Bytes/s respectively. + * + * If @flags contains VIR_DOMAIN_BLOCK_REBASE_2_RELATIVE, the name recorded + * into the active disk as the location for @base will be kept relative. + * The operation will fail if libvirt can't infer the name. + * + * Returns 0 if the operation has started, -1 on failure. + * + * Since: 11.8.0 + */ +int +virDomainBlockRebase2(virDomainPtr dom, + const char *disk, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(dom, "disk=%s, params=%p, nparams=%u, flags=0x%x", + disk, params, nparams, flags); + VIR_TYPED_PARAMS_DEBUG(params, nparams); + + virResetLastError(); + + virCheckDomainReturn(dom, -1); + conn = dom->conn; + + virCheckReadOnlyGoto(conn->flags, error); + virCheckNonNullArgGoto(disk, error); + + if (conn->driver->domainBlockRebase2) { + int ret; + ret = conn->driver->domainBlockRebase2(dom, disk, params, nparams, + flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(dom->conn); + return -1; +} + + /** * virDomainBlockCopy: * @dom: pointer to domain object diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index c506acd2ed..aba336c6c3 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_11.8.0 { + global: + virDomainBlockRebase2; +} LIBVIRT_11.2.0; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed87..efda3255c0 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7865,6 +7865,7 @@ static virHypervisorDriver hypervisor_driver = { .domainBlockJobSetSpeed = remoteDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = remoteDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = remoteDomainBlockRebase, /* 0.9.10 */ + .domainBlockRebase2 = remoteDomainBlockRebase2, /* 11.8.0 */ .domainBlockCopy = remoteDomainBlockCopy, /* 1.2.9 */ .domainBlockCommit = remoteDomainBlockCommit, /* 0.10.2 */ .connectSetKeepAlive = remoteConnectSetKeepAlive, /* 0.9.8 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 3c93203210..506cd146ad 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -119,6 +119,9 @@ const REMOTE_DOMAIN_NUMA_PARAMETERS_MAX = 16; /* Upper limit on list of perf events. */ const REMOTE_DOMAIN_PERF_EVENTS_MAX = 64; +/* Upper limit on block rebase2 tunable parameters. */ +const REMOTE_DOMAIN_BLOCK_REBASE2_PARAMETERS_MAX = 16; + /* Upper limit on block copy tunable parameters. */ const REMOTE_DOMAIN_BLOCK_COPY_PARAMETERS_MAX = 16; @@ -1440,6 +1443,12 @@ struct remote_domain_block_rebase_args { unsigned hyper bandwidth; unsigned int flags; }; +struct remote_domain_block_rebase2_args { + remote_nonnull_domain dom; + remote_nonnull_string path; + remote_typed_param params<REMOTE_DOMAIN_BLOCK_REBASE2_PARAMETERS_MAX>; + unsigned int flags; +}; struct remote_domain_block_copy_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -7119,5 +7128,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: both + * @acl: domain:block_write + */ + REMOTE_PROC_DOMAIN_BLOCK_REBASE2 = 454 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 0f87d13a5a..9cfb476fd9 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1010,6 +1010,15 @@ struct remote_domain_block_rebase_args { uint64_t bandwidth; u_int flags; }; +struct remote_domain_block_rebase2_args { + remote_nonnull_domain dom; + remote_nonnull_string path; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; + u_int flags; +}; struct remote_domain_block_copy_args { remote_nonnull_domain dom; remote_nonnull_string path; @@ -3791,4 +3800,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_THROTTLE_GROUP = 451, REMOTE_PROC_DOMAIN_DEL_THROTTLE_GROUP = 452, REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453, + REMOTE_PROC_DOMAIN_BLOCK_REBASE2 = 454, }; -- 2.43.5

On Fri, Aug 29, 2025 at 10:55:29AM +0300, Nikolai Barybin via Devel wrote:
This new API is a combination of virDomainBlockPull and virDomainBlockRebase (without copy mode) which supports extendable list of virTypedParameterPtr params.
It is designed to allow more configurable use of underlying 'block-stream' QMP command.
If "base" == NULL and flags == 0 it is equivalent to virDomainBlockPull.
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- include/libvirt/libvirt-domain.h | 43 +++++++++++++++++++++ src/driver-hypervisor.h | 8 ++++ src/libvirt-domain.c | 65 ++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 17 ++++++++- src/remote_protocol-structs | 10 +++++ 7 files changed, 148 insertions(+), 1 deletion(-)
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index ca110bdf85..58e3746a09 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11197,6 +11197,71 @@ virDomainBlockRebase(virDomainPtr dom, const char *disk, }
+/** + * virDomainBlockRebase2:
Our normal convention would be to call this virDomainBlockRebaseParams With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Parse params list and pass arguments to underlying call to qemuDomainBlockPullCommon. Now bandwidth unit of messurement is passed as param and passed further with a use of VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES flag. virDomainBlockRebase2 doesn't use equavivalent external flag. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_driver.c | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ac72ea5cb0..b5ca195bfe 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14634,6 +14634,53 @@ qemuDomainBlockRebase(virDomainPtr dom, return ret; } +static int +qemuDomainBlockRebase2(virDomainPtr dom, + const char *path, + virTypedParameterPtr params, + int nparams, + unsigned int flags) +{ + virDomainObj *vm; + size_t i = 0; + unsigned long long bandwidth = 0; + const char *base = NULL; + + virCheckFlags(VIR_DOMAIN_BLOCK_REBASE_2_RELATIVE, -1); + + if (virTypedParamsValidate(params, nparams, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE, + VIR_TYPED_PARAM_STRING, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_MIB, + VIR_TYPED_PARAM_ULLONG, + VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_BYTES, + VIR_TYPED_PARAM_ULLONG, + NULL) < 0) + return -1; + + if (!(vm = qemuDomainObjFromDomain(dom))) + return -1; + + if (virDomainBlockRebase2EnsureACL(dom->conn, vm->def) < 0) + return -1; + + for (i = 0; i < nparams; i++) { + virTypedParameterPtr param = ¶ms[i]; + + if (STREQ(param->field, VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_MIB)) { + bandwidth = param->value.ul; + } else if (STREQ(param->field, VIR_DOMAIN_BLOCK_REBASE_PARAM_BANDWIDTH_BYTES)) { + bandwidth = param->value.ul; + flags |= VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES; + } else if (STREQ(param->field, VIR_DOMAIN_BLOCK_REBASE_PARAM_BASE)) { + base = param->value.s; + } + } + + return qemuDomainBlockPullCommon(vm, path, base, bandwidth, flags); +} + + static int qemuDomainBlockCopy(virDomainPtr dom, @@ -20579,6 +20626,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockRebase2 = qemuDomainBlockRebase2, /* 11.8.0 */ .domainBlockCopy = qemuDomainBlockCopy, /* 1.2.9 */ .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .connectIsAlive = qemuConnectIsAlive, /* 0.9.8 */ -- 2.43.5
participants (2)
-
Daniel P. Berrangé
-
Nikolai Barybin