qemu

Форк
0
/
cpu-topology.c 
469 строк · 14.3 Кб
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
    CpuS390Polarization 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 = &current_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 = &current_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
                                 CpuS390Entitlement 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, CpuS390Entitlement 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

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.