cpu_conf.c 15.7 KB
Newer Older
1 2 3
/*
 * cpu_conf.h: CPU XML handling
 *
4
 * Copyright (C) 2009, 2010 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
 *
 * 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
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Authors:
 *      Jiri Denemark <jdenemar@redhat.com>
 */

#include <config.h>

#include "virterror_internal.h"
#include "memory.h"
#include "util.h"
#include "buf.h"
#include "cpu_conf.h"

#define VIR_FROM_THIS VIR_FROM_CPU

34
#define virCPUReportError(code, ...)                              \
35
    virReportErrorHelper(VIR_FROM_CPU, code, __FILE__,            \
36
                         __FUNCTION__, __LINE__, __VA_ARGS__)
37

38 39 40
VIR_ENUM_IMPL(virCPU, VIR_CPU_TYPE_LAST,
              "host", "guest", "auto")

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
VIR_ENUM_IMPL(virCPUMatch, VIR_CPU_MATCH_LAST,
              "minimum",
              "exact",
              "strict")

VIR_ENUM_IMPL(virCPUFeaturePolicy, VIR_CPU_FEATURE_LAST,
              "force",
              "require",
              "optional",
              "disable",
              "forbid")


void
virCPUDefFree(virCPUDefPtr def)
{
    unsigned int i;

    if (!def)
        return;

    VIR_FREE(def->model);
    VIR_FREE(def->arch);
J
Jiri Denemark 已提交
64
    VIR_FREE(def->vendor);
65 66 67 68 69 70 71 72 73

    for (i = 0 ; i < def->nfeatures ; i++)
        VIR_FREE(def->features[i].name);
    VIR_FREE(def->features);

    VIR_FREE(def);
}


74 75 76 77 78 79 80 81 82 83 84 85
virCPUDefPtr
virCPUDefCopy(const virCPUDefPtr cpu)
{
    virCPUDefPtr copy;
    unsigned int i;

    if (!cpu)
        return NULL;

    if (VIR_ALLOC(copy) < 0
        || (cpu->arch && !(copy->arch = strdup(cpu->arch)))
        || (cpu->model && !(copy->model = strdup(cpu->model)))
J
Jiri Denemark 已提交
86
        || (cpu->vendor && !(copy->vendor = strdup(cpu->vendor)))
87 88
        || VIR_ALLOC_N(copy->features, cpu->nfeatures) < 0)
        goto no_memory;
E
Eric Blake 已提交
89
    copy->nfeatures_max = cpu->nfeatures;
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

    copy->type = cpu->type;
    copy->match = cpu->match;
    copy->sockets = cpu->sockets;
    copy->cores = cpu->cores;
    copy->threads = cpu->threads;
    copy->nfeatures = cpu->nfeatures;

    for (i = 0; i < copy->nfeatures; i++) {
        copy->features[i].policy = cpu->features[i].policy;
        if (!(copy->features[i].name = strdup(cpu->features[i].name)))
            goto no_memory;
    }

    return copy;

no_memory:
    virReportOOMError();
    virCPUDefFree(copy);
    return NULL;
}


113
virCPUDefPtr
114
virCPUDefParseXML(const xmlNodePtr node,
115 116 117 118 119 120 121 122
                  xmlXPathContextPtr ctxt,
                  enum virCPUType mode)
{
    virCPUDefPtr def;
    xmlNodePtr *nodes = NULL;
    int n;
    unsigned int i;

123 124 125 126 127 128 129
    if (!xmlStrEqual(node->name, BAD_CAST "cpu")) {
        virCPUReportError(VIR_ERR_INTERNAL_ERROR,
                          "%s",
                          _("XML does not contain expected 'cpu' element"));
        return NULL;
    }

130
    if (VIR_ALLOC(def) < 0) {
131
        virReportOOMError();
132 133 134
        return NULL;
    }

135
    if (mode == VIR_CPU_TYPE_AUTO) {
136 137 138 139 140 141 142
        if (virXPathBoolean("boolean(./arch)", ctxt)) {
            if (virXPathBoolean("boolean(./@match)", ctxt)) {
                virCPUReportError(VIR_ERR_XML_ERROR, "%s",
                        _("'arch' element element cannot be used inside 'cpu'"
                          " element with 'match' attribute'"));
                goto error;
            }
143
            def->type = VIR_CPU_TYPE_HOST;
144
        } else
145 146
            def->type = VIR_CPU_TYPE_GUEST;
    } else
147 148 149
        def->type = mode;

