xref: /openbmc/qemu/hw/s390x/cpu-topology.c (revision f4f54b58)
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 
27 /*
28  * s390_topology is used to keep the topology information.
29  * .cores_per_socket: tracks information on the count of cores
30  *                    per socket.
31  * .polarization: tracks machine polarization.
32  */
33 S390Topology s390_topology = {
34     /* will be initialized after the CPU model is realized */
35     .cores_per_socket = NULL,
36     .polarization = S390_CPU_POLARIZATION_HORIZONTAL,
37 };
38 
39 /**
40  * s390_socket_nb:
41  * @cpu: s390x CPU
42  *
43  * Returns the socket number used inside the cores_per_socket array
44  * for a topology tree entry
45  */
46 static int s390_socket_nb_from_ids(int drawer_id, int book_id, int socket_id)
47 {
48     return (drawer_id * current_machine->smp.books + book_id) *
49            current_machine->smp.sockets + socket_id;
50 }
51 
52 /**
53  * s390_socket_nb:
54  * @cpu: s390x CPU
55  *
56  * Returns the socket number used inside the cores_per_socket array
57  * for a cpu.
58  */
59 static int s390_socket_nb(S390CPU *cpu)
60 {
61     return s390_socket_nb_from_ids(cpu->env.drawer_id, cpu->env.book_id,
62                                    cpu->env.socket_id);
63 }
64 
65 /**
66  * s390_has_topology:
67  *
68  * Return: true if the topology is supported by the machine.
69  */
70 bool s390_has_topology(void)
71 {
72     return false;
73 }
74 
75 /**
76  * s390_topology_init:
77  * @ms: the machine state where the machine topology is defined
78  *
79  * Keep track of the machine topology.
80  *
81  * Allocate an array to keep the count of cores per socket.
82  * The index of the array starts at socket 0 from book 0 and
83  * drawer 0 up to the maximum allowed by the machine topology.
84  */
85 static void s390_topology_init(MachineState *ms)
86 {
87     CpuTopology *smp = &ms->smp;
88 
89     s390_topology.cores_per_socket = g_new0(uint8_t, smp->sockets *
90                                             smp->books * smp->drawers);
91 }
92 
93 /**
94  * s390_topology_cpu_default:
95  * @cpu: pointer to a S390CPU
96  * @errp: Error pointer
97  *
98  * Setup the default topology if no attributes are already set.
99  * Passing a CPU with some, but not all, attributes set is considered
100  * an error.
101  *
102  * The function calculates the (drawer_id, book_id, socket_id)
103  * topology by filling the cores starting from the first socket
104  * (0, 0, 0) up to the last (smp->drawers, smp->books, smp->sockets).
105  *
106  * CPU type and dedication have defaults values set in the
107  * s390x_cpu_properties, entitlement must be adjust depending on the
108  * dedication.
109  *
110  * Returns false if it is impossible to setup a default topology
111  * true otherwise.
112  */
113 static bool s390_topology_cpu_default(S390CPU *cpu, Error **errp)
114 {
115     CpuTopology *smp = &current_machine->smp;
116     CPUS390XState *env = &cpu->env;
117 
118     /* All geometry topology attributes must be set or all unset */
119     if ((env->socket_id < 0 || env->book_id < 0 || env->drawer_id < 0) &&
120         (env->socket_id >= 0 || env->book_id >= 0 || env->drawer_id >= 0)) {
121         error_setg(errp,
122                    "Please define all or none of the topology geometry attributes");
123         return false;
124     }
125 
126     /* If one value is unset all are unset -> calculate defaults */
127     if (env->socket_id < 0) {
128         env->socket_id = s390_std_socket(env->core_id, smp);
129         env->book_id = s390_std_book(env->core_id, smp);
130         env->drawer_id = s390_std_drawer(env->core_id, smp);
131     }
132 
133     /*
134      * When the user specifies the entitlement as 'auto' on the command line,
135      * QEMU will set the entitlement as:
136      * Medium when the CPU is not dedicated.
137      * High when dedicated is true.
138      */
139     if (env->entitlement == S390_CPU_ENTITLEMENT_AUTO) {
140         if (env->dedicated) {
141             env->entitlement = S390_CPU_ENTITLEMENT_HIGH;
142         } else {
143             env->entitlement = S390_CPU_ENTITLEMENT_MEDIUM;
144         }
145     }
146     return true;
147 }
148 
149 /**
150  * s390_topology_check:
151  * @socket_id: socket to check
152  * @book_id: book to check
153  * @drawer_id: drawer to check
154  * @entitlement: entitlement to check
155  * @dedicated: dedication to check
156  * @errp: Error pointer
157  *
158  * The function checks if the topology
159  * attributes fits inside the system topology.
160  *
161  * Returns false if the specified topology does not match with
162  * the machine topology.
163  */
164 static bool s390_topology_check(uint16_t socket_id, uint16_t book_id,
165                                 uint16_t drawer_id, uint16_t entitlement,
166                                 bool dedicated, Error **errp)
167 {
168     CpuTopology *smp = &current_machine->smp;
169 
170     if (socket_id >= smp->sockets) {
171         error_setg(errp, "Unavailable socket: %d", socket_id);
172         return false;
173     }
174     if (book_id >= smp->books) {
175         error_setg(errp, "Unavailable book: %d", book_id);
176         return false;
177     }
178     if (drawer_id >= smp->drawers) {
179         error_setg(errp, "Unavailable drawer: %d", drawer_id);
180         return false;
181     }
182     if (entitlement >= S390_CPU_ENTITLEMENT__MAX) {
183         error_setg(errp, "Unknown entitlement: %d", entitlement);
184         return false;
185     }
186     if (dedicated && (entitlement == S390_CPU_ENTITLEMENT_LOW ||
187                       entitlement == S390_CPU_ENTITLEMENT_MEDIUM)) {
188         error_setg(errp, "A dedicated CPU implies high entitlement");
189         return false;
190     }
191     return true;
192 }
193 
194 /**
195  * s390_update_cpu_props:
196  * @ms: the machine state
197  * @cpu: the CPU for which to update the properties from the environment.
198  *
199  */
200 static void s390_update_cpu_props(MachineState *ms, S390CPU *cpu)
201 {
202     CpuInstanceProperties *props;
203 
204     props = &ms->possible_cpus->cpus[cpu->env.core_id].props;
205 
206     props->socket_id = cpu->env.socket_id;
207     props->book_id = cpu->env.book_id;
208     props->drawer_id = cpu->env.drawer_id;
209 }
210 
211 /**
212  * s390_topology_setup_cpu:
213  * @ms: MachineState used to initialize the topology structure on
214  *      first call.
215  * @cpu: the new S390CPU to insert in the topology structure
216  * @errp: the error pointer
217  *
218  * Called from CPU hotplug to check and setup the CPU attributes
219  * before the CPU is inserted in the topology.
220  * There is no need to update the MTCR explicitly here because it
221  * will be updated by KVM on creation of the new CPU.
222  */
223 void s390_topology_setup_cpu(MachineState *ms, S390CPU *cpu, Error **errp)
224 {
225     int entry;
226 
227     /*
228      * We do not want to initialize the topology if the CPU model
229      * does not support topology, consequently, we have to wait for
230      * the first CPU to be realized, which realizes the CPU model
231      * to initialize the topology structures.
232      *
233      * s390_topology_setup_cpu() is called from the CPU hotplug.
234      */
235     if (!s390_topology.cores_per_socket) {
236         s390_topology_init(ms);
237     }
238 
239     if (!s390_topology_cpu_default(cpu, errp)) {
240         return;
241     }
242 
243     if (!s390_topology_check(cpu->env.socket_id, cpu->env.book_id,
244                              cpu->env.drawer_id, cpu->env.entitlement,
245                              cpu->env.dedicated, errp)) {
246         return;
247     }
248 
249     /* Do we still have space in the socket */
250     entry = s390_socket_nb(cpu);
251     if (s390_topology.cores_per_socket[entry] >= ms->smp.cores) {
252         error_setg(errp, "No more space on this socket");
253         return;
254     }
255 
256     /* Update the count of cores in sockets */
257     s390_topology.cores_per_socket[entry] += 1;
258 
259     /* topology tree is reflected in props */
260     s390_update_cpu_props(ms, cpu);
261 }
262