[libvirt] [PATCH 0/5] Add API for individual/specific vCPU hotplug

The new API allows selecting specific vCPUs to be added/removed from the VM. The existing APIs allowed only adding/removing from the end which did not play well with NUMA. Peter Krempa (5): lib: Add API for specific vCPU hot(un)plug virsh: Implement command for virDomainSetVcpu called setvcpu qemu: Implement individual vcpu hotplug API test: qemuhotplugtest: Add testing of individual vcpu hotplug news: Mention specific vCPU hotplug API docs/news.xml | 10 + include/libvirt/libvirt-domain.h | 5 + src/driver-hypervisor.h | 7 + src/libvirt-domain.c | 48 ++++ src/libvirt_public.syms | 5 + src/qemu/qemu_driver.c | 74 +++++ src/qemu/qemu_hotplug.c | 153 ++++++++++ src/qemu/qemu_hotplug.h | 7 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +- src/remote_protocol-structs | 7 + tests/qemuhotplugtest.c | 63 ++++ .../ppc64-modern-individual-domain.xml | 20 ++ .../ppc64-modern-individual-monitor.json | 318 +++++++++++++++++++++ .../ppc64-modern-individual-result-conf.xml | 64 +++++ .../ppc64-modern-individual-result-live.xml | 72 +++++ .../x86-modern-individual-add-domain.xml | 21 ++ .../x86-modern-individual-add-monitor.json | 299 +++++++++++++++++++ .../x86-modern-individual-add-result-conf.xml | 40 +++ .../x86-modern-individual-add-result-live.xml | 48 ++++ tools/virsh-domain.c | 88 ++++++ tools/virsh.pod | 19 ++ 22 files changed, 1384 insertions(+), 1 deletion(-) create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-domain.xml create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-monitor.json create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-result-conf.xml create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-result-live.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-domain.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-monitor.json create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-result-conf.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-result-live.xml -- 2.11.0

Similarly to domainSetGuestVcpus this commit adds API which allows to modify state of individual vcpus rather than just setting the count. This allows to enable CPUs in specific guest NUMA nodes to achieve any necessary configuration. --- include/libvirt/libvirt-domain.h | 5 +++++ src/driver-hypervisor.h | 7 ++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++- src/remote_protocol-structs | 7 ++++++ 7 files changed, 88 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e303140a2..c0f715d66 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4528,4 +4528,9 @@ int virDomainSetGuestVcpus(virDomainPtr domain, int state, unsigned int flags); +int virDomainSetVcpu(virDomainPtr domain, + const char *vcpumap, + int state, + unsigned int flags); + #endif /* __VIR_LIBVIRT_DOMAIN_H__ */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 51af73200..b81420aef 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1251,6 +1251,12 @@ typedef int int state, unsigned int flags); +typedef int +(*virDrvDomainSetVcpu)(virDomainPtr domain, + const char *cpumap, + int state, + unsigned int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; typedef virHypervisorDriver *virHypervisorDriverPtr; @@ -1489,6 +1495,7 @@ struct _virHypervisorDriver { virDrvDomainMigrateStartPostCopy domainMigrateStartPostCopy; virDrvDomainGetGuestVcpus domainGetGuestVcpus; virDrvDomainSetGuestVcpus domainSetGuestVcpus; + virDrvDomainSetVcpu domainSetVcpu; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 5b3e84205..619a9fccb 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -11749,3 +11749,51 @@ virDomainSetGuestVcpus(virDomainPtr domain, virDispatchError(domain->conn); return -1; } + + +/** + * virDomainSetVcpu: + * @domain: pointer to domain object + * @vcpumap: text representation of a bitmap of vcpus to set + * @state: 0 to disable/1 to enable cpus described by @vcpumap + * @flags: bitwise-OR of virDomainModificationImpact + * + * Enables/disables individual vcpus described by @vcpumap in the hypervisor. + * + * Various hypervisor implementations may limit to operate on just 1 + * hotpluggable entity (which may contain multiple vCPUs on certain platforms). + * + * Note that OSes and hypervisors may require vCPU 0 to stay online. + * + * Returns 0 on success, -1 on error. + */ +int +virDomainSetVcpu(virDomainPtr domain, + const char *vcpumap, + int state, + unsigned int flags) +{ + VIR_DOMAIN_DEBUG(domain, "vcpumap='%s' state=%i flags=%x", + NULLSTR(vcpumap), state, flags); + + virResetLastError(); + + virCheckDomainReturn(domain, -1); + virCheckReadOnlyGoto(domain->conn->flags, error); + + virCheckNonNullArgGoto(vcpumap, error); + + if (domain->conn->driver->domainSetVcpu) { + int ret; + ret = domain->conn->driver->domainSetVcpu(domain, vcpumap, state, flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(domain->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 62885ac41..04ef58021 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -753,4 +753,9 @@ LIBVIRT_3.0.0 { virConnectSecretEventDeregisterAny; } LIBVIRT_2.2.0; +LIBVIRT_3.1.0 { + global: + virDomainSetVcpu; +} LIBVIRT_3.0.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 a3f7d9b0b..0c8bfeed1 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8402,6 +8402,7 @@ static virHypervisorDriver hypervisor_driver = { .domainMigrateStartPostCopy = remoteDomainMigrateStartPostCopy, /* 1.3.3 */ .domainGetGuestVcpus = remoteDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = remoteDomainSetGuestVcpus, /* 2.0.0 */ + .domainSetVcpu = remoteDomainSetVcpu, /* 3.1.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 68469a5b1..b2a67c801 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3353,6 +3353,13 @@ struct remote_domain_set_guest_vcpus_args { unsigned int flags; }; +struct remote_domain_set_vcpu_args { + remote_nonnull_domain dom; + remote_nonnull_string cpumap; + int state; + unsigned int flags; +}; + struct remote_domain_event_callback_metadata_change_msg { int callbackID; @@ -6018,6 +6025,13 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383 + REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383, + /** + * @generate: both + * @acl: domain:write + * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE + * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG + */ + REMOTE_PROC_DOMAIN_SET_VCPU = 384 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 0360600cf..e1e53d21b 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2800,6 +2800,12 @@ struct remote_domain_set_guest_vcpus_args { int state; u_int flags; }; +struct remote_domain_set_vcpu_args { + remote_nonnull_domain dom; + remote_nonnull_string cpumap; + int state; + u_int flags; +}; struct remote_domain_event_callback_metadata_change_msg { int callbackID; remote_nonnull_domain dom; @@ -3210,4 +3216,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_SECRET_EVENT_DEREGISTER_ANY = 381, REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382, REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383, + REMOTE_PROC_DOMAIN_SET_VCPU = 384, }; -- 2.11.0