    if (def->type == VIR_CPU_TYPE_GUEST) {
150 151 152
        char *match = virXMLPropString(node, "match");

        if (!match) {
153 154 155 156
            if (virXPathBoolean("boolean(./model)", ctxt))
                def->match = VIR_CPU_MATCH_EXACT;
            else
                def->match = -1;
157 158 159 160 161
        } else {
            def->match = virCPUMatchTypeFromString(match);
            VIR_FREE(match);

            if (def->match < 0) {
162
                virCPUReportError(VIR_ERR_INTERNAL_ERROR,
163 164 165
                        "%s", _("Invalid match attribute for CPU specification"));
                goto error;
            }
166 167 168 169
        }
    }

    if (def->type == VIR_CPU_TYPE_HOST) {
170
        def->arch = virXPathString("string(./arch[1])", ctxt);
171
        if (!def->arch) {
172
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
173 174 175 176 177
                    "%s", _("Missing CPU architecture"));
            goto error;
        }
    }

178
    if (!(def->model = virXPathString("string(./model[1])", ctxt)) &&
179
        def->type == VIR_CPU_TYPE_HOST) {
180
        virCPUReportError(VIR_ERR_INTERNAL_ERROR,
181 182 183 184
                "%s", _("Missing CPU model name"));
        goto error;
    }

J
Jiri Denemark 已提交
185 186 187 188 189 190 191
    def->vendor = virXPathString("string(./vendor[1])", ctxt);
    if (def->vendor && !def->model) {
        virCPUReportError(VIR_ERR_INTERNAL_ERROR,
                "%s", _("CPU vendor specified without CPU model"));
        goto error;
    }

192
    if (virXPathNode("./topology[1]", ctxt)) {
193 194 195
        int ret;
        unsigned long ul;

196
        ret = virXPathULong("string(./topology[1]/@sockets)",
197 198
                            ctxt, &ul);
        if (ret < 0) {
199
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
200 201 202 203 204
                    "%s", _("Missing 'sockets' attribute in CPU topology"));
            goto error;
        }
        def->sockets = (unsigned int) ul;

205
        ret = virXPathULong("string(./topology[1]/@cores)",
206 207
                            ctxt, &ul);
        if (ret < 0) {
208
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
209 210 211 212 213
                    "%s", _("Missing 'cores' attribute in CPU topology"));
            goto error;
        }
        def->cores = (unsigned int) ul;

214
        ret = virXPathULong("string(./topology[1]/@threads)",
215 216
                            ctxt, &ul);
        if (ret < 0) {
217
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
218 219 220 221 222 223
                    "%s", _("Missing 'threads' attribute in CPU topology"));
            goto error;
        }
        def->threads = (unsigned int) ul;

        if (!def->sockets || !def->cores || !def->threads) {
224
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
225 226 227 228 229
                    "%s", _("Invalid CPU topology"));
            goto error;
        }
    }

230
    n = virXPathNodeSet("./feature", ctxt, &nodes);
231 232 233 234
    if (n < 0)
        goto error;

    if (n > 0) {
235
        if (!def->model) {
236
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
237 238 239 240
                    "%s", _("Non-empty feature list specified without CPU model"));
            goto error;
        }

E
Eric Blake 已提交
241 242
        if (VIR_RESIZE_N(def->features, def->nfeatures_max,
                         def->nfeatures, n) < 0)
