cpu.c 14.0 KB
Newer Older
J
Jiri Denemark 已提交
1 2 3
/*
 * cpu.c: internal functions for CPU manipulation
 *
4
 * Copyright (C) 2009-2013 Red Hat, Inc.
J
Jiri Denemark 已提交
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
J
Jiri Denemark 已提交
19 20 21 22 23 24 25
 *
 * Authors:
 *      Jiri Denemark <jdenemar@redhat.com>
 */

#include <config.h>

26
#include "virlog.h"
27
#include "viralloc.h"
28
#include "virxml.h"
J
Jiri Denemark 已提交
29
#include "cpu.h"
30
#include "cpu_map.h"
J
Jiri Denemark 已提交
31
#include "cpu_x86.h"
P
Prerna Saxena 已提交
32
#include "cpu_powerpc.h"
T
Thang Pham 已提交
33
#include "cpu_s390.h"
C
Chuck Short 已提交
34
#include "cpu_arm.h"
35
#include "cpu_aarch64.h"
J
Jiri Denemark 已提交
36
#include "cpu_generic.h"
37
#include "util/virstring.h"
J
Jiri Denemark 已提交
38 39 40 41 42 43 44


#define NR_DRIVERS ARRAY_CARDINALITY(drivers)
#define VIR_FROM_THIS VIR_FROM_CPU

static struct cpuArchDriver *drivers[] = {
    &cpuDriverX86,
P
Prerna Saxena 已提交
45
    &cpuDriverPowerPC,
T
Thang Pham 已提交
46
    &cpuDriverS390,
C
Chuck Short 已提交
47
    &cpuDriverArm,
48
    &cpuDriverAARCH64,
J
Jiri Denemark 已提交
49 50 51 52 53 54
    /* generic driver must always be the last one */
    &cpuDriverGeneric
};


static struct cpuArchDriver *
55
cpuGetSubDriver(virArch arch)
J
Jiri Denemark 已提交
56
{
57 58
    size_t i;
    size_t j;
J
Jiri Denemark 已提交
59

60
    if (arch == VIR_ARCH_NONE) {
61 62
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("undefined hardware architecture"));
J
Jiri Denemark 已提交
63 64 65 66 67
        return NULL;
    }

    for (i = 0; i < NR_DRIVERS - 1; i++) {
        for (j = 0; j < drivers[i]->narch; j++) {
68
            if (arch == drivers[i]->arch[j])
J
Jiri Denemark 已提交
69 70 71 72 73 74 75 76 77 78
                return drivers[i];
        }
    }

    /* use generic driver by default */
    return drivers[NR_DRIVERS - 1];
}


virCPUCompareResult
79
cpuCompareXML(virCPUDefPtr host,
J
Jiri Denemark 已提交
80 81 82 83 84 85 86
              const char *xml)
{
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virCPUDefPtr cpu = NULL;
    virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;

87 88
    VIR_DEBUG("host=%p, xml=%s", host, NULLSTR(xml));

89
    if (!(doc = virXMLParseStringCtxt(xml, _("(CPU_definition)"), &ctxt)))
90
        goto cleanup;
J
Jiri Denemark 已提交
91

92
    cpu = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_AUTO);
J
Jiri Denemark 已提交
93 94 95
    if (cpu == NULL)
        goto cleanup;

96
    if (!cpu->model) {
97 98
        virReportError(VIR_ERR_OPERATION_INVALID,
                       "%s", _("no CPU model specified"));
99 100 101
        goto cleanup;
    }

102
    ret = cpuCompare(host, cpu);
J
Jiri Denemark 已提交
103 104 105 106 107 108 109 110 111 112 113

cleanup:
    virCPUDefFree(cpu);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(doc);

    return ret;
}


virCPUCompareResult
114
cpuCompare(virCPUDefPtr host,
J
Jiri Denemark 已提交
115 116 117 118
           virCPUDefPtr cpu)
{
    struct cpuArchDriver *driver;

119 120
    VIR_DEBUG("host=%p, cpu=%p", host, cpu);

121
    if ((driver = cpuGetSubDriver(host->arch)) == NULL)
J
Jiri Denemark 已提交
122 123 124
        return VIR_CPU_COMPARE_ERROR;

    if (driver->compare == NULL) {
125 126
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot compare CPUs of %s architecture"),
127
                       virArchToString(host->arch));
J
Jiri Denemark 已提交
128 129 130 131 132 133 134 135
        return VIR_CPU_COMPARE_ERROR;
    }

    return driver->compare(host, cpu);
}


int
136
cpuDecode(virCPUDefPtr cpu,
137
          const virCPUData *data,
138
          const char **models,
139 140
          unsigned int nmodels,
          const char *preferred)
