2
0

cpu-topology.c 14 KB

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