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 * Currently a 4-level topology hierarchy is supported on ARM virt machines
  66 *  -sockets/clusters/cores/threads
  67 */
  68#define SMP_CONFIG_WITH_CLUSTERS(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \
  69        {                                                     \
  70            .has_cpus     = ha, .cpus     = a,                \
  71            .has_sockets  = hb, .sockets  = b,                \
  72            .has_clusters = hc, .clusters = c,                \
  73            .has_cores    = hd, .cores    = d,                \
  74            .has_threads  = he, .threads  = e,                \
  75            .has_maxcpus  = hf, .maxcpus  = f,                \
  76        }
  77
  78/**
  79 * @config - the given SMP configuration
  80 * @expect_prefer_sockets - the expected parsing result for the
  81 * valid configuration, when sockets are preferred over cores
  82 * @expect_prefer_cores - the expected parsing result for the
  83 * valid configuration, when cores are preferred over sockets
  84 * @expect_error - the expected error report when the given
  85 * configuration is invalid
  86 */
  87typedef struct SMPTestData {
  88    SMPConfiguration config;
  89    CpuTopology expect_prefer_sockets;
  90    CpuTopology expect_prefer_cores;
  91    const char *expect_error;
  92} SMPTestData;
  93
  94/*
  95 * List all the possible valid sub-collections of the generic 5
  96 * topology parameters (i.e. cpus/maxcpus/sockets/cores/threads),
  97 * then test the automatic calculation algorithm of the missing
  98 * values in the parser.
  99 */
 100static const struct SMPTestData data_generic_valid[] = {
 101    {
 102        /* config: no configuration provided
 103         * expect: cpus=1,sockets=1,cores=1,threads=1,maxcpus=1 */
 104        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, F, 0),
 105        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
 106        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(1, 1, 1, 1, 1),
 107    }, {
 108        /* config: -smp 8
 109         * prefer_sockets: cpus=8,sockets=8,cores=1,threads=1,maxcpus=8
 110         * prefer_cores: cpus=8,sockets=1,cores=8,threads=1,maxcpus=8 */
 111        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, F, 0),
 112        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 1, 8),
 113        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 1, 8),
 114    }, {
 115        /* config: -smp sockets=2
 116         * expect: cpus=2,sockets=2,cores=1,threads=1,maxcpus=2 */
 117        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, F, 0),
 118        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
 119        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 2, 1, 1, 2),
 120    }, {
 121        /* config: -smp cores=4
 122         * expect: cpus=4,sockets=1,cores=4,threads=1,maxcpus=4 */
 123        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, F, 0),
 124        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
 125        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 1, 4, 1, 4),
 126    }, {
 127        /* config: -smp threads=2
 128         * expect: cpus=2,sockets=1,cores=1,threads=2,maxcpus=2 */
 129        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, F, 0),
 130        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
 131        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(2, 1, 1, 2, 2),
 132    }, {
 133        /* config: -smp maxcpus=16
 134         * prefer_sockets: cpus=16,sockets=16,cores=1,threads=1,maxcpus=16
 135         * prefer_cores: cpus=16,sockets=1,cores=16,threads=1,maxcpus=16 */
 136        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, F, 0, T, 16),
 137        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 16, 1, 1, 16),
 138        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 16, 1, 16),
 139    }, {
 140        /* config: -smp 8,sockets=2
 141         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 142        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, F, 0),
 143        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 144        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 145    }, {
 146        /* config: -smp 8,cores=4
 147         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 148        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, F, 0),
 149        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 150        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 151    }, {
 152        /* config: -smp 8,threads=2
 153         * prefer_sockets: cpus=8,sockets=4,cores=1,threads=2,maxcpus=8
 154         * prefer_cores: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
 155        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, F, 0),
 156        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 1, 2, 8),
 157        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 158    }, {
 159        /* config: -smp 8,maxcpus=16
 160         * prefer_sockets: cpus=8,sockets=16,cores=1,threads=1,maxcpus=16
 161         * prefer_cores: cpus=8,sockets=1,cores=16,threads=1,maxcpus=16 */
 162        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, F, 0, T, 16),
 163        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 16, 1, 1, 16),
 164        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 16, 1, 16),
 165    }, {
 166        /* config: -smp sockets=2,cores=4
 167         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 168        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, F, 0),
 169        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 170        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 171    }, {
 172        /* config: -smp sockets=2,threads=2
 173         * expect: cpus=4,sockets=2,cores=1,threads=2,maxcpus=4 */
 174        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, F, 0),
 175        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
 176        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(4, 2, 1, 2, 4),
 177    }, {
 178        /* config: -smp sockets=2,maxcpus=16
 179         * expect: cpus=16,sockets=2,cores=8,threads=1,maxcpus=16 */
 180        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, F, 0, T, 16),
 181        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
 182        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 8, 1, 16),
 183    }, {
 184        /* config: -smp cores=4,threads=2
 185         * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
 186        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, F, 0),
 187        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 188        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 189    }, {
 190        /* config: -smp cores=4,maxcpus=16
 191         * expect: cpus=16,sockets=4,cores=4,threads=1,maxcpus=16 */
 192        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, F, 0, T, 16),
 193        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
 194        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 4, 4, 1, 16),
 195    }, {
 196        /* config: -smp threads=2,maxcpus=16
 197         * prefer_sockets: cpus=16,sockets=8,cores=1,threads=2,maxcpus=16
 198         * prefer_cores: cpus=16,sockets=1,cores=8,threads=2,maxcpus=16 */
 199        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, F, 0, T, 2, T, 16),
 200        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 8, 1, 2, 16),
 201        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 1, 8, 2, 16),
 202    }, {
 203        /* config: -smp 8,sockets=2,cores=4
 204         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 205        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, F, 0),
 206        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 207        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 208    }, {
 209        /* config: -smp 8,sockets=2,threads=2
 210         * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */
 211        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, F, 0),
 212        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
 213        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8),
 214    }, {
 215        /* config: -smp 8,sockets=2,maxcpus=16
 216         * expect: cpus=8,sockets=2,cores=8,threads=1,maxcpus=16 */
 217        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, F, 0, T, 16),
 218        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
 219        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 8, 1, 16),
 220    }, {
 221        /* config: -smp 8,cores=4,threads=2
 222         * expect: cpus=8,sockets=1,cores=4,threads=2,maxcpus=8 */
 223        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, F, 0),
 224        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 225        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 4, 2, 8),
 226    }, {
 227        /* config: -smp 8,cores=4,maxcpus=16
 228         * expect: cpus=8,sockets=4,cores=4,threads=1,maxcpus=16 */
 229        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, F, 0, T, 16),
 230        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
 231        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 4, 4, 1, 16),
 232    }, {
 233        /* config: -smp 8,threads=2,maxcpus=16
 234         * prefer_sockets: cpus=8,sockets=8,cores=1,threads=2,maxcpus=16
 235         * prefer_cores: cpus=8,sockets=1,cores=8,threads=2,maxcpus=16 */
 236        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, F, 0, T, 2, T, 16),
 237        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 8, 1, 2, 16),
 238        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 1, 8, 2, 16),
 239    }, {
 240        /* config: -smp sockets=2,cores=4,threads=2
 241         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 242        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, F, 0),
 243        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 244        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 245    }, {
 246        /* config: -smp sockets=2,cores=4,maxcpus=16
 247         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 248        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, F, 0, T, 16),
 249        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 250        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 251    }, {
 252        /* config: -smp sockets=2,threads=2,maxcpus=16
 253         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 254        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, F, 0, T, 2, T, 16),
 255        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 256        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 257    }, {
 258        /* config: -smp cores=4,threads=2,maxcpus=16
 259         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 260        .config = SMP_CONFIG_GENERIC(F, 0, F, 0, T, 4, T, 2, T, 16),
 261        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 262        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 263    }, {
 264        /* config: -smp 8,sockets=2,cores=4,threads=1
 265         * expect: cpus=8,sockets=2,cores=4,threads=1,maxcpus=8 */
 266        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 1, F, 0),
 267        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 268        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 1, 8),
 269    }, {
 270        /* config: -smp 8,sockets=2,cores=4,maxcpus=16
 271         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 272        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, F, 0, T, 16),
 273        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 274        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 275    }, {
 276        /* config: -smp 8,sockets=2,threads=2,maxcpus=16
 277         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 278        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, F, 0, T, 2, T, 16),
 279        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 280        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 281    }, {
 282        /* config: -smp 8,cores=4,threads=2,maxcpus=16
 283         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 284        .config = SMP_CONFIG_GENERIC(T, 8, F, 0, T, 4, T, 2, T, 16),
 285        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 286        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 287    }, {
 288        /* config: -smp sockets=2,cores=4,threads=2,maxcpus=16
 289         * expect: cpus=16,sockets=2,cores=4,threads=2,maxcpus=16 */
 290        .config = SMP_CONFIG_GENERIC(F, 0, T, 2, T, 4, T, 2, T, 16),
 291        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 292        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(16, 2, 4, 2, 16),
 293    }, {
 294        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=16
 295         * expect: cpus=8,sockets=2,cores=4,threads=2,maxcpus=16 */
 296        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16),
 297        .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 298        .expect_prefer_cores   = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16),
 299    },
 300};
 301
 302static const struct SMPTestData data_generic_invalid[] = {
 303    {
 304        /* config: -smp 2,dies=2 */
 305        .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
 306        .expect_error = "dies not supported by this machine's CPU topology",
 307    }, {
 308        /* config: -smp 2,clusters=2 */
 309        .config = SMP_CONFIG_WITH_CLUSTERS(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0),
 310        .expect_error = "clusters not supported by this machine's CPU topology",
 311    }, {
 312        /* config: -smp 8,sockets=2,cores=4,threads=2,maxcpus=8 */
 313        .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 8),
 314        .expect_error = "Invalid CPU topology: "
 315                        "product of the hierarchy must match maxcpus: "
 316                        "sockets (2) * cores (4) * threads (2) "
 317                        "!= maxcpus (8)",
 318    }, {
 319        /* config: -smp 18,sockets=2,cores=4,threads=2,maxcpus=16 */
 320        .config = SMP_CONFIG_GENERIC(T, 18, T, 2, T, 4, T, 2, T, 16),
 321        .expect_error = "Invalid CPU topology: "
 322                        "maxcpus must be equal to or greater than smp: "
 323                        "sockets (2) * cores (4) * threads (2) "
 324                        "== maxcpus (16) < smp_cpus (18)",
 325    }, {
 326        /* config: -smp 1
 327         * should tweak the supported min CPUs to 2 for testing */
 328        .config = SMP_CONFIG_GENERIC(T, 1, F, 0, F, 0, F, 0, F, 0),
 329        .expect_error = "Invalid SMP CPUs 1. The min CPUs supported "
 330                        "by machine '" SMP_MACHINE_NAME "' is 2",
 331    }, {
 332        /* config: -smp 512
 333         * should tweak the supported max CPUs to 511 for testing */
 334        .config = SMP_CONFIG_GENERIC(T, 512, F, 0, F, 0, F, 0, F, 0),
 335        .expect_error = "Invalid SMP CPUs 512. The max CPUs supported "
 336                        "by machine '" SMP_MACHINE_NAME "' is 511",
 337    },
 338};
 339
 340static const struct SMPTestData data_with_dies_invalid[] = {
 341    {
 342        /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */
 343        .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
 344        .expect_error = "Invalid CPU topology: "
 345                        "product of the hierarchy must match maxcpus: "
 346                        "sockets (2) * dies (2) * cores (4) * threads (2) "
 347                        "!= maxcpus (16)",
 348    }, {
 349        /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */
 350        .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
 351        .expect_error = "Invalid CPU topology: "
 352                        "maxcpus must be equal to or greater than smp: "
 353                        "sockets (2) * dies (2) * cores (4) * threads (2) "
 354                        "== maxcpus (32) < smp_cpus (34)",
 355    },
 356};
 357
 358static const struct SMPTestData data_with_clusters_invalid[] = {
 359    {
 360        /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */
 361        .config = SMP_CONFIG_WITH_CLUSTERS(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16),
 362        .expect_error = "Invalid CPU topology: "
 363                        "product of the hierarchy must match maxcpus: "
 364                        "sockets (2) * clusters (2) * cores (4) * threads (2) "
 365                        "!= maxcpus (16)",
 366    }, {
 367        /* config: -smp 34,sockets=2,clusters=2,cores=4,threads=2,maxcpus=32 */
 368        .config = SMP_CONFIG_WITH_CLUSTERS(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32),
 369        .expect_error = "Invalid CPU topology: "
 370                        "maxcpus must be equal to or greater than smp: "
 371                        "sockets (2) * clusters (2) * cores (4) * threads (2) "
 372                        "== maxcpus (32) < smp_cpus (34)",
 373    },
 374};
 375
 376static char *smp_config_to_string(const SMPConfiguration *config)
 377{
 378    return g_strdup_printf(
 379        "(SMPConfiguration) {\n"
 380        "    .has_cpus     = %5s, cpus     = %" PRId64 ",\n"
 381        "    .has_sockets  = %5s, sockets  = %" PRId64 ",\n"
 382        "    .has_dies     = %5s, dies     = %" PRId64 ",\n"
 383        "    .has_clusters = %5s, clusters = %" PRId64 ",\n"
 384        "    .has_cores    = %5s, cores    = %" PRId64 ",\n"
 385        "    .has_threads  = %5s, threads  = %" PRId64 ",\n"
 386        "    .has_maxcpus  = %5s, maxcpus  = %" PRId64 ",\n"
 387        "}",
 388        config->has_cpus ? "true" : "false", config->cpus,
 389        config->has_sockets ? "true" : "false", config->sockets,
 390        config->has_dies ? "true" : "false", config->dies,
 391        config->has_clusters ? "true" : "false", config->clusters,
 392        config->has_cores ? "true" : "false", config->cores,
 393        config->has_threads ? "true" : "false", config->threads,
 394        config->has_maxcpus ? "true" : "false", config->maxcpus);
 395}
 396
 397static char *cpu_topology_to_string(const CpuTopology *topo)
 398{
 399    return g_strdup_printf(
 400        "(CpuTopology) {\n"
 401        "    .cpus     = %u,\n"
 402        "    .sockets  = %u,\n"
 403        "    .dies     = %u,\n"
 404        "    .clusters = %u,\n"
 405        "    .cores    = %u,\n"
 406        "    .threads  = %u,\n"
 407        "    .max_cpus = %u,\n"
 408        "}",
 409        topo->cpus, topo->sockets, topo->dies, topo->clusters,
 410        topo->cores, topo->threads, topo->max_cpus);
 411}
 412
 413static void check_parse(MachineState *ms, const SMPConfiguration *config,
 414                        const CpuTopology *expect_topo, const char *expect_err,
 415                        bool is_valid)
 416{
 417    g_autofree char *config_str = smp_config_to_string(config);
 418    g_autofree char *expect_topo_str = cpu_topology_to_string(expect_topo);
 419    g_autofree char *output_topo_str = NULL;
 420    Error *err = NULL;
 421
 422    /* call the generic parser */
 423    machine_parse_smp_config(ms, config, &err);
 424
 425    output_topo_str = cpu_topology_to_string(&ms->smp);
 426
 427    /* when the configuration is supposed to be valid */
 428    if (is_valid) {
 429        if ((err == NULL) &&
 430            (ms->smp.cpus == expect_topo->cpus) &&
 431            (ms->smp.sockets == expect_topo->sockets) &&
 432            (ms->smp.dies == expect_topo->dies) &&
 433            (ms->smp.clusters == expect_topo->clusters) &&
 434            (ms->smp.cores == expect_topo->cores) &&
 435            (ms->smp.threads == expect_topo->threads) &&
 436            (ms->smp.max_cpus == expect_topo->max_cpus)) {
 437            return;
 438        }
 439
 440        if (err != NULL) {
 441            g_printerr("Test smp_parse failed!\n"
 442                       "Input configuration: %s\n"
 443                       "Should be valid: yes\n"
 444                       "Expected topology: %s\n\n"
 445                       "Result is valid: no\n"
 446                       "Output error report: %s\n",
 447                       config_str, expect_topo_str, error_get_pretty(err));
 448            goto end;
 449        }
 450
 451        g_printerr("Test smp_parse failed!\n"
 452                   "Input configuration: %s\n"
 453                   "Should be valid: yes\n"
 454                   "Expected topology: %s\n\n"
 455                   "Result is valid: yes\n"
 456                   "Output topology: %s\n",
 457                   config_str, expect_topo_str, output_topo_str);
 458        goto end;
 459    }
 460
 461    /* when the configuration is supposed to be invalid */
 462    if (err != NULL) {
 463        if (expect_err == NULL ||
 464            g_str_equal(expect_err, error_get_pretty(err))) {
 465            error_free(err);
 466            return;
 467        }
 468
 469        g_printerr("Test smp_parse failed!\n"
 470                   "Input configuration: %s\n"
 471                   "Should be valid: no\n"
 472                   "Expected error report: %s\n\n"
 473                   "Result is valid: no\n"
 474                   "Output error report: %s\n",
 475                   config_str, expect_err, error_get_pretty(err));
 476        goto end;
 477    }
 478
 479    g_printerr("Test smp_parse failed!\n"
 480               "Input configuration: %s\n"
 481               "Should be valid: no\n"
 482               "Expected error report: %s\n\n"
 483               "Result is valid: yes\n"
 484               "Output topology: %s\n",
 485               config_str, expect_err, output_topo_str);
 486
 487end:
 488    if (err != NULL) {
 489        error_free(err);
 490    }
 491
 492    abort();
 493}
 494
 495static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid)
 496{
 497    MachineClass *mc = MACHINE_GET_CLASS(ms);
 498
 499    mc->smp_props.prefer_sockets = true;
 500    check_parse(ms, &data->config, &data->expect_prefer_sockets,
 501                data->expect_error, is_valid);
 502
 503    mc->smp_props.prefer_sockets = false;
 504    check_parse(ms, &data->config, &data->expect_prefer_cores,
 505                data->expect_error, is_valid);
 506}
 507
 508/* The parsed results of the unsupported parameters should be 1 */
 509static void unsupported_params_init(const MachineClass *mc, SMPTestData *data)
 510{
 511    if (!mc->smp_props.dies_supported) {
 512        data->expect_prefer_sockets.dies = 1;
 513        data->expect_prefer_cores.dies = 1;
 514    }
 515
 516    if (!mc->smp_props.clusters_supported) {
 517        data->expect_prefer_sockets.clusters = 1;
 518        data->expect_prefer_cores.clusters = 1;
 519    }
 520}
 521
 522static void machine_base_class_init(ObjectClass *oc, void *data)
 523{
 524    MachineClass *mc = MACHINE_CLASS(oc);
 525
 526    mc->min_cpus = MIN_CPUS;
 527    mc->max_cpus = MAX_CPUS;
 528
 529    mc->name = g_strdup(SMP_MACHINE_NAME);
 530}
 531
 532static void machine_generic_invalid_class_init(ObjectClass *oc, void *data)
 533{
 534    MachineClass *mc = MACHINE_CLASS(oc);
 535
 536    /* Force invalid min CPUs and max CPUs */
 537    mc->min_cpus = 2;
 538    mc->max_cpus = 511;
 539}
 540
 541static void machine_with_dies_class_init(ObjectClass *oc, void *data)
 542{
 543    MachineClass *mc = MACHINE_CLASS(oc);
 544
 545    mc->smp_props.dies_supported = true;
 546}
 547
 548static void machine_with_clusters_class_init(ObjectClass *oc, void *data)
 549{
 550    MachineClass *mc = MACHINE_CLASS(oc);
 551
 552    mc->smp_props.clusters_supported = true;
 553}
 554
 555static void test_generic_valid(const void *opaque)
 556{
 557    const char *machine_type = opaque;
 558    Object *obj = object_new(machine_type);
 559    MachineState *ms = MACHINE(obj);
 560    MachineClass *mc = MACHINE_GET_CLASS(obj);
 561    SMPTestData data = {};
 562    int i;
 563
 564    for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
 565        data = data_generic_valid[i];
 566        unsupported_params_init(mc, &data);
 567
 568        smp_parse_test(ms, &data, true);
 569
 570        /* Unsupported parameters can be provided with their values as 1 */
 571        data.config.has_dies = true;
 572        data.config.dies = 1;
 573        smp_parse_test(ms, &data, true);
 574    }
 575
 576    object_unref(obj);
 577}
 578
 579static void test_generic_invalid(const void *opaque)
 580{
 581    const char *machine_type = opaque;
 582    Object *obj = object_new(machine_type);
 583    MachineState *ms = MACHINE(obj);
 584    MachineClass *mc = MACHINE_GET_CLASS(obj);
 585    SMPTestData data = {};
 586    int i;
 587
 588    for (i = 0; i < ARRAY_SIZE(data_generic_invalid); i++) {
 589        data = data_generic_invalid[i];
 590        unsupported_params_init(mc, &data);
 591
 592        smp_parse_test(ms, &data, false);
 593    }
 594
 595    object_unref(obj);
 596}
 597
 598static void test_with_dies(const void *opaque)
 599{
 600    const char *machine_type = opaque;
 601    Object *obj = object_new(machine_type);
 602    MachineState *ms = MACHINE(obj);
 603    MachineClass *mc = MACHINE_GET_CLASS(obj);
 604    SMPTestData data = {};
 605    unsigned int num_dies = 2;
 606    int i;
 607
 608    for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
 609        data = data_generic_valid[i];
 610        unsupported_params_init(mc, &data);
 611
 612        /* when dies parameter is omitted, it will be set as 1 */
 613        data.expect_prefer_sockets.dies = 1;
 614        data.expect_prefer_cores.dies = 1;
 615
 616        smp_parse_test(ms, &data, true);
 617
 618        /* when dies parameter is specified */
 619        data.config.has_dies = true;
 620        data.config.dies = num_dies;
 621        if (data.config.has_cpus) {
 622            data.config.cpus *= num_dies;
 623        }
 624        if (data.config.has_maxcpus) {
 625            data.config.maxcpus *= num_dies;
 626        }
 627
 628        data.expect_prefer_sockets.dies = num_dies;
 629        data.expect_prefer_sockets.cpus *= num_dies;
 630        data.expect_prefer_sockets.max_cpus *= num_dies;
 631        data.expect_prefer_cores.dies = num_dies;
 632        data.expect_prefer_cores.cpus *= num_dies;
 633        data.expect_prefer_cores.max_cpus *= num_dies;
 634
 635        smp_parse_test(ms, &data, true);
 636    }
 637
 638    for (i = 0; i < ARRAY_SIZE(data_with_dies_invalid); i++) {
 639        data = data_with_dies_invalid[i];
 640        unsupported_params_init(mc, &data);
 641
 642        smp_parse_test(ms, &data, false);
 643    }
 644
 645    object_unref(obj);
 646}
 647
 648static void test_with_clusters(const void *opaque)
 649{
 650    const char *machine_type = opaque;
 651    Object *obj = object_new(machine_type);
 652    MachineState *ms = MACHINE(obj);
 653    MachineClass *mc = MACHINE_GET_CLASS(obj);
 654    SMPTestData data = {};
 655    unsigned int num_clusters = 2;
 656    int i;
 657
 658    for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) {
 659        data = data_generic_valid[i];
 660        unsupported_params_init(mc, &data);
 661
 662        /* when clusters parameter is omitted, it will be set as 1 */
 663        data.expect_prefer_sockets.clusters = 1;
 664        data.expect_prefer_cores.clusters = 1;
 665
 666        smp_parse_test(ms, &data, true);
 667
 668        /* when clusters parameter is specified */
 669        data.config.has_clusters = true;
 670        data.config.clusters = num_clusters;
 671        if (data.config.has_cpus) {
 672            data.config.cpus *= num_clusters;
 673        }
 674        if (data.config.has_maxcpus) {
 675            data.config.maxcpus *= num_clusters;
 676        }
 677
 678        data.expect_prefer_sockets.clusters = num_clusters;
 679        data.expect_prefer_sockets.cpus *= num_clusters;
 680        data.expect_prefer_sockets.max_cpus *= num_clusters;
 681        data.expect_prefer_cores.clusters = num_clusters;
 682        data.expect_prefer_cores.cpus *= num_clusters;
 683        data.expect_prefer_cores.max_cpus *= num_clusters;
 684
 685        smp_parse_test(ms, &data, true);
 686    }
 687
 688    for (i = 0; i < ARRAY_SIZE(data_with_clusters_invalid); i++) {
 689        data = data_with_clusters_invalid[i];
 690        unsupported_params_init(mc, &data);
 691
 692        smp_parse_test(ms, &data, false);
 693    }
 694
 695    object_unref(obj);
 696}
 697
 698/* Type info of the tested machine */
 699static const TypeInfo smp_machine_types[] = {
 700    {
 701        .name           = TYPE_MACHINE,
 702        .parent         = TYPE_OBJECT,
 703        .abstract       = true,
 704        .class_init     = machine_base_class_init,
 705        .class_size     = sizeof(MachineClass),
 706        .instance_size  = sizeof(MachineState),
 707    }, {
 708        .name           = MACHINE_TYPE_NAME("smp-generic-valid"),
 709        .parent         = TYPE_MACHINE,
 710    }, {
 711        .name           = MACHINE_TYPE_NAME("smp-generic-invalid"),
 712        .parent         = TYPE_MACHINE,
 713        .class_init     = machine_generic_invalid_class_init,
 714    }, {
 715        .name           = MACHINE_TYPE_NAME("smp-with-dies"),
 716        .parent         = TYPE_MACHINE,
 717        .class_init     = machine_with_dies_class_init,
 718    }, {
 719        .name           = MACHINE_TYPE_NAME("smp-with-clusters"),
 720        .parent         = TYPE_MACHINE,
 721        .class_init     = machine_with_clusters_class_init,
 722    }
 723};
 724
 725DEFINE_TYPES(smp_machine_types)
 726
 727int main(int argc, char *argv[])
 728{
 729    module_call_init(MODULE_INIT_QOM);
 730
 731    g_test_init(&argc, &argv, NULL);
 732
 733    g_test_add_data_func("/test-smp-parse/generic/valid",
 734                         MACHINE_TYPE_NAME("smp-generic-valid"),
 735                         test_generic_valid);
 736    g_test_add_data_func("/test-smp-parse/generic/invalid",
 737                         MACHINE_TYPE_NAME("smp-generic-invalid"),
 738                         test_generic_invalid);
 739    g_test_add_data_func("/test-smp-parse/with_dies",
 740                         MACHINE_TYPE_NAME("smp-with-dies"),
 741                         test_with_dies);
 742    g_test_add_data_func("/test-smp-parse/with_clusters",
 743                         MACHINE_TYPE_NAME("smp-with-clusters"),
 744                         test_with_clusters);
 745
 746    g_test_run();
 747
 748    return 0;
 749}
 750