On Fri, Feb 10, 2017 at 06:10:18PM +0100, Peter Krempa wrote:
Similarly to domainSetGuestVcpus this commit adds API which allows to modify state of individual vcpus rather than just setting the count.
This allows to enable CPUs in specific guest NUMA nodes to achieve any necessary configuration. --- include/libvirt/libvirt-domain.h | 5 +++++ src/driver-hypervisor.h | 7 ++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++- src/remote_protocol-structs | 7 ++++++ 7 files changed, 88 insertions(+), 1 deletion(-)
ACK Pavel

On Fri, Feb 10, 2017 at 06:10:18PM +0100, Peter Krempa wrote:
Similarly to domainSetGuestVcpus this commit adds API which allows to modify state of individual vcpus rather than just setting the count.
This allows to enable CPUs in specific guest NUMA nodes to achieve any necessary configuration. --- include/libvirt/libvirt-domain.h | 5 +++++ src/driver-hypervisor.h | 7 ++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++- src/remote_protocol-structs | 7 ++++++ 7 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e303140a2..c0f715d66 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4528,4 +4528,9 @@ int virDomainSetGuestVcpus(virDomainPtr domain, int state, unsigned int flags);
+int virDomainSetVcpu(virDomainPtr domain, + const char *vcpumap, + int state, + unsigned int flags);
In adding support for this to perl/go bindings I see that this is using a different style to the existing APIs. With the exception of the virDomainSetGuestVcpus() API, everything else dealing with CPU maps uses an 'unsigned char *cpu' & 'int maplen' pair to store the data as a bitmap. I think we should change this API to also use the bitmap rather than the textual format as it gives consistency with other APIs, particularly when mapping into other languages. For example in Go, the bitmaps get turned into a boolean array, so it is a very natural representation to manipulate - the textual format is rather inconvenient in this respect. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On Wed, Feb 22, 2017 at 12:16:51 +0000, Daniel Berrange wrote:
On Fri, Feb 10, 2017 at 06:10:18PM +0100, Peter Krempa wrote:
Similarly to domainSetGuestVcpus this commit adds API which allows to modify state of individual vcpus rather than just setting the count.
This allows to enable CPUs in specific guest NUMA nodes to achieve any necessary configuration. --- include/libvirt/libvirt-domain.h | 5 +++++ src/driver-hypervisor.h | 7 ++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++- src/remote_protocol-structs | 7 ++++++ 7 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e303140a2..c0f715d66 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4528,4 +4528,9 @@ int virDomainSetGuestVcpus(virDomainPtr domain, int state, unsigned int flags);
+int virDomainSetVcpu(virDomainPtr domain, + const char *vcpumap, + int state, + unsigned int flags);
In adding support for this to perl/go bindings I see that this is using a different style to the existing APIs. With the exception of the virDomainSetGuestVcpus() API, everything else dealing with CPU maps uses an 'unsigned char *cpu' & 'int maplen' pair to store the data as a bitmap. I think we should change this API to also use the bitmap rather than the textual format as it gives consistency with other APIs, particularly when mapping into other languages. For example in Go, the bitmaps get turned into a boolean array, so it is a very natural representation to manipulate - the textual format is rather inconvenient in this respect.
I modelled this API after the qemuDomainSetGuestVcpus and qemuDomainGetGuestVcpusParams which already uses the string representation rather the harder-to-use bitmap one, since they are in for a while we can't change those.

