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