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