qemu
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3* CPU Topology
4*
5* Copyright IBM Corp. 2022, 2023
6* Author(s): Pierre Morel <pmorel@linux.ibm.com>
7*
8* S390 topology handling can be divided in two parts:
9*
10* - The first part in this file is taking care of all common functions
11* used by KVM and TCG to create and modify the topology.
12*
13* - The second part, building the topology information data for the
14* guest with CPU and KVM specificity will be implemented inside
15* the target/s390/kvm sub tree.
16*/
17
18#include "qemu/osdep.h"19#include "qapi/error.h"20#include "qemu/error-report.h"21#include "hw/qdev-properties.h"22#include "hw/boards.h"23#include "target/s390x/cpu.h"24#include "hw/s390x/s390-virtio-ccw.h"25#include "hw/s390x/cpu-topology.h"26#include "qapi/qapi-commands-machine-target.h"27#include "qapi/qapi-events-machine-target.h"28
29/*
30* s390_topology is used to keep the topology information.
31* .cores_per_socket: tracks information on the count of cores
32* per socket.
33* .polarization: tracks machine polarization.
34*/
35S390Topology s390_topology = {36/* will be initialized after the CPU model is realized */37.cores_per_socket = NULL,38.polarization = S390_CPU_POLARIZATION_HORIZONTAL,39};40
41/**
42* s390_socket_nb:
43* @cpu: s390x CPU
44*
45* Returns the socket number used inside the cores_per_socket array
46* for a topology tree entry
47*/
48static int s390_socket_nb_from_ids(int drawer_id, int book_id, int socket_id)49{
50return (drawer_id * current_machine->smp.books + book_id) *51current_machine->smp.sockets + socket_id;52}
53
54/**
55* s390_socket_nb:
56* @cpu: s390x CPU
57*
58* Returns the socket number used inside the cores_per_socket array
59* for a cpu.
60*/
61static int s390_socket_nb(S390CPU *cpu)62{
63return s390_socket_nb_from_ids(cpu->env.drawer_id, cpu->env.book_id,64cpu->env.socket_id);65}
66
67/**
68* s390_has_topology:
69*
70* Return: true if the topology is supported by the machine.
71*/
72bool s390_has_topology(void)73{
74return s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY);75}
76
77/**
78* s390_topology_init:
79* @ms: the machine state where the machine topology is defined
80*
81* Keep track of the machine topology.
82*
83* Allocate an array to keep the count of cores per socket.
84* The index of the array starts at socket 0 from book 0 and
85* drawer 0 up to the maximum allowed by the machine topology.
86*/
87static void s390_topology_init(MachineState *ms)88{
89CpuTopology *smp = &ms->smp;90
91s390_topology.cores_per_socket = g_new0(uint8_t, smp->sockets *92smp->books * smp->drawers);93}
94
95/*
96* s390_handle_ptf:
97*
98* @register 1: contains the function code
99*
100* Function codes 0 (horizontal) and 1 (vertical) define the CPU
101* polarization requested by the guest.
102*
103* Function code 2 is handling topology changes and is interpreted
104* by the SIE.
105*/
106void s390_handle_ptf(S390CPU *cpu, uint8_t r1, uintptr_t ra)107{
108CpuS390Polarization polarization;109CPUS390XState *env = &cpu->env;110uint64_t reg = env->regs[r1];111int fc = reg & S390_TOPO_FC_MASK;112
113if (!s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY)) {114s390_program_interrupt(env, PGM_OPERATION, ra);115return;116}117
118if (env->psw.mask & PSW_MASK_PSTATE) {119s390_program_interrupt(env, PGM_PRIVILEGED, ra);120return;121}122
123if (reg & ~S390_TOPO_FC_MASK) {124s390_program_interrupt(env, PGM_SPECIFICATION, ra);125return;126}127
128polarization = S390_CPU_POLARIZATION_VERTICAL;129switch (fc) {130case 0:131polarization = S390_CPU_POLARIZATION_HORIZONTAL;132/* fallthrough */133case 1:134if (s390_topology.polarization == polarization) {135env->regs[r1] |= S390_PTF_REASON_DONE;136setcc(cpu, 2);137} else {138s390_topology.polarization = polarization;139s390_cpu_topology_set_changed(true);140qapi_event_send_cpu_polarization_change(polarization);141setcc(cpu, 0);142}143break;144default:145/* Note that fc == 2 is interpreted by the SIE */146s390_program_interrupt(env, PGM_SPECIFICATION, ra);147}148}
149
150/**
151* s390_topology_reset:
152*
153* Generic reset for CPU topology, calls s390_topology_reset()
154* to reset the kernel Modified Topology Change Record.
155*/
156void s390_topology_reset(void)157{
158s390_cpu_topology_set_changed(false);159s390_topology.polarization = S390_CPU_POLARIZATION_HORIZONTAL;160}
161
162/**
163* s390_topology_cpu_default:
164* @cpu: pointer to a S390CPU
165* @errp: Error pointer
166*
167* Setup the default topology if no attributes are already set.
168* Passing a CPU with some, but not all, attributes set is considered
169* an error.
170*
171* The function calculates the (drawer_id, book_id, socket_id)
172* topology by filling the cores starting from the first socket
173* (0, 0, 0) up to the last (smp->drawers, smp->books, smp->sockets).
174*
175* CPU type and dedication have defaults values set in the
176* s390x_cpu_properties, entitlement must be adjust depending on the
177* dedication.
178*
179* Returns false if it is impossible to setup a default topology
180* true otherwise.
181*/
182static bool s390_topology_cpu_default(S390CPU *cpu, Error **errp)183{
184CpuTopology *smp = ¤t_machine->smp;185CPUS390XState *env = &cpu->env;186
187/* All geometry topology attributes must be set or all unset */188if ((env->socket_id < 0 || env->book_id < 0 || env->drawer_id < 0) &&189(env->socket_id >= 0 || env->book_id >= 0 || env->drawer_id >= 0)) {190error_setg(errp,191"Please define all or none of the topology geometry attributes");192return false;193}194
195/* If one value is unset all are unset -> calculate defaults */196if (env->socket_id < 0) {197env->socket_id = s390_std_socket(env->core_id, smp);198env->book_id = s390_std_book(env->core_id, smp);199env->drawer_id = s390_std_drawer(env->core_id, smp);200}201
202/*203* When the user specifies the entitlement as 'auto' on the command line,
204* QEMU will set the entitlement as:
205* Medium when the CPU is not dedicated.
206* High when dedicated is true.
207*/
208if (env->entitlement == S390_CPU_ENTITLEMENT_AUTO) {209if (env->dedicated) {210env->entitlement = S390_CPU_ENTITLEMENT_HIGH;211} else {212env->entitlement = S390_CPU_ENTITLEMENT_MEDIUM;213}214}215return true;216}
217
218/**
219* s390_topology_check:
220* @socket_id: socket to check
221* @book_id: book to check
222* @drawer_id: drawer to check
223* @entitlement: entitlement to check
224* @dedicated: dedication to check
225* @errp: Error pointer
226*
227* The function checks if the topology
228* attributes fits inside the system topology.
229*
230* Returns false if the specified topology does not match with
231* the machine topology.
232*/
233static bool s390_topology_check(uint16_t socket_id, uint16_t book_id,234uint16_t drawer_id, uint16_t entitlement,235bool dedicated, Error **errp)236{
237CpuTopology *smp = ¤t_machine->smp;238
239if (socket_id >= smp->sockets) {240error_setg(errp, "Unavailable socket: %d", socket_id);241return false;242}243if (book_id >= smp->books) {244error_setg(errp, "Unavailable book: %d", book_id);245return false;246}247if (drawer_id >= smp->drawers) {248error_setg(errp, "Unavailable drawer: %d", drawer_id);249return false;250}251if (entitlement >= S390_CPU_ENTITLEMENT__MAX) {252error_setg(errp, "Unknown entitlement: %d", entitlement);253return false;254}255if (dedicated && (entitlement == S390_CPU_ENTITLEMENT_LOW ||256entitlement == S390_CPU_ENTITLEMENT_MEDIUM)) {257error_setg(errp, "A dedicated CPU implies high entitlement");258return false;259}260return true;261}
262
263/**
264* s390_topology_need_report
265* @cpu: Current cpu
266* @drawer_id: future drawer ID
267* @book_id: future book ID
268* @socket_id: future socket ID
269* @entitlement: future entitlement
270* @dedicated: future dedicated
271*
272* A modified topology change report is needed if the topology
273* tree or the topology attributes change.
274*/
275static bool s390_topology_need_report(S390CPU *cpu, int drawer_id,276int book_id, int socket_id,277uint16_t entitlement, bool dedicated)278{
279return cpu->env.drawer_id != drawer_id ||280cpu->env.book_id != book_id ||281cpu->env.socket_id != socket_id ||282cpu->env.entitlement != entitlement ||283cpu->env.dedicated != dedicated;284}
285
286/**
287* s390_update_cpu_props:
288* @ms: the machine state
289* @cpu: the CPU for which to update the properties from the environment.
290*
291*/
292static void s390_update_cpu_props(MachineState *ms, S390CPU *cpu)293{
294CpuInstanceProperties *props;295
296props = &ms->possible_cpus->cpus[cpu->env.core_id].props;297
298props->socket_id = cpu->env.socket_id;299props->book_id = cpu->env.book_id;300props->drawer_id = cpu->env.drawer_id;301}
302
303/**
304* s390_topology_setup_cpu:
305* @ms: MachineState used to initialize the topology structure on
306* first call.
307* @cpu: the new S390CPU to insert in the topology structure
308* @errp: the error pointer
309*
310* Called from CPU hotplug to check and setup the CPU attributes
311* before the CPU is inserted in the topology.
312* There is no need to update the MTCR explicitly here because it
313* will be updated by KVM on creation of the new CPU.
314*/
315void s390_topology_setup_cpu(MachineState *ms, S390CPU *cpu, Error **errp)316{
317int entry;318
319/*320* We do not want to initialize the topology if the CPU model
321* does not support topology, consequently, we have to wait for
322* the first CPU to be realized, which realizes the CPU model
323* to initialize the topology structures.
324*
325* s390_topology_setup_cpu() is called from the CPU hotplug.
326*/
327if (!s390_topology.cores_per_socket) {328s390_topology_init(ms);329}330
331if (!s390_topology_cpu_default(cpu, errp)) {332return;333}334
335if (!s390_topology_check(cpu->env.socket_id, cpu->env.book_id,336cpu->env.drawer_id, cpu->env.entitlement,337cpu->env.dedicated, errp)) {338return;339}340
341/* Do we still have space in the socket */342entry = s390_socket_nb(cpu);343if (s390_topology.cores_per_socket[entry] >= ms->smp.cores) {344error_setg(errp, "No more space on this socket");345return;346}347
348/* Update the count of cores in sockets */349s390_topology.cores_per_socket[entry] += 1;350
351/* topology tree is reflected in props */352s390_update_cpu_props(ms, cpu);353}
354
355static void s390_change_topology(uint16_t core_id,356bool has_socket_id, uint16_t socket_id,357bool has_book_id, uint16_t book_id,358bool has_drawer_id, uint16_t drawer_id,359bool has_entitlement,360CpuS390Entitlement entitlement,361bool has_dedicated, bool dedicated,362Error **errp)363{
364MachineState *ms = current_machine;365int old_socket_entry;366int new_socket_entry;367bool report_needed;368S390CPU *cpu;369
370cpu = s390_cpu_addr2state(core_id);371if (!cpu) {372error_setg(errp, "Core-id %d does not exist!", core_id);373return;374}375
376/* Get attributes not provided from cpu and verify the new topology */377if (!has_socket_id) {378socket_id = cpu->env.socket_id;379}380if (!has_book_id) {381book_id = cpu->env.book_id;382}383if (!has_drawer_id) {384drawer_id = cpu->env.drawer_id;385}386if (!has_dedicated) {387dedicated = cpu->env.dedicated;388}389
390/*391* When the user specifies the entitlement as 'auto' on the command line,
392* QEMU will set the entitlement as:
393* Medium when the CPU is not dedicated.
394* High when dedicated is true.
395*/
396if (!has_entitlement || entitlement == S390_CPU_ENTITLEMENT_AUTO) {397if (dedicated) {398entitlement = S390_CPU_ENTITLEMENT_HIGH;399} else {400entitlement = S390_CPU_ENTITLEMENT_MEDIUM;401}402}403
404if (!s390_topology_check(socket_id, book_id, drawer_id,405entitlement, dedicated, errp)) {406return;407}408
409/* Check for space on new socket */410old_socket_entry = s390_socket_nb(cpu);411new_socket_entry = s390_socket_nb_from_ids(drawer_id, book_id, socket_id);412
413if (new_socket_entry != old_socket_entry) {414if (s390_topology.cores_per_socket[new_socket_entry] >=415ms->smp.cores) {416error_setg(errp, "No more space on this socket");417return;418}419/* Update the count of cores in sockets */420s390_topology.cores_per_socket[new_socket_entry] += 1;421s390_topology.cores_per_socket[old_socket_entry] -= 1;422}423
424/* Check if we will need to report the modified topology */425report_needed = s390_topology_need_report(cpu, drawer_id, book_id,426socket_id, entitlement,427dedicated);428
429/* All checks done, report new topology into the vCPU */430cpu->env.drawer_id = drawer_id;431cpu->env.book_id = book_id;432cpu->env.socket_id = socket_id;433cpu->env.dedicated = dedicated;434cpu->env.entitlement = entitlement;435
436/* topology tree is reflected in props */437s390_update_cpu_props(ms, cpu);438
439/* Advertise the topology change */440if (report_needed) {441s390_cpu_topology_set_changed(true);442}443}
444
445void qmp_set_cpu_topology(uint16_t core,446bool has_socket, uint16_t socket,447bool has_book, uint16_t book,448bool has_drawer, uint16_t drawer,449bool has_entitlement, CpuS390Entitlement entitlement,450bool has_dedicated, bool dedicated,451Error **errp)452{
453if (!s390_has_topology()) {454error_setg(errp, "This machine doesn't support topology");455return;456}457
458s390_change_topology(core, has_socket, socket, has_book, book,459has_drawer, drawer, has_entitlement, entitlement,460has_dedicated, dedicated, errp);461}
462
463CpuPolarizationInfo *qmp_query_s390x_cpu_polarization(Error **errp)464{
465CpuPolarizationInfo *info = g_new0(CpuPolarizationInfo, 1);466
467info->polarization = s390_topology.polarization;468return info;469}
470