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.1 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 29 typedef struct QOSGraphObject QOSGraphObject; 30 typedef struct QOSGraphNode QOSGraphNode; 31 typedef struct QOSGraphEdge QOSGraphEdge; 32 typedef struct QOSGraphNodeOptions QOSGraphNodeOptions; 33 typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions; 34 typedef struct QOSGraphTestOptions QOSGraphTestOptions; 35 36 /* Constructor for drivers, machines and test */ 37 typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc, 38 void *addr); 39 typedef void *(*QOSCreateMachineFunc) (QTestState *qts); 40 typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc); 41 42 /* QOSGraphObject functions */ 43 typedef void *(*QOSGetDriver) (void *object, const char *interface); 44 typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name); 45 typedef void (*QOSDestructorFunc) (QOSGraphObject *object); 46 typedef void (*QOSStartFunct) (QOSGraphObject *object); 47 48 /* Test options functions */ 49 typedef 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 "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 "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=./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 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 */ 290 struct 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 */ 330 struct 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 */ 375 struct 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 */ 393 void qos_graph_init(void); 394 395 /** 396 * qos_graph_destroy(): deallocates all the hash tables, 397 * freeing all nodes and edges. 398 */ 399 void qos_graph_destroy(void); 400 401 /** 402 * qos_node_destroy(): removes and frees a node from the, 403 * nodes hash table. 404 */ 405 void qos_node_destroy(void *key); 406 407 /** 408 * qos_edge_destroy(): removes and frees an edge from the, 409 * edges hash table. 410 */ 411 void 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 */ 424 void 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 */ 435 void 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 */ 442 void 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 */ 453 void 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 */ 485 void 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 */ 495 void 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 */ 506 void 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 */ 514 void 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 */ 521 const 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 */ 534 void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc); 535 536 /** 537 * qos_object_destroy(): calls the destructor for @obj 538 */ 539 void 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 */ 545 void qos_object_queue_destroy(QOSGraphObject *obj); 546 547 /** 548 * qos_object_start_hw(): calls the start_hw function for @obj 549 */ 550 void 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 */ 559 QOSGraphObject *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 */ 570 QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, 571 QGuestAllocator *alloc, void *arg); 572 573 574 #endif 575