]>
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 | #ifndef QGRAPH_H | |
20 | #define QGRAPH_H | |
21 | ||
fc281c80 | 22 | #include <gmodule.h> |
fc281c80 EGE |
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 "libqos/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 "libqos/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=x86_64-softmmu/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 libqos/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 an edge of type QEDGE_CONTAINS and | |
457 | * adds it to the edge list mapped to @container in the | |
458 | * edge hash table. | |
459 | * | |
460 | * This edge will have @container as source and @contained as destination. | |
461 | * | |
462 | * It also has the possibility to add optional NULL-terminated | |
463 | * @opts parameters (see %QOSGraphEdgeOptions) | |
464 | * | |
465 | * This function can be useful when there are multiple devices | |
466 | * with the same node name contained in a machine/other node | |
467 | * | |
468 | * For example, if "arm/raspi2" contains 2 "generic-sdhci" | |
469 | * devices, the right commands will be: | |
470 | * qos_node_create_machine("arm/raspi2"); | |
471 | * qos_node_create_driver("generic-sdhci", constructor); | |
472 | * //assume rest of the fields are set NULL | |
473 | * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" }; | |
474 | * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" }; | |
475 | * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL); | |
476 | * | |
477 | * Of course this also requires that the @container's get_device function | |
478 | * should implement a case for "emmc" and "sdcard". | |
479 | * | |
480 | * For contains, op1.arg and op1.size_arg represent the arg to pass | |
481 | * to @contained constructor to properly initialize it. | |
482 | */ | |
483 | void qos_node_contains(const char *container, const char *contained, ...); | |
484 | ||
485 | /** | |
486 | * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and | |
487 | * adds it to the edge list mapped to @producer in the | |
488 | * edge hash table. | |
489 | * | |
490 | * This edge will have @producer as source and @interface as destination. | |
491 | */ | |
492 | void qos_node_produces(const char *producer, const char *interface); | |
493 | ||
494 | /** | |
495 | * qos_node_consumes(): creates an edge of type QEDGE_CONSUMED_BY and | |
496 | * adds it to the edge list mapped to @interface in the | |
497 | * edge hash table. | |
498 | * | |
499 | * This edge will have @interface as source and @consumer as destination. | |
500 | * It also has the possibility to add an optional @opts | |
501 | * (see %QOSGraphEdgeOptions) | |
502 | */ | |
503 | void qos_node_consumes(const char *consumer, const char *interface, | |
504 | QOSGraphEdgeOptions *opts); | |
505 | ||
506 | /** | |
507 | * qos_invalidate_command_line(): invalidates current command line, so that | |
508 | * qgraph framework cannot try to cache the current command line and | |
509 | * forces QEMU to restart. | |
510 | */ | |
511 | void qos_invalidate_command_line(void); | |
512 | ||
513 | /** | |
514 | * qos_get_current_command_line(): return the command line required by the | |
515 | * machine and driver objects. This is the same string that was passed to | |
516 | * the test's "before" callback, if any. | |
517 | */ | |
518 | const char *qos_get_current_command_line(void); | |
519 | ||
520 | /** | |
521 | * qos_allocate_objects(): | |
522 | * @qts: The #QTestState that will be referred to by the machine object. | |
523 | * @alloc: Where to store the allocator for the machine object, or %NULL. | |
524 | * | |
525 | * Allocate driver objects for the current test | |
526 | * path, but relative to the QTestState @qts. | |
527 | * | |
528 | * Returns a test object just like the one that was passed to | |
529 | * the test function, but relative to @qts. | |
530 | */ | |
531 | void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc); | |
532 | ||
533 | /** | |
534 | * qos_object_destroy(): calls the destructor for @obj | |
535 | */ | |
536 | void qos_object_destroy(QOSGraphObject *obj); | |
537 | ||
538 | /** | |
539 | * qos_object_queue_destroy(): queue the destructor for @obj so that it is | |
540 | * called at the end of the test | |
541 | */ | |
542 | void qos_object_queue_destroy(QOSGraphObject *obj); | |
543 | ||
544 | /** | |
545 | * qos_object_start_hw(): calls the start_hw function for @obj | |
546 | */ | |
547 | void qos_object_start_hw(QOSGraphObject *obj); | |
548 | ||
549 | /** | |
550 | * qos_machine_new(): instantiate a new machine node | |
551 | * @node: A machine node to be instantiated | |
552 | * @qts: The #QTestState that will be referred to by the machine object. | |
553 | * | |
554 | * Returns a machine object. | |
555 | */ | |
556 | QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); | |
557 | ||
558 | /** | |
559 | * qos_machine_new(): instantiate a new driver node | |
560 | * @node: A driver node to be instantiated | |
561 | * @parent: A #QOSGraphObject to be consumed by the new driver node | |
562 | * @alloc: An allocator to be used by the new driver node. | |
563 | * @arg: The argument for the consumed-by edge to @node. | |
564 | * | |
565 | * Calls the constructor for the driver object. | |
566 | */ | |
567 | QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, | |
568 | QGuestAllocator *alloc, void *arg); | |
569 | ||
570 | ||
571 | #endif |