J
Jiri Denemark 已提交
141 142 143
{
    struct cpuArchDriver *driver;

144 145
    VIR_DEBUG("cpu=%p, data=%p, nmodels=%u, preferred=%s",
              cpu, data, nmodels, NULLSTR(preferred));
146
    if (models) {
147
        size_t i;
148
        for (i = 0; i < nmodels; i++)
149
            VIR_DEBUG("models[%zu]=%s", i, NULLSTR(models[i]));
150 151
    }

152
    if (models == NULL && nmodels != 0) {
153 154
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("nonzero nmodels doesn't match with NULL models"));
155 156 157
        return -1;
    }

J
Jiri Denemark 已提交
158
    if (cpu == NULL) {
159 160
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("invalid CPU definition"));
J
Jiri Denemark 已提交
161 162 163
        return -1;
    }

164
    if ((driver = cpuGetSubDriver(cpu->arch)) == NULL)
J
Jiri Denemark 已提交
165 166 167
        return -1;

    if (driver->decode == NULL) {
168 169
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot decode CPU data for %s architecture"),
170
                       virArchToString(cpu->arch));
J
Jiri Denemark 已提交
171 172 173
        return -1;
    }

174
    return driver->decode(cpu, data, models, nmodels, preferred, 0);
J
Jiri Denemark 已提交
175 176 177 178
}


int
179
cpuEncode(virArch arch,
180
          const virCPUDef *cpu,
181 182 183 184 185 186
          virCPUDataPtr *forced,
          virCPUDataPtr *required,
          virCPUDataPtr *optional,
          virCPUDataPtr *disabled,
          virCPUDataPtr *forbidden,
          virCPUDataPtr *vendor)
J
Jiri Denemark 已提交
187 188 189
{
    struct cpuArchDriver *driver;

190
    VIR_DEBUG("arch=%s, cpu=%p, forced=%p, required=%p, "
J
Jiri Denemark 已提交
191
              "optional=%p, disabled=%p, forbidden=%p, vendor=%p",
192
              virArchToString(arch), cpu, forced, required,
J
Jiri Denemark 已提交
193
              optional, disabled, forbidden, vendor);
194

195
    if ((driver = cpuGetSubDriver(arch)) == NULL)
J
Jiri Denemark 已提交
196 197 198
        return -1;

    if (driver->encode == NULL) {
199 200
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot encode CPU data for %s architecture"),
201
                       virArchToString(arch));
J
Jiri Denemark 已提交
202 203 204
        return -1;
    }

J
Jiri Denemark 已提交
205
    return driver->encode(arch, cpu, forced, required,
J
Jiri Denemark 已提交
206
                          optional, disabled, forbidden, vendor);
J
Jiri Denemark 已提交
207 208 209 210
}


void
J
Jiri Denemark 已提交
211
cpuDataFree(virCPUDataPtr data)
J
Jiri Denemark 已提交
212 213 214
{
    struct cpuArchDriver *driver;

J
Jiri Denemark 已提交
215
    VIR_DEBUG("data=%p", data);
216

J
Jiri Denemark 已提交
217 218 219
    if (data == NULL)
        return;

J
Jiri Denemark 已提交
220
    if ((driver = cpuGetSubDriver(data->arch)) == NULL)
J
Jiri Denemark 已提交
221 222 223
        return;

    if (driver->free == NULL) {
224 225
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot free CPU data for %s architecture"),
J
Jiri Denemark 已提交
226
                       virArchToString(data->arch));
J
Jiri Denemark 已提交
227 228 229
        return;
    }

E
Eric Blake 已提交
230
    (driver->free)(data);
J
Jiri Denemark 已提交
231 232 233
}


234
virCPUDataPtr
235
cpuNodeData(virArch arch)
J
Jiri Denemark 已提交
236 237 238
{
    struct cpuArchDriver *driver;

239
    VIR_DEBUG("arch=%s", virArchToString(arch));
240

241
    if ((driver = cpuGetSubDriver(arch)) == NULL)
J
Jiri Denemark 已提交
242 243 244
        return NULL;

    if (driver->nodeData == NULL) {
245 246
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot get node CPU data for %s architecture"),
247
                       virArchToString(arch));
J
Jiri Denemark 已提交
248 249 250
        return NULL;
    }

251
    return driver->nodeData(arch);
J
Jiri Denemark 已提交
252 253 254 255
}


virCPUCompareResult
256
cpuGuestData(virCPUDefPtr host,
J
Jiri Denemark 已提交
257
             virCPUDefPtr guest,
258
             virCPUDataPtr *data,
259
             char **msg)