On Wed, Feb 22, 2017 at 01:34:54PM +0100, Peter Krempa wrote:
On Wed, Feb 22, 2017 at 12:16:51 +0000, Daniel Berrange wrote:
On Fri, Feb 10, 2017 at 06:10:18PM +0100, Peter Krempa wrote:
Similarly to domainSetGuestVcpus this commit adds API which allows to modify state of individual vcpus rather than just setting the count.
This allows to enable CPUs in specific guest NUMA nodes to achieve any necessary configuration. --- include/libvirt/libvirt-domain.h | 5 +++++ src/driver-hypervisor.h | 7 ++++++ src/libvirt-domain.c | 48 ++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 +++++ src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 16 +++++++++++++- src/remote_protocol-structs | 7 ++++++ 7 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index e303140a2..c0f715d66 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -4528,4 +4528,9 @@ int virDomainSetGuestVcpus(virDomainPtr domain, int state, unsigned int flags);
+int virDomainSetVcpu(virDomainPtr domain, + const char *vcpumap, + int state, + unsigned int flags);
In adding support for this to perl/go bindings I see that this is using a different style to the existing APIs. With the exception of the virDomainSetGuestVcpus() API, everything else dealing with CPU maps uses an 'unsigned char *cpu' & 'int maplen' pair to store the data as a bitmap. I think we should change this API to also use the bitmap rather than the textual format as it gives consistency with other APIs, particularly when mapping into other languages. For example in Go, the bitmaps get turned into a boolean array, so it is a very natural representation to manipulate - the textual format is rather inconvenient in this respect.
I modelled this API after the qemuDomainSetGuestVcpus and qemuDomainGetGuestVcpusParams which already uses the string representation rather the harder-to-use bitmap one, since they are in for a while we can't change those.
Yep, we can't change those, but we can't avoid spreading the inconsistency further, as all the other methods relating to CPUs aginst virDomain use a bitmap Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On Wed, Feb 22, 2017 at 12:55:21 +0000, Daniel Berrange wrote:
On Wed, Feb 22, 2017 at 01:34:54PM +0100, Peter Krempa wrote:
On Wed, Feb 22, 2017 at 12:16:51 +0000, Daniel Berrange wrote:
On Fri, Feb 10, 2017 at 06:10:18PM +0100, Peter Krempa wrote:
Similarly to domainSetGuestVcpus this commit adds API which allows to modify state of individual vcpus rather than just setting the count.
This allows to enable CPUs in specific guest NUMA nodes to achieve any necessary configuration. ---
[...]
I modelled this API after the qemuDomainSetGuestVcpus and qemuDomainGetGuestVcpusParams which already uses the string representation rather the harder-to-use bitmap one, since they are in for a while we can't change those.
Yep, we can't change those, but we can't avoid spreading the inconsistency further, as all the other methods relating to CPUs aginst virDomain use a bitmap
I obviously disagree with changing this (and don't expect me to change the API) since I like the text representation in C way more than the bitmaps. The bindings still can use the natural bitmap representation by converting the strings same way how we need to do it in virsh with the binary bitmaps.

Add a simple virsh command handler which makes use of the new API. --- tools/virsh-domain.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 19 ++++++++++++ 2 files changed, 107 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 023ec8a8b..09a9f8203 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -7015,6 +7015,88 @@ cmdGuestvcpus(vshControl *ctl, const vshCmd *cmd) /* + * "setvcpu" command + */ +static const vshCmdInfo info_setvcpu[] = { + {.name = "help", + .data = N_("attach/detach vcpu or groups of threads") + }, + {.name = "desc", + .data = N_("Add or remove vcpus") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_setvcpu[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL, + {.name = "vcpulist", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("ids of vcpus to manipulate") + }, + {.name = "enable", + .type = VSH_OT_BOOL, + .help = N_("enable cpus specified by cpumap") + }, + {.name = "disable", + .type = VSH_OT_BOOL, + .help = N_("disable cpus specified by cpumap") + }, + VIRSH_COMMON_OPT_DOMAIN_CONFIG, + VIRSH_COMMON_OPT_DOMAIN_LIVE, + VIRSH_COMMON_OPT_DOMAIN_CURRENT, + {.name = NULL} +}; + +static bool +cmdSetvcpu(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom; + bool enable = vshCommandOptBool(cmd, "enable"); + bool disable = vshCommandOptBool(cmd, "disable"); + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + const char *vcpulist = NULL; + int state = 0; + bool ret = false; + unsigned int flags = VIR_DOMAIN_AFFECT_CURRENT; + + VSH_EXCLUSIVE_OPTIONS_VAR(enable, disable); + + VSH_EXCLUSIVE_OPTIONS("current", "live"); + VSH_EXCLUSIVE_OPTIONS("current", "config"); + + if (config) + flags |= VIR_DOMAIN_AFFECT_CONFIG; + if (live) + flags |= VIR_DOMAIN_AFFECT_LIVE; + + if (!(enable || disable)) { + vshError(ctl, "%s", _("one of --enable, --disable is required")); + return false; + } + + if (vshCommandOptStringReq(ctl, cmd, "vcpulist", &vcpulist)) + return false; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (enable) + state = 1; + + if (virDomainSetVcpu(dom, vcpulist, state, flags) < 0) + goto cleanup; + + ret = true; + + cleanup: + virDomainFree(dom); + return ret; +} + + +/* * "iothreadinfo" command */ static const vshCmdInfo info_iothreadinfo[] = { @@ -13951,5 +14033,11 @@ const vshCmdDef domManagementCmds[] = { .info = info_guestvcpus, .flags = 0 }, + {.name = "setvcpu", + .handler = cmdSetvcpu, + .opts = opts_setvcpu, + .info = info_setvcpu, + .flags = 0 + }, {.name = NULL} }; diff --git a/tools/virsh.pod b/tools/virsh.pod index a47040984..606792df1 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -2418,6 +2418,25 @@ be hot-plugged the next time the domain is booted. As such, it must only be used with the I<--config> flag, and not with the I<--live> or the I<--current> flag. +=item B<setvcpu> I<domain> I<vcpulist> [I<--enable>] | [I<--disable>] +[[I<--live>] [I<--config>] | [I<--current>]] + +Change state of individual vCPUs using hot(un)plug mechanism. + +See B<vcpupin> for information on format of I<vcpulist>. Hypervisor drivers may +require that I<vcpulist> contains exactly vCPUs belonging to one hotpluggable +entity. This is usually just a single vCPU but certain architectures such as +ppc64 require a full core to be specified at once. + +Note that hypervisors may refuse to disable certain vcpus such as vcpu 0 or +others. + +If I<--live> is specified, affect a running domain. +If I<--config> is specified, affect the next startup of a persistent domain. +If I<--current> is specified, affect the current domain state. This is the +default. Both I<--live> and I<--config> flags may be given, but I<--current> is +exclusive. + =item B<shutdown> I<domain> [I<--mode MODE-LIST>] Gracefully shuts down a domain. This coordinates with the domain OS -- 2.11.0

On Fri, Feb 10, 2017 at 06:10:19PM +0100, Peter Krempa wrote:
Add a simple virsh command handler which makes use of the new API. --- tools/virsh-domain.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 19 ++++++++++++ 2 files changed, 107 insertions(+)
ACK Pavel

Add code that validates user's selection of cores and then uses the existing code to plug in the vCPU. --- src/qemu/qemu_driver.c | 74 +++++++++++++++++++++++ src/qemu/qemu_hotplug.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 7 +++ 3 files changed, 234 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 89bc833de..2b875b3e9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20190,6 +20190,79 @@ qemuDomainSetGuestVcpus(virDomainPtr dom, } +static int +qemuDomainSetVcpu(virDomainPtr dom, + const char *cpumap, + int state, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainDefPtr def = NULL; + virDomainDefPtr persistentDef = NULL; + virBitmapPtr map = NULL; + ssize_t lastvcpu; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (state != 0 && state != 1) { + virReportInvalidArg(state, "%s", _("unsupported state value")); + return -1; + } + + if (virBitmapParse(cpumap, &map, QEMU_GUEST_VCPU_MAX_ID) < 0) + goto cleanup; + + if ((lastvcpu = virBitmapLastSetBit(map)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("no vcpus selected for modification")); + goto cleanup; + } + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainSetVcpuEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) + goto endjob; + + if (persistentDef) { + if (lastvcpu >= virDomainDefGetVcpusMax(persistentDef)) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu %zd is not present in persistent config"), + lastvcpu); + goto endjob; + } + } + + if (def) { + if (lastvcpu >= virDomainDefGetVcpusMax(def)) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu %zd is not present in live config"), + lastvcpu); + goto endjob; + } + } + + ret = qemuDomainSetVcpuInternal(driver, vm, def, persistentDef, map, !!state); + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + virBitmapFree(map); + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectOpen = qemuConnectOpen, /* 0.2.0 */ @@ -20403,6 +20476,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainMigrateStartPostCopy = qemuDomainMigrateStartPostCopy, /* 1.3.3 */ .domainGetGuestVcpus = qemuDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = qemuDomainSetGuestVcpus, /* 2.0.0 */ + .domainSetVcpu = qemuDomainSetVcpu, /* 3.1.0 */ }; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 2f209f12b..363d1070b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5700,3 +5700,156 @@ qemuDomainSetVcpusInternal(virQEMUDriverPtr driver, virObjectUnref(cfg); return ret; } + + +static void +qemuDomainSetVcpuInactive(virDomainDefPtr def, + virBitmapPtr map, + bool state) +{ + virDomainVcpuDefPtr vcpu; + ssize_t next = -1; + + def->individualvcpus = true; + + while ((next = virBitmapNextSetBit(map, next)) > 0) { + if (!(vcpu = virDomainDefGetVcpu(def, next))) + continue; + + vcpu->online = state; + vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; + vcpu->order = 0; + } +} + + +/** + * qemuDomainFilterHotplugVcpuEntities: + * + * Returns a bitmap of hotpluggable vcpu entities that correspond to the logical + * vcpus requested in @vcpus. + */ +static virBitmapPtr +qemuDomainFilterHotplugVcpuEntities(virDomainDefPtr def, + virBitmapPtr vcpus, + bool state) +{ + qemuDomainVcpuPrivatePtr vcpupriv; + virDomainVcpuDefPtr vcpu; + virBitmapPtr map = NULL; + virBitmapPtr ret = NULL; + ssize_t next = -1; + size_t i; + + if (!(map = virBitmapNewCopy(vcpus))) + return NULL; + + /* make sure that all selected vcpus are in the correct state */ + while ((next = virBitmapNextSetBit(map, next)) > 0) { + if (!(vcpu = virDomainDefGetVcpu(def, next))) + continue; + + if (vcpu->online == state) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu '%zu' is already in requested state"), next); + goto cleanup; + } + + if (vcpu->online && !vcpu->hotpluggable) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu '%zu' can't be hotunplugged"), next); + goto cleanup; + } + } + + /* Make sure that all vCPUs belonging to a single hotpluggable entity were + * selected and then de-select any sub-threads of it. */ + next = -1; + while ((next = virBitmapNextSetBit(map, next)) > 0) { + if (!(vcpu = virDomainDefGetVcpu(def, next))) + continue; + + vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu); + + if (vcpupriv->vcpus == 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu '%zu' belongs to a larger hotpluggable entity, " + "but siblings were not selected"), next); + goto cleanup; + } + + for (i = next + 1; i < next + vcpupriv->vcpus; i++) { + if (!virBitmapIsBitSet(map, i)) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu '%zu' was not selected but it belongs to " + "hotpluggable entity '%zu-%zu' which was " + "partially selected"), + i, next, next + vcpupriv->vcpus - 1); + goto cleanup; + } + + /* clear the subthreads */ + ignore_value(virBitmapClearBit(map, i)); + } + } + + VIR_STEAL_PTR(ret, map); + + cleanup: + virBitmapFree(map); + return ret; +} + + +int +qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDefPtr def, + virDomainDefPtr persistentDef, + virBitmapPtr map, + bool state) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virBitmapPtr livevcpus = NULL; + int ret = -1; + + if (def) { + if (!qemuDomainSupportsNewVcpuHotplug(vm)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("this qemu version does not support specific " + "vCPU hotplug")); + goto cleanup; + } + + if (!(livevcpus = qemuDomainFilterHotplugVcpuEntities(def, map, state))) + goto cleanup; + + /* Make sure that only one hotpluggable entity is selected. + * qemuDomainSetVcpusLive allows setting more at once but error + * resolution in case of a partial failure is hard, so don't let users + * do so */ + if (virBitmapCountBits(livevcpus) != 1) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("only one hotpluggable entity can be selected")); + goto cleanup; + } + } + + if (livevcpus && + qemuDomainSetVcpusLive(driver, cfg, vm, livevcpus, state) < 0) + goto cleanup; + + if (persistentDef) { + qemuDomainSetVcpuInactive(persistentDef, map, state); + + if (virDomainSaveConfig(cfg->configDir, driver->caps, persistentDef) < 0) + goto cleanup; + } + + ret = 0; + + cleanup: + virBitmapFree(livevcpus); + virObjectUnref(cfg); + return ret; +} diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 13242eec9..0b11c1ed9 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -143,4 +143,11 @@ int qemuDomainSetVcpusInternal(virQEMUDriverPtr driver, unsigned int nvcpus, bool hotpluggable); +int qemuDomainSetVcpuInternal(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDefPtr def, + virDomainDefPtr persistentDef, + virBitmapPtr vcpus, + bool state); + #endif /* __QEMU_HOTPLUG_H__ */ -- 2.11.0

On Fri, Feb 10, 2017 at 06:10:20PM +0100, Peter Krempa wrote:
Add code that validates user's selection of cores and then uses the existing code to plug in the vCPU. --- src/qemu/qemu_driver.c | 74 +++++++++++++++++++++++ src/qemu/qemu_hotplug.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 7 +++ 3 files changed, 234 insertions(+)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 89bc833de..2b875b3e9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20190,6 +20190,79 @@ qemuDomainSetGuestVcpus(virDomainPtr dom, }
+static int +qemuDomainSetVcpu(virDomainPtr dom, + const char *cpumap, + int state, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm = NULL; + virDomainDefPtr def = NULL; + virDomainDefPtr persistentDef = NULL; + virBitmapPtr map = NULL; + ssize_t lastvcpu; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_AFFECT_LIVE | + VIR_DOMAIN_AFFECT_CONFIG, -1); + + if (state != 0 && state != 1) { + virReportInvalidArg(state, "%s", _("unsupported state value")); + return -1; + } + + if (virBitmapParse(cpumap, &map, QEMU_GUEST_VCPU_MAX_ID) < 0) + goto cleanup; + + if ((lastvcpu = virBitmapLastSetBit(map)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("no vcpus selected for modification")); + goto cleanup; + } + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainSetVcpuEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0) + goto endjob; + + if (persistentDef) { + if (lastvcpu >= virDomainDefGetVcpusMax(persistentDef)) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu %zd is not present in persistent config"), + lastvcpu); + goto endjob; + } + } + + if (def) { + if (lastvcpu >= virDomainDefGetVcpusMax(def)) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu %zd is not present in live config"), + lastvcpu); + goto endjob; + } + } + + ret = qemuDomainSetVcpuInternal(driver, vm, def, persistentDef, map, !!state); + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + virBitmapFree(map); + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectOpen = qemuConnectOpen, /* 0.2.0 */ @@ -20403,6 +20476,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainMigrateStartPostCopy = qemuDomainMigrateStartPostCopy, /* 1.3.3 */ .domainGetGuestVcpus = qemuDomainGetGuestVcpus, /* 2.0.0 */ .domainSetGuestVcpus = qemuDomainSetGuestVcpus, /* 2.0.0 */ + .domainSetVcpu = qemuDomainSetVcpu, /* 3.1.0 */ };
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 2f209f12b..363d1070b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5700,3 +5700,156 @@ qemuDomainSetVcpusInternal(virQEMUDriverPtr driver, virObjectUnref(cfg); return ret; } + + +static void +qemuDomainSetVcpuInactive(virDomainDefPtr def, + virBitmapPtr map, + bool state)
qemuDomainSetVcpuConfig is probably a better name because we use *Live vs *Config naming.
+{ + virDomainVcpuDefPtr vcpu; + ssize_t next = -1; + + def->individualvcpus = true; + + while ((next = virBitmapNextSetBit(map, next)) > 0) { + if (!(vcpu = virDomainDefGetVcpu(def, next))) + continue; + + vcpu->online = state; + vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; + vcpu->order = 0; + } +} + + +/** + * qemuDomainFilterHotplugVcpuEntities: + * + * Returns a bitmap of hotpluggable vcpu entities that correspond to the logical + * vcpus requested in @vcpus. + */ +static virBitmapPtr +qemuDomainFilterHotplugVcpuEntities(virDomainDefPtr def, + virBitmapPtr vcpus, + bool state) +{ + qemuDomainVcpuPrivatePtr vcpupriv; + virDomainVcpuDefPtr vcpu; + virBitmapPtr map = NULL; + virBitmapPtr ret = NULL; + ssize_t next = -1; + size_t i; + + if (!(map = virBitmapNewCopy(vcpus))) + return NULL; + + /* make sure that all selected vcpus are in the correct state */ + while ((next = virBitmapNextSetBit(map, next)) > 0) { + if (!(vcpu = virDomainDefGetVcpu(def, next))) + continue; + + if (vcpu->online == state) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu '%zu' is already in requested state"), next); + goto cleanup; + } + + if (vcpu->online && !vcpu->hotpluggable) { + virReportError(VIR_ERR_INVALID_ARG, + _("vcpu '%zu' can't be hotunplugged"), next); + goto cleanup; + } + } + + /* Make sure that all vCPUs belonging to a single hotpluggable entity were + * selected and then de-select any sub-threads of it. */ + next = -1; + while ((next = virBitmapNextSetBit(map, next)) > 0) { + if (!(vcpu = virDomainDefGetVcpu(def, next))) + continue; + + vcpupriv = QEMU_DOMAIN_VCPU_PRIVATE(vcpu);
Remove the extra space :) ACK with the issues fixed. Pavel

Test that the vcpu entity selection code works properly --- tests/qemuhotplugtest.c | 63 ++++ .../ppc64-modern-individual-domain.xml | 20 ++ .../ppc64-modern-individual-monitor.json | 318 +++++++++++++++++++++ .../ppc64-modern-individual-result-conf.xml | 64 +++++ .../ppc64-modern-individual-result-live.xml | 72 +++++ .../x86-modern-individual-add-domain.xml | 21 ++ .../x86-modern-individual-add-monitor.json | 299 +++++++++++++++++++ .../x86-modern-individual-add-result-conf.xml | 40 +++ .../x86-modern-individual-add-result-live.xml | 48 ++++ 9 files changed, 945 insertions(+) create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-domain.xml create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-monitor.json create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-result-conf.xml create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-result-live.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-domain.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-monitor.json create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-result-conf.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-result-live.xml diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index 8a58d5468..cdeb3f1bf 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -493,6 +493,8 @@ testQemuHotplugCpuFinalize(struct testQemuHotplugCpuData *data) struct testQemuHotplugCpuParams { const char *test; int newcpus; + const char *cpumap; + bool state; bool modern; bool fail; }; @@ -534,6 +536,46 @@ testQemuHotplugCpuGroup(const void *opaque) static int +testQemuHotplugCpuIndividual(const void *opaque) +{ + const struct testQemuHotplugCpuParams *params = opaque; + struct testQemuHotplugCpuData *data = NULL; + virBitmapPtr map = NULL; + int ret = -1; + int rc; + + if (!(data = testQemuHotplugCpuPrepare(params->test, params->modern))) + return -1; + + if (virBitmapParse(params->cpumap, &map, 128) < 0) + goto cleanup; + + rc = qemuDomainSetVcpuInternal(&driver, data->vm, data->vm->def, + data->vm->newDef, map, params->state); + + if (params->fail) { + if (rc == 0) + fprintf(stderr, "cpu test '%s' should have failed\n", params->test); + else + ret = 0; + + goto cleanup; + } else { + if (rc < 0) + goto cleanup; + } + + ret = testQemuHotplugCpuFinalize(data); + + cleanup: + virBitmapFree(map); + testQemuHotplugCpuDataFree(data); + return ret; +} + + + +static int mymain(void) { int ret = 0; @@ -789,6 +831,27 @@ mymain(void) DO_TEST_CPU_GROUP("ppc64-modern-bulk", 23, true, true); DO_TEST_CPU_GROUP("ppc64-modern-bulk", 25, true, true); +#define DO_TEST_CPU_INDIVIDUAL(prefix, mapstr, statefl, modernhp, expectfail) \ + do { \ + cpudata.test = prefix; \ + cpudata.cpumap = mapstr; \ + cpudata.state = statefl; \ + cpudata.modern = modernhp; \ + cpudata.fail = expectfail; \ + if (virTestRun("hotplug vcpus group " prefix, \ + testQemuHotplugCpuIndividual, &cpudata) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_CPU_INDIVIDUAL("x86-modern-individual-add", "7", true, true, false); + DO_TEST_CPU_INDIVIDUAL("x86-modern-individual-add", "6,7", true, true, true); + DO_TEST_CPU_INDIVIDUAL("x86-modern-individual-add", "7", false, true, true); + DO_TEST_CPU_INDIVIDUAL("x86-modern-individual-add", "7", true, false, true); + + DO_TEST_CPU_INDIVIDUAL("ppc64-modern-individual", "16-23", true, true, false); + DO_TEST_CPU_INDIVIDUAL("ppc64-modern-individual", "16-22", true, true, true); + DO_TEST_CPU_INDIVIDUAL("ppc64-modern-individual", "17", true, true, true); + qemuTestDriverFree(&driver); return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/qemuhotplugtestcpus/ppc64-modern-individual-domain.xml b/tests/qemuhotplugtestcpus/ppc64-modern-individual-domain.xml new file mode 100644 index 000000000..eb04e42b6 --- /dev/null +++ b/tests/qemuhotplugtestcpus/ppc64-modern-individual-domain.xml @@ -0,0 +1,20 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' current='8'>32</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets="1" cores="4" threads="8"/> + </cpu> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + </devices> +</domain> diff --git a/tests/qemuhotplugtestcpus/ppc64-modern-individual-monitor.json b/tests/qemuhotplugtestcpus/ppc64-modern-individual-monitor.json new file mode 100644 index 000000000..ad5cc984a --- /dev/null +++ b/tests/qemuhotplugtestcpus/ppc64-modern-individual-monitor.json @@ -0,0 +1,318 @@ +{"execute":"query-hotpluggable-cpus","id":"libvirt-1"} + +{ + "return": [ + { + "props": { + "core-id": 24 + }, + "vcpus-count": 8, + "type": "host-spapr-cpu-core" + }, + { + "props": { + "core-id": 16 + }, + "vcpus-count": 8, + "type": "host-spapr-cpu-core" + }, + { + "props": { + "core-id": 8 + }, + "vcpus-count": 8, + "type": "host-spapr-cpu-core" + }, + { + "props": { + "core-id": 0 + }, + "vcpus-count": 8, + "qom-path": "/machine/unattached/device[1]", + "type": "host-spapr-cpu-core" + } + ], + "id": "libvirt-15" +} + +{"execute":"query-cpus","id":"libvirt-2"} + +{ + "return": [ + { + "arch": "ppc", + "current": true, + "CPU": 0, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[0]", + "halted": false, + "thread_id": 21925 + }, + { + "arch": "ppc", + "current": false, + "CPU": 1, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[1]", + "halted": false, + "thread_id": 21926 + }, + { + "arch": "ppc", + "current": false, + "CPU": 2, + "nip": -4611686018422360608, + "qom_path": "/machine/unattached/device[1]/thread[2]", + "halted": false, + "thread_id": 21927 + }, + { + "arch": "ppc", + "current": false, + "CPU": 3, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[3]", + "halted": false, + "thread_id": 21928 + }, + { + "arch": "ppc", + "current": false, + "CPU": 4, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[4]", + "halted": false, + "thread_id": 21930 + }, + { + "arch": "ppc", + "current": false, + "CPU": 5, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[5]", + "halted": false, + "thread_id": 21931 + }, + { + "arch": "ppc", + "current": false, + "CPU": 6, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[6]", + "halted": false, + "thread_id": 21932 + }, + { + "arch": "ppc", + "current": false, + "CPU": 7, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[7]", + "halted": false, + "thread_id": 21933 + } + ], + "id": "libvirt-12" +} + +{ + "execute": "device_add", + "arguments": { + "driver": "host-spapr-cpu-core", + "id": "vcpu16", + "core-id": 16 + }, + "id": "libvirt-3" +} + +{"return": {}} + +{"execute":"query-hotpluggable-cpus","id":"libvirt-4"} + +{ + "return": [ + { + "props": { + "core-id": 24 + }, + "vcpus-count": 8, + "type": "host-spapr-cpu-core" + }, + { + "props": { + "core-id": 16 + }, + "vcpus-count": 8, + "qom-path": "/machine/peripheral/vcpu16", + "type": "host-spapr-cpu-core" + }, + { + "props": { + "core-id": 8 + }, + "vcpus-count": 8, + "type": "host-spapr-cpu-core" + }, + { + "props": { + "core-id": 0 + }, + "vcpus-count": 8, + "qom-path": "/machine/unattached/device[1]", + "type": "host-spapr-cpu-core" + } + ], + "id": "libvirt-15" +} + +{"execute":"query-cpus","id":"libvirt-5"} + +{ + "return": [ + { + "arch": "ppc", + "current": true, + "CPU": 0, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[0]", + "halted": false, + "thread_id": 21925 + }, + { + "arch": "ppc", + "current": false, + "CPU": 1, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[1]", + "halted": false, + "thread_id": 21926 + }, + { + "arch": "ppc", + "current": false, + "CPU": 2, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[2]", + "halted": false, + "thread_id": 21927 + }, + { + "arch": "ppc", + "current": false, + "CPU": 3, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[3]", + "halted": false, + "thread_id": 21928 + }, + { + "arch": "ppc", + "current": false, + "CPU": 4, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[4]", + "halted": false, + "thread_id": 21930 + }, + { + "arch": "ppc", + "current": false, + "CPU": 5, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[5]", + "halted": false, + "thread_id": 21931 + }, + { + "arch": "ppc", + "current": false, + "CPU": 6, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[6]", + "halted": false, + "thread_id": 21932 + }, + { + "arch": "ppc", + "current": false, + "CPU": 7, + "nip": -4611686018426772172, + "qom_path": "/machine/unattached/device[1]/thread[7]", + "halted": false, + "thread_id": 21933 + }, + { + "arch": "ppc", + "current": false, + "CPU": 8, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[0]", + "halted": false, + "thread_id": 22131 + }, + { + "arch": "ppc", + "current": false, + "CPU": 9, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[1]", + "halted": false, + "thread_id": 22132 + }, + { + "arch": "ppc", + "current": false, + "CPU": 10, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[2]", + "halted": false, + "thread_id": 22133 + }, + { + "arch": "ppc", + "current": false, + "CPU": 11, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[3]", + "halted": false, + "thread_id": 22134 + }, + { + "arch": "ppc", + "current": false, + "CPU": 12, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[4]", + "halted": false, + "thread_id": 22135 + }, + { + "arch": "ppc", + "current": false, + "CPU": 13, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[5]", + "halted": false, + "thread_id": 22136 + }, + { + "arch": "ppc", + "current": false, + "CPU": 14, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[6]", + "halted": false, + "thread_id": 22137 + }, + { + "arch": "ppc", + "current": false, + "CPU": 15, + "nip": -4611686018426772172, + "qom_path": "/machine/peripheral/vcpu16/thread[7]", + "halted": false, + "thread_id": 22138 + } + ], + "id": "libvirt-14" +} diff --git a/tests/qemuhotplugtestcpus/ppc64-modern-individual-result-conf.xml b/tests/qemuhotplugtestcpus/ppc64-modern-individual-result-conf.xml new file mode 100644 index 000000000..bda1bc579 --- /dev/null +++ b/tests/qemuhotplugtestcpus/ppc64-modern-individual-result-conf.xml @@ -0,0 +1,64 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' current='16'>32</vcpu> + <vcpus> + <vcpu id='0' enabled='yes' hotpluggable='no'/> + <vcpu id='1' enabled='yes' hotpluggable='no'/> + <vcpu id='2' enabled='yes' hotpluggable='no'/> + <vcpu id='3' enabled='yes' hotpluggable='no'/> + <vcpu id='4' enabled='yes' hotpluggable='no'/> + <vcpu id='5' enabled='yes' hotpluggable='no'/> + <vcpu id='6' enabled='yes' hotpluggable='no'/> + <vcpu id='7' enabled='yes' hotpluggable='no'/> + <vcpu id='8' enabled='no' hotpluggable='yes'/> + <vcpu id='9' enabled='no' hotpluggable='yes'/> + <vcpu id='10' enabled='no' hotpluggable='yes'/> + <vcpu id='11' enabled='no' hotpluggable='yes'/> + <vcpu id='12' enabled='no' hotpluggable='yes'/> + <vcpu id='13' enabled='no' hotpluggable='yes'/> + <vcpu id='14' enabled='no' hotpluggable='yes'/> + <vcpu id='15' enabled='no' hotpluggable='yes'/> + <vcpu id='16' enabled='yes' hotpluggable='yes'/> + <vcpu id='17' enabled='yes' hotpluggable='yes'/> + <vcpu id='18' enabled='yes' hotpluggable='yes'/> + <vcpu id='19' enabled='yes' hotpluggable='yes'/> + <vcpu id='20' enabled='yes' hotpluggable='yes'/> + <vcpu id='21' enabled='yes' hotpluggable='yes'/> + <vcpu id='22' enabled='yes' hotpluggable='yes'/> + <vcpu id='23' enabled='yes' hotpluggable='yes'/> + <vcpu id='24' enabled='no' hotpluggable='yes'/> + <vcpu id='25' enabled='no' hotpluggable='yes'/> + <vcpu id='26' enabled='no' hotpluggable='yes'/> + <vcpu id='27' enabled='no' hotpluggable='yes'/> + <vcpu id='28' enabled='no' hotpluggable='yes'/> + <vcpu id='29' enabled='no' hotpluggable='yes'/> + <vcpu id='30' enabled='no' hotpluggable='yes'/> + <vcpu id='31' enabled='no' hotpluggable='yes'/> + </vcpus> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets='1' cores='4' threads='8'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuhotplugtestcpus/ppc64-modern-individual-result-live.xml b/tests/qemuhotplugtestcpus/ppc64-modern-individual-result-live.xml new file mode 100644 index 000000000..8621b62b7 --- /dev/null +++ b/tests/qemuhotplugtestcpus/ppc64-modern-individual-result-live.xml @@ -0,0 +1,72 @@ +<domain type='qemu' id='7'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' current='16'>32</vcpu> + <vcpus> + <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='1' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='2' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='3' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='4' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='5' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='6' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='7' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='8' enabled='no' hotpluggable='yes'/> + <vcpu id='9' enabled='no' hotpluggable='yes'/> + <vcpu id='10' enabled='no' hotpluggable='yes'/> + <vcpu id='11' enabled='no' hotpluggable='yes'/> + <vcpu id='12' enabled='no' hotpluggable='yes'/> + <vcpu id='13' enabled='no' hotpluggable='yes'/> + <vcpu id='14' enabled='no' hotpluggable='yes'/> + <vcpu id='15' enabled='no' hotpluggable='yes'/> + <vcpu id='16' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='17' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='18' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='19' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='20' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='21' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='22' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='23' enabled='yes' hotpluggable='yes' order='2'/> + <vcpu id='24' enabled='no' hotpluggable='yes'/> + <vcpu id='25' enabled='no' hotpluggable='yes'/> + <vcpu id='26' enabled='no' hotpluggable='yes'/> + <vcpu id='27' enabled='no' hotpluggable='yes'/> + <vcpu id='28' enabled='no' hotpluggable='yes'/> + <vcpu id='29' enabled='no' hotpluggable='yes'/> + <vcpu id='30' enabled='no' hotpluggable='yes'/> + <vcpu id='31' enabled='no' hotpluggable='yes'/> + </vcpus> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets='1' cores='4' threads='8'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci'/> + </controller> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <memballoon model='virtio'> + <alias name='balloon0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuhotplugtestcpus/x86-modern-individual-add-domain.xml b/tests/qemuhotplugtestcpus/x86-modern-individual-add-domain.xml new file mode 100644 index 000000000..1c2a5b131 --- /dev/null +++ b/tests/qemuhotplugtestcpus/x86-modern-individual-add-domain.xml @@ -0,0 +1,21 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' current='5'>8</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets="4" cores="2" threads="1"/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + </devices> +</domain> diff --git a/tests/qemuhotplugtestcpus/x86-modern-individual-add-monitor.json b/tests/qemuhotplugtestcpus/x86-modern-individual-add-monitor.json new file mode 100644 index 000000000..294198b27 --- /dev/null +++ b/tests/qemuhotplugtestcpus/x86-modern-individual-add-monitor.json @@ -0,0 +1,299 @@ +{"execute":"query-hotpluggable-cpus","id":"libvirt-1"} + +{ + "return": [ + { + "props": { + "core-id": 1, + "thread-id": 1, + "socket-id": 1 + }, + "vcpus-count": 1, + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 1, + "thread-id": 0, + "socket-id": 1 + }, + "vcpus-count": 1, + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 1, + "socket-id": 1 + }, + "vcpus-count": 1, + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 0, + "socket-id": 1 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[5]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 1, + "thread-id": 1, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[4]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 1, + "thread-id": 0, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[3]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 1, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[2]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 0, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[0]", + "type": "qemu64-x86_64-cpu" + } + ], + "id": "libvirt-23" +} + +{"execute":"query-cpus","id":"libvirt-2"} + +{ + "return": [ + { + "arch": "x86", + "current": true, + "CPU": 0, + "qom_path": "/machine/unattached/device[0]", + "pc": -2130415978, + "halted": true, + "thread_id": 518291 + }, + { + "arch": "x86", + "current": false, + "CPU": 1, + "qom_path": "/machine/unattached/device[2]", + "pc": -2130415978, + "halted": true, + "thread_id": 518292 + }, + { + "arch": "x86", + "current": false, + "CPU": 2, + "qom_path": "/machine/unattached/device[3]", + "pc": -2130415978, + "halted": true, + "thread_id": 518294 + }, + { + "arch": "x86", + "current": false, + "CPU": 3, + "qom_path": "/machine/unattached/device[4]", + "pc": -2130415978, + "halted": true, + "thread_id": 518295 + }, + { + "arch": "x86", + "current": false, + "CPU": 4, + "qom_path": "/machine/unattached/device[5]", + "pc": -2130415978, + "halted": true, + "thread_id": 518296 + } + ], + "id": "libvirt-22" +} + +{ + "execute": "device_add", + "arguments": { + "driver": "qemu64-x86_64-cpu", + "id": "vcpu7", + "socket-id": 1, + "core-id": 1, + "thread-id": 1 + }, + "id": "libvirt-3" +} + +{"return": {}} + +{"execute":"query-hotpluggable-cpus","id":"libvirt-4"} + +{ + "return": [ + { + "props": { + "core-id": 1, + "thread-id": 1, + "socket-id": 1 + }, + "vcpus-count": 1, + "qom-path": "/machine/peripheral/vcpu7", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 1, + "thread-id": 0, + "socket-id": 1 + }, + "vcpus-count": 1, + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 1, + "socket-id": 1 + }, + "vcpus-count": 1, + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 0, + "socket-id": 1 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[5]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 1, + "thread-id": 1, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[4]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 1, + "thread-id": 0, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[3]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 1, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[2]", + "type": "qemu64-x86_64-cpu" + }, + { + "props": { + "core-id": 0, + "thread-id": 0, + "socket-id": 0 + }, + "vcpus-count": 1, + "qom-path": "/machine/unattached/device[0]", + "type": "qemu64-x86_64-cpu" + } + ], + "id": "libvirt-23" +} + +{"execute":"query-cpus","id":"libvirt-5"} + +{ + "return": [ + { + "arch": "x86", + "current": true, + "CPU": 0, + "qom_path": "/machine/unattached/device[0]", + "pc": -2130415978, + "halted": true, + "thread_id": 518291 + }, + { + "arch": "x86", + "current": false, + "CPU": 1, + "qom_path": "/machine/unattached/device[2]", + "pc": -2130415978, + "halted": true, + "thread_id": 518292 + }, + { + "arch": "x86", + "current": false, + "CPU": 2, + "qom_path": "/machine/unattached/device[3]", + "pc": -2130415978, + "halted": true, + "thread_id": 518294 + }, + { + "arch": "x86", + "current": false, + "CPU": 3, + "qom_path": "/machine/unattached/device[4]", + "pc": -2130415978, + "halted": true, + "thread_id": 518295 + }, + { + "arch": "x86", + "current": false, + "CPU": 4, + "qom_path": "/machine/unattached/device[5]", + "pc": -2130415978, + "halted": true, + "thread_id": 518296 + }, + { + "arch": "x86", + "current": false, + "CPU": 5, + "qom_path": "/machine/peripheral/vcpu7", + "pc": -2130415978, + "halted": true, + "thread_id": 518297 + } + ], + "id": "libvirt-22" +} diff --git a/tests/qemuhotplugtestcpus/x86-modern-individual-add-result-conf.xml b/tests/qemuhotplugtestcpus/x86-modern-individual-add-result-conf.xml new file mode 100644 index 000000000..75e833558 --- /dev/null +++ b/tests/qemuhotplugtestcpus/x86-modern-individual-add-result-conf.xml @@ -0,0 +1,40 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' current='6'>8</vcpu> + <vcpus> + <vcpu id='0' enabled='yes' hotpluggable='no'/> + <vcpu id='1' enabled='yes' hotpluggable='no'/> + <vcpu id='2' enabled='yes' hotpluggable='no'/> + <vcpu id='3' enabled='yes' hotpluggable='no'/> + <vcpu id='4' enabled='yes' hotpluggable='no'/> + <vcpu id='5' enabled='no' hotpluggable='yes'/> + <vcpu id='6' enabled='no' hotpluggable='yes'/> + <vcpu id='7' enabled='yes' hotpluggable='yes'/> + </vcpus> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets='4' cores='2' threads='1'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuhotplugtestcpus/x86-modern-individual-add-result-live.xml b/tests/qemuhotplugtestcpus/x86-modern-individual-add-result-live.xml new file mode 100644 index 000000000..a21db6f3b --- /dev/null +++ b/tests/qemuhotplugtestcpus/x86-modern-individual-add-result-live.xml @@ -0,0 +1,48 @@ +<domain type='qemu' id='7'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219100</memory> + <currentMemory unit='KiB'>219100</currentMemory> + <vcpu placement='static' current='6'>8</vcpu> + <vcpus> + <vcpu id='0' enabled='yes' hotpluggable='no' order='1'/> + <vcpu id='1' enabled='yes' hotpluggable='no' order='2'/> + <vcpu id='2' enabled='yes' hotpluggable='no' order='3'/> + <vcpu id='3' enabled='yes' hotpluggable='no' order='4'/> + <vcpu id='4' enabled='yes' hotpluggable='no' order='5'/> + <vcpu id='5' enabled='no' hotpluggable='yes'/> + <vcpu id='6' enabled='no' hotpluggable='yes'/> + <vcpu id='7' enabled='yes' hotpluggable='yes' order='6'/> + </vcpus> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='network'/> + </os> + <cpu> + <topology sockets='4' cores='2' threads='1'/> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <controller type='usb' index='0'> + <alias name='usb'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'> + <alias name='pci'/> + </controller> + <input type='mouse' bus='ps2'> + <alias name='input0'/> + </input> + <input type='keyboard' bus='ps2'> + <alias name='input1'/> + </input> + <memballoon model='virtio'> + <alias name='balloon0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </memballoon> + </devices> +</domain> -- 2.11.0

On Fri, Feb 10, 2017 at 06:10:21PM +0100, Peter Krempa wrote:
Test that the vcpu entity selection code works properly --- tests/qemuhotplugtest.c | 63 ++++ .../ppc64-modern-individual-domain.xml | 20 ++ .../ppc64-modern-individual-monitor.json | 318 +++++++++++++++++++++ .../ppc64-modern-individual-result-conf.xml | 64 +++++ .../ppc64-modern-individual-result-live.xml | 72 +++++ .../x86-modern-individual-add-domain.xml | 21 ++ .../x86-modern-individual-add-monitor.json | 299 +++++++++++++++++++ .../x86-modern-individual-add-result-conf.xml | 40 +++ .../x86-modern-individual-add-result-live.xml | 48 ++++ 9 files changed, 945 insertions(+) create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-domain.xml create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-monitor.json create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-result-conf.xml create mode 100644 tests/qemuhotplugtestcpus/ppc64-modern-individual-result-live.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-domain.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-monitor.json create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-result-conf.xml create mode 100644 tests/qemuhotplugtestcpus/x86-modern-individual-add-result-live.xml
ACK Pavel

--- docs/news.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/news.xml b/docs/news.xml index a214b3316..3a540deba 100644 --- a/docs/news.xml +++ b/docs/news.xml @@ -44,6 +44,16 @@ openvswitch calls in the libvirtd configuration file. </description> </change> + <change> + <summary> + Add API for individual/specific vCPU hotplug + </summary> + <description> + The new API allows selecting specific vCPUs to be added/removed from + the VM. The existing APIs allowed only adding/removing from the end + which did not play well with NUMA. + </description> + </change> </section> <section title="Improvements"> <change> -- 2.11.0
participants (3)
-
Daniel P. Berrange
-
Pavel Hrdina
-
Peter Krempa