1 /* SPDX-License-Identifier: GPL-2.0-or-later */
5 * Copyright IBM Corp. 2022, 2023
6 * Author(s): Pierre Morel <pmorel@linux.ibm.com>
8 * S390 topology handling can be divided in two parts:
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.
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.
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"
30 * s390_topology is used to keep the topology information.
31 * .cores_per_socket: tracks information on the count of cores
33 * .polarization: tracks machine polarization.
35 S390Topology s390_topology
= {
36 /* will be initialized after the CPU model is realized */
37 .cores_per_socket
= NULL
,
38 .polarization
= S390_CPU_POLARIZATION_HORIZONTAL
,
45 * Returns the socket number used inside the cores_per_socket array
46 * for a topology tree entry
48 static int s390_socket_nb_from_ids(int drawer_id
, int book_id
, int socket_id
)
50 return (drawer_id
* current_machine
->smp
.books
+ book_id
) *
51 current_machine
->smp
.sockets
+ socket_id
;
58 * Returns the socket number used inside the cores_per_socket array
61 static int s390_socket_nb(S390CPU
*cpu
)
63 return s390_socket_nb_from_ids(cpu
->env
.drawer_id
, cpu
->env
.book_id
,
70 * Return: true if the topology is supported by the machine.
72 bool s390_has_topology(void)
74 return s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY
);
79 * @ms: the machine state where the machine topology is defined
81 * Keep track of the machine topology.
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.
87 static void s390_topology_init(MachineState
*ms
)
89 CpuTopology
*smp
= &ms
->smp
;
91 s390_topology
.cores_per_socket
= g_new0(uint8_t, smp
->sockets
*
92 smp
->books
* smp
->drawers
);
98 * @register 1: contains the function code
100 * Function codes 0 (horizontal) and 1 (vertical) define the CPU
101 * polarization requested by the guest.
103 * Function code 2 is handling topology changes and is interpreted
106 void s390_handle_ptf(S390CPU
*cpu
, uint8_t r1
, uintptr_t ra
)
108 S390CpuPolarization polarization
;
109 CPUS390XState
*env
= &cpu
->env
;
110 uint64_t reg
= env
->regs
[r1
];
111 int fc
= reg
& S390_TOPO_FC_MASK
;
113 if (!s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY
)) {
114 s390_program_interrupt(env
, PGM_OPERATION
, ra
);
118 if (env
->psw
.mask
& PSW_MASK_PSTATE
) {
119 s390_program_interrupt(env
, PGM_PRIVILEGED
, ra
);
123 if (reg
& ~S390_TOPO_FC_MASK
) {
124 s390_program_interrupt(env
, PGM_SPECIFICATION
, ra
);
128 polarization
= S390_CPU_POLARIZATION_VERTICAL
;
131 polarization
= S390_CPU_POLARIZATION_HORIZONTAL
;
134 if (s390_topology
.polarization
== polarization
) {
135 env
->regs
[r1
] |= S390_PTF_REASON_DONE
;
138 s390_topology
.polarization
= polarization
;
139 s390_cpu_topology_set_changed(true);
140 qapi_event_send_cpu_polarization_change(polarization
);
145 /* Note that fc == 2 is interpreted by the SIE */
146 s390_program_interrupt(env
, PGM_SPECIFICATION
, ra
);
151 * s390_topology_reset:
153 * Generic reset for CPU topology, calls s390_topology_reset()
154 * to reset the kernel Modified Topology Change Record.
156 void s390_topology_reset(void)
158 s390_cpu_topology_set_changed(false);
159 s390_topology
.polarization
= S390_CPU_POLARIZATION_HORIZONTAL
;
163 * s390_topology_cpu_default:
164 * @cpu: pointer to a S390CPU
165 * @errp: Error pointer
167 * Setup the default topology if no attributes are already set.
168 * Passing a CPU with some, but not all, attributes set is considered
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).
175 * CPU type and dedication have defaults values set in the
176 * s390x_cpu_properties, entitlement must be adjust depending on the
179 * Returns false if it is impossible to setup a default topology
182 static bool s390_topology_cpu_default(S390CPU
*cpu
, Error
**errp
)
184 CpuTopology
*smp
= ¤t_machine
->smp
;
185 CPUS390XState
*env
= &cpu
->env
;
187 /* All geometry topology attributes must be set or all unset */
188 if ((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)) {
191 "Please define all or none of the topology geometry attributes");
195 /* If one value is unset all are unset -> calculate defaults */
196 if (env
->socket_id
< 0) {
197 env
->socket_id
= s390_std_socket(env
->core_id
, smp
);
198 env
->book_id
= s390_std_book(env
->core_id
, smp
);
199 env
->drawer_id
= s390_std_drawer(env
->core_id
, smp
);
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.
208 if (env
->entitlement
== S390_CPU_ENTITLEMENT_AUTO
) {
209 if (env
->dedicated
) {
210 env
->entitlement
= S390_CPU_ENTITLEMENT_HIGH
;
212 env
->entitlement
= S390_CPU_ENTITLEMENT_MEDIUM
;
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
227 * The function checks if the topology
228 * attributes fits inside the system topology.
230 * Returns false if the specified topology does not match with
231 * the machine topology.
233 static bool s390_topology_check(uint16_t socket_id
, uint16_t book_id
,
234 uint16_t drawer_id
, uint16_t entitlement
,
235 bool dedicated
, Error
**errp
)
237 CpuTopology
*smp
= ¤t_machine
->smp
;
239 if (socket_id
>= smp
->sockets
) {
240 error_setg(errp
, "Unavailable socket: %d", socket_id
);
243 if (book_id
>= smp
->books
) {
244 error_setg(errp
, "Unavailable book: %d", book_id
);
247 if (drawer_id
>= smp
->drawers
) {
248 error_setg(errp
, "Unavailable drawer: %d", drawer_id
);
251 if (entitlement
>= S390_CPU_ENTITLEMENT__MAX
) {
252 error_setg(errp
, "Unknown entitlement: %d", entitlement
);
255 if (dedicated
&& (entitlement
== S390_CPU_ENTITLEMENT_LOW
||
256 entitlement
== S390_CPU_ENTITLEMENT_MEDIUM
)) {
257 error_setg(errp
, "A dedicated CPU implies high entitlement");
264 * s390_topology_need_report
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
272 * A modified topology change report is needed if the topology
273 * tree or the topology attributes change.
275 static bool s390_topology_need_report(S390CPU
*cpu
, int drawer_id
,
276 int book_id
, int socket_id
,
277 uint16_t entitlement
, bool dedicated
)
279 return cpu
->env
.drawer_id
!= drawer_id
||
280 cpu
->env
.book_id
!= book_id
||
281 cpu
->env
.socket_id
!= socket_id
||
282 cpu
->env
.entitlement
!= entitlement
||
283 cpu
->env
.dedicated
!= dedicated
;
287 * s390_update_cpu_props:
288 * @ms: the machine state
289 * @cpu: the CPU for which to update the properties from the environment.
292 static void s390_update_cpu_props(MachineState
*ms
, S390CPU
*cpu
)
294 CpuInstanceProperties
*props
;
296 props
= &ms
->possible_cpus
->cpus
[cpu
->env
.core_id
].props
;
298 props
->socket_id
= cpu
->env
.socket_id
;
299 props
->book_id
= cpu
->env
.book_id
;
300 props
->drawer_id
= cpu
->env
.drawer_id
;
304 * s390_topology_setup_cpu:
305 * @ms: MachineState used to initialize the topology structure on
307 * @cpu: the new S390CPU to insert in the topology structure
308 * @errp: the error pointer
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.
315 void s390_topology_setup_cpu(MachineState
*ms
, S390CPU
*cpu
, Error
**errp
)
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.
325 * s390_topology_setup_cpu() is called from the CPU hotplug.
327 if (!s390_topology
.cores_per_socket
) {
328 s390_topology_init(ms
);
331 if (!s390_topology_cpu_default(cpu
, errp
)) {
335 if (!s390_topology_check(cpu
->env
.socket_id
, cpu
->env
.book_id
,
336 cpu
->env
.drawer_id
, cpu
->env
.entitlement
,
337 cpu
->env
.dedicated
, errp
)) {
341 /* Do we still have space in the socket */
342 entry
= s390_socket_nb(cpu
);
343 if (s390_topology
.cores_per_socket
[entry
] >= ms
->smp
.cores
) {
344 error_setg(errp
, "No more space on this socket");
348 /* Update the count of cores in sockets */
349 s390_topology
.cores_per_socket
[entry
] += 1;
351 /* topology tree is reflected in props */
352 s390_update_cpu_props(ms
, cpu
);
355 static void s390_change_topology(uint16_t core_id
,
356 bool has_socket_id
, uint16_t socket_id
,
357 bool has_book_id
, uint16_t book_id
,
358 bool has_drawer_id
, uint16_t drawer_id
,
359 bool has_entitlement
,
360 S390CpuEntitlement entitlement
,
361 bool has_dedicated
, bool dedicated
,
364 MachineState
*ms
= current_machine
;
365 int old_socket_entry
;
366 int new_socket_entry
;
370 cpu
= s390_cpu_addr2state(core_id
);
372 error_setg(errp
, "Core-id %d does not exist!", core_id
);
376 /* Get attributes not provided from cpu and verify the new topology */
377 if (!has_socket_id
) {
378 socket_id
= cpu
->env
.socket_id
;
381 book_id
= cpu
->env
.book_id
;
383 if (!has_drawer_id
) {
384 drawer_id
= cpu
->env
.drawer_id
;
386 if (!has_dedicated
) {
387 dedicated
= cpu
->env
.dedicated
;
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.
396 if (!has_entitlement
|| entitlement
== S390_CPU_ENTITLEMENT_AUTO
) {
398 entitlement
= S390_CPU_ENTITLEMENT_HIGH
;
400 entitlement
= S390_CPU_ENTITLEMENT_MEDIUM
;
404 if (!s390_topology_check(socket_id
, book_id
, drawer_id
,
405 entitlement
, dedicated
, errp
)) {
409 /* Check for space on new socket */
410 old_socket_entry
= s390_socket_nb(cpu
);
411 new_socket_entry
= s390_socket_nb_from_ids(drawer_id
, book_id
, socket_id
);
413 if (new_socket_entry
!= old_socket_entry
) {
414 if (s390_topology
.cores_per_socket
[new_socket_entry
] >=
416 error_setg(errp
, "No more space on this socket");
419 /* Update the count of cores in sockets */
420 s390_topology
.cores_per_socket
[new_socket_entry
] += 1;
421 s390_topology
.cores_per_socket
[old_socket_entry
] -= 1;
424 /* Check if we will need to report the modified topology */
425 report_needed
= s390_topology_need_report(cpu
, drawer_id
, book_id
,
426 socket_id
, entitlement
,
429 /* All checks done, report new topology into the vCPU */
430 cpu
->env
.drawer_id
= drawer_id
;
431 cpu
->env
.book_id
= book_id
;
432 cpu
->env
.socket_id
= socket_id
;
433 cpu
->env
.dedicated
= dedicated
;
434 cpu
->env
.entitlement
= entitlement
;
436 /* topology tree is reflected in props */
437 s390_update_cpu_props(ms
, cpu
);
439 /* Advertise the topology change */
441 s390_cpu_topology_set_changed(true);
445 void qmp_set_cpu_topology(uint16_t core
,
446 bool has_socket
, uint16_t socket
,
447 bool has_book
, uint16_t book
,
448 bool has_drawer
, uint16_t drawer
,
449 bool has_entitlement
, S390CpuEntitlement entitlement
,
450 bool has_dedicated
, bool dedicated
,
453 if (!s390_has_topology()) {
454 error_setg(errp
, "This machine doesn't support topology");
458 s390_change_topology(core
, has_socket
, socket
, has_book
, book
,
459 has_drawer
, drawer
, has_entitlement
, entitlement
,
460 has_dedicated
, dedicated
, errp
);
463 CpuPolarizationInfo
*qmp_query_s390x_cpu_polarization(Error
**errp
)
465 CpuPolarizationInfo
*info
= g_new0(CpuPolarizationInfo
, 1);
467 info
->polarization
= s390_topology
.polarization
;