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