On Tue, 2023-04-25 at 18:14 +0200, Pierre Morel wrote: > The modification of the CPU attributes are done through a monitor > command. > > It allows to move the core inside the topology tree to optimize > the cache usage in the case the host's hypervisor previously > moved the CPU. > > The same command allows to modify the CPU attributes modifiers > like polarization entitlement and the dedicated attribute to notify > the guest if the host admin modified scheduling or dedication of a vCPU. > > With this knowledge the guest has the possibility to optimize the > usage of the vCPUs. > > The command has a feature unstable for the moment. > > Signed-off-by: Pierre Morel <pmorel@xxxxxxxxxxxxx> Logic is sound, minor stuff below. > --- > qapi/machine-target.json | 37 +++++++++++ > hw/s390x/cpu-topology.c | 136 +++++++++++++++++++++++++++++++++++++++ > 2 files changed, 173 insertions(+) > > diff --git a/qapi/machine-target.json b/qapi/machine-target.json > index 42a6a40333..3b7a0b77f4 100644 > --- a/qapi/machine-target.json > +++ b/qapi/machine-target.json > @@ -4,6 +4,8 @@ > # This work is licensed under the terms of the GNU GPL, version 2 or later. > # See the COPYING file in the top-level directory. > > +{ 'include': 'machine-common.json' } > + > ## > # @CpuModelInfo: > # > @@ -354,3 +356,38 @@ > { 'enum': 'CpuS390Polarization', > 'prefix': 'S390_CPU_POLARIZATION', > 'data': [ 'horizontal', 'vertical' ] } > + > +## > +# @set-cpu-topology: > +# > +# @core-id: the vCPU ID to be moved > +# @socket-id: optional destination socket where to move the vCPU > +# @book-id: optional destination book where to move the vCPU > +# @drawer-id: optional destination drawer where to move the vCPU > +# @entitlement: optional entitlement > +# @dedicated: optional, if the vCPU is dedicated to a real CPU > +# > +# Features: > +# @unstable: This command may still be modified. > +# > +# Modifies the topology by moving the CPU inside the topology > +# tree or by changing a modifier attribute of a CPU. > +# Default value for optional parameter is the current value > +# used by the CPU. > +# > +# Returns: Nothing on success, the reason on failure. > +# > +# Since: 8.1 > +## > +{ 'command': 'set-cpu-topology', > + 'data': { > + 'core-id': 'uint16', > + '*socket-id': 'uint16', > + '*book-id': 'uint16', > + '*drawer-id': 'uint16', > + '*entitlement': 'CpuS390Entitlement', > + '*dedicated': 'bool' > + }, > + 'features': [ 'unstable' ], > + 'if': { 'all': [ 'TARGET_S390X' , 'CONFIG_KVM' ] } > +} > diff --git a/hw/s390x/cpu-topology.c b/hw/s390x/cpu-topology.c > index d9cd3dc3ce..e5fb976594 100644 > --- a/hw/s390x/cpu-topology.c > +++ b/hw/s390x/cpu-topology.c > @@ -16,6 +16,7 @@ > #include "target/s390x/cpu.h" > #include "hw/s390x/s390-virtio-ccw.h" > #include "hw/s390x/cpu-topology.h" > +#include "qapi/qapi-commands-machine-target.h" > > /* > * s390_topology is used to keep the topology information. > @@ -261,6 +262,27 @@ static bool s390_topology_check(uint16_t socket_id, uint16_t book_id, > return true; > } > > +/** > + * s390_topology_need_report > + * @cpu: Current cpu > + * @drawer_id: future drawer ID > + * @book_id: future book ID > + * @socket_id: future socket ID Entitlement and dedicated are missing here. > + * > + * A modified topology change report is needed if the topology > + * tree or the topology attributes change. > + */ > +static int s390_topology_need_report(S390CPU *cpu, int drawer_id, I'd prefer a bool return type. > + int book_id, int socket_id, > + uint16_t entitlement, bool dedicated) > +{ > + return cpu->env.drawer_id != drawer_id || > + cpu->env.book_id != book_id || > + cpu->env.socket_id != socket_id || > + cpu->env.entitlement != entitlement || > + cpu->env.dedicated != dedicated; > +} > + > /** > * s390_update_cpu_props: > * @ms: the machine state > @@ -330,3 +352,117 @@ void s390_topology_setup_cpu(MachineState *ms, S390CPU *cpu, Error **errp) > /* topology tree is reflected in props */ > s390_update_cpu_props(ms, cpu); > } > + > +static void s390_change_topology(uint16_t core_id, > + bool has_socket_id, uint16_t socket_id, > + bool has_book_id, uint16_t book_id, > + bool has_drawer_id, uint16_t drawer_id, > + bool has_entitlement, uint16_t entitlement, I would keep the enum type for entitlement. > + bool has_dedicated, bool dedicated, > + Error **errp) > +{ > + MachineState *ms = current_machine; > + int old_socket_entry; > + int new_socket_entry; > + int report_needed; > + S390CPU *cpu; > + ERRP_GUARD(); > + > + if (core_id >= ms->smp.max_cpus) { > + error_setg(errp, "Core-id %d out of range!", core_id); > + return; > + } > + > + cpu = (S390CPU *)ms->possible_cpus->cpus[core_id].cpu; You can replace this with cpu = s390_cpu_addr2state(core_id); and get rid of the if above that checks for out of range. > + if (!cpu) { > + error_setg(errp, "Core-id %d does not exist!", core_id); > + return; > + } > + > + /* Get attributes not provided from cpu and verify the new topology */ > + if (!has_socket_id) { > + socket_id = cpu->env.socket_id; > + } > + if (!has_book_id) { > + book_id = cpu->env.book_id; > + } > + if (!has_drawer_id) { > + drawer_id = cpu->env.drawer_id; > + } > + if (!has_dedicated) { > + dedicated = cpu->env.dedicated; > + } > + > + /* > + * When the user specifies the entitlement as 'auto' on the command line, > + * qemu will set the entitlement as: > + * Medium when the CPU is not dedicated. > + * High when dedicated is true. > + */ > + if (!has_entitlement || (entitlement == S390_CPU_ENTITLEMENT_AUTO)) { > + if (dedicated) { > + entitlement = S390_CPU_ENTITLEMENT_HIGH; > + } else { > + entitlement = S390_CPU_ENTITLEMENT_MEDIUM; > + } > + } > + > + if (!s390_topology_check(socket_id, book_id, drawer_id, > + entitlement, dedicated, errp)) > + return; > + > + /* Check for space on new socket */ > + old_socket_entry = s390_socket_nb(cpu); > + new_socket_entry = __s390_socket_nb(drawer_id, book_id, socket_id); > + > + if (new_socket_entry != old_socket_entry) { > + if (s390_topology.cores_per_socket[new_socket_entry] >= > + s390_topology.smp->cores) { > + error_setg(errp, "No more space on this socket"); > + return; > + } > + /* Update the count of cores in sockets */ > + s390_topology.cores_per_socket[new_socket_entry] += 1; > + s390_topology.cores_per_socket[old_socket_entry] -= 1; > + } > + > + /* Check if we will need to report the modified topology */ > + report_needed = s390_topology_need_report(cpu, drawer_id, book_id, > + socket_id, entitlement, > + dedicated); > + > + /* All checks done, report new topology into the vCPU */ > + cpu->env.drawer_id = drawer_id; > + cpu->env.book_id = book_id; > + cpu->env.socket_id = socket_id; > + cpu->env.dedicated = dedicated; > + cpu->env.entitlement = entitlement; > + > + /* topology tree is reflected in props */ > + s390_update_cpu_props(ms, cpu); > + > + /* Advertise the topology change */ > + if (report_needed) { > + s390_cpu_topology_set_changed(true); > + } > +} > + > +void qmp_set_cpu_topology(uint16_t core, > + bool has_socket, uint16_t socket, > + bool has_book, uint16_t book, > + bool has_drawer, uint16_t drawer, > + bool has_entitlement, CpuS390Entitlement entitlement, > + bool has_dedicated, bool dedicated, > + Error **errp) > +{ > + ERRP_GUARD(); > + > + if (!s390_has_topology()) { > + error_setg(errp, "This machine doesn't support topology"); > + return; > + } > + > + s390_change_topology(core, has_socket, socket, has_book, book, > + has_drawer, drawer, has_entitlement, entitlement, > + has_dedicated, dedicated, errp); > +}