1*ff41da50SThomas Huth.. _qgraph: 2*ff41da50SThomas Huth 3*ff41da50SThomas HuthQtest Driver Framework 4*ff41da50SThomas Huth====================== 5*ff41da50SThomas Huth 6*ff41da50SThomas HuthIn order to test a specific driver, plain libqos tests need to 7*ff41da50SThomas Huthtake care of booting QEMU with the right machine and devices. 8*ff41da50SThomas HuthThis makes each test "hardcoded" for a specific configuration, reducing 9*ff41da50SThomas Huththe possible coverage that it can reach. 10*ff41da50SThomas Huth 11*ff41da50SThomas HuthFor example, the sdhci device is supported on both x86_64 and ARM boards, 12*ff41da50SThomas Huththerefore a generic sdhci test should test all machines and drivers that 13*ff41da50SThomas Huthsupport that device. 14*ff41da50SThomas HuthUsing only libqos APIs, the test has to manually take care of 15*ff41da50SThomas Huthcovering all the setups, and build the correct command line. 16*ff41da50SThomas Huth 17*ff41da50SThomas HuthThis also introduces backward compatibility issues: if a device/driver command 18*ff41da50SThomas Huthline name is changed, all tests that use that will not work 19*ff41da50SThomas Huthproperly anymore and need to be adjusted. 20*ff41da50SThomas Huth 21*ff41da50SThomas HuthThe aim of qgraph is to create a graph of drivers, machines and tests such that 22*ff41da50SThomas Hutha test aimed to a certain driver does not have to care of 23*ff41da50SThomas Huthbooting the right QEMU machine, pick the right device, build the command line 24*ff41da50SThomas Huthand so on. Instead, it only defines what type of device it is testing 25*ff41da50SThomas Huth(interface in qgraph terms) and the framework takes care of 26*ff41da50SThomas Huthcovering all supported types of devices and machine architectures. 27*ff41da50SThomas Huth 28*ff41da50SThomas HuthFollowing the above example, an interface would be ``sdhci``, 29*ff41da50SThomas Huthso the sdhci-test should only care of linking its qgraph node with 30*ff41da50SThomas Huththat interface. In this way, if the command line of a sdhci driver 31*ff41da50SThomas Huthis changed, only the respective qgraph driver node has to be adjusted. 32*ff41da50SThomas Huth 33*ff41da50SThomas HuthQGraph concepts 34*ff41da50SThomas Huth--------------- 35*ff41da50SThomas Huth 36*ff41da50SThomas HuthThe graph is composed by nodes that represent machines, drivers, tests 37*ff41da50SThomas Huthand edges that define the relationships between them (``CONSUMES``, ``PRODUCES``, and 38*ff41da50SThomas Huth``CONTAINS``). 39*ff41da50SThomas Huth 40*ff41da50SThomas HuthNodes 41*ff41da50SThomas Huth~~~~~ 42*ff41da50SThomas Huth 43*ff41da50SThomas HuthA node can be of four types: 44*ff41da50SThomas Huth 45*ff41da50SThomas Huth- **QNODE_MACHINE**: for example ``arm/raspi2b`` 46*ff41da50SThomas Huth- **QNODE_DRIVER**: for example ``generic-sdhci`` 47*ff41da50SThomas Huth- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci`` 48*ff41da50SThomas Huth drivers). 49*ff41da50SThomas Huth An interface is not explicitly created, it will be automatically 50*ff41da50SThomas Huth instantiated when a node consumes or produces it. 51*ff41da50SThomas Huth An interface is simply a struct that abstracts the various drivers 52*ff41da50SThomas Huth for the same type of device, and offers an API to the nodes that 53*ff41da50SThomas Huth use it ("consume" relation in qgraph terms) that is implemented/backed up by the drivers that implement it ("produce" relation in qgraph terms). 54*ff41da50SThomas Huth- **QNODE_TEST**: for example ``sdhci-test``. A test consumes an interface 55*ff41da50SThomas Huth and tests the functions provided by it. 56*ff41da50SThomas Huth 57*ff41da50SThomas HuthNotes for the nodes: 58*ff41da50SThomas Huth 59*ff41da50SThomas Huth- QNODE_MACHINE: each machine struct must have a ``QGuestAllocator`` and 60*ff41da50SThomas Huth implement ``get_driver()`` to return the allocator mapped to the interface 61*ff41da50SThomas Huth "memory". The function can also return ``NULL`` if the allocator 62*ff41da50SThomas Huth is not set. 63*ff41da50SThomas Huth- QNODE_DRIVER: driver names must be unique, and machines and nodes 64*ff41da50SThomas Huth planned to be "consumed" by other nodes must match QEMU 65*ff41da50SThomas Huth drivers name, otherwise they won't be discovered 66*ff41da50SThomas Huth 67*ff41da50SThomas HuthEdges 68*ff41da50SThomas Huth~~~~~ 69*ff41da50SThomas Huth 70*ff41da50SThomas HuthAn edge relation between two nodes (drivers or machines) ``X`` and ``Y`` can be: 71*ff41da50SThomas Huth 72*ff41da50SThomas Huth- ``X CONSUMES Y``: ``Y`` can be plugged into ``X`` 73*ff41da50SThomas Huth- ``X PRODUCES Y``: ``X`` provides the interface ``Y`` 74*ff41da50SThomas Huth- ``X CONTAINS Y``: ``Y`` is part of ``X`` component 75*ff41da50SThomas Huth 76*ff41da50SThomas HuthExecution steps 77*ff41da50SThomas Huth~~~~~~~~~~~~~~~ 78*ff41da50SThomas Huth 79*ff41da50SThomas HuthThe basic framework steps are the following: 80*ff41da50SThomas Huth 81*ff41da50SThomas Huth- All nodes and edges are created in their respective 82*ff41da50SThomas Huth machine/driver/test files 83*ff41da50SThomas Huth- The framework starts QEMU and asks for a list of available devices 84*ff41da50SThomas Huth and machines (note that only machines and "consumed" nodes are mapped 85*ff41da50SThomas Huth 1:1 with QEMU devices) 86*ff41da50SThomas Huth- The framework walks the graph starting from the available machines and 87*ff41da50SThomas Huth performs a Depth First Search for tests 88*ff41da50SThomas Huth- Once a test is found, the path is walked again and all drivers are 89*ff41da50SThomas Huth allocated accordingly and the final interface is passed to the test 90*ff41da50SThomas Huth- The test is executed 91*ff41da50SThomas Huth- Unused objects are cleaned and the path discovery is continued 92*ff41da50SThomas Huth 93*ff41da50SThomas HuthDepending on the QEMU binary used, only some drivers/machines will be 94*ff41da50SThomas Huthavailable and only test that are reached by them will be executed. 95*ff41da50SThomas Huth 96*ff41da50SThomas HuthCommand line 97*ff41da50SThomas Huth~~~~~~~~~~~~ 98*ff41da50SThomas Huth 99*ff41da50SThomas HuthCommand line is built by using node names and optional arguments 100*ff41da50SThomas Huthpassed by the user when building the edges. 101*ff41da50SThomas Huth 102*ff41da50SThomas HuthThere are three types of command line arguments: 103*ff41da50SThomas Huth 104*ff41da50SThomas Huth- ``in node`` : created from the node name. For example, machines will 105*ff41da50SThomas Huth have ``-M <machine>`` to its command line, while devices 106*ff41da50SThomas Huth ``-device <device>``. It is automatically done by the framework. 107*ff41da50SThomas Huth- ``after node`` : added as additional argument to the node name. 108*ff41da50SThomas Huth This argument is added optionally when creating edges, 109*ff41da50SThomas Huth by setting the parameter ``after_cmd_line`` and 110*ff41da50SThomas Huth ``extra_edge_opts`` in ``QOSGraphEdgeOptions``. 111*ff41da50SThomas Huth The framework automatically adds 112*ff41da50SThomas Huth a comma before ``extra_edge_opts``, 113*ff41da50SThomas Huth because it is going to add attributes 114*ff41da50SThomas Huth after the destination node pointed by 115*ff41da50SThomas Huth the edge containing these options, and automatically 116*ff41da50SThomas Huth adds a space before ``after_cmd_line``, because it 117*ff41da50SThomas Huth adds an additional device, not an attribute. 118*ff41da50SThomas Huth- ``before node`` : added as additional argument to the node name. 119*ff41da50SThomas Huth This argument is added optionally when creating edges, 120*ff41da50SThomas Huth by setting the parameter ``before_cmd_line`` in 121*ff41da50SThomas Huth ``QOSGraphEdgeOptions``. This attribute 122*ff41da50SThomas Huth is going to add attributes before the destination node 123*ff41da50SThomas Huth pointed by the edge containing these options. It is 124*ff41da50SThomas Huth helpful to commands that are not node-representable, 125*ff41da50SThomas Huth such as ``-fdsev`` or ``-netdev``. 126*ff41da50SThomas Huth 127*ff41da50SThomas HuthWhile adding command line in edges is always used, not all nodes names are 128*ff41da50SThomas Huthused in every path walk: this is because the contained or produced ones 129*ff41da50SThomas Huthare already added by QEMU, so only nodes that "consumes" will be used to 130*ff41da50SThomas Huthbuild the command line. Also, nodes that will have ``{ "abstract" : true }`` 131*ff41da50SThomas Huthas QMP attribute will loose their command line, since they are not proper 132*ff41da50SThomas Huthdevices to be added in QEMU. 133*ff41da50SThomas Huth 134*ff41da50SThomas HuthExample:: 135*ff41da50SThomas Huth 136*ff41da50SThomas Huth QOSGraphEdgeOptions opts = { 137*ff41da50SThomas Huth .before_cmd_line = "-drive id=drv0,if=none,file=null-co://," 138*ff41da50SThomas Huth "file.read-zeroes=on,format=raw", 139*ff41da50SThomas Huth .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0", 140*ff41da50SThomas Huth 141*ff41da50SThomas Huth opts.extra_device_opts = "id=vs0"; 142*ff41da50SThomas Huth }; 143*ff41da50SThomas Huth 144*ff41da50SThomas Huth qos_node_create_driver("virtio-scsi-device", 145*ff41da50SThomas Huth virtio_scsi_device_create); 146*ff41da50SThomas Huth qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts); 147*ff41da50SThomas Huth 148*ff41da50SThomas HuthWill produce the following command line: 149*ff41da50SThomas Huth``-drive id=drv0,if=none,file=null-co://, -device virtio-scsi-device,id=vs0 -device scsi-hd,bus=vs0.0,drive=drv0`` 150*ff41da50SThomas Huth 151*ff41da50SThomas HuthTroubleshooting unavailable tests 152*ff41da50SThomas Huth~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 153*ff41da50SThomas Huth 154*ff41da50SThomas HuthIf there is no path from an available machine to a test then that test will be 155*ff41da50SThomas Huthunavailable and won't execute. This can happen if a test or driver did not set 156*ff41da50SThomas Huthup its qgraph node correctly. It can also happen if the necessary machine type 157*ff41da50SThomas Huthor device is missing from the QEMU binary because it was compiled out or 158*ff41da50SThomas Huthotherwise. 159*ff41da50SThomas Huth 160*ff41da50SThomas HuthIt is possible to troubleshoot unavailable tests by running:: 161*ff41da50SThomas Huth 162*ff41da50SThomas Huth $ QTEST_QEMU_BINARY=build/qemu-system-x86_64 build/tests/qtest/qos-test --verbose 163*ff41da50SThomas Huth # ALL QGRAPH EDGES: { 164*ff41da50SThomas Huth # src='virtio-net' 165*ff41da50SThomas Huth # |-> dest='virtio-net-tests/vhost-user/multiqueue' type=2 (node=0x559142109e30) 166*ff41da50SThomas Huth # |-> dest='virtio-net-tests/vhost-user/migrate' type=2 (node=0x559142109d00) 167*ff41da50SThomas Huth # src='virtio-net-pci' 168*ff41da50SThomas Huth # |-> dest='virtio-net' type=1 (node=0x55914210d740) 169*ff41da50SThomas Huth # src='pci-bus' 170*ff41da50SThomas Huth # |-> dest='virtio-net-pci' type=2 (node=0x55914210d880) 171*ff41da50SThomas Huth # src='pci-bus-pc' 172*ff41da50SThomas Huth # |-> dest='pci-bus' type=1 (node=0x559142103f40) 173*ff41da50SThomas Huth # src='i440FX-pcihost' 174*ff41da50SThomas Huth # |-> dest='pci-bus-pc' type=0 (node=0x55914210ac70) 175*ff41da50SThomas Huth # src='x86_64/pc' 176*ff41da50SThomas Huth # |-> dest='i440FX-pcihost' type=0 (node=0x5591421117f0) 177*ff41da50SThomas Huth # src='' 178*ff41da50SThomas Huth # |-> dest='x86_64/pc' type=0 (node=0x559142111600) 179*ff41da50SThomas Huth # |-> dest='arm/raspi2b' type=0 (node=0x559142110740) 180*ff41da50SThomas Huth ... 181*ff41da50SThomas Huth # } 182*ff41da50SThomas Huth # ALL QGRAPH NODES: { 183*ff41da50SThomas Huth # name='virtio-net-tests/announce-self' type=3 cmd_line='(null)' [available] 184*ff41da50SThomas Huth # name='arm/raspi2b' type=0 cmd_line='-M raspi2b ' [UNAVAILABLE] 185*ff41da50SThomas Huth ... 186*ff41da50SThomas Huth # } 187*ff41da50SThomas Huth 188*ff41da50SThomas HuthThe ``virtio-net-tests/announce-self`` test is listed as "available" in the 189*ff41da50SThomas Huth"ALL QGRAPH NODES" output. This means the test will execute. We can follow the 190*ff41da50SThomas Huthqgraph path in the "ALL QGRAPH EDGES" output as follows: '' -> 'x86_64/pc' -> 191*ff41da50SThomas Huth'i440FX-pcihost' -> 'pci-bus-pc' -> 'pci-bus' -> 'virtio-net-pci' -> 192*ff41da50SThomas Huth'virtio-net'. The root of the qgraph is '' and the depth first search begins 193*ff41da50SThomas Huththere. 194*ff41da50SThomas Huth 195*ff41da50SThomas HuthThe ``arm/raspi2b`` machine node is listed as "UNAVAILABLE". Although it is 196*ff41da50SThomas Huthreachable from the root via '' -> 'arm/raspi2b' the node is unavailable because 197*ff41da50SThomas Huththe QEMU binary did not list it when queried by the framework. This is expected 198*ff41da50SThomas Huthbecause we used the ``qemu-system-x86_64`` binary which does not support ARM 199*ff41da50SThomas Huthmachine types. 200*ff41da50SThomas Huth 201*ff41da50SThomas HuthIf a test is unexpectedly listed as "UNAVAILABLE", first check that the "ALL 202*ff41da50SThomas HuthQGRAPH EDGES" output reports edge connectivity from the root ('') to the test. 203*ff41da50SThomas HuthIf there is no connectivity then the qgraph nodes were not set up correctly and 204*ff41da50SThomas Huththe driver or test code is incorrect. If there is connectivity, check the 205*ff41da50SThomas Huthavailability of each node in the path in the "ALL QGRAPH NODES" output. The 206*ff41da50SThomas Huthfirst unavailable node in the path is the reason why the test is unavailable. 207*ff41da50SThomas HuthTypically this is because the QEMU binary lacks support for the necessary 208*ff41da50SThomas Huthmachine type or device. 209*ff41da50SThomas Huth 210*ff41da50SThomas HuthCreating a new driver and its interface 211*ff41da50SThomas Huth--------------------------------------- 212*ff41da50SThomas Huth 213*ff41da50SThomas HuthHere we continue the ``sdhci`` use case, with the following scenario: 214*ff41da50SThomas Huth 215*ff41da50SThomas Huth- ``sdhci-test`` aims to test the ``read[q,w], writeq`` functions 216*ff41da50SThomas Huth offered by the ``sdhci`` drivers. 217*ff41da50SThomas Huth- The current ``sdhci`` device is supported by both ``x86_64/pc`` and ``ARM`` 218*ff41da50SThomas Huth (in this example we focus on the ``arm-raspi2b``) machines. 219*ff41da50SThomas Huth- QEMU offers 2 types of drivers: ``QSDHCI_MemoryMapped`` for ``ARM`` and 220*ff41da50SThomas Huth ``QSDHCI_PCI`` for ``x86_64/pc``. Both implement the 221*ff41da50SThomas Huth ``read[q,w], writeq`` functions. 222*ff41da50SThomas Huth 223*ff41da50SThomas HuthIn order to implement such scenario in qgraph, the test developer needs to: 224*ff41da50SThomas Huth 225*ff41da50SThomas Huth- Create the ``x86_64/pc`` machine node. This machine uses the 226*ff41da50SThomas Huth ``pci-bus`` architecture so it ``contains`` a PCI driver, 227*ff41da50SThomas Huth ``pci-bus-pc``. The actual path is 228*ff41da50SThomas Huth 229*ff41da50SThomas Huth ``x86_64/pc --contains--> 1440FX-pcihost --contains--> 230*ff41da50SThomas Huth pci-bus-pc --produces--> pci-bus``. 231*ff41da50SThomas Huth 232*ff41da50SThomas Huth For the sake of this example, 233*ff41da50SThomas Huth we do not focus on the PCI interface implementation. 234*ff41da50SThomas Huth- Create the ``sdhci-pci`` driver node, representing ``QSDHCI_PCI``. 235*ff41da50SThomas Huth The driver uses the PCI bus (and its API), 236*ff41da50SThomas Huth so it must ``consume`` the ``pci-bus`` generic interface (which abstracts 237*ff41da50SThomas Huth all the pci drivers available) 238*ff41da50SThomas Huth 239*ff41da50SThomas Huth ``sdhci-pci --consumes--> pci-bus`` 240*ff41da50SThomas Huth- Create an ``arm/raspi2b`` machine node. This machine ``contains`` 241*ff41da50SThomas Huth a ``generic-sdhci`` memory mapped ``sdhci`` driver node, representing 242*ff41da50SThomas Huth ``QSDHCI_MemoryMapped``. 243*ff41da50SThomas Huth 244*ff41da50SThomas Huth ``arm/raspi2b --contains--> generic-sdhci`` 245*ff41da50SThomas Huth- Create the ``sdhci`` interface node. This interface offers the 246*ff41da50SThomas Huth functions that are shared by all ``sdhci`` devices. 247*ff41da50SThomas Huth The interface is produced by ``sdhci-pci`` and ``generic-sdhci``, 248*ff41da50SThomas Huth the available architecture-specific drivers. 249*ff41da50SThomas Huth 250*ff41da50SThomas Huth ``sdhci-pci --produces--> sdhci`` 251*ff41da50SThomas Huth 252*ff41da50SThomas Huth ``generic-sdhci --produces--> sdhci`` 253*ff41da50SThomas Huth- Create the ``sdhci-test`` test node. The test ``consumes`` the 254*ff41da50SThomas Huth ``sdhci`` interface, using its API. It doesn't need to look at 255*ff41da50SThomas Huth the supported machines or drivers. 256*ff41da50SThomas Huth 257*ff41da50SThomas Huth ``sdhci-test --consumes--> sdhci`` 258*ff41da50SThomas Huth 259*ff41da50SThomas Huth``arm-raspi2b`` machine, simplified from 260*ff41da50SThomas Huth``tests/qtest/libqos/arm-raspi2-machine.c``:: 261*ff41da50SThomas Huth 262*ff41da50SThomas Huth #include "qgraph.h" 263*ff41da50SThomas Huth 264*ff41da50SThomas Huth struct QRaspi2Machine { 265*ff41da50SThomas Huth QOSGraphObject obj; 266*ff41da50SThomas Huth QGuestAllocator alloc; 267*ff41da50SThomas Huth QSDHCI_MemoryMapped sdhci; 268*ff41da50SThomas Huth }; 269*ff41da50SThomas Huth 270*ff41da50SThomas Huth static void *raspi2_get_driver(void *object, const char *interface) 271*ff41da50SThomas Huth { 272*ff41da50SThomas Huth QRaspi2Machine *machine = object; 273*ff41da50SThomas Huth if (!g_strcmp0(interface, "memory")) { 274*ff41da50SThomas Huth return &machine->alloc; 275*ff41da50SThomas Huth } 276*ff41da50SThomas Huth 277*ff41da50SThomas Huth fprintf(stderr, "%s not present in arm/raspi2b\n", interface); 278*ff41da50SThomas Huth g_assert_not_reached(); 279*ff41da50SThomas Huth } 280*ff41da50SThomas Huth 281*ff41da50SThomas Huth static QOSGraphObject *raspi2_get_device(void *obj, 282*ff41da50SThomas Huth const char *device) 283*ff41da50SThomas Huth { 284*ff41da50SThomas Huth QRaspi2Machine *machine = obj; 285*ff41da50SThomas Huth if (!g_strcmp0(device, "generic-sdhci")) { 286*ff41da50SThomas Huth return &machine->sdhci.obj; 287*ff41da50SThomas Huth } 288*ff41da50SThomas Huth 289*ff41da50SThomas Huth fprintf(stderr, "%s not present in arm/raspi2b\n", device); 290*ff41da50SThomas Huth g_assert_not_reached(); 291*ff41da50SThomas Huth } 292*ff41da50SThomas Huth 293*ff41da50SThomas Huth static void *qos_create_machine_arm_raspi2(QTestState *qts) 294*ff41da50SThomas Huth { 295*ff41da50SThomas Huth QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1); 296*ff41da50SThomas Huth 297*ff41da50SThomas Huth alloc_init(&machine->alloc, ...); 298*ff41da50SThomas Huth 299*ff41da50SThomas Huth /* Get node(s) contained inside (CONTAINS) */ 300*ff41da50SThomas Huth machine->obj.get_device = raspi2_get_device; 301*ff41da50SThomas Huth 302*ff41da50SThomas Huth /* Get node(s) produced (PRODUCES) */ 303*ff41da50SThomas Huth machine->obj.get_driver = raspi2_get_driver; 304*ff41da50SThomas Huth 305*ff41da50SThomas Huth /* free the object */ 306*ff41da50SThomas Huth machine->obj.destructor = raspi2_destructor; 307*ff41da50SThomas Huth qos_init_sdhci_mm(&machine->sdhci, ...); 308*ff41da50SThomas Huth return &machine->obj; 309*ff41da50SThomas Huth } 310*ff41da50SThomas Huth 311*ff41da50SThomas Huth static void raspi2_register_nodes(void) 312*ff41da50SThomas Huth { 313*ff41da50SThomas Huth /* arm/raspi2b --contains--> generic-sdhci */ 314*ff41da50SThomas Huth qos_node_create_machine("arm/raspi2b", 315*ff41da50SThomas Huth qos_create_machine_arm_raspi2); 316*ff41da50SThomas Huth qos_node_contains("arm/raspi2b", "generic-sdhci", NULL); 317*ff41da50SThomas Huth } 318*ff41da50SThomas Huth 319*ff41da50SThomas Huth libqos_init(raspi2_register_nodes); 320*ff41da50SThomas Huth 321*ff41da50SThomas Huth``x86_64/pc`` machine, simplified from 322*ff41da50SThomas Huth``tests/qtest/libqos/x86_64_pc-machine.c``:: 323*ff41da50SThomas Huth 324*ff41da50SThomas Huth #include "qgraph.h" 325*ff41da50SThomas Huth 326*ff41da50SThomas Huth struct i440FX_pcihost { 327*ff41da50SThomas Huth QOSGraphObject obj; 328*ff41da50SThomas Huth QPCIBusPC pci; 329*ff41da50SThomas Huth }; 330*ff41da50SThomas Huth 331*ff41da50SThomas Huth struct QX86PCMachine { 332*ff41da50SThomas Huth QOSGraphObject obj; 333*ff41da50SThomas Huth QGuestAllocator alloc; 334*ff41da50SThomas Huth i440FX_pcihost bridge; 335*ff41da50SThomas Huth }; 336*ff41da50SThomas Huth 337*ff41da50SThomas Huth /* i440FX_pcihost */ 338*ff41da50SThomas Huth 339*ff41da50SThomas Huth static QOSGraphObject *i440FX_host_get_device(void *obj, 340*ff41da50SThomas Huth const char *device) 341*ff41da50SThomas Huth { 342*ff41da50SThomas Huth i440FX_pcihost *host = obj; 343*ff41da50SThomas Huth if (!g_strcmp0(device, "pci-bus-pc")) { 344*ff41da50SThomas Huth return &host->pci.obj; 345*ff41da50SThomas Huth } 346*ff41da50SThomas Huth fprintf(stderr, "%s not present in i440FX-pcihost\n", device); 347*ff41da50SThomas Huth g_assert_not_reached(); 348*ff41da50SThomas Huth } 349*ff41da50SThomas Huth 350*ff41da50SThomas Huth /* x86_64/pc machine */ 351*ff41da50SThomas Huth 352*ff41da50SThomas Huth static void *pc_get_driver(void *object, const char *interface) 353*ff41da50SThomas Huth { 354*ff41da50SThomas Huth QX86PCMachine *machine = object; 355*ff41da50SThomas Huth if (!g_strcmp0(interface, "memory")) { 356*ff41da50SThomas Huth return &machine->alloc; 357*ff41da50SThomas Huth } 358*ff41da50SThomas Huth 359*ff41da50SThomas Huth fprintf(stderr, "%s not present in x86_64/pc\n", interface); 360*ff41da50SThomas Huth g_assert_not_reached(); 361*ff41da50SThomas Huth } 362*ff41da50SThomas Huth 363*ff41da50SThomas Huth static QOSGraphObject *pc_get_device(void *obj, const char *device) 364*ff41da50SThomas Huth { 365*ff41da50SThomas Huth QX86PCMachine *machine = obj; 366*ff41da50SThomas Huth if (!g_strcmp0(device, "i440FX-pcihost")) { 367*ff41da50SThomas Huth return &machine->bridge.obj; 368*ff41da50SThomas Huth } 369*ff41da50SThomas Huth 370*ff41da50SThomas Huth fprintf(stderr, "%s not present in x86_64/pc\n", device); 371*ff41da50SThomas Huth g_assert_not_reached(); 372*ff41da50SThomas Huth } 373*ff41da50SThomas Huth 374*ff41da50SThomas Huth static void *qos_create_machine_pc(QTestState *qts) 375*ff41da50SThomas Huth { 376*ff41da50SThomas Huth QX86PCMachine *machine = g_new0(QX86PCMachine, 1); 377*ff41da50SThomas Huth 378*ff41da50SThomas Huth /* Get node(s) contained inside (CONTAINS) */ 379*ff41da50SThomas Huth machine->obj.get_device = pc_get_device; 380*ff41da50SThomas Huth 381*ff41da50SThomas Huth /* Get node(s) produced (PRODUCES) */ 382*ff41da50SThomas Huth machine->obj.get_driver = pc_get_driver; 383*ff41da50SThomas Huth 384*ff41da50SThomas Huth /* free the object */ 385*ff41da50SThomas Huth machine->obj.destructor = pc_destructor; 386*ff41da50SThomas Huth pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS); 387*ff41da50SThomas Huth 388*ff41da50SThomas Huth /* Get node(s) contained inside (CONTAINS) */ 389*ff41da50SThomas Huth machine->bridge.obj.get_device = i440FX_host_get_device; 390*ff41da50SThomas Huth 391*ff41da50SThomas Huth return &machine->obj; 392*ff41da50SThomas Huth } 393*ff41da50SThomas Huth 394*ff41da50SThomas Huth static void pc_machine_register_nodes(void) 395*ff41da50SThomas Huth { 396*ff41da50SThomas Huth /* x86_64/pc --contains--> 1440FX-pcihost --contains--> 397*ff41da50SThomas Huth * pci-bus-pc [--produces--> pci-bus (in pci.h)] */ 398*ff41da50SThomas Huth qos_node_create_machine("x86_64/pc", qos_create_machine_pc); 399*ff41da50SThomas Huth qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL); 400*ff41da50SThomas Huth 401*ff41da50SThomas Huth /* contained drivers don't need a constructor, 402*ff41da50SThomas Huth * they will be init by the parent */ 403*ff41da50SThomas Huth qos_node_create_driver("i440FX-pcihost", NULL); 404*ff41da50SThomas Huth qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL); 405*ff41da50SThomas Huth } 406*ff41da50SThomas Huth 407*ff41da50SThomas Huth libqos_init(pc_machine_register_nodes); 408*ff41da50SThomas Huth 409*ff41da50SThomas Huth``sdhci`` taken from ``tests/qtest/libqos/sdhci.c``:: 410*ff41da50SThomas Huth 411*ff41da50SThomas Huth /* Interface node, offers the sdhci API */ 412*ff41da50SThomas Huth struct QSDHCI { 413*ff41da50SThomas Huth uint16_t (*readw)(QSDHCI *s, uint32_t reg); 414*ff41da50SThomas Huth uint64_t (*readq)(QSDHCI *s, uint32_t reg); 415*ff41da50SThomas Huth void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val); 416*ff41da50SThomas Huth /* other fields */ 417*ff41da50SThomas Huth }; 418*ff41da50SThomas Huth 419*ff41da50SThomas Huth /* Memory Mapped implementation of QSDHCI */ 420*ff41da50SThomas Huth struct QSDHCI_MemoryMapped { 421*ff41da50SThomas Huth QOSGraphObject obj; 422*ff41da50SThomas Huth QSDHCI sdhci; 423*ff41da50SThomas Huth /* other driver-specific fields */ 424*ff41da50SThomas Huth }; 425*ff41da50SThomas Huth 426*ff41da50SThomas Huth /* PCI implementation of QSDHCI */ 427*ff41da50SThomas Huth struct QSDHCI_PCI { 428*ff41da50SThomas Huth QOSGraphObject obj; 429*ff41da50SThomas Huth QSDHCI sdhci; 430*ff41da50SThomas Huth /* other driver-specific fields */ 431*ff41da50SThomas Huth }; 432*ff41da50SThomas Huth 433*ff41da50SThomas Huth /* Memory mapped implementation of QSDHCI */ 434*ff41da50SThomas Huth 435*ff41da50SThomas Huth static void *sdhci_mm_get_driver(void *obj, const char *interface) 436*ff41da50SThomas Huth { 437*ff41da50SThomas Huth QSDHCI_MemoryMapped *smm = obj; 438*ff41da50SThomas Huth if (!g_strcmp0(interface, "sdhci")) { 439*ff41da50SThomas Huth return &smm->sdhci; 440*ff41da50SThomas Huth } 441*ff41da50SThomas Huth fprintf(stderr, "%s not present in generic-sdhci\n", interface); 442*ff41da50SThomas Huth g_assert_not_reached(); 443*ff41da50SThomas Huth } 444*ff41da50SThomas Huth 445*ff41da50SThomas Huth void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts, 446*ff41da50SThomas Huth uint32_t addr, QSDHCIProperties *common) 447*ff41da50SThomas Huth { 448*ff41da50SThomas Huth /* Get node contained inside (CONTAINS) */ 449*ff41da50SThomas Huth sdhci->obj.get_driver = sdhci_mm_get_driver; 450*ff41da50SThomas Huth 451*ff41da50SThomas Huth /* SDHCI interface API */ 452*ff41da50SThomas Huth sdhci->sdhci.readw = sdhci_mm_readw; 453*ff41da50SThomas Huth sdhci->sdhci.readq = sdhci_mm_readq; 454*ff41da50SThomas Huth sdhci->sdhci.writeq = sdhci_mm_writeq; 455*ff41da50SThomas Huth sdhci->qts = qts; 456*ff41da50SThomas Huth } 457*ff41da50SThomas Huth 458*ff41da50SThomas Huth /* PCI implementation of QSDHCI */ 459*ff41da50SThomas Huth 460*ff41da50SThomas Huth static void *sdhci_pci_get_driver(void *object, 461*ff41da50SThomas Huth const char *interface) 462*ff41da50SThomas Huth { 463*ff41da50SThomas Huth QSDHCI_PCI *spci = object; 464*ff41da50SThomas Huth if (!g_strcmp0(interface, "sdhci")) { 465*ff41da50SThomas Huth return &spci->sdhci; 466*ff41da50SThomas Huth } 467*ff41da50SThomas Huth 468*ff41da50SThomas Huth fprintf(stderr, "%s not present in sdhci-pci\n", interface); 469*ff41da50SThomas Huth g_assert_not_reached(); 470*ff41da50SThomas Huth } 471*ff41da50SThomas Huth 472*ff41da50SThomas Huth static void *sdhci_pci_create(void *pci_bus, 473*ff41da50SThomas Huth QGuestAllocator *alloc, 474*ff41da50SThomas Huth void *addr) 475*ff41da50SThomas Huth { 476*ff41da50SThomas Huth QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1); 477*ff41da50SThomas Huth QPCIBus *bus = pci_bus; 478*ff41da50SThomas Huth uint64_t barsize; 479*ff41da50SThomas Huth 480*ff41da50SThomas Huth qpci_device_init(&spci->dev, bus, addr); 481*ff41da50SThomas Huth 482*ff41da50SThomas Huth /* SDHCI interface API */ 483*ff41da50SThomas Huth spci->sdhci.readw = sdhci_pci_readw; 484*ff41da50SThomas Huth spci->sdhci.readq = sdhci_pci_readq; 485*ff41da50SThomas Huth spci->sdhci.writeq = sdhci_pci_writeq; 486*ff41da50SThomas Huth 487*ff41da50SThomas Huth /* Get node(s) produced (PRODUCES) */ 488*ff41da50SThomas Huth spci->obj.get_driver = sdhci_pci_get_driver; 489*ff41da50SThomas Huth 490*ff41da50SThomas Huth spci->obj.start_hw = sdhci_pci_start_hw; 491*ff41da50SThomas Huth spci->obj.destructor = sdhci_destructor; 492*ff41da50SThomas Huth return &spci->obj; 493*ff41da50SThomas Huth } 494*ff41da50SThomas Huth 495*ff41da50SThomas Huth static void qsdhci_register_nodes(void) 496*ff41da50SThomas Huth { 497*ff41da50SThomas Huth QOSGraphEdgeOptions opts = { 498*ff41da50SThomas Huth .extra_device_opts = "addr=04.0", 499*ff41da50SThomas Huth }; 500*ff41da50SThomas Huth 501*ff41da50SThomas Huth /* generic-sdhci */ 502*ff41da50SThomas Huth /* generic-sdhci --produces--> sdhci */ 503*ff41da50SThomas Huth qos_node_create_driver("generic-sdhci", NULL); 504*ff41da50SThomas Huth qos_node_produces("generic-sdhci", "sdhci"); 505*ff41da50SThomas Huth 506*ff41da50SThomas Huth /* sdhci-pci */ 507*ff41da50SThomas Huth /* sdhci-pci --produces--> sdhci 508*ff41da50SThomas Huth * sdhci-pci --consumes--> pci-bus */ 509*ff41da50SThomas Huth qos_node_create_driver("sdhci-pci", sdhci_pci_create); 510*ff41da50SThomas Huth qos_node_produces("sdhci-pci", "sdhci"); 511*ff41da50SThomas Huth qos_node_consumes("sdhci-pci", "pci-bus", &opts); 512*ff41da50SThomas Huth } 513*ff41da50SThomas Huth 514*ff41da50SThomas Huth libqos_init(qsdhci_register_nodes); 515*ff41da50SThomas Huth 516*ff41da50SThomas HuthIn the above example, all possible types of relations are created:: 517*ff41da50SThomas Huth 518*ff41da50SThomas Huth x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc 519*ff41da50SThomas Huth | 520*ff41da50SThomas Huth sdhci-pci --consumes--> pci-bus <--produces--+ 521*ff41da50SThomas Huth | 522*ff41da50SThomas Huth +--produces--+ 523*ff41da50SThomas Huth | 524*ff41da50SThomas Huth v 525*ff41da50SThomas Huth sdhci 526*ff41da50SThomas Huth ^ 527*ff41da50SThomas Huth | 528*ff41da50SThomas Huth +--produces-- + 529*ff41da50SThomas Huth | 530*ff41da50SThomas Huth arm/raspi2b --contains--> generic-sdhci 531*ff41da50SThomas Huth 532*ff41da50SThomas Huthor inverting the consumes edge in consumed_by:: 533*ff41da50SThomas Huth 534*ff41da50SThomas Huth x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc 535*ff41da50SThomas Huth | 536*ff41da50SThomas Huth sdhci-pci <--consumed by-- pci-bus <--produces--+ 537*ff41da50SThomas Huth | 538*ff41da50SThomas Huth +--produces--+ 539*ff41da50SThomas Huth | 540*ff41da50SThomas Huth v 541*ff41da50SThomas Huth sdhci 542*ff41da50SThomas Huth ^ 543*ff41da50SThomas Huth | 544*ff41da50SThomas Huth +--produces-- + 545*ff41da50SThomas Huth | 546*ff41da50SThomas Huth arm/raspi2b --contains--> generic-sdhci 547*ff41da50SThomas Huth 548*ff41da50SThomas HuthAdding a new test 549*ff41da50SThomas Huth----------------- 550*ff41da50SThomas Huth 551*ff41da50SThomas HuthGiven the above setup, adding a new test is very simple. 552*ff41da50SThomas Huth``sdhci-test``, taken from ``tests/qtest/sdhci-test.c``:: 553*ff41da50SThomas Huth 554*ff41da50SThomas Huth static void check_capab_sdma(QSDHCI *s, bool supported) 555*ff41da50SThomas Huth { 556*ff41da50SThomas Huth uint64_t capab, capab_sdma; 557*ff41da50SThomas Huth 558*ff41da50SThomas Huth capab = s->readq(s, SDHC_CAPAB); 559*ff41da50SThomas Huth capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); 560*ff41da50SThomas Huth g_assert_cmpuint(capab_sdma, ==, supported); 561*ff41da50SThomas Huth } 562*ff41da50SThomas Huth 563*ff41da50SThomas Huth static void test_registers(void *obj, void *data, 564*ff41da50SThomas Huth QGuestAllocator *alloc) 565*ff41da50SThomas Huth { 566*ff41da50SThomas Huth QSDHCI *s = obj; 567*ff41da50SThomas Huth 568*ff41da50SThomas Huth /* example test */ 569*ff41da50SThomas Huth check_capab_sdma(s, s->props.capab.sdma); 570*ff41da50SThomas Huth } 571*ff41da50SThomas Huth 572*ff41da50SThomas Huth static void register_sdhci_test(void) 573*ff41da50SThomas Huth { 574*ff41da50SThomas Huth /* sdhci-test --consumes--> sdhci */ 575*ff41da50SThomas Huth qos_add_test("registers", "sdhci", test_registers, NULL); 576*ff41da50SThomas Huth } 577*ff41da50SThomas Huth 578*ff41da50SThomas Huth libqos_init(register_sdhci_test); 579*ff41da50SThomas Huth 580*ff41da50SThomas HuthHere a new test is created, consuming ``sdhci`` interface node 581*ff41da50SThomas Huthand creating a valid path from both machines to a test. 582*ff41da50SThomas HuthFinal graph will be like this:: 583*ff41da50SThomas Huth 584*ff41da50SThomas Huth x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc 585*ff41da50SThomas Huth | 586*ff41da50SThomas Huth sdhci-pci --consumes--> pci-bus <--produces--+ 587*ff41da50SThomas Huth | 588*ff41da50SThomas Huth +--produces--+ 589*ff41da50SThomas Huth | 590*ff41da50SThomas Huth v 591*ff41da50SThomas Huth sdhci <--consumes-- sdhci-test 592*ff41da50SThomas Huth ^ 593*ff41da50SThomas Huth | 594*ff41da50SThomas Huth +--produces-- + 595*ff41da50SThomas Huth | 596*ff41da50SThomas Huth arm/raspi2b --contains--> generic-sdhci 597*ff41da50SThomas Huth 598*ff41da50SThomas Huthor inverting the consumes edge in consumed_by:: 599*ff41da50SThomas Huth 600*ff41da50SThomas Huth x86_64/pc --contains--> 1440FX-pcihost --contains--> pci-bus-pc 601*ff41da50SThomas Huth | 602*ff41da50SThomas Huth sdhci-pci <--consumed by-- pci-bus <--produces--+ 603*ff41da50SThomas Huth | 604*ff41da50SThomas Huth +--produces--+ 605*ff41da50SThomas Huth | 606*ff41da50SThomas Huth v 607*ff41da50SThomas Huth sdhci --consumed by--> sdhci-test 608*ff41da50SThomas Huth ^ 609*ff41da50SThomas Huth | 610*ff41da50SThomas Huth +--produces-- + 611*ff41da50SThomas Huth | 612*ff41da50SThomas Huth arm/raspi2b --contains--> generic-sdhci 613*ff41da50SThomas Huth 614*ff41da50SThomas HuthAssuming there the binary is 615*ff41da50SThomas Huth``QTEST_QEMU_BINARY=./qemu-system-x86_64`` 616*ff41da50SThomas Hutha valid test path will be: 617*ff41da50SThomas Huth``/x86_64/pc/1440FX-pcihost/pci-bus-pc/pci-bus/sdhci-pc/sdhci/sdhci-test`` 618*ff41da50SThomas Huth 619*ff41da50SThomas Huthand for the binary ``QTEST_QEMU_BINARY=./qemu-system-arm``: 620*ff41da50SThomas Huth 621*ff41da50SThomas Huth``/arm/raspi2b/generic-sdhci/sdhci/sdhci-test`` 622*ff41da50SThomas Huth 623*ff41da50SThomas HuthAdditional examples are also in ``test-qgraph.c`` 624*ff41da50SThomas Huth 625*ff41da50SThomas HuthQgraph API reference 626*ff41da50SThomas Huth-------------------- 627*ff41da50SThomas Huth 628*ff41da50SThomas Huth.. kernel-doc:: tests/qtest/libqos/qgraph.h 629