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 #include "qemu/osdep.h"
20 #include "../libqtest.h"
21 #include "qemu/queue.h"
22 #include "qgraph_internal.h"
23 #include "qgraph.h"
24
25 #define QGRAPH_PRINT_DEBUG 0
26 #define QOS_ROOT ""
27 typedef struct QOSStackElement QOSStackElement;
28
29 /* Graph Edge.*/
30 struct QOSGraphEdge {
31 QOSEdgeType type;
32 char *dest;
33 void *arg; /* just for QEDGE_CONTAINS
34 * and QEDGE_CONSUMED_BY */
35 char *extra_device_opts; /* added to -device option, "," is
36 * automatically added
37 */
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;
42 };
43
44 typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList;
45
46 /**
47 * Stack used to keep track of the discovered path when using
48 * the DFS algorithm
49 */
50 struct QOSStackElement {
51 QOSGraphNode *node;
52 QOSStackElement *parent;
53 QOSGraphEdge *parent_edge;
54 int length;
55 };
56
57 /* Each entry in these hash table will consist of <string, node/edge> pair. */
58 static GHashTable *edge_table;
59 static GHashTable *node_table;
60
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;
64
65 /**
66 * add_edge(): creates an edge of type @type
67 * from @source to @dest node, and inserts it in the
68 * edges hash table
69 *
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.
74 */
add_edge(const char * source,const char * dest,QOSEdgeType type,QOSGraphEdgeOptions * opts)75 static void add_edge(const char *source, const char *dest,
76 QOSEdgeType type, QOSGraphEdgeOptions *opts)
77 {
78 char *key;
79 QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source);
80 QOSGraphEdgeOptions def_opts = { };
81
82 if (!list) {
83 list = g_new0(QOSGraphEdgeList, 1);
84 key = g_strdup(source);
85 g_hash_table_insert(edge_table, key, list);
86 }
87
88 if (!opts) {
89 opts = &def_opts;
90 }
91
92 QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
93 edge->type = type;
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);
97
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;
104
105 QSLIST_INSERT_HEAD(list, edge, edge_list);
106 }
107
108 /* destroy_edges(): frees all edges inside a given @list */
destroy_edges(void * list)109 static void destroy_edges(void *list)
110 {
111 QOSGraphEdge *temp;
112 QOSGraphEdgeList *elist = list;
113
114 while (!QSLIST_EMPTY(elist)) {
115 temp = QSLIST_FIRST(elist);
116 QSLIST_REMOVE_HEAD(elist, edge_list);
117 g_free(temp->dest);
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);
122 g_free(temp->arg);
123 g_free(temp);
124 }
125 g_free(elist);
126 }
127
128 /**
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.
132 */
create_node(const char * name,QOSNodeType type)133 static QOSGraphNode *create_node(const char *name, QOSNodeType type)
134 {
135 if (g_hash_table_lookup(node_table, name)) {
136 g_printerr("Node %s already created\n", name);
137 abort();
138 }
139
140 QOSGraphNode *node = g_new0(QOSGraphNode, 1);
141 node->type = type;
142 node->available = false;
143 node->name = g_strdup(name);
144 g_hash_table_insert(node_table, node->name, node);
145 return node;
146 }
147
148 /**
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
151 * hash table key
152 */
destroy_node(void * val)153 static void destroy_node(void *val)
154 {
155 QOSGraphNode *node = val;
156 g_free(node->qemu_name);
157 g_free(node->command_line);
158 g_free(node);
159 }
160
161 /**
162 * destroy_string(): frees @key from the nodes hash table.
163 * Actually frees the node->name
164 */
destroy_string(void * key)165 static void destroy_string(void *key)
166 {
167 g_free(key);
168 }
169
170 /**
171 * search_node(): search for a node @key in the nodes hash table
172 * Returns the QOSGraphNode if found, #NULL otherwise
173 */
search_node(const char * key)174 static QOSGraphNode *search_node(const char *key)
175 {
176 return g_hash_table_lookup(node_table, key);
177 }
178
179 /**
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
183 *
184 * Returns: on success: the %QOSGraphEdgeList
185 * otherwise: abort()
186 */
get_edgelist(const char * key)187 static QOSGraphEdgeList *get_edgelist(const char *key)
188 {
189 return g_hash_table_lookup(edge_table, key);
190 }
191
192 /**
193 * search_list_edges(): search for an edge with destination @dest
194 * in the given @edgelist.
195 *
196 * Returns: on success: the %QOSGraphEdge
197 * otherwise: #NULL
198 */
search_list_edges(QOSGraphEdgeList * edgelist,const char * dest)199 static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist,
200 const char *dest)
201 {
202 QOSGraphEdge *tmp, *next;
203 if (!edgelist) {
204 return NULL;
205 }
206 QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) {
207 if (g_strcmp0(tmp->dest, dest) == 0) {
208 break;
209 }
210 }
211 return tmp;
212 }
213
214 /**
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
219 *
220 * Returns: on success: the %QOSGraphNode
221 * otherwise: #NULL
222 */
search_machine(const char * name)223 static QOSGraphNode *search_machine(const char *name)
224 {
225 QOSGraphNode *n;
226 QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT);
227 QOSGraphEdge *e = search_list_edges(root_list, name);
228 if (!e) {
229 return NULL;
230 }
231 n = search_node(e->dest);
232 if (n->type == QNODE_MACHINE) {
233 return n;
234 }
235 return NULL;
236 }
237
238 /**
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.
244 */
create_interface(const char * node)245 static void create_interface(const char *node)
246 {
247 QOSGraphNode *interface;
248 interface = search_node(node);
249 if (!interface) {
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);
253 abort();
254 }
255 }
256
257 /**
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.
261 *
262 * It is also possible to pass an optional @args that will be
263 * concatenated to the command line.
264 *
265 * For machines, prepend -M to the machine name. ", @rgs" is added
266 * after the -M <machine> command.
267 */
build_machine_cmd_line(QOSGraphNode * node,const char * args)268 static void build_machine_cmd_line(QOSGraphNode *node, const char *args)
269 {
270 char *machine = qos_get_machine_type(node->name);
271 if (args) {
272 node->command_line = g_strconcat("-M ", machine, ",", args, NULL);
273 } else {
274 node->command_line = g_strconcat("-M ", machine, " ", NULL);
275 }
276 }
277
278 /**
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.
282 *
283 * Driver do not need additional command line, since it will be
284 * provided by the edge options.
285 *
286 * For drivers, prepend -device to the node name.
287 */
build_driver_cmd_line(QOSGraphNode * node)288 static void build_driver_cmd_line(QOSGraphNode *node)
289 {
290 const char *name = node->qemu_name ?: node->name;
291 node->command_line = g_strconcat(" -device ", name, NULL);
292 }
293
294 /* qos_print_cb(): callback prints all path found by the DFS algorithm. */
qos_print_cb(QOSGraphNode * path,int length)295 static void qos_print_cb(QOSGraphNode *path, int length)
296 {
297 #if QGRAPH_PRINT_DEBUG
298 printf("%d elements\n", length);
299
300 if (!path) {
301 return;
302 }
303
304 while (path->path_edge) {
305 printf("%s ", path->name);
306 switch (path->path_edge->type) {
307 case QEDGE_PRODUCES:
308 printf("--PRODUCES--> ");
309 break;
310 case QEDGE_CONSUMED_BY:
311 printf("--CONSUMED_BY--> ");
312 break;
313 case QEDGE_CONTAINS:
314 printf("--CONTAINS--> ");
315 break;
316 }
317 path = search_node(path->path_edge->dest);
318 }
319
320 printf("%s\n\n", path->name);
321 #endif
322 }
323
324 /* qos_push(): push a node @el and edge @e in the qos_node_stack */
qos_push(QOSGraphNode * el,QOSStackElement * parent,QOSGraphEdge * e)325 static void qos_push(QOSGraphNode *el, QOSStackElement *parent,
326 QOSGraphEdge *e)
327 {
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");
331 abort();
332 }
333
334 if (parent) {
335 len = parent->length + 1;
336 }
337 qos_node_stack[qos_node_tos++] = (QOSStackElement) {
338 .node = el,
339 .parent = parent,
340 .parent_edge = e,
341 .length = len,
342 };
343 }
344
345 /* qos_tos(): returns the top of stack, without popping */
qos_tos(void)346 static QOSStackElement *qos_tos(void)
347 {
348 return &qos_node_stack[qos_node_tos - 1];
349 }
350
351 /* qos_pop(): pops an element from the tos, setting it unvisited*/
qos_pop(void)352 static QOSStackElement *qos_pop(void)
353 {
354 if (qos_node_tos == 0) {
355 g_printerr("QOSStack: empty stack, cannot pop");
356 abort();
357 }
358 QOSStackElement *e = qos_tos();
359 e->node->visited = false;
360 qos_node_tos--;
361 return e;
362 }
363
364 /**
365 * qos_reverse_path(): reverses the found path, going from
366 * test-to-machine to machine-to-test
367 */
qos_reverse_path(QOSStackElement * el)368 static QOSGraphNode *qos_reverse_path(QOSStackElement *el)
369 {
370 if (!el) {
371 return NULL;
372 }
373
374 el->node->path_edge = NULL;
375
376 while (el->parent) {
377 el->parent->node->path_edge = el->parent_edge;
378 el = el->parent;
379 }
380
381 return el->node;
382 }
383
384 /**
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.
389 *
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.
393 */
qos_traverse_graph(QOSGraphNode * root,QOSTestCallback callback)394 static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback)
395 {
396 QOSGraphNode *v, *dest_node, *path;
397 QOSStackElement *s_el;
398 QOSGraphEdge *e, *next;
399 QOSGraphEdgeList *list;
400
401 qos_push(root, NULL, NULL);
402
403 while (qos_node_tos > 0) {
404 s_el = qos_tos();
405 v = s_el->node;
406 if (v->visited) {
407 qos_pop();
408 continue;
409 }
410 v->visited = true;
411 list = get_edgelist(v->name);
412 if (!list) {
413 qos_pop();
414 if (v->type == QNODE_TEST) {
415 v->visited = false;
416 path = qos_reverse_path(s_el);
417 callback(path, s_el->length);
418 }
419 } else {
420 QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
421 dest_node = search_node(e->dest);
422
423 if (!dest_node) {
424 fprintf(stderr, "node %s in %s -> %s does not exist\n",
425 e->dest, v->name, e->dest);
426 abort();
427 }
428
429 if (!dest_node->visited && dest_node->available) {
430 qos_push(dest_node, s_el, e);
431 }
432 }
433 }
434 }
435 }
436
437 /* QGRAPH API*/
438
qos_graph_get_node(const char * key)439 QOSGraphNode *qos_graph_get_node(const char *key)
440 {
441 return search_node(key);
442 }
443
qos_graph_has_node(const char * node)444 bool qos_graph_has_node(const char *node)
445 {
446 QOSGraphNode *n = search_node(node);
447 return n != NULL;
448 }
449
qos_graph_get_node_type(const char * node)450 QOSNodeType qos_graph_get_node_type(const char *node)
451 {
452 QOSGraphNode *n = search_node(node);
453 if (n) {
454 return n->type;
455 }
456 return -1;
457 }
458
qos_graph_get_node_availability(const char * node)459 bool qos_graph_get_node_availability(const char *node)
460 {
461 QOSGraphNode *n = search_node(node);
462 if (n) {
463 return n->available;
464 }
465 return false;
466 }
467
qos_graph_get_edge(const char * node,const char * dest)468 QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest)
469 {
470 QOSGraphEdgeList *list = get_edgelist(node);
471 return search_list_edges(list, dest);
472 }
473
qos_graph_edge_get_type(QOSGraphEdge * edge)474 QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge)
475 {
476 if (!edge) {
477 return -1;
478 }
479 return edge->type;
480 }
481
qos_graph_edge_get_dest(QOSGraphEdge * edge)482 char *qos_graph_edge_get_dest(QOSGraphEdge *edge)
483 {
484 if (!edge) {
485 return NULL;
486 }
487 return edge->dest;
488 }
489
qos_graph_edge_get_arg(QOSGraphEdge * edge)490 void *qos_graph_edge_get_arg(QOSGraphEdge *edge)
491 {
492 if (!edge) {
493 return NULL;
494 }
495 return edge->arg;
496 }
497
qos_graph_edge_get_after_cmd_line(QOSGraphEdge * edge)498 char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge)
499 {
500 if (!edge) {
501 return NULL;
502 }
503 return edge->after_cmd_line;
504 }
505
qos_graph_edge_get_before_cmd_line(QOSGraphEdge * edge)506 char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge)
507 {
508 if (!edge) {
509 return NULL;
510 }
511 return edge->before_cmd_line;
512 }
513
qos_graph_edge_get_extra_device_opts(QOSGraphEdge * edge)514 char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge)
515 {
516 if (!edge) {
517 return NULL;
518 }
519 return edge->extra_device_opts;
520 }
521
qos_graph_edge_get_name(QOSGraphEdge * edge)522 char *qos_graph_edge_get_name(QOSGraphEdge *edge)
523 {
524 if (!edge) {
525 return NULL;
526 }
527 return edge->edge_name;
528 }
529
qos_graph_has_edge(const char * start,const char * dest)530 bool qos_graph_has_edge(const char *start, const char *dest)
531 {
532 QOSGraphEdgeList *list = get_edgelist(start);
533 QOSGraphEdge *e = search_list_edges(list, dest);
534 return e != NULL;
535 }
536
qos_graph_get_machine(const char * node)537 QOSGraphNode *qos_graph_get_machine(const char *node)
538 {
539 return search_machine(node);
540 }
541
qos_graph_has_machine(const char * node)542 bool qos_graph_has_machine(const char *node)
543 {
544 QOSGraphNode *m = search_machine(node);
545 return m != NULL;
546 }
547
qos_print_graph(void)548 void qos_print_graph(void)
549 {
550 qos_graph_foreach_test_path(qos_print_cb);
551 }
552
qos_graph_init(void)553 void qos_graph_init(void)
554 {
555 if (!node_table) {
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);
559 }
560
561 if (!edge_table) {
562 edge_table = g_hash_table_new_full(g_str_hash, g_str_equal,
563 destroy_string, destroy_edges);
564 }
565 }
566
qos_graph_destroy(void)567 void qos_graph_destroy(void)
568 {
569 if (node_table) {
570 g_hash_table_destroy(node_table);
571 }
572
573 if (edge_table) {
574 g_hash_table_destroy(edge_table);
575 }
576
577 node_table = NULL;
578 edge_table = NULL;
579 }
580
qos_node_destroy(void * key)581 void qos_node_destroy(void *key)
582 {
583 g_hash_table_remove(node_table, key);
584 }
585
qos_edge_destroy(void * key)586 void qos_edge_destroy(void *key)
587 {
588 g_hash_table_remove(edge_table, key);
589 }
590
qos_add_test(const char * name,const char * interface,QOSTestFunc test_func,QOSGraphTestOptions * opts)591 void qos_add_test(const char *name, const char *interface,
592 QOSTestFunc test_func, QOSGraphTestOptions *opts)
593 {
594 QOSGraphNode *node;
595 char *test_name = g_strdup_printf("%s-tests/%s", interface, name);
596 QOSGraphTestOptions def_opts = { };
597
598 if (!opts) {
599 opts = &def_opts;
600 }
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);
606
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);
611 g_free(test_name);
612 }
613
qos_node_create_machine(const char * name,QOSCreateMachineFunc function)614 void qos_node_create_machine(const char *name, QOSCreateMachineFunc function)
615 {
616 qos_node_create_machine_args(name, function, NULL);
617 }
618
qos_node_create_machine_args(const char * name,QOSCreateMachineFunc function,const char * opts)619 void qos_node_create_machine_args(const char *name,
620 QOSCreateMachineFunc function,
621 const char *opts)
622 {
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);
627 }
628
qos_node_create_driver(const char * name,QOSCreateDriverFunc function)629 void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
630 {
631 QOSGraphNode *node = create_node(name, QNODE_DRIVER);
632 build_driver_cmd_line(node);
633 node->u.driver.constructor = function;
634 }
635
qos_node_create_driver_named(const char * name,const char * qemu_name,QOSCreateDriverFunc function)636 void qos_node_create_driver_named(const char *name, const char *qemu_name,
637 QOSCreateDriverFunc function)
638 {
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;
643 }
644
qos_node_contains(const char * container,const char * contained,QOSGraphEdgeOptions * opts,...)645 void qos_node_contains(const char *container, const char *contained,
646 QOSGraphEdgeOptions *opts, ...)
647 {
648 va_list va;
649
650 if (opts == NULL) {
651 add_edge(container, contained, QEDGE_CONTAINS, NULL);
652 return;
653 }
654
655 va_start(va, opts);
656 do {
657 add_edge(container, contained, QEDGE_CONTAINS, opts);
658 opts = va_arg(va, QOSGraphEdgeOptions *);
659 } while (opts != NULL);
660
661 va_end(va);
662 }
663
qos_node_produces(const char * producer,const char * interface)664 void qos_node_produces(const char *producer, const char *interface)
665 {
666 create_interface(interface);
667 add_edge(producer, interface, QEDGE_PRODUCES, NULL);
668 }
669
qos_node_consumes(const char * consumer,const char * interface,QOSGraphEdgeOptions * opts)670 void qos_node_consumes(const char *consumer, const char *interface,
671 QOSGraphEdgeOptions *opts)
672 {
673 create_interface(interface);
674 add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts);
675 }
676
qos_graph_node_set_availability_explicit(const char * node,bool av)677 static void qos_graph_node_set_availability_explicit(const char *node, bool av)
678 {
679 QOSGraphEdgeList *elist;
680 QOSGraphNode *n = search_node(node);
681 QOSGraphEdge *e, *next;
682 if (!n) {
683 return;
684 }
685 n->available = av;
686 elist = get_edgelist(node);
687 if (!elist) {
688 return;
689 }
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);
693 }
694 }
695 }
696
697 /*
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.
701 */
qos_graph_node_set_availability(const char * node,bool av)702 void qos_graph_node_set_availability(const char *node, bool av)
703 {
704 GList *l;
705 QOSGraphEdgeList *elist;
706 QOSGraphEdge *e, *next;
707 QOSGraphNode *n;
708 GList *keys = g_hash_table_get_keys(node_table);
709
710 for (l = keys; l != NULL; l = l->next) {
711 const gchar *key = l->data;
712 n = g_hash_table_lookup(node_table, key);
713 /*
714 * node's 'qemu_name' is set if there is more than one device with
715 * the same QEMU (QMP) device name
716 */
717 const char *node_name = n->qemu_name ?: n->name;
718 if (g_strcmp0(node_name, node) == 0) {
719 n->available = av;
720 elist = get_edgelist(n->name);
721 if (elist) {
722 QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
723 if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES)
724 {
725 qos_graph_node_set_availability_explicit(e->dest, av);
726 }
727 }
728 }
729 }
730 }
731 g_list_free(keys);
732 }
733
qos_graph_foreach_test_path(QOSTestCallback fn)734 void qos_graph_foreach_test_path(QOSTestCallback fn)
735 {
736 QOSGraphNode *root = qos_graph_get_node(QOS_ROOT);
737 qos_traverse_graph(root, fn);
738 }
739
qos_machine_new(QOSGraphNode * node,QTestState * qts)740 QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts)
741 {
742 QOSGraphObject *obj;
743
744 g_assert(node->type == QNODE_MACHINE);
745 obj = node->u.machine.constructor(qts);
746 obj->free = g_free;
747 return obj;
748 }
749
qos_driver_new(QOSGraphNode * node,QOSGraphObject * parent,QGuestAllocator * alloc,void * arg)750 QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
751 QGuestAllocator *alloc, void *arg)
752 {
753 QOSGraphObject *obj;
754
755 g_assert(node->type == QNODE_DRIVER);
756 obj = node->u.driver.constructor(parent, alloc, arg);
757 obj->free = g_free;
758 return obj;
759 }
760
qos_object_destroy(QOSGraphObject * obj)761 void qos_object_destroy(QOSGraphObject *obj)
762 {
763 if (!obj) {
764 return;
765 }
766 if (obj->destructor) {
767 obj->destructor(obj);
768 }
769 if (obj->free) {
770 obj->free(obj);
771 }
772 }
773
qos_object_queue_destroy(QOSGraphObject * obj)774 void qos_object_queue_destroy(QOSGraphObject *obj)
775 {
776 g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj);
777 }
778
qos_object_start_hw(QOSGraphObject * obj)779 void qos_object_start_hw(QOSGraphObject *obj)
780 {
781 if (obj->start_hw) {
782 obj->start_hw(obj);
783 }
784 }
785
qos_get_machine_type(char * name)786 char *qos_get_machine_type(char *name)
787 {
788 while (*name != '\0' && *name != '/') {
789 name++;
790 }
791
792 if (!*name || !name[1]) {
793 fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n");
794 abort();
795 }
796
797 return name + 1;
798 }
799
qos_delete_cmd_line(const char * name)800 void qos_delete_cmd_line(const char *name)
801 {
802 QOSGraphNode *node = search_node(name);
803 if (node) {
804 g_free(node->command_line);
805 node->command_line = NULL;
806 }
807 }
808
qos_dump_graph(void)809 void qos_dump_graph(void)
810 {
811 GList *keys;
812 GList *l;
813 QOSGraphEdgeList *list;
814 QOSGraphEdge *e, *next;
815 QOSGraphNode *dest_node, *node;
816
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);
827 if (!dest_node) {
828 qos_printf_literal(" <------- ERROR !");
829 }
830 qos_printf_literal("\n");
831 }
832 }
833 g_list_free(keys);
834 qos_printf("}\n");
835
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);
844 }
845 qos_printf_literal("type=%d cmd_line='%s' [%s]\n",
846 node->type, node->command_line,
847 node->available ? "available" : "UNAVAILABLE"
848 );
849 }
850 g_list_free(keys);
851 qos_printf("}\n");
852 }
853