243 244 245 246 247 248 249 250 251 252 253 254 255
            goto no_memory;
        def->nfeatures = n;
    }

    for (i = 0 ; i < n ; i++) {
        char *name;
        int policy; /* enum virDomainCPUFeaturePolicy */
        unsigned int j;

        if (def->type == VIR_CPU_TYPE_GUEST) {
            char *strpolicy;

            strpolicy = virXMLPropString(nodes[i], "policy");
256 257 258 259
            if (strpolicy == NULL)
                policy = VIR_CPU_FEATURE_REQUIRE;
            else
                policy = virCPUFeaturePolicyTypeFromString(strpolicy);
260 261 262
            VIR_FREE(strpolicy);

            if (policy < 0) {
263
                virCPUReportError(VIR_ERR_INTERNAL_ERROR,
264 265 266 267 268 269 270 271 272
                        "%s", _("Invalid CPU feature policy"));
                goto error;
            }
        }
        else
            policy = -1;

        if (!(name = virXMLPropString(nodes[i], "name")) || *name == 0) {
            VIR_FREE(name);
273
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
274 275 276 277 278 279
                    "%s", _("Invalid CPU feature name"));
            goto error;
        }

        for (j = 0 ; j < i ; j++) {
            if (STREQ(name, def->features[j].name)) {
280
                virCPUReportError(VIR_ERR_INTERNAL_ERROR,
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
                        _("CPU feature `%s' specified more than once"),
                        name);
                VIR_FREE(name);
                goto error;
            }
        }

        def->features[i].name = name;
        def->features[i].policy = policy;
    }

cleanup:
    VIR_FREE(nodes);

    return def;

no_memory:
298
    virReportOOMError();
299 300 301 302 303 304 305 306 307

error:
    virCPUDefFree(def);
    def = NULL;
    goto cleanup;
}


char *
308
virCPUDefFormat(virCPUDefPtr def,
309 310 311 312 313
                const char *indent,
                int flags)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;

314
    if (virCPUDefFormatBuf(&buf, def, indent, flags) < 0)
315 316 317 318 319 320 321 322
        goto cleanup;

    if (virBufferError(&buf))
        goto no_memory;

    return virBufferContentAndReset(&buf);

no_memory:
323
    virReportOOMError();
324
cleanup:
325
    virBufferFreeAndReset(&buf);
326 327 328 329 330
    return NULL;
}


int
331
virCPUDefFormatBuf(virBufferPtr buf,
332 333 334 335 336 337 338 339 340 341 342 343
                   virCPUDefPtr def,
                   const char *indent,
                   int flags)
{
    unsigned int i;

    if (!def)
        return 0;

    if (indent == NULL)
        indent = "";

344
    if (!def->model && def->nfeatures) {
345
        virCPUReportError(VIR_ERR_INTERNAL_ERROR,
346
                "%s", _("Non-empty feature list specified without CPU model"));
347 348 349 350
        return -1;
    }

    if (!(flags & VIR_CPU_FORMAT_EMBEDED)) {
351
        if (def->type == VIR_CPU_TYPE_GUEST && def->model) {
352 353
            const char *match;
            if (!(match = virCPUMatchTypeToString(def->match))) {
354
                virCPUReportError(VIR_ERR_INTERNAL_ERROR,
355 356 357 358
                        _("Unexpected CPU match policy %d"), def->match);
                return -1;
            }

359
            virBufferAsprintf(buf, "%s<cpu match='%s'>\n", indent, match);
360 361
        }
        else
362
            virBufferAsprintf(buf, "%s<cpu>\n", indent);
363 364

        if (def->arch)
365
            virBufferAsprintf(buf, "%s  <arch>%s</arch>\n", indent, def->arch);
366 367
    }

368
    if (def->model)
369
        virBufferAsprintf(buf, "%s  <model>%s</model>\n", indent, def->model);
370

J
Jiri Denemark 已提交
371
    if (def->vendor) {
372
        virBufferAsprintf(buf, "%s  <vendor>%s</vendor>\n",
J
Jiri Denemark 已提交
373 374 375
                          indent, def->vendor);
    }

376
    if (def->sockets && def->cores && def->threads) {
377 378 379 380
        virBufferAsprintf(buf, "%s  <topology", indent);
        virBufferAsprintf(buf, " sockets='%u'", def->sockets);
        virBufferAsprintf(buf, " cores='%u'", def->cores);
        virBufferAsprintf(buf, " threads='%u'", def->threads);
381 382 383 384 385 386 387
        virBufferAddLit(buf, "/>\n");
    }

    for (i = 0 ; i < def->nfeatures ; i++) {
        virCPUFeatureDefPtr feature = def->features + i;

        if (!feature->name) {
388
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
389 390 391 392 393 394 395 396 397
                    "%s", _("Missing CPU feature name"));
            return -1;
        }

        if (def->type == VIR_CPU_TYPE_GUEST) {
            const char *policy;

            policy = virCPUFeaturePolicyTypeToString(feature->policy);
            if (!policy) {
398
                virCPUReportError(VIR_ERR_INTERNAL_ERROR,
399 400 401
                        _("Unexpected CPU feature policy %d"), feature->policy);
                return -1;
            }
402
            virBufferAsprintf(buf, "%s  <feature policy='%s' name='%s'/>\n",
403 404 405
                    indent, policy, feature->name);
        }
        else {
406
            virBufferAsprintf(buf, "%s  <feature name='%s'/>\n",
407 408 409 410 411
                    indent, feature->name);
        }
    }

    if (!(flags & VIR_CPU_FORMAT_EMBEDED))
