3 ========================================
5 ========================================
7 This Qgraph API provides all basic functions to create a graph
8 and instantiate nodes representing machines, drivers and tests
9 representing their relations with ``CONSUMES``, ``PRODUCES``, and
12 The idea is to have a framework where each test asks for a specific
13 driver, and the framework takes care of allocating the proper devices
14 required and passing the correct command line arguments to QEMU.
19 A node can be of four types:
21 - **QNODE_MACHINE**: for example ``arm/raspi2``
22 - **QNODE_DRIVER**: for example ``generic-sdhci``
23 - **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci``
25 An interface is not explicitly created, it will be automatically
26 instantiated when a node consumes or produces it.
27 - **QNODE_TEST**: for example ``sdhci-test``, consumes an interface and
28 tests the functions provided.
32 - QNODE_MACHINE: each machine struct must have a ``QGuestAllocator`` and
33 implement ``get_driver()`` to return the allocator mapped to the interface
34 "memory". The function can also return ``NULL`` if the allocator
36 - QNODE_DRIVER: driver names must be unique, and machines and nodes
37 planned to be "consumed" by other nodes must match QEMU
38 drivers name, otherwise they won't be discovered
43 An edge relation between two nodes (drivers or machines) `X` and `Y` can be:
45 - ``X CONSUMES Y``: `Y` can be plugged into `X`
46 - ``X PRODUCES Y``: `X` provides the interface `Y`
47 - ``X CONTAINS Y``: `Y` is part of `X` component
52 The basic framework steps are the following:
54 - All nodes and edges are created in their respective
55 machine/driver/test files
56 - The framework starts QEMU and asks for a list of available devices
57 and machines (note that only machines and "consumed" nodes are mapped
58 1:1 with QEMU devices)
59 - The framework walks the graph starting from the available machines and
60 performs a Depth First Search for tests
61 - Once a test is found, the path is walked again and all drivers are
62 allocated accordingly and the final interface is passed to the test
63 - The test is executed
64 - Unused objects are cleaned and the path discovery is continued
66 Depending on the QEMU binary used, only some drivers/machines will be
67 available and only test that are reached by them will be executed.
69 Creating a new driver and its interface
70 """""""""""""""""""""""""""""""""""""""""
82 static void my_destructor(QOSGraphObject *obj)
87 static void *my_get_driver(void *object, const char *interface) {
88 My_driver *dev = object;
89 if (!g_strcmp0(interface, "my_interface")) {
95 static void *my_get_device(void *object, const char *device) {
96 My_driver *dev = object;
97 if (!g_strcmp0(device, "my_driver_contained")) {
103 static void *my_driver_constructor(void *node_consumed,
104 QOSGraphObject *alloc)
106 My_driver dev = g_new(My_driver, 1);
108 // get the node pointed by the produce edge
109 dev->obj.get_driver = my_get_driver;
111 // get the node pointed by the contains
112 dev->obj.get_device = my_get_device;
115 dev->obj.destructor = my_destructor;
117 do_something_with_node_consumed(node_consumed);
119 // set all fields of contained device
120 init_contained_device(&dev->cont);
124 static void register_my_driver(void)
126 qos_node_create_driver("my_driver", my_driver_constructor);
128 // contained drivers don't need a constructor,
129 // they will be init by the parent.
130 qos_node_create_driver("my_driver_contained", NULL);
132 // For the sake of this example, assume machine x86_64/pc
133 // contains "other_node".
134 // This relation, along with the machine and "other_node"
135 // creation, should be defined in the x86_64_pc-machine.c file.
136 // "my_driver" will then consume "other_node"
137 qos_node_contains("my_driver", "my_driver_contained");
138 qos_node_produces("my_driver", "my_interface");
139 qos_node_consumes("my_driver", "other_node");
142 In the above example, all possible types of relations are created:
143 node "my_driver" consumes, contains and produces other nodes.
146 x86_64/pc -->contains--> other_node <--consumes-- my_driver
148 my_driver_contained <--contains--+
150 my_interface <--produces--+
152 or inverting the consumes edge in consumed_by::
154 x86_64/pc -->contains--> other_node --consumed_by--> my_driver
156 my_driver_contained <--contains--+
158 my_interface <--produces--+
167 static void my_test_function(void *obj, void *data)
169 Node_produced *interface_to_test = obj;
170 // test interface_to_test
173 static void register_my_test(void)
175 qos_add_test("my_interface", "my_test", my_test_function);
178 libqos_init(register_my_test);
180 Here a new test is created, consuming "my_interface" node
181 and creating a valid path from a machine to a test.
182 Final graph will be like this::
184 x86_64/pc --contains--> other_node <--consumes-- my_driver
186 my_driver_contained <--contains--+
188 my_test --consumes--> my_interface <--produces--+
190 or inverting the consumes edge in consumed_by::
192 x86_64/pc --contains--> other_node --consumed_by--> my_driver
194 my_driver_contained <--contains--+
196 my_test <--consumed_by-- my_interface <--produces--+
198 Assuming there the binary is
199 ``QTEST_QEMU_BINARY=./qemu-system-x86_64``
200 a valid test path will be:
201 ``/x86_64/pc/other_node/my_driver/my_interface/my_test``.
203 Additional examples are also in ``test-qgraph.c``
208 Command line is built by using node names and optional arguments
209 passed by the user when building the edges.
211 There are three types of command line arguments:
213 - ``in node`` : created from the node name. For example, machines will
214 have ``-M <machine>`` to its command line, while devices
215 ``-device <device>``. It is automatically done by the framework.
216 - ``after node`` : added as additional argument to the node name.
217 This argument is added optionally when creating edges,
218 by setting the parameter @after_cmd_line and
219 @extra_edge_opts in #QOSGraphEdgeOptions.
220 The framework automatically adds
221 a comma before @extra_edge_opts,
222 because it is going to add attributes
223 after the destination node pointed by
224 the edge containing these options, and automatically
225 adds a space before @after_cmd_line, because it
226 adds an additional device, not an attribute.
227 - ``before node`` : added as additional argument to the node name.
228 This argument is added optionally when creating edges,
229 by setting the parameter @before_cmd_line in
230 #QOSGraphEdgeOptions. This attribute
231 is going to add attributes before the destination node
232 pointed by the edge containing these options. It is
233 helpful to commands that are not node-representable,
234 such as ``-fdsev`` or ``-netdev``.
236 While adding command line in edges is always used, not all nodes names are
237 used in every path walk: this is because the contained or produced ones
238 are already added by QEMU, so only nodes that "consumes" will be used to
239 build the command line. Also, nodes that will have ``{ "abstract" : true }``
240 as QMP attribute will loose their command line, since they are not proper
241 devices to be added in QEMU.
245 QOSGraphEdgeOptions opts = {
248 .after_cmd_line = "-device other",
249 .before_cmd_line = "-netdev something",
250 .extra_edge_opts = "addr=04.0",
252 QOSGraphNodeS *node = qos_node_create_driver("my_node", constructor);
253 qos_node_consumes_args("my_node", "interface", &opts);
255 Will produce the following command line:
256 ``-netdev something -device my_node,addr=04.0 -device other``
261 .. kernel-doc:: tests/qtest/libqos/qgraph.h