]>
Commit | Line | Data |
---|---|---|
fc281c80 EGE |
1 | /* |
2 | * libqos driver framework | |
3 | * | |
4 | * Copyright (c) 2018 Emanuele Giuseppe Esposito <[email protected]> | |
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 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 "libqos/qgraph_internal.h" | |
23 | #include "libqos/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 enty 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 | */ | |
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); | |
c19f2b71 | 80 | QOSGraphEdgeOptions def_opts = { }; |
fc281c80 EGE |
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) { | |
c19f2b71 | 89 | opts = &def_opts; |
fc281c80 EGE |
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_memdup(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 */ | |
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 | */ | |
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 | */ | |
153 | static void destroy_node(void *val) | |
154 | { | |
155 | QOSGraphNode *node = val; | |
156 | g_free(node->command_line); | |
157 | g_free(node); | |
158 | } | |
159 | ||
160 | /** | |
161 | * destroy_string(): frees @key from the nodes hash table. | |
162 | * Actually frees the node->name | |
163 | */ | |
164 | static void destroy_string(void *key) | |
165 | { | |
166 | g_free(key); | |
167 | } | |
168 | ||
169 | /** | |
170 | * search_node(): search for a node @key in the nodes hash table | |
171 | * Returns the QOSGraphNode if found, #NULL otherwise | |
172 | */ | |
173 | static QOSGraphNode *search_node(const char *key) | |
174 | { | |
175 | return g_hash_table_lookup(node_table, key); | |
176 | } | |
177 | ||
178 | /** | |
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 | |
182 | * | |
183 | * Returns: on success: the %QOSGraphEdgeList | |
184 | * otherwise: abort() | |
185 | */ | |
186 | static QOSGraphEdgeList *get_edgelist(const char *key) | |
187 | { | |
188 | return g_hash_table_lookup(edge_table, key); | |
189 | } | |
190 | ||
191 | /** | |
192 | * search_list_edges(): search for an edge with destination @dest | |
193 | * in the given @edgelist. | |
194 | * | |
195 | * Returns: on success: the %QOSGraphEdge | |
196 | * otherwise: #NULL | |
197 | */ | |
198 | static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist, | |
199 | const char *dest) | |
200 | { | |
201 | QOSGraphEdge *tmp, *next; | |
202 | if (!edgelist) { | |
203 | return NULL; | |
204 | } | |
205 | QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) { | |
206 | if (g_strcmp0(tmp->dest, dest) == 0) { | |
207 | break; | |
208 | } | |
209 | } | |
210 | return tmp; | |
211 | } | |
212 | ||
213 | /** | |
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 | |
218 | * | |
219 | * Returns: on success: the %QOSGraphNode | |
220 | * otherwise: #NULL | |
221 | */ | |
222 | static QOSGraphNode *search_machine(const char *name) | |
223 | { | |
224 | QOSGraphNode *n; | |
225 | QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT); | |
226 | QOSGraphEdge *e = search_list_edges(root_list, name); | |
227 | if (!e) { | |
228 | return NULL; | |
229 | } | |
230 | n = search_node(e->dest); | |
231 | if (n->type == QNODE_MACHINE) { | |
232 | return n; | |
233 | } | |
234 | return NULL; | |
235 | } | |
236 | ||
237 | /** | |
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. | |
243 | */ | |
244 | static void create_interface(const char *node) | |
245 | { | |
246 | QOSGraphNode *interface; | |
247 | interface = search_node(node); | |
248 | if (!interface) { | |
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); | |
252 | abort(); | |
253 | } | |
254 | } | |
255 | ||
256 | /** | |
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. | |
260 | * | |
261 | * It is also possible to pass an optional @args that will be | |
262 | * concatenated to the command line. | |
263 | * | |
264 | * For machines, prepend -M to the machine name. ", @rgs" is added | |
265 | * after the -M <machine> command. | |
266 | */ | |
267 | static void build_machine_cmd_line(QOSGraphNode *node, const char *args) | |
268 | { | |
269 | char *machine = qos_get_machine_type(node->name); | |
270 | if (args) { | |
271 | node->command_line = g_strconcat("-M ", machine, ",", args, NULL); | |
272 | } else { | |
273 | node->command_line = g_strconcat("-M ", machine, " ", NULL); | |
274 | } | |
275 | } | |
276 | ||
277 | /** | |
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. | |
281 | * | |
282 | * Driver do not need additional command line, since it will be | |
283 | * provided by the edge options. | |
284 | * | |
285 | * For drivers, prepend -device to the node name. | |
286 | */ | |
287 | static void build_driver_cmd_line(QOSGraphNode *node) | |
288 | { | |
289 | node->command_line = g_strconcat(" -device ", node->name, NULL); | |
290 | } | |
291 | ||
292 | /* qos_print_cb(): callback prints all path found by the DFS algorithm. */ | |
293 | static void qos_print_cb(QOSGraphNode *path, int length) | |
294 | { | |
295 | #if QGRAPH_PRINT_DEBUG | |
296 | printf("%d elements\n", length); | |
297 | ||
298 | if (!path) { | |
299 | return; | |
300 | } | |
301 | ||
302 | while (path->path_edge) { | |
303 | printf("%s ", path->name); | |
304 | switch (path->path_edge->type) { | |
305 | case QEDGE_PRODUCES: | |
306 | printf("--PRODUCES--> "); | |
307 | break; | |
308 | case QEDGE_CONSUMED_BY: | |
309 | printf("--CONSUMED_BY--> "); | |
310 | break; | |
311 | case QEDGE_CONTAINS: | |
312 | printf("--CONTAINS--> "); | |
313 | break; | |
314 | } | |
315 | path = search_node(path->path_edge->dest); | |
316 | } | |
317 | ||
318 | printf("%s\n\n", path->name); | |
319 | #endif | |
320 | } | |
321 | ||
322 | /* qos_push(): push a node @el and edge @e in the qos_node_stack */ | |
323 | static void qos_push(QOSGraphNode *el, QOSStackElement *parent, | |
324 | QOSGraphEdge *e) | |
325 | { | |
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"); | |
329 | abort(); | |
330 | } | |
331 | ||
332 | if (parent) { | |
333 | len = parent->length + 1; | |
334 | } | |
335 | qos_node_stack[qos_node_tos++] = (QOSStackElement) { | |
336 | .node = el, | |
337 | .parent = parent, | |
338 | .parent_edge = e, | |
339 | .length = len, | |
340 | }; | |
341 | } | |
342 | ||
343 | /* qos_tos(): returns the top of stack, without popping */ | |
344 | static QOSStackElement *qos_tos(void) | |
345 | { | |
346 | return &qos_node_stack[qos_node_tos - 1]; | |
347 | } | |
348 | ||
349 | /* qos_pop(): pops an element from the tos, setting it unvisited*/ | |
350 | static QOSStackElement *qos_pop(void) | |
351 | { | |
352 | if (qos_node_tos == 0) { | |
353 | g_printerr("QOSStack: empty stack, cannot pop"); | |
354 | abort(); | |
355 | } | |
356 | QOSStackElement *e = qos_tos(); | |
357 | e->node->visited = false; | |
358 | qos_node_tos--; | |
359 | return e; | |
360 | } | |
361 | ||
362 | /** | |
363 | * qos_reverse_path(): reverses the found path, going from | |
364 | * test-to-machine to machine-to-test | |
365 | */ | |
366 | static QOSGraphNode *qos_reverse_path(QOSStackElement *el) | |
367 | { | |
368 | if (!el) { | |
369 | return NULL; | |
370 | } | |
371 | ||
372 | el->node->path_edge = NULL; | |
373 | ||
374 | while (el->parent) { | |
375 | el->parent->node->path_edge = el->parent_edge; | |
376 | el = el->parent; | |
377 | } | |
378 | ||
379 | return el->node; | |
380 | } | |
381 | ||
382 | /** | |
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. | |
387 | * | |
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. | |
391 | */ | |
392 | static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback) | |
393 | { | |
394 | QOSGraphNode *v, *dest_node, *path; | |
395 | QOSStackElement *s_el; | |
396 | QOSGraphEdge *e, *next; | |
397 | QOSGraphEdgeList *list; | |
398 | ||
399 | qos_push(root, NULL, NULL); | |
400 | ||
401 | while (qos_node_tos > 0) { | |
402 | s_el = qos_tos(); | |
403 | v = s_el->node; | |
404 | if (v->visited) { | |
405 | qos_pop(); | |
406 | continue; | |
407 | } | |
408 | v->visited = true; | |
409 | list = get_edgelist(v->name); | |
410 | if (!list) { | |
411 | qos_pop(); | |
412 | if (v->type == QNODE_TEST) { | |
413 | v->visited = false; | |
414 | path = qos_reverse_path(s_el); | |
415 | callback(path, s_el->length); | |
416 | } | |
417 | } else { | |
418 | QSLIST_FOREACH_SAFE(e, list, edge_list, next) { | |
419 | dest_node = search_node(e->dest); | |
420 | ||
421 | if (!dest_node) { | |
422 | fprintf(stderr, "node %s in %s -> %s does not exist\n", | |
423 | e->dest, v->name, e->dest); | |
424 | abort(); | |
425 | } | |
426 | ||
427 | if (!dest_node->visited && dest_node->available) { | |
428 | qos_push(dest_node, s_el, e); | |
429 | } | |
430 | } | |
431 | } | |
432 | } | |
433 | } | |
434 | ||
435 | /* QGRAPH API*/ | |
436 | ||
437 | QOSGraphNode *qos_graph_get_node(const char *key) | |
438 | { | |
439 | return search_node(key); | |
440 | } | |
441 | ||
442 | bool qos_graph_has_node(const char *node) | |
443 | { | |
444 | QOSGraphNode *n = search_node(node); | |
445 | return n != NULL; | |
446 | } | |
447 | ||
448 | QOSNodeType qos_graph_get_node_type(const char *node) | |
449 | { | |
450 | QOSGraphNode *n = search_node(node); | |
451 | if (n) { | |
452 | return n->type; | |
453 | } | |
454 | return -1; | |
455 | } | |
456 | ||
457 | bool qos_graph_get_node_availability(const char *node) | |
458 | { | |
459 | QOSGraphNode *n = search_node(node); | |
460 | if (n) { | |
461 | return n->available; | |
462 | } | |
463 | return false; | |
464 | } | |
465 | ||
466 | QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest) | |
467 | { | |
468 | QOSGraphEdgeList *list = get_edgelist(node); | |
469 | return search_list_edges(list, dest); | |
470 | } | |
471 | ||
472 | QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge) | |
473 | { | |
474 | if (!edge) { | |
475 | return -1; | |
476 | } | |
477 | return edge->type;; | |
478 | } | |
479 | ||
480 | char *qos_graph_edge_get_dest(QOSGraphEdge *edge) | |
481 | { | |
482 | if (!edge) { | |
483 | return NULL; | |
484 | } | |
485 | return edge->dest; | |
486 | } | |
487 | ||
488 | void *qos_graph_edge_get_arg(QOSGraphEdge *edge) | |
489 | { | |
490 | if (!edge) { | |
491 | return NULL; | |
492 | } | |
493 | return edge->arg; | |
494 | } | |
495 | ||
496 | char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge) | |
497 | { | |
498 | if (!edge) { | |
499 | return NULL; | |
500 | } | |
501 | return edge->after_cmd_line; | |
502 | } | |
503 | ||
504 | char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge) | |
505 | { | |
506 | if (!edge) { | |
507 | return NULL; | |
508 | } | |
509 | return edge->before_cmd_line; | |
510 | } | |
511 | ||
512 | char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge) | |
513 | { | |
514 | if (!edge) { | |
515 | return NULL; | |
516 | } | |
517 | return edge->extra_device_opts; | |
518 | } | |
519 | ||
520 | char *qos_graph_edge_get_name(QOSGraphEdge *edge) | |
521 | { | |
522 | if (!edge) { | |
523 | return NULL; | |
524 | } | |
525 | return edge->edge_name; | |
526 | } | |
527 | ||
528 | bool qos_graph_has_edge(const char *start, const char *dest) | |
529 | { | |
530 | QOSGraphEdgeList *list = get_edgelist(start); | |
531 | QOSGraphEdge *e = search_list_edges(list, dest); | |
532 | return e != NULL; | |
533 | } | |
534 | ||
535 | QOSGraphNode *qos_graph_get_machine(const char *node) | |
536 | { | |
537 | return search_machine(node); | |
538 | } | |
539 | ||
540 | bool qos_graph_has_machine(const char *node) | |
541 | { | |
542 | QOSGraphNode *m = search_machine(node); | |
543 | return m != NULL; | |
544 | } | |
545 | ||
546 | void qos_print_graph(void) | |
547 | { | |
548 | qos_graph_foreach_test_path(qos_print_cb); | |
549 | } | |
550 | ||
551 | void qos_graph_init(void) | |
552 | { | |
553 | if (!node_table) { | |
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); | |
557 | } | |
558 | ||
559 | if (!edge_table) { | |
560 | edge_table = g_hash_table_new_full(g_str_hash, g_str_equal, | |
561 | destroy_string, destroy_edges); | |
562 | } | |
563 | } | |
564 | ||
565 | void qos_graph_destroy(void) | |
566 | { | |
567 | if (node_table) { | |
568 | g_hash_table_destroy(node_table); | |
569 | } | |
570 | ||
571 | if (edge_table) { | |
572 | g_hash_table_destroy(edge_table); | |
573 | } | |
574 | ||
575 | node_table = NULL; | |
576 | edge_table = NULL; | |
577 | } | |
578 | ||
579 | void qos_node_destroy(void *key) | |
580 | { | |
581 | g_hash_table_remove(node_table, key); | |
582 | } | |
583 | ||
584 | void qos_edge_destroy(void *key) | |
585 | { | |
586 | g_hash_table_remove(edge_table, key); | |
587 | } | |
588 | ||
589 | void qos_add_test(const char *name, const char *interface, | |
590 | QOSTestFunc test_func, QOSGraphTestOptions *opts) | |
591 | { | |
592 | QOSGraphNode *node; | |
593 | char *test_name = g_strdup_printf("%s-tests/%s", interface, name);; | |
c19f2b71 | 594 | QOSGraphTestOptions def_opts = { }; |
fc281c80 EGE |
595 | |
596 | if (!opts) { | |
c19f2b71 | 597 | opts = &def_opts; |
fc281c80 EGE |
598 | } |
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); | |
604 | ||
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); | |
609 | g_free(test_name); | |
610 | } | |
611 | ||
612 | void qos_node_create_machine(const char *name, QOSCreateMachineFunc function) | |
613 | { | |
614 | qos_node_create_machine_args(name, function, NULL); | |
615 | } | |
616 | ||
617 | void qos_node_create_machine_args(const char *name, | |
618 | QOSCreateMachineFunc function, | |
619 | const char *opts) | |
620 | { | |
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); | |
625 | } | |
626 | ||
627 | void qos_node_create_driver(const char *name, QOSCreateDriverFunc function) | |
628 | { | |
629 | QOSGraphNode *node = create_node(name, QNODE_DRIVER); | |
630 | build_driver_cmd_line(node); | |
631 | node->u.driver.constructor = function; | |
632 | } | |
633 | ||
634 | void qos_node_contains(const char *container, const char *contained, | |
635 | ...) | |
636 | { | |
637 | va_list va; | |
638 | va_start(va, contained); | |
639 | QOSGraphEdgeOptions *opts; | |
640 | ||
641 | do { | |
642 | opts = va_arg(va, QOSGraphEdgeOptions *); | |
643 | add_edge(container, contained, QEDGE_CONTAINS, opts); | |
644 | } while (opts != NULL); | |
645 | ||
646 | va_end(va); | |
647 | } | |
648 | ||
649 | void qos_node_produces(const char *producer, const char *interface) | |
650 | { | |
651 | create_interface(interface); | |
652 | add_edge(producer, interface, QEDGE_PRODUCES, NULL); | |
653 | } | |
654 | ||
655 | void qos_node_consumes(const char *consumer, const char *interface, | |
656 | QOSGraphEdgeOptions *opts) | |
657 | { | |
658 | create_interface(interface); | |
659 | add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts); | |
660 | } | |
661 | ||
662 | void qos_graph_node_set_availability(const char *node, bool av) | |
663 | { | |
664 | QOSGraphEdgeList *elist; | |
665 | QOSGraphNode *n = search_node(node); | |
666 | QOSGraphEdge *e, *next; | |
667 | if (!n) { | |
668 | return; | |
669 | } | |
670 | n->available = av; | |
671 | elist = get_edgelist(node); | |
672 | if (!elist) { | |
673 | return; | |
674 | } | |
675 | QSLIST_FOREACH_SAFE(e, elist, edge_list, next) { | |
676 | if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) { | |
677 | qos_graph_node_set_availability(e->dest, av); | |
678 | } | |
679 | } | |
680 | } | |
681 | ||
682 | void qos_graph_foreach_test_path(QOSTestCallback fn) | |
683 | { | |
684 | QOSGraphNode *root = qos_graph_get_node(QOS_ROOT); | |
685 | qos_traverse_graph(root, fn); | |
686 | } | |
687 | ||
688 | QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts) | |
689 | { | |
690 | QOSGraphObject *obj; | |
691 | ||
692 | g_assert(node->type == QNODE_MACHINE); | |
693 | obj = node->u.machine.constructor(qts); | |
694 | obj->free = g_free; | |
695 | return obj; | |
696 | } | |
697 | ||
698 | QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, | |
699 | QGuestAllocator *alloc, void *arg) | |
700 | { | |
701 | QOSGraphObject *obj; | |
702 | ||
703 | g_assert(node->type == QNODE_DRIVER); | |
704 | obj = node->u.driver.constructor(parent, alloc, arg); | |
705 | obj->free = g_free; | |
706 | return obj; | |
707 | } | |
708 | ||
709 | void qos_object_destroy(QOSGraphObject *obj) | |
710 | { | |
711 | if (!obj) { | |
712 | return; | |
713 | } | |
714 | if (obj->destructor) { | |
715 | obj->destructor(obj); | |
716 | } | |
717 | if (obj->free) { | |
718 | obj->free(obj); | |
719 | } | |
720 | } | |
721 | ||
722 | void qos_object_queue_destroy(QOSGraphObject *obj) | |
723 | { | |
724 | g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj); | |
725 | } | |
726 | ||
727 | void qos_object_start_hw(QOSGraphObject *obj) | |
728 | { | |
729 | if (obj->start_hw) { | |
730 | obj->start_hw(obj); | |
731 | } | |
732 | } | |
733 | ||
734 | char *qos_get_machine_type(char *name) | |
735 | { | |
736 | while (*name != '\0' && *name != '/') { | |
737 | name++; | |
738 | } | |
739 | ||
740 | if (!*name || !name[1]) { | |
741 | fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n"); | |
742 | abort(); | |
743 | } | |
744 | ||
745 | return name + 1; | |
746 | } | |
747 | ||
748 | void qos_delete_cmd_line(const char *name) | |
749 | { | |
750 | QOSGraphNode *node = search_node(name); | |
751 | if (node) { | |
752 | g_free(node->command_line); | |
753 | node->command_line = NULL; | |
754 | } | |
755 | } |