xref: /openbmc/qemu/tests/qtest/libqos/qgraph.h (revision 7f6c3d1a)
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