]> git.proxmox.com Git - mirror_qemu.git/blob - tests/libqos/qgraph.h
ef0c73837a60b3f5b427a255d63d937e17aaa117
[mirror_qemu.git] / tests / libqos / qgraph.h
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
33 typedef struct QOSGraphObject QOSGraphObject;
34 typedef struct QOSGraphNode QOSGraphNode;
35 typedef struct QOSGraphEdge QOSGraphEdge;
36 typedef struct QOSGraphNodeOptions QOSGraphNodeOptions;
37 typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions;
38 typedef struct QOSGraphTestOptions QOSGraphTestOptions;
39
40 /* Constructor for drivers, machines and test */
41 typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc,
42 void *addr);
43 typedef void *(*QOSCreateMachineFunc) (QTestState *qts);
44 typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc);
45
46 /* QOSGraphObject functions */
47 typedef void *(*QOSGetDriver) (void *object, const char *interface);
48 typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name);
49 typedef void (*QOSDestructorFunc) (QOSGraphObject *object);
50 typedef void (*QOSStartFunct) (QOSGraphObject *object);
51
52 /* Test options functions */
53 typedef 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 */
294 struct 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 */
334 struct 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 */
379 struct 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 */
397 void qos_graph_init(void);
398
399 /**
400 * qos_graph_destroy(): deallocates all the hash tables,
401 * freeing all nodes and edges.
402 */
403 void qos_graph_destroy(void);
404
405 /**
406 * qos_node_destroy(): removes and frees a node from the,
407 * nodes hash table.
408 */
409 void qos_node_destroy(void *key);
410
411 /**
412 * qos_edge_destroy(): removes and frees an edge from the,
413 * edges hash table.
414 */
415 void 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 */
428 void 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 */
439 void 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 */
446 void 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 */
457 void 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 */
487 void 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 */
496 void 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 */
507 void 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 */
515 void 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 */
522 const 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 */
535 void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc);
536
537 /**
538 * qos_object_destroy(): calls the destructor for @obj
539 */
540 void 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 */
546 void qos_object_queue_destroy(QOSGraphObject *obj);
547
548 /**
549 * qos_object_start_hw(): calls the start_hw function for @obj
550 */
551 void 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 */
560 QOSGraphObject *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 */
571 QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
572 QGuestAllocator *alloc, void *arg);
573
574
575 #endif