Defining and registering (vdev trace)
During its startup stage, the trace vdev constructs and configures itself, then registers itself in the VM framework.
As with other vdevs (and just like most physical devices), the life of vdev trace can be divided into two stages:
- startup – the vdev constructs itself
- running – the vdev responds to requests from the pseudo-driver in the guest
The marker between the trace vdev's startup stage and its running stage is receipt of the VDEV_CTRL_GUEST_CONFIGURED callback. This callback marks the point at which the vdev and the qvm process have finished constructing and configuring the vdev and the VM. At this point, the vdev trace may start calling functions, such as vdev_thread_create(), that may not be called during the vdev startup stage.
Overview of the startup stage
The first thing every vdev must do is register with the hypervisor framework; that is, it must tell the qvm process instance that is assembling the VM in which the vdev will reside:
- that the vdev exists
- what the vdev's options are
- any other information the qvm process needs to know about the
vdev; for example, the trace vdev specifies the
constructorargument, which specifies that the vdev shared object should be loaded at run time
Defining the device
The vdev defines itself as a data structure. This structure is initialized by the startup, and updated as the state of the vdev changes:
/// One instance of the trace device.
struct trace_state {
pthread_t thread; ///< The thread id for the vdtrace_thread
uint32_t counter; ///< A counter that increments for each trace_device read
int emit_logger; ///< 1 = write to logger (as defined by qvm config file). Default=0
int emit_trace; ///< 1 = emit a trace event to the hypervisor host. Default=0
int trace_event; ///< The event to emit (only if emit_trace is specified)
};
The trace vdev's vdtrace_*() functions update this
structure when the vdev is running (see The running vdev (vdev trace)
in this
chapter).
Defining the options
Like other vdevs in QNX hypervisor VMs, the trace vdev doesn't need to
define all its options. It needs to define only those options specific to itself; that is,
options that aren't common to all vdevs. Common options, such as the
loc option, are defined by the qvm process,
which looks after parsing them (see Common vdev options
in the User's Guide).
The code snippets below show how the trace vdev defines its options:
enum {
OPT_LOGGER_IDX,
OPT_TRACE_EVENT_IDX,
OPT_NUM_OPTS
};
...
static const char *const trace_options[OPT_NUM_OPTS + 1] = {
[OPT_LOGGER_IDX] = QVM_OPTION_NO_ARG_PREFIX "emit-logger",
[OPT_TRACE_EVENT_IDX] = "emit-trace",
[OPT_NUM_OPTS] = NULL,
};
Don't repeat an option name in a context (for information about options in
contexts, see Contexts
in the User's Guide
Configuration
chapter).
That is, when you define your vdev's options, don't repeat an option name, unless you know that that is what your vdev needs (see vdev hpet in the User's Guide for an example of a vdev that repeats an option).
When a VDEV_CTRL_OPTIONS_END callback is issued, the qvm process instance returns to a higher context; that is, parsing moves on to the next context: another VM component or vdev. This means you may use the same option name for different options in different vdevs—though this may confuse your users.
Parsing the vdev options
In the trace vdev, parsing the options and assembling the vdev is
handled by the vdtrace_control() function, using pre-defined callbacks
(see vdtrace_control()
in Running:
vdtrace_*() functions
).
The trace vdev's vdtrace_control() function calls
qvm_parse_num(); the qvm/utils.h header
file defines this and other qvm_parse_*() functions, which your
vdev should call to parse its options (see the utils.h
chapter in the Virtual Device Developer's API
Reference).
When you call the qvm_parse_*() functions, the qvm process looks after checking the validity of the VM configuration (including your vdev). If the configuration isn't valid, the qvm process instance parsing the configuration exits with exit code 65. If the qvm process exits while parsing your vdev's options, the message accompanying the exit code can help you understand the error. The message can include:
- Unknown option — the qvm encountered an option in the
configuration that isn't defined in your vdev, or you have marked your vdev
QVM_OPTION_NO_ARG_PREFIX but the configuration includes an
option (see
Definitions in vdev-core.h
in the Virtual Device Developer's API Reference). - Expecting a number near XXX — the vdev expects an option, but none is provided in the configuration.
For more information about the exit code, see qvm exit codes
in the User's
Guide
Monitoring and Troubleshooting
chapter.
Notice that vdtrace_control() includes the VDEV_CTRL_GEN_FDT and
VDEV_CTRL_GEN_ACPI pre-defined callbacks, which can be called to generate a node for
the new device in the VM's FDT, or add the device to the VM's ACPI tables (see
Definitions in vdev-core.h
in the
Virtual Device Developer's API Reference).
if (vdp->v_block.location == QSL_NO_LOCATION) {
qvm_logf(QVM_OUTI_QVM, "%s: OPTIONS_END no location specified", __func__);
} else {
qvm_logf(QVM_OUTI_QVM, "%s: OPTIONS_END Location 0x%lx", __func__, vdp->v_block.location);
}
break;Note that the qvm process can't handle this check; it has no way of knowing what options your vdev requires in addition to the common options.
Defining the vdev in the factory structure
In vdev trace, registering the function with the VM framework is handled by the vdev's vdtrace_register() function, which, in this order:
- Defines the vdev's options (see
Defining the options
above). - Populates the vdev_factory data structure with the information that defines vdev trace.
- Calls the vdev_register_factory() function to register the vdev as defined by the information in the vdev_factory structure.
After defining its vdev-specific options, the vdtrace_register() function populates a vdev_factory structure with the information needed to define the vdev, and calls this populated structure vdtrace_factory.
The vdtrace_factory structure is populated as follows:
- next
- Always set this to NULL; reserved for the qvm process.
- control
- The function that controls the vdev: vdtrace_control()
(see
vdtrace_control()
). - vread
- The vdev's vread() function:
vdtrace_vread(); when the guest writes, the VM must
call this function (see
vdtrace_vread() and vdtrace_vwrite()
). - vwrite
- The vdev's vwrite() function:
vdtrace_vwrite(); when the guest reads, the VM must
call this function (see
vdtrace_vread() and vdtrace_vwrite()
). - option_list
- The vdev's option definitions: trace_options (see
Defining the options
above). - name
- The vdev's name. Typically, set to NULL, in which case the build process adds the vdev- prefix and the .so suffix to the vdev's directory name when it creates the vdev shared object.
- factory_flags
- These flags control the vdev's capabilities; they must be set when the vdev is created (see factory_flags in the Virtual Device Developer's API Reference).
- acc_size
- Accepts only 32-bit access (four bytes) at a time:
1u << 4. - extra_space
- Set aside local data space for this vdev; the space required is defined by
the trace_state data structure (see
Defining the device
in this chapter). - safety
- Mark this vdev as a safety variant; this designation is fictitious, and only
included here as an example (see
VDEV_SAFETY_*
in the Virtual Device Developer's API Reference).
For reference, here is the bit of code in the vdtrace_register() function that populates the vdtrace_factory structure to define vdev trace:
static struct vdev_factory vdtrace_factory = {
.next = NULL, // Patched
.control = vdtrace_control,
.vread = vdtrace_vread,
.vwrite = vdtrace_vwrite,
.option_list = trace_options,
.name = NULL, // Patched
.factory_flags = VFF_INTR_NO | VFF_MUTEX | VFF_VDEV_MEM,
// Accept only 32-bit access.
.acc_sizes = 1u << 4,
// Make sure that local data space is available for the internal structure.
.extra_space = sizeof(struct trace_state),
// For the safety version of vdev-trace, require "safety"; otherwise,
// don't require "safety".
.safety = VDEV_SAFETY_SELECTED,
};
Note that, by convention, the factory page data structure includes the suffix _factory, and the members and the functions they reference follow a similar naming pattern (e.g., vdtrace_control, vdtrace_control).
You don't need to follow this convention, but it may make reading vdev code easier when you come back to it in the future.
For more information about vdev_factory and
vdev_factory_flags, see the vdev-core.h
chapter in the
Virtual Device Developer's API Reference.
Registering the vdev
After it populates the vdtrace_factory structure with the vdev trace definition, the vdtrace_register() function calls vdev_register_factory(), handing it the vdev definition and the vdev's application binary interface (ABI) version:
vdev_register_factory(&vdtrace_factory, QVM_VDEV_ABI);
See vdev_register_factory(), and Definitions in abi.h
in the API
Reference.
