|
Packit |
534379 |
# Plugin Architecture #
|
|
Packit |
534379 |
The OPAE Plugin Architecture describes the interfaces and data structures
|
|
Packit |
534379 |
involved in designing and building the core plugin framework, OPAE compatible
|
|
Packit |
534379 |
plugins, and an OPAE application that uses the OPAE API. An OPAE plugin is a
|
|
Packit |
534379 |
software library that can be loaded dynamically at runtime and is either
|
|
Packit |
534379 |
specific to a given platform or is a proxy for one or more remote endpoints.
|
|
Packit |
534379 |
OPAE plugins use the OPAE API for their prototype definitions but are free to
|
|
Packit |
534379 |
use any internal data structures and functions in their implementations.
|
|
Packit |
534379 |
While it is not required that a plugin implements the complete OPAE API, it
|
|
Packit |
534379 |
is required, however, to adhere to the plugin interface. Futhermore, any OPAE
|
|
Packit |
534379 |
API functions implemented by a plugin must follow their corresponding
|
|
Packit |
534379 |
function interfaces as defined in the OPAE API specification.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
## Objective ##
|
|
Packit |
534379 |
The objective of this document is to provide architectural details about the
|
|
Packit |
534379 |
plugin interface as well as the Plugin Manager, the Plugin Loader, and an OPAE
|
|
Packit |
534379 |
plugin.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The requirements for the Plugin Architecture are as follows:
|
|
Packit |
534379 |
* Describe plugin types.
|
|
Packit |
534379 |
* Define the plugin interface.
|
|
Packit |
534379 |
This is how plugins register with the OPAE Plugin Manager and includes
|
|
Packit |
534379 |
defining API functions as well as plugin configuration functions.
|
|
Packit |
534379 |
* Describe how OPAE API calls are forwarded to an appropriate implementation.
|
|
Packit |
534379 |
* Define the C API that applications link to. This API will:
|
|
Packit |
534379 |
* Be a superset of the APIs defined in the existing OPAE C API and any other
|
|
Packit |
534379 |
extension APIs.
|
|
Packit |
534379 |
* Define functions that control how the system is configured and initialized.
|
|
Packit |
534379 |
* Use as much of the existing OPAE APIs as possible with few modifications to the API.
|
|
Packit |
534379 |
* Define a configuration schema that can be used to configure:
|
|
Packit |
534379 |
* What plugins to load.
|
|
Packit |
534379 |
* Plugin-specific parameters.
|
|
Packit |
534379 |
* Policies for how OPAE APIs are enabled and connected at runtime.
|
|
Packit |
534379 |
* Policies for error handling.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
While it is possible to use the Plugin Manager to design a framework
|
|
Packit |
534379 |
for pooling of OPAE resources, that is outside of the scope of this document.
|
|
Packit |
534379 |
While this document and any samples in this document may refer to using remote
|
|
Packit |
534379 |
resources, details of how to manage and connect to remote endpoints are also
|
|
Packit |
534379 |
out of scope for the plugin architecture, although proxy or remote resources may
|
|
Packit |
534379 |
be mentioned.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
## High Level Design ##
|
|
Packit |
534379 |
In order for a plugin design to be scalable and extensible, some data
|
|
Packit |
534379 |
structures and operations should be decoupled and abstracted with well-defined
|
|
Packit |
534379 |
interfaces that are used to connect the different components. For OPAE, the
|
|
Packit |
534379 |
components that make up the plugin design are the Plugin Manager, Plugin
|
|
Packit |
534379 |
Loader, and the Plugin libraries. The following provide brief descriptions of
|
|
Packit |
534379 |
these components. More detailed descriptions of these components and their
|
|
Packit |
534379 |
interfaces are provided later in this document.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### The Plugin Manger ###
|
|
Packit |
534379 |
The Plugin Manager implements the OPAE C API and is responsible for delegating
|
|
Packit |
534379 |
its calls to the appropriate plugin.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### The Plugin Loader ###
|
|
Packit |
534379 |
The plugin loader can be considered a component of the Plugin Manager.
|
|
Packit |
534379 |
Its job is to load plugins and initialize them.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Plugin Types ###
|
|
Packit |
534379 |
The OPAE codebase and library will include a set of default or native plugins
|
|
Packit |
534379 |
that require little to no configuration. The goal of these plugins is to:
|
|
Packit |
534379 |
* Be backwards compatible with the devices/drivers currently supported by OPAE.
|
|
Packit |
534379 |
* Support remote resources via RDMA transport.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Plugin ###
|
|
Packit |
534379 |
A plugin is a library that implements functions defined in the OPAE API
|
|
Packit |
534379 |
specification. It is called by the Plugin Manager to discover or operate
|
|
Packit |
534379 |
on OPAE resources.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
## Interface Design ##
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Plugin Interface ###
|
|
Packit |
534379 |
The following list describes features that are compatible with the Plugin Manager and Plugin Loader:
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* It must implement a configuration routine in a function called
|
|
Packit |
534379 |
`opae_plugin_configure` to provide a mechanism for any necessary
|
|
Packit |
534379 |
configuration of the plugin. It must follow the following function
|
|
Packit |
534379 |
signature:
|
|
Packit |
534379 |
* The function takes two arguments of type `opae_api_adapter_table *` and `const char *`.
|
|
Packit |
534379 |
* The first argument is a pointer to an adapter table structure that the
|
|
Packit |
534379 |
Plugin Manager has allocated and pre-initialized. The plugin will set
|
|
Packit |
534379 |
both the API function pointers here as well as the function pointers used
|
|
Packit |
534379 |
by the plugin framework.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* The second is a pointer to the configuration data which will be encoded
|
|
Packit |
534379 |
in a JSON structure. In order to avoid introducing dependencies on
|
|
Packit |
534379 |
other libraries, it will be expected that the JSON structure be
|
|
Packit |
534379 |
serialized before passing it to the plugin. It is up to the plugin to
|
|
Packit |
534379 |
determine how it will deserialize the configuration data.
|
|
Packit |
534379 |
* The function must return zero (0) upon successful configuration and a
|
|
Packit |
534379 |
non-zero value otherwise. It is up to the plugin developer to define and
|
|
Packit |
534379 |
document return codes.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The following is an example of the configuration function declaration:
|
|
Packit |
534379 |
```C
|
|
Packit |
534379 |
int opae_plugin_configure(opae_api_adapter_table *table, const char *jsonConfig);
|
|
Packit |
534379 |
```
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* It may define an optional initialization routine in a function called `opae_plugin_initialize` to provide a mechanism for initialization of the plugin. It must follow the function signature:
|
|
Packit |
534379 |
* The function takes no arguments.
|
|
Packit |
534379 |
* The function must return zero (0) upon successful initialization and a non-zero value otherwise. It is up to the plugin developer to define and document return codes.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The following is an example of the initialization function declaration:
|
|
Packit |
534379 |
```C
|
|
Packit |
534379 |
int opae_plugin_initialize(void);
|
|
Packit |
534379 |
```
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* It may define an optional finalization routine in a function called `opae_plugin_finalize` to provide a mechanism for plugin finalization (or any cleanup routines). It must follow the function signature:
|
|
Packit |
534379 |
* The function takes no arguments.
|
|
Packit |
534379 |
* The function must return zero (0) upon successful initialization and a non-zero value otherwise. It is up to the plugin developer to define and document return codes.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The following is an example of the finalization function declaration:
|
|
Packit |
534379 |
```C
|
|
Packit |
534379 |
int opae_plugin_finalize(void);
|
|
Packit |
534379 |
```
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* It may define two optional functions used to indicate if the plugin supports
|
|
Packit |
534379 |
devices based on the device type or the device host. Both functions return
|
|
Packit |
534379 |
bool and both functions take one argument of type `const char*`. The plugin will
|
|
Packit |
534379 |
use the argument to determine if a device is supported, returning true
|
|
Packit |
534379 |
if the device is supported and false otherwise.
|
|
Packit |
534379 |
If either of these functions is set in the adapter table, the function will
|
|
Packit |
534379 |
be called by the OPAE library during enumeration to determine if
|
|
Packit |
534379 |
`fpgaEnumerate` should be called in the plugin.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* The `opae_api_adapter_table` is used to fill out a plugin's API and
|
|
Packit |
534379 |
initialization/finalization functions. This structure looks something like:
|
|
Packit |
534379 |
|
|
Packit |
534379 |
```C
|
|
Packit |
534379 |
struct opae_api_adapter_table {
|
|
Packit |
534379 |
...
|
|
Packit |
534379 |
|
|
Packit |
534379 |
fpga_result (*fpgaEnumerate)(const fpga_properties *, uint32_t, fpga_token *, uint32_t, uint32_t *);
|
|
Packit |
534379 |
fpga_result (*fpgaOpen)(fpga_token, fpga_handle *, int);
|
|
Packit |
534379 |
fpga_result (*fpgaClose)(fpga_handle);
|
|
Packit |
534379 |
// ... Other API functions
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// configuration functions
|
|
Packit |
534379 |
int (*initialize)(void);
|
|
Packit |
534379 |
int (*finalize)(void);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
// first-level query
|
|
Packit |
534379 |
bool (*supports_device)(const char *device_type);
|
|
Packit |
534379 |
bool (*supports_host)(const char *hostname);
|
|
Packit |
534379 |
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
```
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* Any OPAE API functions it implements must use the same function signature as
|
|
Packit |
534379 |
defined by the OPAE API specification.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* The configuration interfaces implemented must have the ABI visibility set to
|
|
Packit |
534379 |
default. This allows the Plugin Manager to lookup this symbol and call it.
|
|
Packit |
534379 |
It is implicitly set by not setting the visibility attribute or by
|
|
Packit |
534379 |
explicitly setting it to default as listed in the example below:
|
|
Packit |
534379 |
```C
|
|
Packit |
534379 |
#define DLL_PUBLIC __attribute__((visibility ("default")))
|
|
Packit |
534379 |
|
|
Packit |
534379 |
int DLL_PUBLIC opae_plugin_configure(opae_api_adapter_table *a, const char *c);
|
|
Packit |
534379 |
```
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#### Required Changes to OPAE API ####
|
|
Packit |
534379 |
Two new properties will be introduced to the `fpga_properties` structure to
|
|
Packit |
534379 |
aid in filtering and identifying resources by device type or by host. These
|
|
Packit |
534379 |
two properties are:
|
|
Packit |
534379 |
* `host`
|
|
Packit |
534379 |
|
|
Packit |
534379 |
This can be a host name or an IP address used to indicate a remote host.
|
|
Packit |
534379 |
For backwards compatibility, the absence of this property in an
|
|
Packit |
534379 |
`fpga_properties` structure will indicate resources on the local host. A
|
|
Packit |
534379 |
proxy plugin for remote endpoints should set the host name of the
|
|
Packit |
534379 |
corresponding endpoints here. To aid in filtering for resources generically
|
|
Packit |
534379 |
by host, the following three keywords will be reserved:
|
|
Packit |
534379 |
* `localhost`
|
|
Packit |
534379 |
This is equivalent to not including a host property and refers to
|
|
Packit |
534379 |
resources on the local host.
|
|
Packit |
534379 |
* `^localhost`
|
|
Packit |
534379 |
This will be used to indicate that the matching criteria exclude local
|
|
Packit |
534379 |
resources (only include resources from remote hosts).
|
|
Packit |
534379 |
* `*`
|
|
Packit |
534379 |
This is a wildcard used to indicate resources on any host (which can
|
|
Packit |
534379 |
be local or remote).
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* `device type`
|
|
Packit |
534379 |
|
|
Packit |
534379 |
This is an enumeration and is used to indicate the device type (or device
|
|
Packit |
534379 |
family) similar to `device id`. However, the difference with `device id` is
|
|
Packit |
534379 |
that a `device id` refers to the ID registered with the PCI ID repository and
|
|
Packit |
534379 |
is unique to one product releaes. A `device type` is broader and refers to a
|
|
Packit |
534379 |
family of devices that include one or more `device ids`. The enumerated
|
|
Packit |
534379 |
values are TBD.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The accessor methods that will be added to the OPAE API are:
|
|
Packit |
534379 |
* `fpgaPropertiesSetHost(const fpga_properties, fpga_token *, char *)`
|
|
Packit |
534379 |
* `fpgaPropertiesGetHost(const fpga_properties, fpga_token *, char *)`
|
|
Packit |
534379 |
* `fpgaPropertiesSetDeviceType(const fpga_properties, fpga_device_type)`
|
|
Packit |
534379 |
* `fpgaPropertiesGetDeviceType(const fpga_properties, fpga_device_type *)`
|
|
Packit |
534379 |
|
|
Packit |
534379 |
## Component Designs ##
|
|
Packit |
534379 |
|
|
Packit |
534379 |
Because the data structures defined in the OPAE API are opaque types, any
|
|
Packit |
534379 |
implementation of the API (including the Plugin Manager) is free to define its
|
|
Packit |
534379 |
own versions of the concrete types to fit its own implementation.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The Plugin Manager defines its internal versions of these concrete types as
|
|
Packit |
534379 |
data structures that are composed of both the adapter table and the plugin's instance
|
|
Packit |
534379 |
of an opaque type. The Plugin Manager will then use this association to forward
|
|
Packit |
534379 |
calls to appropriate function pointers in the adapter table.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Plugin Manager ###
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The Plugin Manager is the software component that is linked as a shared library
|
|
Packit |
534379 |
and implements the OPAE C API. Because it implements the OPAE C API, it can be
|
|
Packit |
534379 |
linked at runtime by any application that links against the API. It will then
|
|
Packit |
534379 |
forward API calls to the appropriate plugins that have been loaded.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The Plugin Manager parses the plugins section of the configuration file to
|
|
Packit |
534379 |
determine the list of plugins to load.
|
|
Packit |
534379 |
The manager then invokes the Plugin Loader to load each plugin. The result of
|
|
Packit |
534379 |
loading a plugin is the adapter table for the plugin. The Plugin Manager
|
|
Packit |
534379 |
maintains the following mappings:
|
|
Packit |
534379 |
|
|
Packit |
534379 |
* Each API adapter table is mapped to its plugin.
|
|
Packit |
534379 |
* Each enumerated `fpga_token` is mapped to its plugin.
|
|
Packit |
534379 |
* Each opened `fpga_handle` is mapped to its plugin.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#### Enumeration ####
|
|
Packit |
534379 |
|
|
Packit |
534379 |
When the API's main `fpgaEnumerate` is called, the Plugin Manager iterates
|
|
Packit |
534379 |
over each loaded plugin, using its adapter table to call the plugin's
|
|
Packit |
534379 |
`fpgaEnumerate` entry point. The tokens resulting from an individual
|
|
Packit |
534379 |
plugin enumeration are each mapped to the originating plugin. Finally,
|
|
Packit |
534379 |
the tokens are collected into the token array for returning to the caller.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#### Opening a device ####
|
|
Packit |
534379 |
|
|
Packit |
534379 |
When the API's main `fpgaOpen` is called, the Plugin Manager resolves
|
|
Packit |
534379 |
the given token to its plugin. The adapter table's `fpgaOpen` is then
|
|
Packit |
534379 |
invoked. Finally, the resulting `fpga_handle` is mapped to its
|
|
Packit |
534379 |
originating plugin, and the handle is returned to the caller.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Plugin Loader ###
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The plugin loader is responsible for opening each plugin and constructing a
|
|
Packit |
534379 |
plugin adapter table based on the contained API entry points. The loader
|
|
Packit |
534379 |
calls opae_plugin_configure(), passing a pre-initialized adapter table object
|
|
Packit |
534379 |
and any relevant configuration data. It is expected that the plugin set
|
|
Packit |
534379 |
function pointer fields in the adapter table that point to API functions
|
|
Packit |
534379 |
implemented by the plugin. The adapter table also has fields for setting
|
|
Packit |
534379 |
functions defined in the plugin that can be called by the Plugin Manager for
|
|
Packit |
534379 |
non API related functionality. These include initialization, finalization, and
|
|
Packit |
534379 |
pre-filtering functions that can be used for plugin selection during
|
|
Packit |
534379 |
enumeration.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Configuration Schema ###
|
|
Packit |
534379 |
The OPAE Plugin system will use JSON for defining any runtime configuration
|
|
Packit |
534379 |
parameters. This includes the list of plugins to load, their instance names,
|
|
Packit |
534379 |
and their individual configuration data.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
```JSON
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
"plugins":
|
|
Packit |
534379 |
[
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
"module": "libopae-net-proxy",
|
|
Packit |
534379 |
"name": "tcp-proxy1",
|
|
Packit |
534379 |
"config": {
|
|
Packit |
534379 |
"transport": "tcp",
|
|
Packit |
534379 |
"discovery": "none",
|
|
Packit |
534379 |
"hosts": ["host1", "host2", "host3"]
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
"load_policy" : {}
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
{
|
|
Packit |
534379 |
"module": "libopae-net-proxy",
|
|
Packit |
534379 |
"name": "rdma-proxy1",
|
|
Packit |
534379 |
"config": {
|
|
Packit |
534379 |
"transport": "rdma",
|
|
Packit |
534379 |
"discovery": "none",
|
|
Packit |
534379 |
"hosts": ["host1", "host2", "host3"]
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
"load_policy": {}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
]
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
```
|
|
Packit |
534379 |
|
|
Packit |
534379 |
## Example Use Case ##
|
|
Packit |
534379 |
|
|
Packit |
534379 |
The diagrams below illustrate a case of a client application linking to the
|
|
Packit |
534379 |
plugin-enabled OPAE library. The Plugin Manager in OPAE is responsible for
|
|
Packit |
534379 |
managing plugins and forwarding API function calls to any plugins that have
|
|
Packit |
534379 |
been registered. The Plugin Manager wraps any API data structures
|
|
Packit |
534379 |
(`fpga_token`, `fpga_handle`) created by API functions in any of its
|
|
Packit |
534379 |
registered plugins in its own version of the data structures before returning
|
|
Packit |
534379 |
them to the client application. When operating on its own API data
|
|
Packit |
534379 |
structures, it will unwrap them to get a plugin's API data structure along
|
|
Packit |
534379 |
with its adapter table.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### Initialization ###
|
|
Packit |
534379 |
This first diagram below shows the initialization sequence when the OPAE
|
|
Packit |
534379 |
library is first loaded. The initialization routine in the OPAE library can
|
|
Packit |
534379 |
either be called explicitly from the client application or implicitly by the
|
|
Packit |
534379 |
library's constructor. In either case, when the initialization routine is
|
|
Packit |
534379 |
called, it parses the configuration file to get configuration parameters for
|
|
Packit |
534379 |
any of its plugins (including native plugins). As mentioned in [Plugin
|
|
Packit |
534379 |
Types](#plugin-types) section, native plugins should require little or no
|
|
Packit |
534379 |
configuration. However, any configuration parameters that can be overridden
|
|
Packit |
534379 |
can be included in the configuration file. See the psuedo-code for the
|
|
Packit |
534379 |
[Plugin Manager](cpseudo.md#plugin-manager) and [Plugin
|
|
Packit |
534379 |
Loader](cpseudo.md#plugin-loader) for skeleton implementations.
|
|
Packit |
534379 |
![plugin initialization](plugin_init.svg)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
### OPAE Stack ###
|
|
Packit |
534379 |
Calling OPAE API functions requires going up and down the OPAE SW stack.
|
|
Packit |
534379 |
These examples refer to the SW stack with respect to the OPAE usermode APIs.
|
|
Packit |
534379 |
Refer to the pseudocode for skeleton implementations of routines for API
|
|
Packit |
534379 |
functions in the [OPAE Stack](cpseudo.md#opae-stack).
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#### Enumeration ####
|
|
Packit |
534379 |
This next diagram below shows the enumeration flow from a client application
|
|
Packit |
534379 |
to an arbitrary plugin, A. The filter, a set of `fpga_properties` objects, is
|
|
Packit |
534379 |
passed to the plugin. Upon successful enumeration by the plugin, it returns a
|
|
Packit |
534379 |
set of `fpga_token` structures (A_tokens) to the caller (the
|
|
Packit |
534379 |
OPAE.PluginManager). For each token in the returned tokens, the Plugin
|
|
Packit |
534379 |
Manager wraps these tokens into its own internal token data structure which
|
|
Packit |
534379 |
is composed of the token from the plugin and the adapter table that contains
|
|
Packit |
534379 |
the plugin's API functions.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
![enumeration](plugin_enum.svg)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#### Opening a Resource ####
|
|
Packit |
534379 |
Opening a resource requires unwrapping an `fpga_token` object by the Plugin
|
|
Packit |
534379 |
Manager to get both the plugin's adapter table and the plugin's version of
|
|
Packit |
534379 |
the `fpga_token` object. It then calls the `open` function in the adapter
|
|
Packit |
534379 |
table with the plugin's token (or the wrapped token). The diagram below shows
|
|
Packit |
534379 |
this sequence of events.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
![opening a resource](plugin_open.svg)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
#### Remote Enumeration ####
|
|
Packit |
534379 |
The pseudo-code for [Proxy Plugins](cpseudo.md#proxy-plugins) outlines notional
|
|
Packit |
534379 |
implementations for the two kinds of proxy plugins. The diagram below
|
|
Packit |
534379 |
illustrates the general sequence.
|
|
Packit |
534379 |
![remote enumeration](plugin_remote.svg)
|