J
Jiri Denemark 已提交
260 261 262
{
    struct cpuArchDriver *driver;

263
    VIR_DEBUG("host=%p, guest=%p, data=%p, msg=%p", host, guest, data, msg);
264

265
    if ((driver = cpuGetSubDriver(host->arch)) == NULL)
J
Jiri Denemark 已提交
266 267 268
        return VIR_CPU_COMPARE_ERROR;

    if (driver->guestData == NULL) {
269 270
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot compute guest CPU data for %s architecture"),
271
                       virArchToString(host->arch));
J
Jiri Denemark 已提交
272 273 274
        return VIR_CPU_COMPARE_ERROR;
    }

275
    return driver->guestData(host, guest, data, msg);
J
Jiri Denemark 已提交
276
}
277 278 279 280 281 282


char *
cpuBaselineXML(const char **xmlCPUs,
               unsigned int ncpus,
               const char **models,
283 284
               unsigned int nmodels,
               unsigned int flags)
285 286 287 288 289 290
{
    xmlDocPtr doc = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virCPUDefPtr *cpus = NULL;
    virCPUDefPtr cpu = NULL;
    char *cpustr;
291
    size_t i;
292

293 294 295
    VIR_DEBUG("ncpus=%u, nmodels=%u", ncpus, nmodels);
    if (xmlCPUs) {
        for (i = 0; i < ncpus; i++)
296
            VIR_DEBUG("xmlCPUs[%zu]=%s", i, NULLSTR(xmlCPUs[i]));
297 298 299
    }
    if (models) {
        for (i = 0; i < nmodels; i++)
300
            VIR_DEBUG("models[%zu]=%s", i, NULLSTR(models[i]));
301 302
    }

303
    if (xmlCPUs == NULL && ncpus != 0) {
304 305
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("nonzero ncpus doesn't match with NULL xmlCPUs"));
306 307 308 309
        return NULL;
    }

    if (ncpus < 1) {
310
        virReportError(VIR_ERR_INVALID_ARG, "%s", _("No CPUs given"));
311 312 313 314
        return NULL;
    }

    if (VIR_ALLOC_N(cpus, ncpus))
315
        goto error;
316 317

    for (i = 0; i < ncpus; i++) {
318
        if (!(doc = virXMLParseStringCtxt(xmlCPUs[i], _("(CPU_definition)"), &ctxt)))
319 320
            goto error;

321 322 323 324 325 326 327 328 329 330
        cpus[i] = virCPUDefParseXML(ctxt->node, ctxt, VIR_CPU_TYPE_HOST);
        if (cpus[i] == NULL)
            goto error;

        xmlXPathFreeContext(ctxt);
        xmlFreeDoc(doc);
        ctxt = NULL;
        doc = NULL;
    }

331
    if (!(cpu = cpuBaseline(cpus, ncpus, models, nmodels, flags)))
332 333
        goto error;

334
    cpustr = virCPUDefFormat(cpu, 0);
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357

cleanup:
    if (cpus) {
        for (i = 0; i < ncpus; i++)
            virCPUDefFree(cpus[i]);
        VIR_FREE(cpus);
    }
    virCPUDefFree(cpu);
    xmlXPathFreeContext(ctxt);
    xmlFreeDoc(doc);

    return cpustr;

error:
    cpustr = NULL;
    goto cleanup;
}


virCPUDefPtr
cpuBaseline(virCPUDefPtr *cpus,
            unsigned int ncpus,
            const char **models,
358 359
            unsigned int nmodels,
            unsigned int flags)
360 361
{
    struct cpuArchDriver *driver;
362
    size_t i;
363 364 365 366

    VIR_DEBUG("ncpus=%u, nmodels=%u", ncpus, nmodels);
    if (cpus) {
        for (i = 0; i < ncpus; i++)
367
            VIR_DEBUG("cpus[%zu]=%p", i, cpus[i]);
368 369 370
    }
    if (models) {
        for (i = 0; i < nmodels; i++)
371
            VIR_DEBUG("models[%zu]=%s", i, NULLSTR(models[i]));
372
    }
373 374

    if (cpus == NULL && ncpus != 0) {
375 376
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("nonzero ncpus doesn't match with NULL cpus"));
377 378 379 380
        return NULL;
    }

    if (ncpus < 1) {
381
        virReportError(VIR_ERR_INVALID_ARG, "%s", _("No CPUs given"));
382 383 384 385
        return NULL;
    }

    if (models == NULL && nmodels != 0) {
386 387
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("nonzero nmodels doesn't match with NULL models"));
388 389 390 391 392 393 394
        return NULL;
    }

    if ((driver = cpuGetSubDriver(cpus[0]->arch)) == NULL)
        return NULL;

    if (driver->baseline == NULL) {
395 396
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot compute baseline CPU of %s architecture"),
397
                       virArchToString(cpus[0]->arch));