412
        virBufferAsprintf(buf, "%s</cpu>\n", indent);
413 414 415 416 417 418

    return 0;
}


int
419
virCPUDefAddFeature(virCPUDefPtr def,
420 421 422 423 424 425 426
                    const char *name,
                    int policy)
{
    int i;

    for (i = 0 ; i < def->nfeatures ; i++) {
        if (STREQ(name, def->features[i].name)) {
427
            virCPUReportError(VIR_ERR_INTERNAL_ERROR,
428 429 430 431 432
                    _("CPU feature `%s' specified more than once"), name);
            return -1;
        }
    }

E
Eric Blake 已提交
433 434
    if (VIR_RESIZE_N(def->features, def->nfeatures_max,
                     def->nfeatures, 1) < 0)
435 436 437 438 439 440 441 442 443 444 445 446 447 448
        goto no_memory;

    if (def->type == VIR_CPU_TYPE_HOST)
        policy = -1;

    if (!(def->features[def->nfeatures].name = strdup(name)))
        goto no_memory;

    def->features[def->nfeatures].policy = policy;
    def->nfeatures++;

    return 0;

no_memory:
449
    virReportOOMError();
450 451
    return -1;
}
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 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 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

bool
virCPUDefIsEqual(virCPUDefPtr src,
                 virCPUDefPtr dst)
{
    bool identical = false;
    int i;

    if (!src && !dst)
        return true;

    if ((src && !dst) || (!src && dst)) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                          _("Target CPU does not match source"));
        goto cleanup;
    }

    if (src->type != dst->type) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU type %s does not match source %s"),
                          virCPUTypeToString(dst->type),
                          virCPUTypeToString(src->type));
        goto cleanup;
    }

    if (STRNEQ_NULLABLE(src->arch, dst->arch)) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU arch %s does not match source %s"),
                          NULLSTR(dst->arch), NULLSTR(src->arch));
        goto cleanup;
    }

    if (STRNEQ_NULLABLE(src->model, dst->model)) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU model %s does not match source %s"),
                          NULLSTR(dst->model), NULLSTR(src->model));
        goto cleanup;
    }

    if (STRNEQ_NULLABLE(src->vendor, dst->vendor)) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU vendor %s does not match source %s"),
                          NULLSTR(dst->vendor), NULLSTR(src->vendor));
        goto cleanup;
    }

    if (src->sockets != dst->sockets) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU sockets %d does not match source %d"),
                          dst->sockets, src->sockets);
        goto cleanup;
    }

    if (src->cores != dst->cores) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU cores %d does not match source %d"),
                          dst->cores, src->cores);
        goto cleanup;
    }

    if (src->threads != dst->threads) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU threads %d does not match source %d"),
                          dst->threads, src->threads);
        goto cleanup;
    }

    if (src->nfeatures != dst->nfeatures) {
        virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                          _("Target CPU feature count %zu does not match source %zu"),
                          dst->nfeatures, src->nfeatures);
        goto cleanup;
    }

    for (i = 0 ; i < src->nfeatures ; i++) {
        if (STRNEQ(src->features[i].name, dst->features[i].name)) {
            virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                              _("Target CPU feature %s does not match source %s"),
                              dst->features[i].name, src->features[i].name);
            goto cleanup;
        }

        if (src->features[i].policy != dst->features[i].policy) {
            virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                              _("Target CPU feature policy %s does not match source %s"),
                              virCPUFeaturePolicyTypeToString(dst->features[i].policy),
                              virCPUFeaturePolicyTypeToString(src->features[i].policy));
            goto cleanup;
        }
    }

    identical = true;

cleanup:
    return identical;
}