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"
21 #include "qemu/queue.h"
22 #include "libqos/qgraph_internal.h"
23 #include "libqos/qgraph.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 enty 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_memdup(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
->command_line
);
161 * destroy_string(): frees @key from the nodes hash table.
162 * Actually frees the node->name
164 static void destroy_string(void *key
)
170 * search_node(): search for a node @key in the nodes hash table
171 * Returns the QOSGraphNode if found, #NULL otherwise
173 static QOSGraphNode
*search_node(const char *key
)
175 return g_hash_table_lookup(node_table
, key
);
179 * get_edgelist(): returns the edge list (value) assigned to
180 * the @key in the edge hash table.
181 * This list will contain all edges with source equal to @key
183 * Returns: on success: the %QOSGraphEdgeList
186 static QOSGraphEdgeList
*get_edgelist(const char *key
)
188 return g_hash_table_lookup(edge_table
, key
);
192 * search_list_edges(): search for an edge with destination @dest
193 * in the given @edgelist.
195 * Returns: on success: the %QOSGraphEdge
198 static QOSGraphEdge
*search_list_edges(QOSGraphEdgeList
*edgelist
,
201 QOSGraphEdge
*tmp
, *next
;
205 QSLIST_FOREACH_SAFE(tmp
, edgelist
, edge_list
, next
) {
206 if (g_strcmp0(tmp
->dest
, dest
) == 0) {
214 * search_machine(): search for a machine @name in the node hash
215 * table. A machine is the child of the root node.
216 * This function forces the research in the childs of the root,
217 * to check the node is a proper machine
219 * Returns: on success: the %QOSGraphNode
222 static QOSGraphNode
*search_machine(const char *name
)
225 QOSGraphEdgeList
*root_list
= get_edgelist(QOS_ROOT
);
226 QOSGraphEdge
*e
= search_list_edges(root_list
, name
);
230 n
= search_node(e
->dest
);
231 if (n
->type
== QNODE_MACHINE
) {
238 * create_interface(): checks if there is already
239 * a node @node in the node hash table, if not
240 * creates a node @node of type #QNODE_INTERFACE
241 * and inserts it. If there is one, check it's
242 * a #QNODE_INTERFACE and abort() if it's not.
244 static void create_interface(const char *node
)
246 QOSGraphNode
*interface
;
247 interface
= search_node(node
);
249 create_node(node
, QNODE_INTERFACE
);
250 } else if (interface
->type
!= QNODE_INTERFACE
) {
251 fprintf(stderr
, "Error: Node %s is not an interface\n", node
);
257 * build_machine_cmd_line(): builds the command line for the machine
258 * @node. The node name must be a valid qemu identifier, since it
259 * will be used to build the command line.
261 * It is also possible to pass an optional @args that will be
262 * concatenated to the command line.
264 * For machines, prepend -M to the machine name. ", @rgs" is added
265 * after the -M <machine> command.
267 static void build_machine_cmd_line(QOSGraphNode
*node
, const char *args
)
269 char *machine
= qos_get_machine_type(node
->name
);
271 node
->command_line
= g_strconcat("-M ", machine
, ",", args
, NULL
);
273 node
->command_line
= g_strconcat("-M ", machine
, " ", NULL
);
278 * build_driver_cmd_line(): builds the command line for the driver
279 * @node. The node name must be a valid qemu identifier, since it
280 * will be used to build the command line.
282 * Driver do not need additional command line, since it will be
283 * provided by the edge options.
285 * For drivers, prepend -device to the node name.
287 static void build_driver_cmd_line(QOSGraphNode
*node
)
289 node
->command_line
= g_strconcat(" -device ", node
->name
, NULL
);
292 /* qos_print_cb(): callback prints all path found by the DFS algorithm. */
293 static void qos_print_cb(QOSGraphNode
*path
, int length
)
295 #if QGRAPH_PRINT_DEBUG
296 printf("%d elements\n", length
);
302 while (path
->path_edge
) {
303 printf("%s ", path
->name
);
304 switch (path
->path_edge
->type
) {
306 printf("--PRODUCES--> ");
308 case QEDGE_CONSUMED_BY
:
309 printf("--CONSUMED_BY--> ");
312 printf("--CONTAINS--> ");
315 path
= search_node(path
->path_edge
->dest
);
318 printf("%s\n\n", path
->name
);
322 /* qos_push(): push a node @el and edge @e in the qos_node_stack */
323 static void qos_push(QOSGraphNode
*el
, QOSStackElement
*parent
,
326 int len
= 0; /* root is not counted */
327 if (qos_node_tos
== QOS_PATH_MAX_ELEMENT_SIZE
) {
328 g_printerr("QOSStack: full stack, cannot push");
333 len
= parent
->length
+ 1;
335 qos_node_stack
[qos_node_tos
++] = (QOSStackElement
) {
343 /* qos_tos(): returns the top of stack, without popping */
344 static QOSStackElement
*qos_tos(void)
346 return &qos_node_stack
[qos_node_tos
- 1];
349 /* qos_pop(): pops an element from the tos, setting it unvisited*/
350 static QOSStackElement
*qos_pop(void)
352 if (qos_node_tos
== 0) {
353 g_printerr("QOSStack: empty stack, cannot pop");
356 QOSStackElement
*e
= qos_tos();
357 e
->node
->visited
= false;
363 * qos_reverse_path(): reverses the found path, going from
364 * test-to-machine to machine-to-test
366 static QOSGraphNode
*qos_reverse_path(QOSStackElement
*el
)
372 el
->node
->path_edge
= NULL
;
375 el
->parent
->node
->path_edge
= el
->parent_edge
;
383 * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
384 * starts from the root @machine and walks all possible path until it
385 * reaches a test node.
386 * At that point, it reverses the path found and invokes the @callback.
388 * Being Depth First Search, time complexity is O(|V| + |E|), while
389 * space is O(|V|). In this case, the maximum stack size is set by
390 * QOS_PATH_MAX_ELEMENT_SIZE.
392 static void qos_traverse_graph(QOSGraphNode
*root
, QOSTestCallback callback
)
394 QOSGraphNode
*v
, *dest_node
, *path
;
395 QOSStackElement
*s_el
;
396 QOSGraphEdge
*e
, *next
;
397 QOSGraphEdgeList
*list
;
399 qos_push(root
, NULL
, NULL
);
401 while (qos_node_tos
> 0) {
409 list
= get_edgelist(v
->name
);
412 if (v
->type
== QNODE_TEST
) {
414 path
= qos_reverse_path(s_el
);
415 callback(path
, s_el
->length
);
418 QSLIST_FOREACH_SAFE(e
, list
, edge_list
, next
) {
419 dest_node
= search_node(e
->dest
);
422 fprintf(stderr
, "node %s in %s -> %s does not exist\n",
423 e
->dest
, v
->name
, e
->dest
);
427 if (!dest_node
->visited
&& dest_node
->available
) {
428 qos_push(dest_node
, s_el
, e
);
437 QOSGraphNode
*qos_graph_get_node(const char *key
)
439 return search_node(key
);
442 bool qos_graph_has_node(const char *node
)
444 QOSGraphNode
*n
= search_node(node
);
448 QOSNodeType
qos_graph_get_node_type(const char *node
)
450 QOSGraphNode
*n
= search_node(node
);
457 bool qos_graph_get_node_availability(const char *node
)
459 QOSGraphNode
*n
= search_node(node
);
466 QOSGraphEdge
*qos_graph_get_edge(const char *node
, const char *dest
)
468 QOSGraphEdgeList
*list
= get_edgelist(node
);
469 return search_list_edges(list
, dest
);
472 QOSEdgeType
qos_graph_edge_get_type(QOSGraphEdge
*edge
)
480 char *qos_graph_edge_get_dest(QOSGraphEdge
*edge
)
488 void *qos_graph_edge_get_arg(QOSGraphEdge
*edge
)
496 char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge
*edge
)
501 return edge
->after_cmd_line
;
504 char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge
*edge
)
509 return edge
->before_cmd_line
;
512 char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge
*edge
)
517 return edge
->extra_device_opts
;
520 char *qos_graph_edge_get_name(QOSGraphEdge
*edge
)
525 return edge
->edge_name
;
528 bool qos_graph_has_edge(const char *start
, const char *dest
)
530 QOSGraphEdgeList
*list
= get_edgelist(start
);
531 QOSGraphEdge
*e
= search_list_edges(list
, dest
);
535 QOSGraphNode
*qos_graph_get_machine(const char *node
)
537 return search_machine(node
);
540 bool qos_graph_has_machine(const char *node
)
542 QOSGraphNode
*m
= search_machine(node
);
546 void qos_print_graph(void)
548 qos_graph_foreach_test_path(qos_print_cb
);
551 void qos_graph_init(void)
554 node_table
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
555 destroy_string
, destroy_node
);
556 create_node(QOS_ROOT
, QNODE_DRIVER
);
560 edge_table
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
561 destroy_string
, destroy_edges
);
565 void qos_graph_destroy(void)
568 g_hash_table_destroy(node_table
);
572 g_hash_table_destroy(edge_table
);
579 void qos_node_destroy(void *key
)
581 g_hash_table_remove(node_table
, key
);
584 void qos_edge_destroy(void *key
)
586 g_hash_table_remove(edge_table
, key
);
589 void qos_add_test(const char *name
, const char *interface
,
590 QOSTestFunc test_func
, QOSGraphTestOptions
*opts
)
593 char *test_name
= g_strdup_printf("%s-tests/%s", interface
, name
);
594 QOSGraphTestOptions def_opts
= { };
599 node
= create_node(test_name
, QNODE_TEST
);
600 node
->u
.test
.function
= test_func
;
601 node
->u
.test
.arg
= opts
->arg
;
602 assert(!opts
->edge
.arg
);
603 assert(!opts
->edge
.size_arg
);
605 node
->u
.test
.before
= opts
->before
;
606 node
->u
.test
.subprocess
= opts
->subprocess
;
607 node
->available
= true;
608 add_edge(interface
, test_name
, QEDGE_CONSUMED_BY
, &opts
->edge
);
612 void qos_node_create_machine(const char *name
, QOSCreateMachineFunc function
)
614 qos_node_create_machine_args(name
, function
, NULL
);
617 void qos_node_create_machine_args(const char *name
,
618 QOSCreateMachineFunc function
,
621 QOSGraphNode
*node
= create_node(name
, QNODE_MACHINE
);
622 build_machine_cmd_line(node
, opts
);
623 node
->u
.machine
.constructor
= function
;
624 add_edge(QOS_ROOT
, name
, QEDGE_CONTAINS
, NULL
);
627 void qos_node_create_driver(const char *name
, QOSCreateDriverFunc function
)
629 QOSGraphNode
*node
= create_node(name
, QNODE_DRIVER
);
630 build_driver_cmd_line(node
);
631 node
->u
.driver
.constructor
= function
;
634 void qos_node_contains(const char *container
, const char *contained
,
635 QOSGraphEdgeOptions
*opts
, ...)
640 add_edge(container
, contained
, QEDGE_CONTAINS
, NULL
);
646 add_edge(container
, contained
, QEDGE_CONTAINS
, opts
);
647 opts
= va_arg(va
, QOSGraphEdgeOptions
*);
648 } while (opts
!= NULL
);
653 void qos_node_produces(const char *producer
, const char *interface
)
655 create_interface(interface
);
656 add_edge(producer
, interface
, QEDGE_PRODUCES
, NULL
);
659 void qos_node_consumes(const char *consumer
, const char *interface
,
660 QOSGraphEdgeOptions
*opts
)
662 create_interface(interface
);
663 add_edge(interface
, consumer
, QEDGE_CONSUMED_BY
, opts
);
666 void qos_graph_node_set_availability(const char *node
, bool av
)
668 QOSGraphEdgeList
*elist
;
669 QOSGraphNode
*n
= search_node(node
);
670 QOSGraphEdge
*e
, *next
;
675 elist
= get_edgelist(node
);
679 QSLIST_FOREACH_SAFE(e
, elist
, edge_list
, next
) {
680 if (e
->type
== QEDGE_CONTAINS
|| e
->type
== QEDGE_PRODUCES
) {
681 qos_graph_node_set_availability(e
->dest
, av
);
686 void qos_graph_foreach_test_path(QOSTestCallback fn
)
688 QOSGraphNode
*root
= qos_graph_get_node(QOS_ROOT
);
689 qos_traverse_graph(root
, fn
);
692 QOSGraphObject
*qos_machine_new(QOSGraphNode
*node
, QTestState
*qts
)
696 g_assert(node
->type
== QNODE_MACHINE
);
697 obj
= node
->u
.machine
.constructor(qts
);
702 QOSGraphObject
*qos_driver_new(QOSGraphNode
*node
, QOSGraphObject
*parent
,
703 QGuestAllocator
*alloc
, void *arg
)
707 g_assert(node
->type
== QNODE_DRIVER
);
708 obj
= node
->u
.driver
.constructor(parent
, alloc
, arg
);
713 void qos_object_destroy(QOSGraphObject
*obj
)
718 if (obj
->destructor
) {
719 obj
->destructor(obj
);
726 void qos_object_queue_destroy(QOSGraphObject
*obj
)
728 g_test_queue_destroy((GDestroyNotify
) qos_object_destroy
, obj
);
731 void qos_object_start_hw(QOSGraphObject
*obj
)
738 char *qos_get_machine_type(char *name
)
740 while (*name
!= '\0' && *name
!= '/') {
744 if (!*name
|| !name
[1]) {
745 fprintf(stderr
, "Machine name has to be of the form <arch>/<machine>\n");
752 void qos_delete_cmd_line(const char *name
)
754 QOSGraphNode
*node
= search_node(name
);
756 g_free(node
->command_line
);
757 node
->command_line
= NULL
;