| 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 | #ifndef QGRAPH_H |
| 20 | #define QGRAPH_H |
| 21 | |
| 22 | #include <gmodule.h> |
| 23 | #include "qemu/module.h" |
| 24 | #include "libqos-malloc.h" |
| 25 | |
| 26 | /* maximum path length */ |
| 27 | #define QOS_PATH_MAX_ELEMENT_SIZE 128 |
| 28 | |
| 29 | typedef struct QOSGraphObject QOSGraphObject; |
| 30 | typedef struct QOSGraphNode QOSGraphNode; |
| 31 | typedef struct QOSGraphEdge QOSGraphEdge; |
| 32 | typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions; |
| 33 | typedef struct QOSGraphTestOptions QOSGraphTestOptions; |
| 34 | |
| 35 | /* Constructor for drivers, machines and test */ |
| 36 | typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc, |
| 37 | void *addr); |
| 38 | typedef void *(*QOSCreateMachineFunc) (QTestState *qts); |
| 39 | typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc); |
| 40 | |
| 41 | /* QOSGraphObject functions */ |
| 42 | typedef void *(*QOSGetDriver) (void *object, const char *interface); |
| 43 | typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name); |
| 44 | typedef void (*QOSDestructorFunc) (QOSGraphObject *object); |
| 45 | typedef void (*QOSStartFunct) (QOSGraphObject *object); |
| 46 | |
| 47 | /* Test options functions */ |
| 48 | typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg); |
| 49 | |
| 50 | /** |
| 51 | * struct QOSGraphEdgeOptions: |
| 52 | * Edge options to be passed to the contains/consumes \*_args function. |
| 53 | * @arg: optional arg that will be used by dest edge |
| 54 | * @size_arg: @arg size that will be used by dest edge |
| 55 | * @extra_device_opts: optional additional command line for dest |
| 56 | * edge, used to add additional attributes |
| 57 | * *after* the node command line, the |
| 58 | * framework automatically prepends "," |
| 59 | * to this argument. |
| 60 | * @before_cmd_line: optional additional command line for dest |
| 61 | * edge, used to add additional attributes |
| 62 | * *before* the node command line, usually |
| 63 | * other non-node represented commands, |
| 64 | * like "-fdsev synt" |
| 65 | * @after_cmd_line: optional extra command line to be added |
| 66 | * after the device command. This option |
| 67 | * is used to add other devices |
| 68 | * command line that depend on current node. |
| 69 | * Automatically prepends " " to this argument |
| 70 | * @edge_name: optional edge to differentiate multiple |
| 71 | * devices with same node name |
| 72 | */ |
| 73 | struct QOSGraphEdgeOptions { |
| 74 | void *arg; |
| 75 | uint32_t size_arg; |
| 76 | const char *extra_device_opts; |
| 77 | const char *before_cmd_line; |
| 78 | const char *after_cmd_line; |
| 79 | const char *edge_name; |
| 80 | }; |
| 81 | |
| 82 | /** |
| 83 | * struct QOSGraphTestOptions: |
| 84 | * Test options to be passed to the test functions. |
| 85 | * @edge: edge arguments that will be used by test. |
| 86 | * Note that test *does not* use edge_name, |
| 87 | * and uses instead arg and size_arg as |
| 88 | * data arg for its test function. |
| 89 | * @arg: if @before is non-NULL, pass @arg there. |
| 90 | * Otherwise pass it to the test function. |
| 91 | * @before: executed before the test. Used to add |
| 92 | * additional parameters to the command line |
| 93 | * and modify the argument to the test function. |
| 94 | * @subprocess: run the test in a subprocess. |
| 95 | */ |
| 96 | struct QOSGraphTestOptions { |
| 97 | QOSGraphEdgeOptions edge; |
| 98 | void *arg; |
| 99 | QOSBeforeTest before; |
| 100 | bool subprocess; |
| 101 | }; |
| 102 | |
| 103 | /** |
| 104 | * struct QOSGraphObject: |
| 105 | * Each driver, test or machine of this framework will have a |
| 106 | * QOSGraphObject as first field. |
| 107 | * |
| 108 | * This set of functions offered by QOSGraphObject are executed |
| 109 | * in different stages of the framework: |
| 110 | * @get_driver: see @get_device |
| 111 | * @get_device: Once a machine-to-test path has been |
| 112 | * found, the framework traverses it again and allocates all the |
| 113 | * nodes, using the provided constructor. To satisfy their |
| 114 | * relations, i.e. for produces or contains, where a struct |
| 115 | * constructor needs an external parameter represented by the |
| 116 | * previous node, the framework will call |
| 117 | * @get_device (for contains) or @get_driver (for produces), |
| 118 | * depending on the edge type, passing them the name of the next |
| 119 | * node to be taken and getting from them the corresponding |
| 120 | * pointer to the actual structure of the next node to |
| 121 | * be used in the path. |
| 122 | * @start_hw: This function is executed after all the path objects |
| 123 | * have been allocated, but before the test is run. It starts the |
| 124 | * hw, setting the initial configurations (\*_device_enable) and |
| 125 | * making it ready for the test. |
| 126 | * @destructor: Opposite to the node constructor, destroys the object. |
| 127 | * This function is called after the test has been executed, and |
| 128 | * performs a complete cleanup of each node allocated field. |
| 129 | * In case no constructor is provided, no destructor will be |
| 130 | * called. |
| 131 | * @free: free the memory associated to the QOSGraphObject and its contained |
| 132 | * children |
| 133 | */ |
| 134 | struct QOSGraphObject { |
| 135 | QOSGetDriver get_driver; |
| 136 | QOSGetDevice get_device; |
| 137 | QOSStartFunct start_hw; |
| 138 | QOSDestructorFunc destructor; |
| 139 | GDestroyNotify free; |
| 140 | }; |
| 141 | |
| 142 | /** |
| 143 | * qos_graph_init(): initialize the framework, creates two hash |
| 144 | * tables: one for the nodes and another for the edges. |
| 145 | */ |
| 146 | void qos_graph_init(void); |
| 147 | |
| 148 | /** |
| 149 | * qos_graph_destroy(): deallocates all the hash tables, |
| 150 | * freeing all nodes and edges. |
| 151 | */ |
| 152 | void qos_graph_destroy(void); |
| 153 | |
| 154 | /** |
| 155 | * qos_node_destroy(): removes and frees a node from the |
| 156 | * nodes hash table. |
| 157 | * @key: Name of the node |
| 158 | */ |
| 159 | void qos_node_destroy(void *key); |
| 160 | |
| 161 | /** |
| 162 | * qos_edge_destroy(): removes and frees an edge from the |
| 163 | * edges hash table. |
| 164 | * @key: Name of the node |
| 165 | */ |
| 166 | void qos_edge_destroy(void *key); |
| 167 | |
| 168 | /** |
| 169 | * qos_add_test(): adds a test node @name to the nodes hash table. |
| 170 | * @name: Name of the test |
| 171 | * @interface: Name of the interface node it consumes |
| 172 | * @test_func: Actual test to perform |
| 173 | * @opts: Facultative options (see %QOSGraphTestOptions) |
| 174 | * |
| 175 | * The test will consume a @interface node, and once the |
| 176 | * graph walking algorithm has found it, the @test_func will be |
| 177 | * executed. It also has the possibility to |
| 178 | * add an optional @opts (see %QOSGraphTestOptions). |
| 179 | * |
| 180 | * For tests, opts->edge.arg and size_arg represent the arg to pass |
| 181 | * to @test_func |
| 182 | */ |
| 183 | void qos_add_test(const char *name, const char *interface, |
| 184 | QOSTestFunc test_func, |
| 185 | QOSGraphTestOptions *opts); |
| 186 | |
| 187 | /** |
| 188 | * qos_node_create_machine(): creates the machine @name and |
| 189 | * adds it to the node hash table. |
| 190 | * @name: Name of the machine |
| 191 | * @function: Machine constructor |
| 192 | * |
| 193 | * This node will be of type QNODE_MACHINE and have @function |
| 194 | * as constructor |
| 195 | */ |
| 196 | void qos_node_create_machine(const char *name, QOSCreateMachineFunc function); |
| 197 | |
| 198 | /** |
| 199 | * qos_node_create_machine_args(): same as qos_node_create_machine, |
| 200 | * but with the possibility to add an optional ", @opts" after -M machine |
| 201 | * command line. |
| 202 | * @name: Name of the machine |
| 203 | * @function: Machine constructor |
| 204 | * @opts: Optional additional command line |
| 205 | */ |
| 206 | void qos_node_create_machine_args(const char *name, |
| 207 | QOSCreateMachineFunc function, |
| 208 | const char *opts); |
| 209 | |
| 210 | /** |
| 211 | * qos_node_create_driver(): creates the driver @name and |
| 212 | * adds it to the node hash table. |
| 213 | * @name: Name of the driver |
| 214 | * @function: Driver constructor |
| 215 | * |
| 216 | * This node will be of type QNODE_DRIVER and have @function |
| 217 | * as constructor |
| 218 | */ |
| 219 | void qos_node_create_driver(const char *name, QOSCreateDriverFunc function); |
| 220 | |
| 221 | /** |
| 222 | * qos_node_create_driver_named(): behaves as qos_node_create_driver() with the |
| 223 | * extension of allowing to specify a different node name vs. associated QEMU |
| 224 | * device name. |
| 225 | * @name: Custom, unique name of the node to be created |
| 226 | * @qemu_name: Actual (official) QEMU driver name the node shall be |
| 227 | * associated with |
| 228 | * @function: Driver constructor |
| 229 | * |
| 230 | * Use this function instead of qos_node_create_driver() if you need to create |
| 231 | * several instances of the same QEMU device. You are free to choose a custom |
| 232 | * node name, however the chosen node name must always be unique. |
| 233 | */ |
| 234 | void qos_node_create_driver_named(const char *name, const char *qemu_name, |
| 235 | QOSCreateDriverFunc function); |
| 236 | |
| 237 | /** |
| 238 | * qos_node_contains(): creates one or more edges of type QEDGE_CONTAINS |
| 239 | * and adds them to the edge list mapped to @container in the |
| 240 | * edge hash table. |
| 241 | * @container: Source node that "contains" |
| 242 | * @contained: Destination node that "is contained" |
| 243 | * @opts: Facultative options (see %QOSGraphEdgeOptions) |
| 244 | * |
| 245 | * The edges will have @container as source and @contained as destination. |
| 246 | * |
| 247 | * If @opts is NULL, a single edge will be added with no options. |
| 248 | * If @opts is non-NULL, the arguments after @contained represent a |
| 249 | * NULL-terminated list of %QOSGraphEdgeOptions structs, and an |
| 250 | * edge will be added for each of them. |
| 251 | * |
| 252 | * This function can be useful when there are multiple devices |
| 253 | * with the same node name contained in a machine/other node |
| 254 | * |
| 255 | * For example, if ``arm/raspi2b`` contains 2 ``generic-sdhci`` |
| 256 | * devices, the right commands will be: |
| 257 | * |
| 258 | * .. code:: |
| 259 | * |
| 260 | * qos_node_create_machine("arm/raspi2b"); |
| 261 | * qos_node_create_driver("generic-sdhci", constructor); |
| 262 | * // assume rest of the fields are set NULL |
| 263 | * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" }; |
| 264 | * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" }; |
| 265 | * qos_node_contains("arm/raspi2b", "generic-sdhci", &op1, &op2, NULL); |
| 266 | * |
| 267 | * Of course this also requires that the @container's get_device function |
| 268 | * should implement a case for "emmc" and "sdcard". |
| 269 | * |
| 270 | * For contains, op1.arg and op1.size_arg represent the arg to pass |
| 271 | * to @contained constructor to properly initialize it. |
| 272 | */ |
| 273 | void qos_node_contains(const char *container, const char *contained, |
| 274 | QOSGraphEdgeOptions *opts, ...); |
| 275 | |
| 276 | /** |
| 277 | * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and |
| 278 | * adds it to the edge list mapped to @producer in the |
| 279 | * edge hash table. |
| 280 | * @producer: Source node that "produces" |
| 281 | * @interface: Interface node that "is produced" |
| 282 | * |
| 283 | * This edge will have @producer as source and @interface as destination. |
| 284 | */ |
| 285 | void qos_node_produces(const char *producer, const char *interface); |
| 286 | |
| 287 | /** |
| 288 | * qos_node_consumes(): creates an edge of type QEDGE_CONSUMED_BY and |
| 289 | * adds it to the edge list mapped to @interface in the |
| 290 | * edge hash table. |
| 291 | * @consumer: Node that "consumes" |
| 292 | * @interface: Interface node that "is consumed by" |
| 293 | * @opts: Facultative options (see %QOSGraphEdgeOptions) |
| 294 | * |
| 295 | * This edge will have @interface as source and @consumer as destination. |
| 296 | * It also has the possibility to add an optional @opts |
| 297 | * (see %QOSGraphEdgeOptions) |
| 298 | */ |
| 299 | void qos_node_consumes(const char *consumer, const char *interface, |
| 300 | QOSGraphEdgeOptions *opts); |
| 301 | |
| 302 | /** |
| 303 | * qos_invalidate_command_line(): invalidates current command line, so that |
| 304 | * qgraph framework cannot try to cache the current command line and |
| 305 | * forces QEMU to restart. |
| 306 | */ |
| 307 | void qos_invalidate_command_line(void); |
| 308 | |
| 309 | /** |
| 310 | * qos_get_current_command_line(): return the command line required by the |
| 311 | * machine and driver objects. This is the same string that was passed to |
| 312 | * the test's "before" callback, if any. |
| 313 | */ |
| 314 | const char *qos_get_current_command_line(void); |
| 315 | |
| 316 | /** |
| 317 | * qos_allocate_objects(): |
| 318 | * @qts: The #QTestState that will be referred to by the machine object. |
| 319 | * @p_alloc: Where to store the allocator for the machine object, or %NULL. |
| 320 | * |
| 321 | * Allocate driver objects for the current test |
| 322 | * path, but relative to the QTestState @qts. |
| 323 | * |
| 324 | * Returns a test object just like the one that was passed to |
| 325 | * the test function, but relative to @qts. |
| 326 | */ |
| 327 | void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc); |
| 328 | |
| 329 | /** |
| 330 | * qos_object_destroy(): calls the destructor for @obj |
| 331 | * @obj: A #QOSGraphObject to destroy |
| 332 | */ |
| 333 | void qos_object_destroy(QOSGraphObject *obj); |
| 334 | |
| 335 | /** |
| 336 | * qos_object_queue_destroy(): queue the destructor for @obj so that it is |
| 337 | * called at the end of the test |
| 338 | * @obj: A #QOSGraphObject to destroy |
| 339 | */ |
| 340 | void qos_object_queue_destroy(QOSGraphObject *obj); |
| 341 | |
| 342 | /** |
| 343 | * qos_object_start_hw(): calls the start_hw function for @obj |
| 344 | * @obj: A #QOSGraphObject containing the start_hw function |
| 345 | */ |
| 346 | void qos_object_start_hw(QOSGraphObject *obj); |
| 347 | |
| 348 | /** |
| 349 | * qos_machine_new(): instantiate a new machine node |
| 350 | * @node: Machine node to be instantiated |
| 351 | * @qts: A #QTestState that will be referred to by the machine object. |
| 352 | * |
| 353 | * Returns a machine object. |
| 354 | */ |
| 355 | QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); |
| 356 | |
| 357 | /** |
| 358 | * qos_driver_new(): instantiate a new driver node |
| 359 | * @node: A driver node to be instantiated |
| 360 | * @parent: A #QOSGraphObject to be consumed by the new driver node |
| 361 | * @alloc: An allocator to be used by the new driver node. |
| 362 | * @arg: The argument for the consumed-by edge to @node. |
| 363 | * |
| 364 | * Calls the constructor for the driver object. |
| 365 | */ |
| 366 | QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, |
| 367 | QGuestAllocator *alloc, void *arg); |
| 368 | |
| 369 | /** |
| 370 | * qos_dump_graph(): prints all currently existing nodes and |
| 371 | * edges to stdout. Just for debugging purposes. |
| 372 | * |
| 373 | * All qtests add themselves to the overall qos graph by calling qgraph |
| 374 | * functions that add device nodes and edges between the individual graph |
| 375 | * nodes for tests. As the actual graph is assmbled at runtime by the qos |
| 376 | * subsystem, it is sometimes not obvious how the overall graph looks like. |
| 377 | * E.g. when writing new tests it may happen that those new tests are simply |
| 378 | * ignored by the qtest framework. |
| 379 | * |
| 380 | * This function allows to identify problems in the created qgraph. Keep in |
| 381 | * mind: only tests with a path down from the actual test case node (leaf) up |
| 382 | * to the graph's root node are actually executed by the qtest framework. And |
| 383 | * the qtest framework uses QMP to automatically check which QEMU drivers are |
| 384 | * actually currently available, and accordingly qos marks certain paths as |
| 385 | * 'unavailable' in such cases (e.g. when QEMU was compiled without support for |
| 386 | * a certain feature). |
| 387 | */ |
| 388 | void qos_dump_graph(void); |
| 389 | |
| 390 | #endif |