qemu/tests/unit/test-smp-parse.c
<<
>>
Prefs
   1/*
   2 * SMP parsing unit-tests
   3 *
   4 * Copyright (c) 2021 Huawei Technologies Co., Ltd
   5 *
   6 * Authors:
   7 *  Yanan Wang <wangyanan55@huawei.com>
   8 *
   9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
  10 * See the COPYING.LIB file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "qom/object.h"
  15#include "qemu/module.h"
  16#include "qapi/error.h"
  17
  18#include "hw/boards.h"
  19
  20#define T true
  21#define F false
  22
  23#define MIN_CPUS 1   /* set the min CPUs supported by the machine as 1 */
  24#define MAX_CPUS 512 /* set the max CPUs supported by the machine as 512 */
  25
  26#define SMP_MACHINE_NAME "TEST-SMP"
  27
  28/*
  29 * Used to define the generic 3-level CPU topology hierarchy
  30 *  -sockets/cores/threads
  31 */
  32#define SMP_CONFIG_GENERIC(ha, a, hb, b, hc, c, hd, d, he, e) \
  33        {                                                     \
  34            .has_cpus    = ha, .cpus    = a,                  \
  35            .has_sockets = hb, .sockets = b,                  \
  36            .has_cores   = hc, .cores   = c,                  \
  37            .has_threads = hd, .threads = d,                  \
  38            .has_maxcpus = he, .maxcpus = e,                  \
  39        }
  40
  41#define CPU_TOPOLOGY_GENERIC(a, b, c, d, e)                   \
  42        {                                                     \
  43            .cpus     = a,                                    \
  44            .sockets  = b,                                    \
  45            .cores    = c,                                    \
  46            .threads  = d,                                    \
  47            .max_cpus = e,                                    \
  48        }
  49
  50/*
  51 * Currently a 4-level topology hierarchy is supported on PC machines
  52 *  -sockets/dies/cores/threads
  53 */
  54#define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
  55        {                                                     \
  56            .has_cpus    = ha, .cpus    = a,                  \
  57            .has_sockets = hb, .sockets = b,                  \
  58            .has_dies    = hc, .dies    = c,                  \
  59            .has_cores   = hd, .cores   = d,                  \
  60            .has_threads = he, .threads = e,                  \
  61            .has_maxcpus = hf, .maxcpus = f,                  \
  62        }
  63
  64/**
  65 * @config - the given SMP configuration
  66 * @expect_prefer_sockets - the expected parsing result for the
  67 * valid configuration, when sockets are preferred over cores
  68 * @expect_prefer_cores - the expected parsing result for the
  69 * valid configuration, when cores are preferred over sockets
  70 * @expect_error - the expected error report when the given
  71 * configuration is invalid
  72 */
  73typedef struct SMPTestData {
  74    SMPConfiguration config;
  75    CpuTopology expect_prefer_sockets;
  76    CpuTopology expect_prefer_cores;
  77    const char *expect_error;
  78} SMPTestData;
  79
  80/*
  81 * List all the possible valid sub-collections of the generic 5
  82 * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads),
  83 * then test the automatic calculation algorithm of the missing
  84 * values in the parser.
  85 */
  86static struct SMPTestData data_generic_valid[] = {
  87    {
  88        /* config: no configuration provided
  89         * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
  90        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0),
  91        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
  92        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
  93    }, {
  94        /* config: -smp 8
  95         * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8
  96         * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */
  97        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0),
  98        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
  99        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
 100    }, {
 101        /* config: -smp sockets=2
 102         * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */
 103        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0),
 104        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
 105        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
 106    }, {
 107        /* config: -smp cores=4
 108         * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */
 109        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0),
 110        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
 111        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
 112    }, {
 113        /* config: -smp threads=2
 114         * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */
 115        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0),
 116        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
 117        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
 118    }, {
 119        /* config: -smp maxcpus=16
 120         * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16
 121         * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */
 122        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16),
 123        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
 124        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
 125    }, {
 126        /* config: -smp 8,sockets=2
 127         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 128        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0),
 129        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 130        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 131    }, {
 132        /* config: -smp 8,cores=4
 133         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 134        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0),
 135        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 136        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 137    }, {
 138        /* config: -smp 8,threads=2
 139         * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8
 140         * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
 141        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0),
 142        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
 143        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 144    }, {
 145        /* config: -smp 8,maxcpus=16
 146         * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16
 147         * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */
 148        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16),
 149        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
 150        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
 151    }, {
 152        /* config: -smp sockets=2,cores=4
 153         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 154        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0),
 155        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 156        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 157    }, {
 158        /* config: -smp sockets=2,threads=2
 159         * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */
 160        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0),
 161        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
 162        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
 163    }, {
 164        /* config: -smp sockets=2,maxcpus=16
 165         * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */
 166        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16),
 167        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
 168        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
 169    }, {
 170        /* config: -smp cores=4,threads=2
 171         * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
 172        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0),
 173        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 174        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 175    }, {
 176        /* config: -smp cores=4,maxcpus=16
 177         * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */
 178        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16),
 179        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
 180        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
 181    }, {
 182        /* config: -smp threads=2,maxcpus=16
 183         * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16
 184         * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */
 185        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16),
 186        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
 187        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
 188    }, {
 189        /* config: -smp 8,sockets=2,cores=4
 190         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 191        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0),
 192        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 193        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 194    }, {
 195        /* config: -smp 8,sockets=2,threads=2
 196         * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
 197        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0),
 198        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
 199        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
 200    }, {
 201        /* config: -smp 8,sockets=2,maxcpus=16
 202         * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */
 203        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16),
 204        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
 205        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
 206    }, {
 207        /* config: -smp 8,cores=4,threads=2
 208         * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
 209        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0),
 210        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 211        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 212    }, {
 213        /* config: -smp 8,cores=4,maxcpus=16
 214         * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */
 215        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16),
 216        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
 217        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
 218    }, {
 219        /* config: -smp 8,threads=2,maxcpus=16
 220         * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16
 221         * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */
 222        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16),
 223        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
 224        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
 225    }, {
 226        /* config: -smp sockets=2,cores=4,threads=2
 227         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 228        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, F, 0),
 229        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 230        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 231    }, {
 232        /* config: -smp sockets=2,cores=4,maxcpus=16
 233         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 234        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, T, 16),
 235        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 236        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 237    }, {
 238        /* config: -smp sockets=2,threads=2,maxcpus=16
 239         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 240        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16),
 241        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 242        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 243    }, {
 244        /* config: -smp cores=4,threads=2,maxcpus=16
 245         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 246        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16),
 247        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 248        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 249    }, {
 250        /* config: -smp 8,sockets=2,cores=4,threads=1
 251         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 252        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0),
 253        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 254        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 255    }, {
 256        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
 257         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 258        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16),
 259        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 260        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 261    }, {
 262        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
 263         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 264        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16),
 265        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 266        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 267    }, {
 268        /* config: -smp 8,cores=4,threads=2,maxcpus=16
 269         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 270        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16),
 271        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 272        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 273    }, {
 274        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
 275         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 276        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, T, 16),
 277        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 278        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 279    }, {
 280        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
 281         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 282        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
 283        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 284        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 285    },
 286};
 287
 288static struct SMPTestData data_generic_invalid[] = {
 289    {
 290        /* config: -smp 2,dies=2 */
 291        .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
 292        .expect_error = "dies not supported by this machine's CPU topology",
 293    }, {
 294        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
 295        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
 296        .expect_error = "Invalid CPU topology: "
 297                        "product of the hierarchy must match maxcpus: "
 298                        "sockets (2) * cores (4) * threads (2) "
 299                        "!= maxcpus (8)",
 300    }, {
 301        /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */
 302        .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16),
 303        .expect_error = "Invalid CPU topology: "
 304                        "maxcpus must be equal to or greater than smp: "
 305                        "sockets (2) * cores (4) * threads (2) "
 306                        "== maxcpus (16) < smp_cpus (18)",
 307    }, {
 308        /* config: -smp 1
 309         * should tweak the supported min CPUs to 2 for testing */
 310        .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0),
 311        .expect_error = "Invalid SMP CPUs 1. The min CPUs supported "
 312                        "by machine '" SMP_MACHINE_NAME "' is 2",
 313    }, {
 314        /* config: -smp 512
 315         * should tweak the supported max CPUs to 511 for testing */
 316        .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0),
 317        .expect_error = "Invalid SMP CPUs 512. The max CPUs supported "
 318                        "by machine '" SMP_MACHINE_NAME "' is 511",
 319    },
 320};
 321
 322static struct SMPTestData data_with_dies_invalid[] = {
 323    {
 324        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
 325        .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
 326        .expect_error = "Invalid CPU topology: "
 327                        "product of the hierarchy must match maxcpus: "
 328                        "sockets (2) * dies (2) * cores (4) * threads (2) "
 329                        "!= maxcpus (16)",
 330    }, {
 331        /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
 332        .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
 333        .expect_error = "Invalid CPU topology: "
 334                        "maxcpus must be equal to or greater than smp: "
 335                        "sockets (2) * dies (2) * cores (4) * threads (2) "
 336                        "== maxcpus (32) < smp_cpus (34)",
 337    },
 338};
 339
 340static char *smp_config_to_string(SMPConfiguration *config)
 341{
 342    return g_strdup_printf(
 343        "(SMPConfiguration) {\n"
 344        "    .has_cpus    = %5s, cpus    = %" PRId64 ",\n"
 345        "    .has_sockets = %5s, sockets = %" PRId64 ",\n"
 346        "    .has_dies    = %5s, dies    = %" PRId64 ",\n"
 347        "    .has_cores   = %5s, cores   = %" PRId64 ",\n"
 348        "    .has_threads = %5s, threads = %" PRId64 ",\n"
 349        "    .has_maxcpus = %5s, maxcpus = %" PRId64 ",\n"
 350        "}",
 351        config->has_cpus ? "true" : "false", config->cpus,
 352        config->has_sockets ? "true" : "false", config->sockets,
 353        config->has_dies ? "true" : "false", config->dies,
 354        config->has_cores ? "true" : "false", config->cores,
 355        config->has_threads ? "true" : "false", config->threads,
 356        config->has_maxcpus ? "true" : "false", config->maxcpus);
 357}
 358
 359static char *cpu_topology_to_string(CpuTopology *topo)
 360{
 361    return g_strdup_printf(
 362        "(CpuTopology) {\n"
 363        "    .cpus     = %u,\n"
 364        "    .sockets  = %u,\n"
 365        "    .dies     = %u,\n"
 366        "    .cores    = %u,\n"
 367        "    .threads  = %u,\n"
 368        "    .max_cpus = %u,\n"
 369        "}",
 370        topo->cpus, topo->sockets, topo->dies,
 371        topo->cores, topo->threads, topo->max_cpus);
 372}
 373
 374static void check_parse(MachineState *ms, SMPConfiguration *config,
 375                        CpuTopology *expect_topo, const char *expect_err,
 376                        bool is_valid)
 377{
 378    g_autofree char *config_str = smp_config_to_string(config);
 379    g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo);
 380    g_autofree char *output_topo_str = NULL;
 381    Error *err = NULL;
 382
 383    /* call the generic parser smp_parse() */
 384    smp_parse(ms, config, &err);
 385
 386    output_topo_str = cpu_topology_to_string(&ms->smp);
 387
 388    /* when the configuration is supposed to be valid */
 389    if (is_valid) {
 390        if ((err == NULL) &&
 391            (ms->smp.cpus == expect_topo->cpus) &&
 392            (ms->smp.sockets == expect_topo->sockets) &&
 393            (ms->smp.dies == expect_topo->dies) &&
 394            (ms->smp.cores == expect_topo->cores) &&
 395            (ms->smp.threads == expect_topo->threads) &&
 396            (ms->smp.max_cpus == expect_topo->max_cpus)) {
 397            return;
 398        }
 399
 400        if (err != NULL) {
 401            g_printerr("Test smp_parse failed!\n"
 402                       "Input configuration: %s\n"
 403                       "Should be valid: yes\n"
 404                       "Expected topology: %s\n\n"
 405                       "Result is valid: no\n"
 406                       "Output error report: %s\n",
 407                       config_str, expect_topo_str, error_get_pretty(err));
 408            goto end;
 409        }
 410
 411        g_printerr("Test smp_parse failed!\n"
 412                   "Input configuration: %s\n"
 413                   "Should be valid: yes\n"
 414                   "Expected topology: %s\n\n"
 415                   "Result is valid: yes\n"
 416                   "Output topology: %s\n",
 417                   config_str, expect_topo_str, output_topo_str);
 418        goto end;
 419    }
 420
 421    /* when the configuration is supposed to be invalid */
 422    if (err != NULL) {
 423        if (expect_err == NULL ||
 424            g_str_equal(expect_err, error_get_pretty(err))) {
 425            error_free(err);
 426            return;
 427        }
 428
 429        g_printerr("Test smp_parse failed!\n"
 430                   "Input configuration: %s\n"
 431                   "Should be valid: no\n"
 432                   "Expected error report: %s\n\n"
 433                   "Result is valid: no\n"
 434                   "Output error report: %s\n",
 435                   config_str, expect_err, error_get_pretty(err));
 436        goto end;
 437    }
 438
 439    g_printerr("Test smp_parse failed!\n"
 440               "Input configuration: %s\n"
 441               "Should be valid: no\n"
 442               "Expected error report: %s\n\n"
 443               "Result is valid: yes\n"
 444               "Output topology: %s\n",
 445               config_str, expect_err, output_topo_str);
 446
 447end:
 448    if (err != NULL) {
 449        error_free(err);
 450    }
 451
 452    abort();
 453}
 454
 455static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
 456{
 457    MachineClass *mc = MACHINE_GET_CLASS(ms);
 458
 459    mc->smp_props.prefer_sockets = true;
 460    check_parse(ms, &data->config, &data->expect_prefer_sockets,
 461                data->expect_error, is_valid);
 462
 463    mc->smp_props.prefer_sockets = false;
 464    check_parse(ms, &data->config, &data->expect_prefer_cores,
 465                data->expect_error, is_valid);
 466}
 467
 468/* The parsed results of the unsupported parameters should be 1 */
 469static void unsupported_params_init(MachineClass *mc, SMPTestData *data)
 470{
 471    if (!mc->smp_props.dies_supported) {
 472        data->expect_prefer_sockets.dies = 1;
 473        data->expect_prefer_cores.dies = 1;
 474    }
 475}
 476
 477static void machine_base_class_init(ObjectClass *oc, void *data)
 478{
 479    MachineClass *mc = MACHINE_CLASS(oc);
 480
 481    mc->min_cpus = MIN_CPUS;
 482    mc->max_cpus = MAX_CPUS;
 483
 484    mc->smp_props.prefer_sockets = true;
 485    mc->smp_props.dies_supported = false;
 486
 487    mc->name = g_strdup(SMP_MACHINE_NAME);
 488}
 489
 490static void test_generic(void)
 491{
 492    Object *obj = object_new(TYPE_MACHINE);
 493    MachineState *ms = MACHINE(obj);
 494    MachineClass *mc = MACHINE_GET_CLASS(obj);
 495    SMPTestData *data = &(SMPTestData){{ }};
 496    int i;
 497
 498    for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
 499        *data = data_generic_valid[i];
 500        unsupported_params_init(mc, data);
 501
 502        smp_parse_test(ms, data, true);
 503
 504        /* Unsupported parameters can be provided with their values as 1 */
 505        data->config.has_dies = true;
 506        data->config.dies = 1;
 507        smp_parse_test(ms, data, true);
 508    }
 509
 510    /* Force invalid min CPUs and max CPUs */
 511    mc->min_cpus = 2;
 512    mc->max_cpus = 511;
 513
 514    for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
 515        *data = data_generic_invalid[i];
 516        unsupported_params_init(mc, data);
 517
 518        smp_parse_test(ms, data, false);
 519    }
 520
 521    /* Reset the supported min CPUs and max CPUs */
 522    mc->min_cpus = MIN_CPUS;
 523    mc->max_cpus = MAX_CPUS;
 524
 525    object_unref(obj);
 526}
 527
 528static void test_with_dies(void)
 529{
 530    Object *obj = object_new(TYPE_MACHINE);
 531    MachineState *ms = MACHINE(obj);
 532    MachineClass *mc = MACHINE_GET_CLASS(obj);
 533    SMPTestData *data = &(SMPTestData){{ }};
 534    unsigned int num_dies = 2;
 535    int i;
 536
 537    /* Force the SMP compat properties */
 538    mc->smp_props.dies_supported = true;
 539
 540    for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
 541        *data = data_generic_valid[i];
 542        unsupported_params_init(mc, data);
 543
 544        /* when dies parameter is omitted, it will be set as 1 */
 545        data->expect_prefer_sockets.dies = 1;
 546        data->expect_prefer_cores.dies = 1;
 547
 548        smp_parse_test(ms, data, true);
 549
 550        /* when dies parameter is specified */
 551        data->config.has_dies = true;
 552        data->config.dies = num_dies;
 553        if (data->config.has_cpus) {
 554            data->config.cpus *= num_dies;
 555        }
 556        if (data->config.has_maxcpus) {
 557            data->config.maxcpus *= num_dies;
 558        }
 559
 560        data->expect_prefer_sockets.dies = num_dies;
 561        data->expect_prefer_sockets.cpus *= num_dies;
 562        data->expect_prefer_sockets.max_cpus *= num_dies;
 563        data->expect_prefer_cores.dies = num_dies;
 564        data->expect_prefer_cores.cpus *= num_dies;
 565        data->expect_prefer_cores.max_cpus *= num_dies;
 566
 567        smp_parse_test(ms, data, true);
 568    }
 569
 570    for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
 571        *data = data_with_dies_invalid[i];
 572        unsupported_params_init(mc, data);
 573
 574        smp_parse_test(ms, data, false);
 575    }
 576
 577    /* Restore the SMP compat properties */
 578    mc->smp_props.dies_supported = false;
 579
 580    object_unref(obj);
 581}
 582
 583/* Type info of the tested machine */
 584static const TypeInfo smp_machine_types[] = {
 585    {
 586        .name           = TYPE_MACHINE,
 587        .parent         = TYPE_OBJECT,
 588        .class_init     = machine_base_class_init,
 589        .class_size     = sizeof(MachineClass),
 590        .instance_size  = sizeof(MachineState),
 591    }
 592};
 593
 594DEFINE_TYPES(smp_machine_types)
 595
 596int main(int argc, char *argv[])
 597{
 598    module_call_init(MODULE_INIT_QOM);
 599
 600    g_test_init(&argc, &argv, NULL);
 601
 602    g_test_add_func("/test-smp-parse/generic", test_generic);
 603    g_test_add_func("/test-smp-parse/with_dies", test_with_dies);
 604
 605    g_test_run();
 606
 607    return 0;
 608}
 609