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 */
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,
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 */
s390_socket_nb_from_ids(int drawer_id,int book_id,int socket_id)48 static int s390_socket_nb_from_ids(int drawer_id, int book_id, int socket_id)
49 {
50 return (drawer_id * current_machine->smp.books + book_id) *
51 current_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 */
s390_socket_nb(S390CPU * cpu)61 static int s390_socket_nb(S390CPU *cpu)
62 {
63 return s390_socket_nb_from_ids(cpu->env.drawer_id, cpu->env.book_id,
64 cpu->env.socket_id);
65 }
66
67 /**
68 * s390_has_topology:
69 *
70 * Return: true if the topology is supported by the machine.
71 */
s390_has_topology(void)72 bool s390_has_topology(void)
73 {
74 return 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 */
s390_topology_init(MachineState * ms)87 static void s390_topology_init(MachineState *ms)
88 {
89 CpuTopology *smp = &ms->smp;
90
91 s390_topology.cores_per_socket = g_new0(uint8_t, smp->sockets *
92 smp->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 */
s390_handle_ptf(S390CPU * cpu,uint8_t r1,uintptr_t ra)106 void s390_handle_ptf(S390CPU *cpu, uint8_t r1, uintptr_t ra)
107 {
108 S390CpuPolarization polarization;
109 CPUS390XState *env = &cpu->env;
110 uint64_t reg = env->regs[r1];
111 int fc = reg & S390_TOPO_FC_MASK;
112
113 if (!s390_has_feat(S390_FEAT_CONFIGURATION_TOPOLOGY)) {
114 s390_program_interrupt(env, PGM_OPERATION, ra);
115 return;
116 }
117
118 if (env->psw.mask & PSW_MASK_PSTATE) {
119 s390_program_interrupt(env, PGM_PRIVILEGED, ra);
120 return;
121 }
122
123 if (reg & ~S390_TOPO_FC_MASK) {
124 s390_program_interrupt(env, PGM_SPECIFICATION, ra);
125 return;
126 }
127
128 polarization = S390_CPU_POLARIZATION_VERTICAL;
129 switch (fc) {
130 case 0:
131 polarization = S390_CPU_POLARIZATION_HORIZONTAL;
132 /* fallthrough */
133 case 1:
134 if (s390_topology.polarization == polarization) {
135 env->regs[r1] |= S390_PTF_REASON_DONE;
136 setcc(cpu, 2);
137 } else {
138 s390_topology.polarization = polarization;
139 s390_cpu_topology_set_changed(true);
140 qapi_event_send_cpu_polarization_change(polarization);
141 setcc(cpu, 0);
142 }
143 break;
144 default:
145 /* Note that fc == 2 is interpreted by the SIE */
146 s390_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 */
s390_topology_reset(void)156 void s390_topology_reset(void)
157 {
158 s390_cpu_topology_set_changed(false);
159 s390_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 */
s390_topology_cpu_default(S390CPU * cpu,Error ** errp)182 static bool s390_topology_cpu_default(S390CPU *cpu, Error **errp)
183 {
184 CpuTopology *smp = ¤t_machine->smp;
185 CPUS390XState *env = &cpu->env;
186
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)) {
190 error_setg(errp,
191 "Please define all or none of the topology geometry attributes");
192 return false;
193 }
194
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);
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 */
208 if (env->entitlement == S390_CPU_ENTITLEMENT_AUTO) {
209 if (env->dedicated) {
210 env->entitlement = S390_CPU_ENTITLEMENT_HIGH;
211 } else {
212 env->entitlement = S390_CPU_ENTITLEMENT_MEDIUM;
213 }
214 }
215 return 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 */
s390_topology_check(uint16_t socket_id,uint16_t book_id,uint16_t drawer_id,uint16_t entitlement,bool dedicated,Error ** errp)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)
236 {
237 CpuTopology *smp = ¤t_machine->smp;
238
239 if (socket_id >= smp->sockets) {
240 error_setg(errp, "Unavailable socket: %d", socket_id);
241 return false;
242 }
243 if (book_id >= smp->books) {
244 error_setg(errp, "Unavailable book: %d", book_id);
245 return false;
246 }
247 if (drawer_id >= smp->drawers) {
248 error_setg(errp, "Unavailable drawer: %d", drawer_id);
249 return false;
250 }
251 if (entitlement >= S390_CPU_ENTITLEMENT__MAX) {
252 error_setg(errp, "Unknown entitlement: %d", entitlement);
253 return false;
254 }
255 if (dedicated && (entitlement == S390_CPU_ENTITLEMENT_LOW ||
256 entitlement == S390_CPU_ENTITLEMENT_MEDIUM)) {
257 error_setg(errp, "A dedicated CPU implies high entitlement");
258 return false;
259 }
260 return 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 */
s390_topology_need_report(S390CPU * cpu,int drawer_id,int book_id,int socket_id,uint16_t entitlement,bool dedicated)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)
278 {
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;
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 */
s390_update_cpu_props(MachineState * ms,S390CPU * cpu)292 static void s390_update_cpu_props(MachineState *ms, S390CPU *cpu)
293 {
294 CpuInstanceProperties *props;
295
296 props = &ms->possible_cpus->cpus[cpu->env.core_id].props;
297
298 props->socket_id = cpu->env.socket_id;
299 props->book_id = cpu->env.book_id;
300 props->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 */
s390_topology_setup_cpu(MachineState * ms,S390CPU * cpu,Error ** errp)315 void s390_topology_setup_cpu(MachineState *ms, S390CPU *cpu, Error **errp)
316 {
317 int 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 */
327 if (!s390_topology.cores_per_socket) {
328 s390_topology_init(ms);
329 }
330
331 if (!s390_topology_cpu_default(cpu, errp)) {
332 return;
333 }
334
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)) {
338 return;
339 }
340
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");
345 return;
346 }
347
348 /* Update the count of cores in sockets */
349 s390_topology.cores_per_socket[entry] += 1;
350
351 /* topology tree is reflected in props */
352 s390_update_cpu_props(ms, cpu);
353 }
354
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,S390CpuEntitlement entitlement,bool has_dedicated,bool dedicated,Error ** errp)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,
362 Error **errp)
363 {
364 MachineState *ms = current_machine;
365 int old_socket_entry;
366 int new_socket_entry;
367 bool report_needed;
368 S390CPU *cpu;
369
370 cpu = s390_cpu_addr2state(core_id);
371 if (!cpu) {
372 error_setg(errp, "Core-id %d does not exist!", core_id);
373 return;
374 }
375
376 /* Get attributes not provided from cpu and verify the new topology */
377 if (!has_socket_id) {
378 socket_id = cpu->env.socket_id;
379 }
380 if (!has_book_id) {
381 book_id = cpu->env.book_id;
382 }
383 if (!has_drawer_id) {
384 drawer_id = cpu->env.drawer_id;
385 }
386 if (!has_dedicated) {
387 dedicated = 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 */
396 if (!has_entitlement || entitlement == S390_CPU_ENTITLEMENT_AUTO) {
397 if (dedicated) {
398 entitlement = S390_CPU_ENTITLEMENT_HIGH;
399 } else {
400 entitlement = S390_CPU_ENTITLEMENT_MEDIUM;
401 }
402 }
403
404 if (!s390_topology_check(socket_id, book_id, drawer_id,
405 entitlement, dedicated, errp)) {
406 return;
407 }
408
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);
412
413 if (new_socket_entry != old_socket_entry) {
414 if (s390_topology.cores_per_socket[new_socket_entry] >=
415 ms->smp.cores) {
416 error_setg(errp, "No more space on this socket");
417 return;
418 }
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;
422 }
423
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,
427 dedicated);
428
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;
435
436 /* topology tree is reflected in props */
437 s390_update_cpu_props(ms, cpu);
438
439 /* Advertise the topology change */
440 if (report_needed) {
441 s390_cpu_topology_set_changed(true);
442 }
443 }
444
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,S390CpuEntitlement entitlement,bool has_dedicated,bool dedicated,Error ** errp)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,
451 Error **errp)
452 {
453 if (!s390_has_topology()) {
454 error_setg(errp, "This machine doesn't support topology");
455 return;
456 }
457
458 s390_change_topology(core, has_socket, socket, has_book, book,
459 has_drawer, drawer, has_entitlement, entitlement,
460 has_dedicated, dedicated, errp);
461 }
462
qmp_query_s390x_cpu_polarization(Error ** errp)463 CpuPolarizationInfo *qmp_query_s390x_cpu_polarization(Error **errp)
464 {
465 CpuPolarizationInfo *info = g_new0(CpuPolarizationInfo, 1);
466
467 info->polarization = s390_topology.polarization;
468 return info;
469 }
470