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