2 * libqos driver framework
4 * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
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.
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.
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/>
19 #include "qemu/osdep.h"
20 #include "../libqtest.h"
21 #include "qemu/queue.h"
22 #include "qgraph_internal.h"
25 #define QGRAPH_PRINT_DEBUG 0
27 typedef struct QOSStackElement QOSStackElement
;
33 void *arg
; /* just for QEDGE_CONTAINS
34 * and QEDGE_CONSUMED_BY */
35 char *extra_device_opts
; /* added to -device option, "," is
38 char *before_cmd_line
; /* added before node cmd_line */
39 char *after_cmd_line
; /* added after -device options */
40 char *edge_name
; /* used by QEDGE_CONTAINS */
41 QSLIST_ENTRY(QOSGraphEdge
) edge_list
;
44 typedef QSLIST_HEAD(, QOSGraphEdge
) QOSGraphEdgeList
;
47 * Stack used to keep track of the discovered path when using
50 struct QOSStackElement
{
52 QOSStackElement
*parent
;
53 QOSGraphEdge
*parent_edge
;
57 /* Each entry in these hash table will consist of <string, node/edge> pair. */
58 static GHashTable
*edge_table
;
59 static GHashTable
*node_table
;
61 /* stack used by the DFS algorithm to store the path from machine to test */
62 static QOSStackElement qos_node_stack
[QOS_PATH_MAX_ELEMENT_SIZE
];
63 static int qos_node_tos
;
66 * add_edge(): creates an edge of type @type
67 * from @source to @dest node, and inserts it in the
70 * Nodes @source and @dest do not necessarily need to exist.
71 * Possibility to add also options (see #QOSGraphEdgeOptions)
72 * edge->edge_name is used as identifier for get_device relationships,
73 * so by default is equal to @dest.
75 static void add_edge(const char *source
, const char *dest
,
76 QOSEdgeType type
, QOSGraphEdgeOptions
*opts
)
79 QOSGraphEdgeList
*list
= g_hash_table_lookup(edge_table
, source
);
80 QOSGraphEdgeOptions def_opts
= { };
83 list
= g_new0(QOSGraphEdgeList
, 1);
84 key
= g_strdup(source
);
85 g_hash_table_insert(edge_table
, key
, list
);
92 QOSGraphEdge
*edge
= g_new0(QOSGraphEdge
, 1);
94 edge
->dest
= g_strdup(dest
);
95 edge
->edge_name
= g_strdup(opts
->edge_name
?: dest
);
96 edge
->arg
= g_memdup2(opts
->arg
, opts
->size_arg
);
98 edge
->before_cmd_line
=
99 opts
->before_cmd_line
? g_strconcat(" ", opts
->before_cmd_line
, NULL
) : NULL
;
100 edge
->extra_device_opts
=
101 opts
->extra_device_opts
? g_strconcat(",", opts
->extra_device_opts
, NULL
) : NULL
;
102 edge
->after_cmd_line
=
103 opts
->after_cmd_line
? g_strconcat(" ", opts
->after_cmd_line
, NULL
) : NULL
;
105 QSLIST_INSERT_HEAD(list
, edge
, edge_list
);
108 /* destroy_edges(): frees all edges inside a given @list */
109 static void destroy_edges(void *list
)
112 QOSGraphEdgeList
*elist
= list
;
114 while (!QSLIST_EMPTY(elist
)) {
115 temp
= QSLIST_FIRST(elist
);
116 QSLIST_REMOVE_HEAD(elist
, edge_list
);
118 g_free(temp
->before_cmd_line
);
119 g_free(temp
->after_cmd_line
);
120 g_free(temp
->extra_device_opts
);
121 g_free(temp
->edge_name
);
129 * create_node(): creates a node @name of type @type
130 * and inserts it to the nodes hash table.
131 * By default, node is not available.
133 static QOSGraphNode
*create_node(const char *name
, QOSNodeType type
)
135 if (g_hash_table_lookup(node_table
, name
)) {
136 g_printerr("Node %s already created\n", name
);
140 QOSGraphNode
*node
= g_new0(QOSGraphNode
, 1);
142 node
->available
= false;
143 node
->name
= g_strdup(name
);
144 g_hash_table_insert(node_table
, node
->name
, node
);
149 * destroy_node(): frees a node @val from the nodes hash table.
150 * Note that node->name is not free'd since it will represent the
153 static void destroy_node(void *val
)
155 QOSGraphNode
*node
= val
;
156 g_free(node
->qemu_name
);
157 g_free(node
->command_line
);
162 * destroy_string(): frees @key from the nodes hash table.
163 * Actually frees the node->name
165 static void destroy_string(void *key
)
171 * search_node(): search for a node @key in the nodes hash table
172 * Returns the QOSGraphNode if found, #NULL otherwise
174 static QOSGraphNode
*search_node(const char *key
)
176 return g_hash_table_lookup(node_table
, key
);
180 * get_edgelist(): returns the edge list (value) assigned to
181 * the @key in the edge hash table.
182 * This list will contain all edges with source equal to @key
184 * Returns: on success: the %QOSGraphEdgeList
187 static QOSGraphEdgeList
*get_edgelist(const char *key
)
189 return g_hash_table_lookup(edge_table
, key
);
193 * search_list_edges(): search for an edge with destination @dest
194 * in the given @edgelist.
196 * Returns: on success: the %QOSGraphEdge
199 static QOSGraphEdge
*search_list_edges(QOSGraphEdgeList
*edgelist
,
202 QOSGraphEdge
*tmp
, *next
;
206 QSLIST_FOREACH_SAFE(tmp
, edgelist
, edge_list
, next
) {
207 if (g_strcmp0(tmp
->dest
, dest
) == 0) {
215 * search_machine(): search for a machine @name in the node hash
216 * table. A machine is the child of the root node.
217 * This function forces the research in the children of the root,
218 * to check the node is a proper machine
220 * Returns: on success: the %QOSGraphNode
223 static QOSGraphNode
*search_machine(const char *name
)
226 QOSGraphEdgeList
*root_list
= get_edgelist(QOS_ROOT
);
227 QOSGraphEdge
*e
= search_list_edges(root_list
, name
);
231 n
= search_node(e
->dest
);
232 if (n
->type
== QNODE_MACHINE
) {
239 * create_interface(): checks if there is already
240 * a node @node in the node hash table, if not
241 * creates a node @node of type #QNODE_INTERFACE
242 * and inserts it. If there is one, check it's
243 * a #QNODE_INTERFACE and abort() if it's not.
245 static void create_interface(const char *node
)
247 QOSGraphNode
*interface
;
248 interface
= search_node(node
);
250 create_node(node
, QNODE_INTERFACE
);
251 } else if (interface
->type
!= QNODE_INTERFACE
) {
252 fprintf(stderr
, "Error: Node %s is not an interface\n", node
);
258 * build_machine_cmd_line(): builds the command line for the machine
259 * @node. The node name must be a valid qemu identifier, since it
260 * will be used to build the command line.
262 * It is also possible to pass an optional @args that will be
263 * concatenated to the command line.
265 * For machines, prepend -M to the machine name. ", @rgs" is added
266 * after the -M <machine> command.
268 static void build_machine_cmd_line(QOSGraphNode
*node
, const char *args
)
270 char *machine
= qos_get_machine_type(node
->name
);
272 node
->command_line
= g_strconcat("-M ", machine
, ",", args
, NULL
);
274 node
->command_line
= g_strconcat("-M ", machine
, " ", NULL
);
279 * build_driver_cmd_line(): builds the command line for the driver
280 * @node. The node name must be a valid qemu identifier, since it
281 * will be used to build the command line.
283 * Driver do not need additional command line, since it will be
284 * provided by the edge options.
286 * For drivers, prepend -device to the node name.
288 static void build_driver_cmd_line(QOSGraphNode
*node
)
290 const char *name
= node
->qemu_name
?: node
->name
;
291 node
->command_line
= g_strconcat(" -device ", name
, NULL
);
294 /* qos_print_cb(): callback prints all path found by the DFS algorithm. */
295 static void qos_print_cb(QOSGraphNode
*path
, int length
)
297 #if QGRAPH_PRINT_DEBUG
298 printf("%d elements\n", length
);
304 while (path
->path_edge
) {
305 printf("%s ", path
->name
);
306 switch (path
->path_edge
->type
) {
308 printf("--PRODUCES--> ");
310 case QEDGE_CONSUMED_BY
:
311 printf("--CONSUMED_BY--> ");
314 printf("--CONTAINS--> ");
317 path
= search_node(path
->path_edge
->dest
);
320 printf("%s\n\n", path
->name
);
324 /* qos_push(): push a node @el and edge @e in the qos_node_stack */
325 static void qos_push(QOSGraphNode
*el
, QOSStackElement
*parent
,
328 int len
= 0; /* root is not counted */
329 if (qos_node_tos
== QOS_PATH_MAX_ELEMENT_SIZE
) {
330 g_printerr("QOSStack: full stack, cannot push");
335 len
= parent
->length
+ 1;
337 qos_node_stack
[qos_node_tos
++] = (QOSStackElement
) {
345 /* qos_tos(): returns the top of stack, without popping */
346 static QOSStackElement
*qos_tos(void)
348 return &qos_node_stack
[qos_node_tos
- 1];
351 /* qos_pop(): pops an element from the tos, setting it unvisited*/
352 static QOSStackElement
*qos_pop(void)
354 if (qos_node_tos
== 0) {
355 g_printerr("QOSStack: empty stack, cannot pop");
358 QOSStackElement
*e
= qos_tos();
359 e
->node
->visited
= false;
365 * qos_reverse_path(): reverses the found path, going from
366 * test-to-machine to machine-to-test
368 static QOSGraphNode
*qos_reverse_path(QOSStackElement
*el
)
374 el
->node
->path_edge
= NULL
;
377 el
->parent
->node
->path_edge
= el
->parent_edge
;
385 * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
386 * starts from the root @machine and walks all possible path until it
387 * reaches a test node.
388 * At that point, it reverses the path found and invokes the @callback.
390 * Being Depth First Search, time complexity is O(|V| + |E|), while
391 * space is O(|V|). In this case, the maximum stack size is set by
392 * QOS_PATH_MAX_ELEMENT_SIZE.
394 static void qos_traverse_graph(QOSGraphNode
*root
, QOSTestCallback callback
)
396 QOSGraphNode
*v
, *dest_node
, *path
;
397 QOSStackElement
*s_el
;
398 QOSGraphEdge
*e
, *next
;
399 QOSGraphEdgeList
*list
;
401 qos_push(root
, NULL
, NULL
);
403 while (qos_node_tos
> 0) {
411 list
= get_edgelist(v
->name
);
414 if (v
->type
== QNODE_TEST
) {
416 path
= qos_reverse_path(s_el
);
417 callback(path
, s_el
->length
);
420 QSLIST_FOREACH_SAFE(e
, list
, edge_list
, next
) {
421 dest_node
= search_node(e
->dest
);
424 fprintf(stderr
, "node %s in %s -> %s does not exist\n",
425 e
->dest
, v
->name
, e
->dest
);
429 if (!dest_node
->visited
&& dest_node
->available
) {
430 qos_push(dest_node
, s_el
, e
);
439 QOSGraphNode
*qos_graph_get_node(const char *key
)
441 return search_node(key
);
444 bool qos_graph_has_node(const char *node
)
446 QOSGraphNode
*n
= search_node(node
);
450 QOSNodeType
qos_graph_get_node_type(const char *node
)
452 QOSGraphNode
*n
= search_node(node
);
459 bool qos_graph_get_node_availability(const char *node
)
461 QOSGraphNode
*n
= search_node(node
);
468 QOSGraphEdge
*qos_graph_get_edge(const char *node
, const char *dest
)
470 QOSGraphEdgeList
*list
= get_edgelist(node
);
471 return search_list_edges(list
, dest
);
474 QOSEdgeType
qos_graph_edge_get_type(QOSGraphEdge
*edge
)
482 char *qos_graph_edge_get_dest(QOSGraphEdge
*edge
)
490 void *qos_graph_edge_get_arg(QOSGraphEdge
*edge
)
498 char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge
*edge
)
503 return edge
->after_cmd_line
;
506 char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge
*edge
)
511 return edge
->before_cmd_line
;
514 char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge
*edge
)
519 return edge
->extra_device_opts
;
522 char *qos_graph_edge_get_name(QOSGraphEdge
*edge
)
527 return edge
->edge_name
;
530 bool qos_graph_has_edge(const char *start
, const char *dest
)
532 QOSGraphEdgeList
*list
= get_edgelist(start
);
533 QOSGraphEdge
*e
= search_list_edges(list
, dest
);
537 QOSGraphNode
*qos_graph_get_machine(const char *node
)
539 return search_machine(node
);
542 bool qos_graph_has_machine(const char *node
)
544 QOSGraphNode
*m
= search_machine(node
);
548 void qos_print_graph(void)
550 qos_graph_foreach_test_path(qos_print_cb
);
553 void qos_graph_init(void)
556 node_table
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
557 destroy_string
, destroy_node
);
558 create_node(QOS_ROOT
, QNODE_DRIVER
);
562 edge_table
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
563 destroy_string
, destroy_edges
);
567 void qos_graph_destroy(void)
570 g_hash_table_destroy(node_table
);
574 g_hash_table_destroy(edge_table
);
581 void qos_node_destroy(void *key
)
583 g_hash_table_remove(node_table
, key
);
586 void qos_edge_destroy(void *key
)
588 g_hash_table_remove(edge_table
, key
);
591 void qos_add_test(const char *name
, const char *interface
,
592 QOSTestFunc test_func
, QOSGraphTestOptions
*opts
)
595 char *test_name
= g_strdup_printf("%s-tests/%s", interface
, name
);
596 QOSGraphTestOptions def_opts
= { };
601 node
= create_node(test_name
, QNODE_TEST
);
602 node
->u
.test
.function
= test_func
;
603 node
->u
.test
.arg
= opts
->arg
;
604 assert(!opts
->edge
.arg
);
605 assert(!opts
->edge
.size_arg
);
607 node
->u
.test
.before
= opts
->before
;
608 node
->u
.test
.subprocess
= opts
->subprocess
;
609 node
->available
= true;
610 add_edge(interface
, test_name
, QEDGE_CONSUMED_BY
, &opts
->edge
);
614 void qos_node_create_machine(const char *name
, QOSCreateMachineFunc function
)
616 qos_node_create_machine_args(name
, function
, NULL
);
619 void qos_node_create_machine_args(const char *name
,
620 QOSCreateMachineFunc function
,
623 QOSGraphNode
*node
= create_node(name
, QNODE_MACHINE
);
624 build_machine_cmd_line(node
, opts
);
625 node
->u
.machine
.constructor
= function
;
626 add_edge(QOS_ROOT
, name
, QEDGE_CONTAINS
, NULL
);
629 void qos_node_create_driver(const char *name
, QOSCreateDriverFunc function
)
631 QOSGraphNode
*node
= create_node(name
, QNODE_DRIVER
);
632 build_driver_cmd_line(node
);
633 node
->u
.driver
.constructor
= function
;
636 void qos_node_create_driver_named(const char *name
, const char *qemu_name
,
637 QOSCreateDriverFunc function
)
639 QOSGraphNode
*node
= create_node(name
, QNODE_DRIVER
);
640 node
->qemu_name
= g_strdup(qemu_name
);
641 build_driver_cmd_line(node
);
642 node
->u
.driver
.constructor
= function
;
645 void qos_node_contains(const char *container
, const char *contained
,
646 QOSGraphEdgeOptions
*opts
, ...)
651 add_edge(container
, contained
, QEDGE_CONTAINS
, NULL
);
657 add_edge(container
, contained
, QEDGE_CONTAINS
, opts
);
658 opts
= va_arg(va
, QOSGraphEdgeOptions
*);
659 } while (opts
!= NULL
);
664 void qos_node_produces(const char *producer
, const char *interface
)
666 create_interface(interface
);
667 add_edge(producer
, interface
, QEDGE_PRODUCES
, NULL
);
670 void qos_node_consumes(const char *consumer
, const char *interface
,
671 QOSGraphEdgeOptions
*opts
)
673 create_interface(interface
);
674 add_edge(interface
, consumer
, QEDGE_CONSUMED_BY
, opts
);
677 static void qos_graph_node_set_availability_explicit(const char *node
, bool av
)
679 QOSGraphEdgeList
*elist
;
680 QOSGraphNode
*n
= search_node(node
);
681 QOSGraphEdge
*e
, *next
;
686 elist
= get_edgelist(node
);
690 QSLIST_FOREACH_SAFE(e
, elist
, edge_list
, next
) {
691 if (e
->type
== QEDGE_CONTAINS
|| e
->type
== QEDGE_PRODUCES
) {
692 qos_graph_node_set_availability_explicit(e
->dest
, av
);
698 * Behaves as qos_graph_node_set_availability_explicit(), except that the
699 * former always matches by node name only, whereas this function matches both
700 * by node name and node's optional 'qemu_name' field.
702 void qos_graph_node_set_availability(const char *node
, bool av
)
705 QOSGraphEdgeList
*elist
;
706 QOSGraphEdge
*e
, *next
;
708 GList
*keys
= g_hash_table_get_keys(node_table
);
710 for (l
= keys
; l
!= NULL
; l
= l
->next
) {
711 const gchar
*key
= l
->data
;
712 n
= g_hash_table_lookup(node_table
, key
);
714 * node's 'qemu_name' is set if there is more than one device with
715 * the same QEMU (QMP) device name
717 const char *node_name
= n
->qemu_name
?: n
->name
;
718 if (g_strcmp0(node_name
, node
) == 0) {
720 elist
= get_edgelist(n
->name
);
722 QSLIST_FOREACH_SAFE(e
, elist
, edge_list
, next
) {
723 if (e
->type
== QEDGE_CONTAINS
|| e
->type
== QEDGE_PRODUCES
)
725 qos_graph_node_set_availability_explicit(e
->dest
, av
);
734 void qos_graph_foreach_test_path(QOSTestCallback fn
)
736 QOSGraphNode
*root
= qos_graph_get_node(QOS_ROOT
);
737 qos_traverse_graph(root
, fn
);
740 QOSGraphObject
*qos_machine_new(QOSGraphNode
*node
, QTestState
*qts
)
744 g_assert(node
->type
== QNODE_MACHINE
);
745 obj
= node
->u
.machine
.constructor(qts
);
750 QOSGraphObject
*qos_driver_new(QOSGraphNode
*node
, QOSGraphObject
*parent
,
751 QGuestAllocator
*alloc
, void *arg
)
755 g_assert(node
->type
== QNODE_DRIVER
);
756 obj
= node
->u
.driver
.constructor(parent
, alloc
, arg
);
761 void qos_object_destroy(QOSGraphObject
*obj
)
766 if (obj
->destructor
) {
767 obj
->destructor(obj
);
774 void qos_object_queue_destroy(QOSGraphObject
*obj
)
776 g_test_queue_destroy((GDestroyNotify
) qos_object_destroy
, obj
);
779 void qos_object_start_hw(QOSGraphObject
*obj
)
786 char *qos_get_machine_type(char *name
)
788 while (*name
!= '\0' && *name
!= '/') {
792 if (!*name
|| !name
[1]) {
793 fprintf(stderr
, "Machine name has to be of the form <arch>/<machine>\n");
800 void qos_delete_cmd_line(const char *name
)
802 QOSGraphNode
*node
= search_node(name
);
804 g_free(node
->command_line
);
805 node
->command_line
= NULL
;
809 void qos_dump_graph(void)
813 QOSGraphEdgeList
*list
;
814 QOSGraphEdge
*e
, *next
;
815 QOSGraphNode
*dest_node
, *node
;
817 qos_printf("ALL QGRAPH EDGES: {\n");
818 keys
= g_hash_table_get_keys(edge_table
);
819 for (l
= keys
; l
!= NULL
; l
= l
->next
) {
820 const gchar
*key
= l
->data
;
821 qos_printf("\t src='%s'\n", key
);
822 list
= get_edgelist(key
);
823 QSLIST_FOREACH_SAFE(e
, list
, edge_list
, next
) {
824 dest_node
= g_hash_table_lookup(node_table
, e
->dest
);
825 qos_printf("\t\t|-> dest='%s' type=%d (node=%p)",
826 e
->dest
, e
->type
, dest_node
);
828 qos_printf_literal(" <------- ERROR !");
830 qos_printf_literal("\n");
836 qos_printf("ALL QGRAPH NODES: {\n");
837 keys
= g_hash_table_get_keys(node_table
);
838 for (l
= keys
; l
!= NULL
; l
= l
->next
) {
839 const gchar
*key
= l
->data
;
840 node
= g_hash_table_lookup(node_table
, key
);
841 qos_printf("\t name='%s' ", key
);
842 if (node
->qemu_name
) {
843 qos_printf_literal("qemu_name='%s' ", node
->qemu_name
);
845 qos_printf_literal("type=%d cmd_line='%s' [%s]\n",
846 node
->type
, node
->command_line
,
847 node
->available
? "available" : "UNAVAILABLE"