aboutsummaryrefslogtreecommitdiff
path: root/docs/devel/qgraph.rst
diff options
context:
space:
mode:
authorEmanuele Giuseppe Esposito <eesposit@redhat.com>2021-03-08 08:32:40 +0100
committerThomas Huth <thuth@redhat.com>2021-03-09 11:26:31 +0100
commit222455ef814935b1ecc6c9a75acbc0e97e31d8a7 (patch)
tree2abbd4a63cdd4967ecc01da343b343978c69541b /docs/devel/qgraph.rst
parent6179f32eeb6b13574ef65c85f836c681d213e577 (diff)
downloadqemu-222455ef814935b1ecc6c9a75acbc0e97e31d8a7.zip
qemu-222455ef814935b1ecc6c9a75acbc0e97e31d8a7.tar.gz
qemu-222455ef814935b1ecc6c9a75acbc0e97e31d8a7.tar.bz2
libqos/qgraph: format qgraph comments for sphinx documentation
Change documentation style and fix minor typos in tests/qtest/libqos/qgraph.h to automatically generate sphinx documentation in docs/devel/qgraph.rst The mechanism explanation that once was in qgraph.h is now moved to qgraph.rst There is no functional change intended. Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> Message-Id: <20210308073240.6363-1-eesposit@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
Diffstat (limited to 'docs/devel/qgraph.rst')
-rw-r--r--docs/devel/qgraph.rst261
1 files changed, 261 insertions, 0 deletions
diff --git a/docs/devel/qgraph.rst b/docs/devel/qgraph.rst
new file mode 100644
index 0000000..62a45cb
--- /dev/null
+++ b/docs/devel/qgraph.rst
@@ -0,0 +1,261 @@
+.. _qgraph:
+
+========================================
+Qtest Driver Framework
+========================================
+
+This Qgraph API provides all basic functions to create a graph
+and instantiate nodes representing machines, drivers and tests
+representing their relations with ``CONSUMES``, ``PRODUCES``, and
+``CONTAINS`` edges.
+
+The idea is to have a framework where each test asks for a specific
+driver, and the framework takes care of allocating the proper devices
+required and passing the correct command line arguments to QEMU.
+
+Nodes
+^^^^^^
+
+A node can be of four types:
+
+- **QNODE_MACHINE**: for example ``arm/raspi2``
+- **QNODE_DRIVER**: for example ``generic-sdhci``
+- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci``
+ drivers).
+ An interface is not explicitly created, it will be automatically
+ instantiated when a node consumes or produces it.
+- **QNODE_TEST**: for example ``sdhci-test``, consumes an interface and
+ tests the functions provided.
+
+Notes for the nodes:
+
+- QNODE_MACHINE: each machine struct must have a ``QGuestAllocator`` and
+ implement ``get_driver()`` to return the allocator mapped to the interface
+ "memory". The function can also return ``NULL`` if the allocator
+ is not set.
+- QNODE_DRIVER: driver names must be unique, and machines and nodes
+ planned to be "consumed" by other nodes must match QEMU
+ drivers name, otherwise they won't be discovered
+
+Edges
+^^^^^^
+
+An edge relation between two nodes (drivers or machines) `X` and `Y` can be:
+
+- ``X CONSUMES Y``: `Y` can be plugged into `X`
+- ``X PRODUCES Y``: `X` provides the interface `Y`
+- ``X CONTAINS Y``: `Y` is part of `X` component
+
+Execution steps
+^^^^^^^^^^^^^^^
+
+The basic framework steps are the following:
+
+- All nodes and edges are created in their respective
+ machine/driver/test files
+- The framework starts QEMU and asks for a list of available devices
+ and machines (note that only machines and "consumed" nodes are mapped
+ 1:1 with QEMU devices)
+- The framework walks the graph starting from the available machines and
+ performs a Depth First Search for tests
+- Once a test is found, the path is walked again and all drivers are
+ allocated accordingly and the final interface is passed to the test
+- The test is executed
+- Unused objects are cleaned and the path discovery is continued
+
+Depending on the QEMU binary used, only some drivers/machines will be
+available and only test that are reached by them will be executed.
+
+Creating a new driver and its interface
+"""""""""""""""""""""""""""""""""""""""""
+
+.. code::
+
+ #include "qgraph.h"
+
+ struct My_driver {
+ QOSGraphObject obj;
+ Node_produced prod;
+ Node_contained cont;
+ }
+
+ static void my_destructor(QOSGraphObject *obj)
+ {
+ g_free(obj);
+ }
+
+ static void *my_get_driver(void *object, const char *interface) {
+ My_driver *dev = object;
+ if (!g_strcmp0(interface, "my_interface")) {
+ return &dev->prod;
+ }
+ abort();
+ }
+
+ static void *my_get_device(void *object, const char *device) {
+ My_driver *dev = object;
+ if (!g_strcmp0(device, "my_driver_contained")) {
+ return &dev->cont;
+ }
+ abort();
+ }
+
+ static void *my_driver_constructor(void *node_consumed,
+ QOSGraphObject *alloc)
+ {
+ My_driver dev = g_new(My_driver, 1);
+
+ // get the node pointed by the produce edge
+ dev->obj.get_driver = my_get_driver;
+
+ // get the node pointed by the contains
+ dev->obj.get_device = my_get_device;
+
+ // free the object
+ dev->obj.destructor = my_destructor;
+
+ do_something_with_node_consumed(node_consumed);
+
+ // set all fields of contained device
+ init_contained_device(&dev->cont);
+ return &dev->obj;
+ }
+
+ static void register_my_driver(void)
+ {
+ qos_node_create_driver("my_driver", my_driver_constructor);
+
+ // contained drivers don't need a constructor,
+ // they will be init by the parent.
+ qos_node_create_driver("my_driver_contained", NULL);
+
+ // For the sake of this example, assume machine x86_64/pc
+ // contains "other_node".
+ // This relation, along with the machine and "other_node"
+ // creation, should be defined in the x86_64_pc-machine.c file.
+ // "my_driver" will then consume "other_node"
+ qos_node_contains("my_driver", "my_driver_contained");
+ qos_node_produces("my_driver", "my_interface");
+ qos_node_consumes("my_driver", "other_node");
+ }
+
+In the above example, all possible types of relations are created:
+node "my_driver" consumes, contains and produces other nodes.
+More specifically::
+
+ x86_64/pc -->contains--> other_node <--consumes-- my_driver
+ |
+ my_driver_contained <--contains--+
+ |
+ my_interface <--produces--+
+
+or inverting the consumes edge in consumed_by::
+
+ x86_64/pc -->contains--> other_node --consumed_by--> my_driver
+ |
+ my_driver_contained <--contains--+
+ |
+ my_interface <--produces--+
+
+Creating new test
+"""""""""""""""""
+
+.. code::
+
+ #include "qgraph.h"
+
+ static void my_test_function(void *obj, void *data)
+ {
+ Node_produced *interface_to_test = obj;
+ // test interface_to_test
+ }
+
+ static void register_my_test(void)
+ {
+ qos_add_test("my_interface", "my_test", my_test_function);
+ }
+
+ libqos_init(register_my_test);
+
+Here a new test is created, consuming "my_interface" node
+and creating a valid path from a machine to a test.
+Final graph will be like this::
+
+ x86_64/pc --contains--> other_node <--consumes-- my_driver
+ |
+ my_driver_contained <--contains--+
+ |
+ my_test --consumes--> my_interface <--produces--+
+
+or inverting the consumes edge in consumed_by::
+
+ x86_64/pc --contains--> other_node --consumed_by--> my_driver
+ |
+ my_driver_contained <--contains--+
+ |
+ my_test <--consumed_by-- my_interface <--produces--+
+
+Assuming there the binary is
+``QTEST_QEMU_BINARY=./qemu-system-x86_64``
+a valid test path will be:
+``/x86_64/pc/other_node/my_driver/my_interface/my_test``.
+
+Additional examples are also in ``test-qgraph.c``
+
+Command line:
+""""""""""""""
+
+Command line is built by using node names and optional arguments
+passed by the user when building the edges.
+
+There are three types of command line arguments:
+
+- ``in node`` : created from the node name. For example, machines will
+ have ``-M <machine>`` to its command line, while devices
+ ``-device <device>``. It is automatically done by the framework.
+- ``after node`` : added as additional argument to the node name.
+ This argument is added optionally when creating edges,
+ by setting the parameter @after_cmd_line and
+ @extra_edge_opts in #QOSGraphEdgeOptions.
+ The framework automatically adds
+ a comma before @extra_edge_opts,
+ because it is going to add attributes
+ after the destination node pointed by
+ the edge containing these options, and automatically
+ adds a space before @after_cmd_line, because it
+ adds an additional device, not an attribute.
+- ``before node`` : added as additional argument to the node name.
+ This argument is added optionally when creating edges,
+ by setting the parameter @before_cmd_line in
+ #QOSGraphEdgeOptions. This attribute
+ is going to add attributes before the destination node
+ pointed by the edge containing these options. It is
+ helpful to commands that are not node-representable,
+ such as ``-fdsev`` or ``-netdev``.
+
+While adding command line in edges is always used, not all nodes names are
+used in every path walk: this is because the contained or produced ones
+are already added by QEMU, so only nodes that "consumes" will be used to
+build the command line. Also, nodes that will have ``{ "abstract" : true }``
+as QMP attribute will loose their command line, since they are not proper
+devices to be added in QEMU.
+
+Example::
+
+ QOSGraphEdgeOptions opts = {
+ .arg = NULL,
+ .size_arg = 0,
+ .after_cmd_line = "-device other",
+ .before_cmd_line = "-netdev something",
+ .extra_edge_opts = "addr=04.0",
+ };
+ QOSGraphNodeS *node = qos_node_create_driver("my_node", constructor);
+ qos_node_consumes_args("my_node", "interface", &opts);
+
+Will produce the following command line:
+``-netdev something -device my_node,addr=04.0 -device other``
+
+Qgraph API reference
+^^^^^^^^^^^^^^^^^^^^
+
+.. kernel-doc:: tests/qtest/libqos/qgraph.h