qemu/tests/libqos/qgraph.h
<<
>>
Prefs
   1/*
   2 * libqos driver framework
   3 *
   4 * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License version 2 as published by the Free Software Foundation.
   9 *
  10 * This library is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * Lesser General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU Lesser General Public
  16 * License along with this library; if not, see <http://www.gnu.org/licenses/>
  17 */
  18
  19#ifndef QGRAPH_H
  20#define QGRAPH_H
  21
  22#include <gmodule.h>
  23#include "qemu/module.h"
  24#include "malloc.h"
  25
  26/* maximum path length */
  27#define QOS_PATH_MAX_ELEMENT_SIZE 50
  28
  29typedef struct QOSGraphObject QOSGraphObject;
  30typedef struct QOSGraphNode QOSGraphNode;
  31typedef struct QOSGraphEdge QOSGraphEdge;
  32typedef struct QOSGraphNodeOptions QOSGraphNodeOptions;
  33typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions;
  34typedef struct QOSGraphTestOptions QOSGraphTestOptions;
  35
  36/* Constructor for drivers, machines and test */
  37typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc,
  38                                      void *addr);
  39typedef void *(*QOSCreateMachineFunc) (QTestState *qts);
  40typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc);
  41
  42/* QOSGraphObject functions */
  43typedef void *(*QOSGetDriver) (void *object, const char *interface);
  44typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name);
  45typedef void (*QOSDestructorFunc) (QOSGraphObject *object);
  46typedef void (*QOSStartFunct) (QOSGraphObject *object);
  47
  48/* Test options functions */
  49typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg);
  50
  51/**
  52 * SECTION: qgraph.h
  53 * @title: Qtest Driver Framework
  54 * @short_description: interfaces to organize drivers and tests
  55 *                     as nodes in a graph
  56 *
  57 * This Qgraph API provides all basic functions to create a graph
  58 * and instantiate nodes representing machines, drivers and tests
  59 * representing their relations with CONSUMES, PRODUCES, and CONTAINS
  60 * edges.
  61 *
  62 * The idea is to have a framework where each test asks for a specific
  63 * driver, and the framework takes care of allocating the proper devices
  64 * required and passing the correct command line arguments to QEMU.
  65 *
  66 * A node can be of four types:
  67 * - QNODE_MACHINE:   for example "arm/raspi2"
  68 * - QNODE_DRIVER:    for example "generic-sdhci"
  69 * - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers)
  70 *                     an interface is not explicitly created, it will be auto-
  71 *                     matically instantiated when a node consumes or produces
  72 *                     it.
  73 * - QNODE_TEST:      for example "sdhci-test", consumes an interface and tests
  74 *                    the functions provided
  75 *
  76 * Notes for the nodes:
  77 * - QNODE_MACHINE: each machine struct must have a QGuestAllocator and
  78 *                  implement get_driver to return the allocator passing
  79 *                  "memory". The function can also return NULL if the
  80 *                  allocator is not set.
  81 * - QNODE_DRIVER:  driver names must be unique, and machines and nodes
  82 *                  planned to be "consumed" by other nodes must match QEMU
  83 *                  drivers name, otherwise they won't be discovered
  84 *
  85 * An edge relation between two nodes (drivers or machines) X and Y can be:
  86 * - X CONSUMES Y: Y can be plugged into X
  87 * - X PRODUCES Y: X provides the interface Y
  88 * - X CONTAINS Y: Y is part of X component
  89 *
  90 * Basic framework steps are the following:
  91 * - All nodes and edges are created in their respective
  92 *   machine/driver/test files
  93 * - The framework starts QEMU and asks for a list of available devices
  94 *   and machines (note that only machines and "consumed" nodes are mapped
  95 *   1:1 with QEMU devices)
  96 * - The framework walks the graph starting from the available machines and
  97 *   performs a Depth First Search for tests
  98 * - Once a test is found, the path is walked again and all drivers are
  99 *   allocated accordingly and the final interface is passed to the test
 100 * - The test is executed
 101 * - Unused objects are cleaned and the path discovery is continued
 102 *
 103 * Depending on the QEMU binary used, only some drivers/machines will be
 104 * available and only test that are reached by them will be executed.
 105 *
 106 * <example>
 107 *   <title>Creating new driver an its interface</title>
 108 *   <programlisting>
 109 #include "libqos/qgraph.h"
 110
 111 struct My_driver {
 112     QOSGraphObject obj;
 113     Node_produced prod;
 114     Node_contained cont;
 115 }
 116
 117 static void my_destructor(QOSGraphObject *obj)
 118 {
 119    g_free(obj);
 120 }
 121
 122 static void my_get_driver(void *object, const char *interface) {
 123    My_driver *dev = object;
 124    if (!g_strcmp0(interface, "my_interface")) {
 125        return &dev->prod;
 126    }
 127    abort();
 128 }
 129
 130 static void my_get_device(void *object, const char *device) {
 131    My_driver *dev = object;
 132    if (!g_strcmp0(device, "my_driver_contained")) {
 133        return &dev->cont;
 134    }
 135    abort();
 136 }
 137
 138 static void *my_driver_constructor(void *node_consumed,
 139                                    QOSGraphObject *alloc)
 140 {
 141    My_driver dev = g_new(My_driver, 1);
 142    // get the node pointed by the produce edge
 143    dev->obj.get_driver = my_get_driver;
 144    // get the node pointed by the contains
 145    dev->obj.get_device = my_get_device;
 146    // free the object
 147    dev->obj.destructor = my_destructor;
 148    do_something_with_node_consumed(node_consumed);
 149    // set all fields of contained device
 150    init_contained_device(&dev->cont);
 151    return &dev->obj;
 152 }
 153
 154 static void register_my_driver(void)
 155 {
 156     qos_node_create_driver("my_driver", my_driver_constructor);
 157     // contained drivers don't need a constructor,
 158     // they will be init by the parent.
 159     qos_node_create_driver("my_driver_contained", NULL);
 160
 161    // For the sake of this example, assume machine x86_64/pc contains
 162    // "other_node".
 163    // This relation, along with the machine and "other_node" creation,
 164    // should be defined in the x86_64_pc-machine.c file.
 165    // "my_driver" will then consume "other_node"
 166    qos_node_contains("my_driver", "my_driver_contained");
 167    qos_node_produces("my_driver", "my_interface");
 168    qos_node_consumes("my_driver", "other_node");
 169 }
 170 *   </programlisting>
 171 * </example>
 172 *
 173 * In the above example, all possible types of relations are created:
 174 * node "my_driver" consumes, contains and produces other nodes.
 175 * more specifically:
 176 * x86_64/pc -->contains--> other_node <--consumes-- my_driver
 177 *                                                       |
 178 *                      my_driver_contained <--contains--+
 179 *                                                       |
 180 *                             my_interface <--produces--+
 181 *
 182 * or inverting the consumes edge in consumed_by:
 183 *
 184 * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
 185 *                                                           |
 186 *                          my_driver_contained <--contains--+
 187 *                                                           |
 188 *                                 my_interface <--produces--+
 189 *
 190 * <example>
 191 *   <title>Creating new test</title>
 192 *   <programlisting>
 193 * #include "libqos/qgraph.h"
 194 *
 195 * static void my_test_function(void *obj, void *data)
 196 * {
 197 *    Node_produced *interface_to_test = obj;
 198 *    // test interface_to_test
 199 * }
 200 *
 201 * static void register_my_test(void)
 202 * {
 203 *    qos_add_test("my_interface", "my_test", my_test_function);
 204 * }
 205 *
 206 * libqos_init(register_my_test);
 207 *
 208 *   </programlisting>
 209 * </example>
 210 *
 211 * Here a new test is created, consuming "my_interface" node
 212 * and creating a valid path from a machine to a test.
 213 * Final graph will be like this:
 214 * x86_64/pc -->contains--> other_node <--consumes-- my_driver
 215 *                                                        |
 216 *                       my_driver_contained <--contains--+
 217 *                                                        |
 218 *        my_test --consumes--> my_interface <--produces--+
 219 *
 220 * or inverting the consumes edge in consumed_by:
 221 *
 222 * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
 223 *                                                           |
 224 *                          my_driver_contained <--contains--+
 225 *                                                           |
 226 *        my_test <--consumed_by-- my_interface <--produces--+
 227 *
 228 * Assuming there the binary is
 229 * QTEST_QEMU_BINARY=x86_64-softmmu/qemu-system-x86_64
 230 * a valid test path will be:
 231 * "/x86_64/pc/other_node/my_driver/my_interface/my_test".
 232 *
 233 * Additional examples are also in libqos/test-qgraph.c
 234 *
 235 * Command line:
 236 * Command line is built by using node names and optional arguments
 237 * passed by the user when building the edges.
 238 *
 239 * There are three types of command line arguments:
 240 * - in node      : created from the node name. For example, machines will
 241 *                  have "-M <machine>" to its command line, while devices
 242 *                  "-device <device>". It is automatically done by the
 243 *                   framework.
 244 * - after node   : added as additional argument to the node name.
 245 *                  This argument is added optionally when creating edges,
 246 *                  by setting the parameter @after_cmd_line and
 247 *                  @extra_edge_opts in #QOSGraphEdgeOptions.
 248 *                  The framework automatically adds
 249 *                  a comma before @extra_edge_opts,
 250 *                  because it is going to add attributes
 251 *                  after the destination node pointed by
 252 *                  the edge containing these options, and automatically
 253 *                  adds a space before @after_cmd_line, because it
 254 *                  adds an additional device, not an attribute.
 255 * - before node  : added as additional argument to the node name.
 256 *                  This argument is added optionally when creating edges,
 257 *                  by setting the parameter @before_cmd_line in
 258 *                  #QOSGraphEdgeOptions. This attribute
 259 *                  is going to add attributes before the destination node
 260 *                  pointed by the edge containing these options. It is
 261 *                  helpful to commands that are not node-representable,
 262 *                  such as "-fdsev" or "-netdev".
 263 *
 264 * While adding command line in edges is always used, not all nodes names are
 265 * used in every path walk: this is because the contained or produced ones
 266 * are already added by QEMU, so only nodes that "consumes" will be used to
 267 * build the command line. Also, nodes that will have { "abstract" : true }
 268 * as QMP attribute will loose their command line, since they are not proper
 269 * devices to be added in QEMU.
 270 *
 271 * Example:
 272 *
 273 QOSGraphEdgeOptions opts = {
 274     .arg = NULL,
 275     .size_arg = 0,
 276     .after_cmd_line = "-device other",
 277     .before_cmd_line = "-netdev something",
 278     .extra_edge_opts = "addr=04.0",
 279 };
 280 QOSGraphNode * node = qos_node_create_driver("my_node", constructor);
 281 qos_node_consumes_args("my_node", "interface", &opts);
 282 *
 283 * Will produce the following command line:
 284 * "-netdev something -device my_node,addr=04.0 -device other"
 285 */
 286
 287/**
 288 * Edge options to be passed to the contains/consumes *_args function.
 289 */
 290struct QOSGraphEdgeOptions {
 291    void *arg;                    /*
 292                                   * optional arg that will be used by
 293                                   * dest edge
 294                                   */
 295    uint32_t size_arg;            /*
 296                                   * optional arg size that will be used by
 297                                   * dest edge
 298                                   */
 299    const char *extra_device_opts;/*
 300                                   *optional additional command line for dest
 301                                   * edge, used to add additional attributes
 302                                   * *after* the node command line, the
 303                                   * framework automatically prepends ","
 304                                   * to this argument.
 305                                   */
 306    const char *before_cmd_line;  /*
 307                                   * optional additional command line for dest
 308                                   * edge, used to add additional attributes
 309                                   * *before* the node command line, usually
 310                                   * other non-node represented commands,
 311                                   * like "-fdsev synt"
 312                                   */
 313    const char *after_cmd_line;   /*
 314                                   * optional extra command line to be added
 315                                   * after the device command. This option
 316                                   * is used to add other devices
 317                                   * command line that depend on current node.
 318                                   * Automatically prepends " " to this
 319                                   * argument
 320                                   */
 321    const char *edge_name;        /*
 322                                   * optional edge to differentiate multiple
 323                                   * devices with same node name
 324                                   */
 325};
 326
 327/**
 328 * Test options to be passed to the test functions.
 329 */
 330struct QOSGraphTestOptions {
 331    QOSGraphEdgeOptions edge;   /* edge arguments that will be used by test.
 332                                 * Note that test *does not* use edge_name,
 333                                 * and uses instead arg and size_arg as
 334                                 * data arg for its test function.
 335                                 */
 336    void *arg;                  /* passed to the .before function, or to the
 337                                 * test function if there is no .before
 338                                 * function
 339                                 */
 340    QOSBeforeTest before;       /* executed before the test. Can add
 341                                 * additional parameters to the command line
 342                                 * and modify the argument to the test function.
 343                                 */
 344    bool subprocess;            /* run the test in a subprocess */
 345};
 346
 347/**
 348 * Each driver, test or machine of this framework will have a
 349 * QOSGraphObject as first field.
 350 *
 351 * This set of functions offered by QOSGraphObject are executed
 352 * in different stages of the framework:
 353 * - get_driver / get_device : Once a machine-to-test path has been
 354 * found, the framework traverses it again and allocates all the
 355 * nodes, using the provided constructor. To satisfy their relations,
 356 * i.e. for produces or contains, where a struct constructor needs
 357 * an external parameter represented by the previous node,
 358 * the framework will call get_device (for contains) or
 359 * get_driver (for produces), depending on the edge type, passing
 360 * them the name of the next node to be taken and getting from them
 361 * the corresponding pointer to the actual structure of the next node to
 362 * be used in the path.
 363 *
 364 * - start_hw: This function is executed after all the path objects
 365 * have been allocated, but before the test is run. It starts the hw, setting
 366 * the initial configurations (*_device_enable) and making it ready for the
 367 * test.
 368 *
 369 * - destructor: Opposite to the node constructor, destroys the object.
 370 * This function is called after the test has been executed, and performs
 371 * a complete cleanup of each node allocated field. In case no constructor
 372 * is provided, no destructor will be called.
 373 *
 374 */
 375struct QOSGraphObject {
 376    /* for produces edges, returns void * */
 377    QOSGetDriver get_driver;
 378    /* for contains edges, returns a QOSGraphObject * */
 379    QOSGetDevice get_device;
 380    /* start the hw, get ready for the test */
 381    QOSStartFunct start_hw;
 382    /* destroy this QOSGraphObject */
 383    QOSDestructorFunc destructor;
 384    /* free the memory associated to the QOSGraphObject and its contained
 385     * children */
 386    GDestroyNotify free;
 387};
 388
 389/**
 390 * qos_graph_init(): initialize the framework, creates two hash
 391 * tables: one for the nodes and another for the edges.
 392 */
 393void qos_graph_init(void);
 394
 395/**
 396 * qos_graph_destroy(): deallocates all the hash tables,
 397 * freeing all nodes and edges.
 398 */
 399void qos_graph_destroy(void);
 400
 401/**
 402 * qos_node_destroy(): removes and frees a node from the,
 403 * nodes hash table.
 404 */
 405void qos_node_destroy(void *key);
 406
 407/**
 408 * qos_edge_destroy(): removes and frees an edge from the,
 409 * edges hash table.
 410 */
 411void qos_edge_destroy(void *key);
 412
 413/**
 414 * qos_add_test(): adds a test node @name to the nodes hash table.
 415 *
 416 * The test will consume a @interface node, and once the
 417 * graph walking algorithm has found it, the @test_func will be
 418 * executed. It also has the possibility to
 419 * add an optional @opts (see %QOSGraphNodeOptions).
 420 *
 421 * For tests, opts->edge.arg and size_arg represent the arg to pass
 422 * to @test_func
 423 */
 424void qos_add_test(const char *name, const char *interface,
 425                  QOSTestFunc test_func,
 426                  QOSGraphTestOptions *opts);
 427
 428/**
 429 * qos_node_create_machine(): creates the machine @name and
 430 * adds it to the node hash table.
 431 *
 432 * This node will be of type QNODE_MACHINE and have @function
 433 * as constructor
 434 */
 435void qos_node_create_machine(const char *name, QOSCreateMachineFunc function);
 436
 437/**
 438 * qos_node_create_machine_args(): same as qos_node_create_machine,
 439 * but with the possibility to add an optional ", @opts" after -M machine
 440 * command line.
 441 */
 442void qos_node_create_machine_args(const char *name,
 443                                  QOSCreateMachineFunc function,
 444                                  const char *opts);
 445
 446/**
 447 * qos_node_create_driver(): creates the driver @name and
 448 * adds it to the node hash table.
 449 *
 450 * This node will be of type QNODE_DRIVER and have @function
 451 * as constructor
 452 */
 453void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
 454
 455/**
 456 * qos_node_contains(): creates one or more edges of type QEDGE_CONTAINS
 457 * and adds them to the edge list mapped to @container in the
 458 * edge hash table.
 459 *
 460 * The edges will have @container as source and @contained as destination.
 461 *
 462 * If @opts is NULL, a single edge will be added with no options.
 463 * If @opts is non-NULL, the arguments after @contained represent a
 464 * NULL-terminated list of %QOSGraphEdgeOptions structs, and an
 465 * edge will be added for each of them.
 466 *
 467 * This function can be useful when there are multiple devices
 468 * with the same node name contained in a machine/other node
 469 *
 470 * For example, if "arm/raspi2" contains 2 "generic-sdhci"
 471 * devices, the right commands will be:
 472 * qos_node_create_machine("arm/raspi2");
 473 * qos_node_create_driver("generic-sdhci", constructor);
 474 * //assume rest of the fields are set NULL
 475 * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
 476 * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
 477 * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
 478 *
 479 * Of course this also requires that the @container's get_device function
 480 * should implement a case for "emmc" and "sdcard".
 481 *
 482 * For contains, op1.arg and op1.size_arg represent the arg to pass
 483 * to @contained constructor to properly initialize it.
 484 */
 485void qos_node_contains(const char *container, const char *contained,
 486                       QOSGraphEdgeOptions *opts, ...);
 487
 488/**
 489 * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and
 490 * adds it to the edge list mapped to @producer in the
 491 * edge hash table.
 492 *
 493 * This edge will have @producer as source and @interface as destination.
 494 */
 495void qos_node_produces(const char *producer, const char *interface);
 496
 497/**
 498 * qos_node_consumes():  creates an edge of type QEDGE_CONSUMED_BY and
 499 * adds it to the edge list mapped to @interface in the
 500 * edge hash table.
 501 *
 502 * This edge will have @interface as source and @consumer as destination.
 503 * It also has the possibility to add an optional @opts
 504 * (see %QOSGraphEdgeOptions)
 505 */
 506void qos_node_consumes(const char *consumer, const char *interface,
 507                       QOSGraphEdgeOptions *opts);
 508
 509/**
 510 * qos_invalidate_command_line(): invalidates current command line, so that
 511 * qgraph framework cannot try to cache the current command line and
 512 * forces QEMU to restart.
 513 */
 514void qos_invalidate_command_line(void);
 515
 516/**
 517 * qos_get_current_command_line(): return the command line required by the
 518 * machine and driver objects.  This is the same string that was passed to
 519 * the test's "before" callback, if any.
 520 */
 521const char *qos_get_current_command_line(void);
 522
 523/**
 524 * qos_allocate_objects():
 525 * @qts: The #QTestState that will be referred to by the machine object.
 526 * @alloc: Where to store the allocator for the machine object, or %NULL.
 527 *
 528 * Allocate driver objects for the current test
 529 * path, but relative to the QTestState @qts.
 530 *
 531 * Returns a test object just like the one that was passed to
 532 * the test function, but relative to @qts.
 533 */
 534void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc);
 535
 536/**
 537 * qos_object_destroy(): calls the destructor for @obj
 538 */
 539void qos_object_destroy(QOSGraphObject *obj);
 540
 541/**
 542 * qos_object_queue_destroy(): queue the destructor for @obj so that it is
 543 * called at the end of the test
 544 */
 545void qos_object_queue_destroy(QOSGraphObject *obj);
 546
 547/**
 548 * qos_object_start_hw(): calls the start_hw function for @obj
 549 */
 550void qos_object_start_hw(QOSGraphObject *obj);
 551
 552/**
 553 * qos_machine_new(): instantiate a new machine node
 554 * @node: A machine node to be instantiated
 555 * @qts: The #QTestState that will be referred to by the machine object.
 556 *
 557 * Returns a machine object.
 558 */
 559QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts);
 560
 561/**
 562 * qos_machine_new(): instantiate a new driver node
 563 * @node: A driver node to be instantiated
 564 * @parent: A #QOSGraphObject to be consumed by the new driver node
 565 * @alloc: An allocator to be used by the new driver node.
 566 * @arg: The argument for the consumed-by edge to @node.
 567 *
 568 * Calls the constructor for the driver object.
 569 */
 570QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
 571                               QGuestAllocator *alloc, void *arg);
 572
 573
 574#endif
 575