398 399 400
        return NULL;
    }

401
    return driver->baseline(cpus, ncpus, models, nmodels, flags);
402
}
403 404 405 406


int
cpuUpdate(virCPUDefPtr guest,
407
          const virCPUDef *host)
408 409 410 411 412 413 414 415 416
{
    struct cpuArchDriver *driver;

    VIR_DEBUG("guest=%p, host=%p", guest, host);

    if ((driver = cpuGetSubDriver(host->arch)) == NULL)
        return -1;

    if (driver->update == NULL) {
417 418
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot update guest CPU data for %s architecture"),
419
                       virArchToString(host->arch));
420 421 422 423 424
        return -1;
    }

    return driver->update(guest, host);
}
D
Daniel P. Berrange 已提交
425 426

int
427
cpuHasFeature(const virCPUData *data,
D
Daniel P. Berrange 已提交
428 429 430 431
              const char *feature)
{
    struct cpuArchDriver *driver;

J
Jiri Denemark 已提交
432
    VIR_DEBUG("data=%p, feature=%s", data, feature);
D
Daniel P. Berrange 已提交
433

J
Jiri Denemark 已提交
434
    if ((driver = cpuGetSubDriver(data->arch)) == NULL)
D
Daniel P. Berrange 已提交
435 436 437
        return -1;

    if (driver->hasFeature == NULL) {
438 439
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot check guest CPU data for %s architecture"),
J
Jiri Denemark 已提交
440
                       virArchToString(data->arch));
D
Daniel P. Berrange 已提交
441 442 443 444 445
        return -1;
    }

    return driver->hasFeature(data, feature);
}
446

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
char *
cpuDataFormat(const virCPUData *data)
{
    struct cpuArchDriver *driver;

    VIR_DEBUG("data=%p", data);

    if (!(driver = cpuGetSubDriver(data->arch)))
        return NULL;

    if (!driver->dataFormat) {
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot format %s CPU data"),
                       virArchToString(data->arch));
        return NULL;
    }

    return driver->dataFormat(data);
}

virCPUDataPtr
cpuDataParse(virArch arch,
             const char *xmlStr)
{
    struct cpuArchDriver *driver;

    VIR_DEBUG("arch=%s, xmlStr=%s", virArchToString(arch), xmlStr);

    if (!(driver = cpuGetSubDriver(arch)))
        return NULL;

    if (!driver->dataParse) {
        virReportError(VIR_ERR_NO_SUPPORT,
                       _("cannot parse %s CPU data"),
                       virArchToString(arch));
        return NULL;
    }

    return driver->dataParse(xmlStr);
}

488 489 490 491 492
bool
cpuModelIsAllowed(const char *model,
                  const char **models,
                  unsigned int nmodels)
{
493
    size_t i;
494 495 496 497 498 499 500 501 502 503

    if (!models || !nmodels)
        return true;

    for (i = 0; i < nmodels; i++) {
        if (models[i] && STREQ(models[i], model))
            return true;
    }
    return false;
}
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581

struct cpuGetModelsData
{
    char **data;
    size_t len;  /* It includes the last element of DATA, which is NULL. */
};

static int
cpuGetArchModelsCb(enum cpuMapElement element,
                   xmlXPathContextPtr ctxt,
                   void *cbdata)
{
    char *name;
    struct cpuGetModelsData *data = cbdata;
    if (element != CPU_MAP_ELEMENT_MODEL)
        return 0;

    name = virXPathString("string(@name)", ctxt);
    if (name == NULL)
        return -1;

    if (!data->data) {
        VIR_FREE(name);
        data->len++;
        return 0;
    }

    return VIR_INSERT_ELEMENT(data->data, data->len - 1, data->len, name);
}


static int
cpuGetArchModels(const char *arch, struct cpuGetModelsData *data)
{
    return cpuMapLoad(arch, cpuGetArchModelsCb, data);
}


int
cpuGetModels(const char *archName, char ***models)
{
    struct cpuGetModelsData data;
    virArch arch;
    struct cpuArchDriver *driver;
    data.data = NULL;
    data.len = 1;

    arch = virArchFromString(archName);
    if (arch == VIR_ARCH_NONE) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("cannot find architecture %s"),
                       archName);
        goto error;
    }

    driver = cpuGetSubDriver(arch);
    if (driver == NULL) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("cannot find a driver for the architecture %s"),
                       archName);
        goto error;
    }

    if (models && VIR_ALLOC_N(data.data, data.len) < 0)
        goto error;

    if (cpuGetArchModels(driver->name, &data) < 0)
        goto error;

    if (models)
        *models = data.data;

    return data.len - 1;

error:
    virStringFreeList(data.data);
    return -1;
}