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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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