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 = ¤t_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 = ¤t_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