M7350/kernel/Documentation/arm/msm/glink.txt

1240 lines
60 KiB
Plaintext
Raw Normal View History

2024-09-09 08:57:42 +00:00
Introduction
============
G-Link, short for Generic Link, is a generic link-layer transport that supports
a plug-in framework for physical transports. This allows it to adapt to
different physical transports such as shared memory, UARTs, buses, and DMA.
It is designed to enable zero copy, is power aware, and supports version and
feature negotiation to enable phased upgrades. The design is symmetrical with
the same high-level design and the same client API across all subsystems
regardless of OS.
Hardware description
====================
The hardware is managed by the transport plug-in. In the initial
driver version, this is done by the SMEM Native Transport which requires
shared memory and an interrupt in each direction between the local and remote
subsystems. This transport is a replacement for SMD.
Software description
=================================
G-Link consists of:
* Client API
* Core that implements the high-level protocol
* Transport plug-ins that handle translation of the high-level protocol to wire
format
The below diagram shows the organization of G-Link. Clients use G-Link to
interface with the Transport Plug-in, which interfaces directly with the
physical transport. The G-Link component is shown in further detail in the
"Design" section.
+-----------+ +---------+ +-----------+
| G-Link |---->|Transport|----->| Physical |
| |<----| Plug-in |<-----| Transport |
+-----------+ +---------+ +-----------+
Design
======
G-Link is conceptually broken down into a command and data queue. The command
queue is used to pass commands for synchronizing buffer status, channel state,
and for the equivalent of packet headers. The data queue is reserved for raw
client data.
For every packet in the data queue, there is at least one command
that acts as the packet header that goes through the command queue. This
separation is necessary to support the zero-copy use case for data packets and
for DMA-type transports that need to have the transfer size known ahead of time.
For copy-based transports, the command and data transports can be merged
together in the transport plug-in resulting in traditional packet headers like
we have today in other common protocols such as IP.
As shown in the diagram below, the client itself communicates directly with the
G-Link core, and uses the Transport Interface and the G-Link Core interface to
create the transport plug-in.
+-----------+
+------+ +---------+ | |
| |<---|Transport|-| |
+-------+ | | | IF | | |
|Clients|<-->| | +---------+ | Transport |
+-------+ |G-Link| | Plug-in |
| Core | | |
| | +-------+ | |
| |-|G-Link |----->| |
| | |Core IF| | |
+------+ +-------+ | |
+-----------+
A channel is a discrete data pipe used by a client to communicate with a
remote system. All channels are multiplexed onto one or more physical
transports.
A receive intent is queued by a client to indicate that it is ready to receive
a packet up to the specified size. The intent is sent to the remote side to
let clients know that they can transmit and expect the client to be able to
process it.
Intents provide back-pressure to transmitting clients and remove the
requirement for flow control. In addition, the intent size is used for
copy-based transports to either reserve buffer space or allocate buffers to
receive data to prevent blocking the underlying transport. Multiple intents
can be queued.
Transport Plug-in
-----------------
The transport plug-in is responsible for marshaling data and commands between
the G-Link core and the physical transport. If the remote side is not running
G-Link, then the transport plug-in will be more complex and will handle
translating G-Link core commands and data into the proper protocol to
interact with the remote system.
Client API
----------
The API into the G-Link core is asynchronous by default, but may have blocking
variants. If necessary, a thin client layer can be built on top of the
asynchronous API to create a synchronous API for clients. All client
notifications are serialized to ensure in-order notification of events.
The G-Link client API includes the following functions for the following
operations. Details of each function are described later in this section.
* glink_open() - Open a G-Link channel
* glink_close() - Close a G-Link channel
* glink_tx() - Transmit data
* glink_txv() - Transmit a buffer chain
* glink_queue_rx_intent() - Queue a receive intent
* glink_rx_done() - Signal consumption of the receive buffer
* glink_sigs_set() - Set a 32-bit signal field for client-specific signaling.
Standard TIOCM bits are reserved in the upper bits of the
field as a conviencence for users, but they are 100
percent pass-through.
* glink_sigs_local_get() - Get the local 32-bit control signal field
* glink_sigs_remote_get() - Get the remote 32-bit control signal field
The TIOCM bitmasks are defined as follows:
#define SMD_CTR_SIG_SHFT 31
#define SMD_CTS_SIG_SHFT 30
#define SMD_CD_SIG_SHFT 29
#define SMD_RI_SIG_SHFT 28
When a channel is opened by a client, the client provides an open
configuration to the G-Link core as a parameter to the glink_open() function.
This open configuration contains the following information:
* The name of the transport (optional)
* The name of the edge (remote processor)
* The name of the channel
The open configuration also contains function pointers for the following
operations, which are defined by the client:
* notify_rx() - Notify the client that data has been received
* notify_rxv() - Notify the client that vector data has been received
* notify_tx_done() - Notify the client that data has been transmitted
* notify_state() - Notify the client that the channel's state has changed
* notify_rx_intent_req() - Notify the client whether their request for a
receive intent was granted
* notify_rx_sigs() - Notify the client that there has been a change in the
state of the remote side's control signals
* notify_rx_abort() - During channel close, return structures associated
with receive intents to the client
* notify_tx_abort() - During channel close, return structures associated
with TX packets to the client
* notify_rx_tracer_pkt() - Notify the client that a tracer packet has been
received
Buffer ownership is transferred between the local G-Link and the remote G-Link
using message passing. A typical transmit sequence is as follows:
1. The remote side queues an RX intent. When the remote client queues the
intent by calling glink_queue_rx_intent(), the ownership of the buffer
transfers to the remote G-Link core, which notifies the local G-Link core
of the receive intent. The remote G-Link core is now ready to receive data
from the local client. In the zero-copy case, the remote G-Link core does
not need to allocate a buffer.
+------+ +------+ +------+
|Local | |Remote| |Remote|
|G-Link| |G-Link| |Client|
+------+ +------+ +------+
| | |
| | glink_queue_rx_intent()|
| +-+<---------------------+-+
| |-| |-|
| |-|(allocate/reserve |-|
| |-| buffer) |-|
| |-|-----+ |-|
| |-| | |-|
| |-|<----+ |-|
| +-+ +-+
| send_intent() | |
|<--------------------------+ |
| | |
| (signal) | |
+-+<-------------------------| |
|-| | |
|-| | |
|-| | |
+-+ | |
| | |
+------+ +------+ +------+
|Local | |Remote| |Remote|
|G-Link| |G-Link| |Client|
+------+ +------+ +------+
2. The local client can allocate a buffer, fill it, and send it to the remote
side. If multiple receive intents are available, then a first-fit
algorithm is used to select the receive intent.
+------+ +------+ +------+ +------+
|Local | |Local | |Remote| |Remote|
|Client| |G-Link| |G-Link| |Client|
+------+ +------+ +------+ +------+
| | | |
| (Allocate tx buffer) | | |
+-+--------+ | | |
|-| | | | |
|-|<-------+ | | |
|-| | | |
|-| (Copy data into | | |
|-| tx buffer) | | |
|-|--------+ | | |
|-| | | | |
|-|<-------+ | | |
|-| | | |
|-| glink_tx() | | |
|-|------------------->+-+ tx() | |
|-| |-|---------->+-+ notify_rx() |
|-| |-| |-|------------>+-+
+-+ |-| (signal) |-| |-|
| |-|---------->|-| |-|
| +-+ |-| |-|
| | +-+ |-|
| | | +-+
| | | |
+------+ +------+ +------+ +------+
|Local | |Local | |Remote| |Remote|
|Client| |G-Link| |G-Link| |Client|
+------+ +------+ +------+ +------+
3. The transmit buffer ownership is returned to the local client after the
remote client has finished with it. At this point, the local client can
destroy/reuse the buffer.
+------+ +------+ +------+ +------+
|Local | |Local | |Remote| |Remote|
|Client| |G-Link| |G-Link| |Client|
+------+ +------+ +------+ +------+
| | | |
| | | glink_rx_done() |
| | +-+<----------------+-+
| | |-| |-|
| | |-| (copy-based |-|
| | |-| transport: |-|
| | |-| destroy/reuse |-|
| | |-| buffer) |-|
| | |-|----------+ +-+
| | |-| | |
| | |-| | |
| | |-|<---------+ |
| | tx_done()|-| |
| +-+<----------|-| |
| |-| |-| |
| |-| (signal) |-| |
| notify_tx_done()|-|<----------|-| |
+-+<---------------|-| |-| |
|-| |-| +-+ |
|-| |-| | |
|-| +-+ | |
+-+ | | |
| | | |
+------+ +------+ +------+ +------+
|Local | |Local | |Remote| |Remote|
|Client| |G-Link| |G-Link| |Client|
+------+ +------+ +------+ +------+
Transport Interface
-------------------
The transport interface is used for function calls from the G-Link core to a
G-Link transport. Modules which implement this interface are G-Link
transports. All function calls include the pointer to the transport instance
and the data fields that should be encoded into a command packet to be sent to
the remote processor. These functions act on the transport itself - they
translate the commands into actions for each different transport. This interface
contains APIs for transport negotiation, channel state, channel data, and
power. Requests that change state always have an ACK to synchronize
the state between the local and remote subsystems.
The transport interface is implemented as follows:
struct glink_transport_if {
/* Negotiation */
void (*tx_cmd_version)(struct glink_transport_if *if_ptr,
uint32_t version,
uint32_t features);
void (*tx_cmd_version_ack)(struct glink_transport_if *if_ptr,
uint32_t version,
uint32_t features);
void (*set_version)(struct glink_transport_if *if_ptr, uint32_t version,
uint32_t features);
/* channel state */
int (*tx_cmd_ch_open)(struct glink_transport_if *if_ptr, uint32_t lcid,
const char *name);
int (*tx_cmd_ch_close)(struct glink_transport_if *if_ptr,
uint32_t lcid);
void (*tx_cmd_ch_remote_open_ack)(struct glink_transport_if *if_ptr,
uint32_t rcid);
void (*tx_cmd_ch_remote_close_ack)(struct glink_transport_if *if_ptr,
uint32_t rcid);
int (*ssr)(struct glink_transport_if *if_ptr);
/* channel data */
int (*allocate_rx_intent)(size_t size,
struct glink_core_rx_intent *intent);
int (*deallocate_rx_intent)(struct glink_core_rx_intent *intent);
int (*tx_cmd_local_rx_intent)(struct glink_transport_if *if_ptr,
uint32_t lcid, size_t size, uint32_t liid);
void (*tx_cmd_local_rx_done)(struct glink_transport_if *if_ptr,
uint32_t lcid, uint32_t liid);
int (*tx)(struct glink_transport_if *if_ptr, uint32_t lcid,
struct glink_core_tx_pkt *pctx);
int (*tx_cmd_rx_intent_req)(struct glink_transport_if *if_ptr,
uint32_t lcid, size_t size);
int (*tx_cmd_remote_rx_intent_req_ack)(
struct glink_transport_if *if_ptr,
uint32_t lcid, bool granted);
int (*tx_cmd_set_sigs)(struct glink_transport_if *if_ptr,
uint32_t lcid, uint32_t sigs);
/* Optional. If NULL at xprt registration, dummies will be used */
int (*poll)(struct glink_transport_if *if_ptr, uint32_t lcid);
int (*mask_rx_irq)(struct glink_transport_if *if_ptr, uint32_t lcid,
bool mask, void *pstruct);
int (*wait_link_down)(struct glink_transport_if *if_ptr);
int (*tx_cmd_tracer_pkt)(struct glink_transport_if *if_ptr,
uint32_t lcid, struct glink_core_tx_pkt *pctx);
/* private pointer for core */
struct glink_core_xprt_ctx *glink_core_priv;
/* core pointer (set during transport registration) */
struct glink_core_if *glink_core_if_ptr;
};
G-Link Core Interface
---------------------
The G-Link Core Interface is used by the transport to call back into G-Link
core for messages or events received from the transport. This interface has
APIs for transport negotiation, power, channel state, and channel data.
Like the transport interface, requests that change state always have an ACK
to synchronize the state between the local and remote subsystems.
The G-Link Core Interface is implemented as follows:
struct glink_core_if {
/* Negotiation */
void (*link_up)(struct glink_transport_if *if_ptr);
void (*rx_cmd_version)(struct glink_transport_if *if_ptr,
uint32_t version,
uint32_t features);
void (*rx_cmd_version_ack)(struct glink_transport_if *if_ptr,
uint32_t version,
uint32_t features);
/* channel management */
void (*rx_cmd_ch_remote_open)(struct glink_transport_if *if_ptr,
uint32_t rcid, const char *name);
void (*rx_cmd_ch_open_ack)(struct glink_transport_if *if_ptr,
uint32_t lcid);
void (*rx_cmd_ch_remote_close)(struct glink_transport_if *if_ptr,
uint32_t rcid);
void (*rx_cmd_ch_close_ack)(struct glink_transport_if *if_ptr,
uint32_t lcid);
void (*ch_state_local_trans)(struct glink_transport_if *if_ptr,
uint32_t lcid,
enum local_channel_state_e new_state);
/* channel data */
struct glink_core_rx_intent *(*rx_get_pkt_ctx)(
struct glink_transport_if *if_ptr,
uint32_t rcid, uint32_t liid);
void (*rx_put_pkt_ctx)(struct glink_transport_if *if_ptr, uint32_t rcid,
struct glink_core_rx_intent *intent_ptr, bool complete);
void (*rx_cmd_remote_rx_intent_put)(struct glink_transport_if *if_ptr,
uint32_t rcid, uint32_t riid, size_t size);
void (*rx_cmd_tx_done)(struct glink_transport_if *if_ptr, uint32_t rcid,
uint32_t riid);
void (*rx_cmd_remote_rx_intent_req)(struct glink_transport_if *if_ptr,
uint32_t rcid, size_t size);
void (*rx_cmd_rx_intent_req_ack)(struct glink_transport_if *if_ptr,
uint32_t rcid, bool granted);
void (*rx_cmd_remote_sigs)(struct glink_transport_if *if_ptr,
uint32_t rcid, uint32_t sigs);
/* channel scheduling */
void (*tx_resume)(struct glink_transport_if *if_ptr);
};
Power Management
================
Power management has yet to be implemented. See the "To do" section for more
information.
SMP/multi-core
==============
Locking and synchronization will be done using mutexes or spinlocks where
appropriate.
Security
========
No known security issues.
Performance
===========
No known performance issues.
Client Interface
================
Open
----
void *glink_open(const struct glink_open_config *cfg)
Opens a logical channel. When this function is called, a notification is sent
to the remote processor. Once the remote processor responds with an open
command, the channel will be opened locally. At this point, the channel is
considered fully open and ready for data operations. The client will be notified
at this point with a GLINK_CONNECTED notification.
When a channel is opened by calling glink_open(), a structure of configuration
information (struct glink_open_config) is passed to it. This includes the name
of the transport, the name of the edge, and the name of the channel, along with
pointers to notification functions:
* notify_rx() - Notify the client that data has been received
* notify_tx_done() - Notify the client that data has been transmitted
* notify_state() - Notify the client that the channel's state has
changed
* notify_rx_intent_req() - Notify the client whether their request for a
receive intent was granted
* notify_rxv() - Receive notification for vector buffers
* notify_rx_sigs() - Notification callback for change in state of remote
side's control signals.
* notify_rx_abort() - During channel close, return structures associated with
receive intents to the client.
* notify_tx_abort() - During channel close, return structures associated with
TX packets to the client.
* notify_rx_tracer_pkt() - Receive notification for tracer packet
This structure is copied internally during the glink_open() call. The full
definition of the structure is below:
struct glink_open_config {
unsigned options;
const char *transport;
const char *edge;
const char *name;
struct glink_ch_ctx (*notify_rx)(void *handle, const void *priv,
const void *pkt_priv, const void *ptr, size_t size);
struct glink_ch_ctx (*notify_tx_done)(void *handle, const void *priv,
const void *pkt_priv, const void *ptr);
struct glink_ch_ctx (*notify_state)(void *handle, const void *priv,
unsigned event);
bool (*notify_rx_intent_req)(void *handle, const void *priv,
size_t req_size);
struct glink_ch_ctx (*notify_rxv)(void *handle, const void *priv,
const void *pkt_priv, void *iovec, size_t size,
void *(*vbuf_provider)(void *iovec, size_t offset,
size_t *size),
void *(*pbuf_provider)(void *iovec, size_t offset,
size_t *size));
struct glink_ch_ctx (*notify_rx_sigs)(void *handle, const void *priv,
uint32_t old_sigs, uint32_t new_sigs);
struct glink_ch_ctx (*notify_rx_abort)(void *handle, const void *priv,
const void *pkt_priv);
struct glink_ch_ctx (*notify_tx_abort)(void *handle, const void *priv,
const void *pkt_priv);
struct glink_ch_ctx (*notify_rx_tracer_pkt)(void *handle,
const void *priv, const void *pkt_priv, const void *ptr,
size_t size);
};
The following are the possible event notification values. The GLINK_CONNECTED
notification is sent using the notify_state() callback once the channel has
been fully opened. See the Close section for the closing state details.
enum {
GLINK_CONNECTED,
GLINK_LOCAL_DISCONNECTED,
GLINK_REMOTE_DISCONNECTED,
};
glink_open() returns the following standard error codes:
* ERR_PTR(-EINVAL) - The glink_open_config structure which was passed to
glink_open() is invalid.
* ERR_PTR(-ENODEV) - No transport is available.
* ERR_PTR(-EBUSY) - The requested channel is not ready to be re-opened.
Close
-----
int glink_close (void *handle)
Closes the logical channel. Once the close request has been processed by the
remote processor, the GLINK_LOCAL_DISCONNECTED notification is sent to the
client. If the remote processor closes the channel first, then the
GLINK_REMOTE_DISCONNECTED notification is sent to the client. After the
GLINK_LOCAL_DISCONNECTED notification is sent, no additional activity will occur
on the channel, regardless of whether the GLINK_REMOTE_DISCONNECTED notification
was sent or not. At this point, it's safe for the callbacks and/or their
resources to be destroyed. If the client wishes to re-establish communication
on the channel, then the client will need to re-open the channel.
glink_close() returns the following standard error codes:
* -EINVAL - The channel to be closed is NULL.
* -EBUSY - The channel to be closed is already closing.
If the channel to be closed is already closed, 0 is returned.
Transmit Data
-------------
int glink_tx(void *handle, void *pkt_priv, void *data, size_t size,
bool req_intent)
Arguments:
handle: The handle returned by glink_open()
pkt_priv: Opaque data value that will be returned to client with the
notify_tx_done() notification
data: Pointer to the data being sent
size: Size of the data being sent
req_intent: Boolean indicating whether or not to request an intent from the
remote channel
Transmit data packet for a matching RX Intent.
If a client would like to transmit a packet, but a suitable RX Intent has not
been queued, the client can request that glink_tx() block and request a
receive intent from the remote system. The remote system can still deny the
request at which point glink_tx() will return -EAGAIN to the client. The call
sequence for this exchange is:
1. Client wants to transmit a packet, and sets the req_intent flag to true in
the call to glink_tx() in order to request an intent if one is not already
available.
3. Remote G-Link calls its client's notify_rx_intent_req() function and that
client returns a boolean indicating whether the intent will be granted or
not
4. If the client grants the remote intent request, glink_tx() receives the
intent and returns success
5. If the client rejects the remote intent request, glink_tx() returns an error
int glink_txv(void *handle, void* pkt_priv, const void *iovec, size_t size,
glink_buffer_provider_fn vbuff_provider,
glink_buffer_proivder_fn pbuff_provider,
bool req_intent)
Arguments:
handle: The handle returned by glink_open()
pkt_priv: Opaque data value that will be returned to client with the
notify_tx_done() notification
iovec: Pointer to the vector
size: Size of the data being sent
vbuf_provider: Client-provided helper function to iterate the vector in
virtual address space
pbuf_provider: Client-provided helper function to iterate the vector in
physical address space
req_intent: Boolean indicating whether or not to request an intent from the
remote channel
glink_txv() provides a transmit function that accommodates clients using vector
buffer implementations (DSM, SKB, etc.), and allows transports to operate on
virtual or physical address mappings when necessary. This is done through the
vbuf_provider() and pbuf_provider() functions, which are defined by the client
and return a pointer to the contiguous vector data after iteration. After
assembling the data from the vector, the call sequence is the same as
glink_tx().
Queue Receive Intent
--------------------
int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size)
Queues a receive intent. A receive intent indicates that the client is ready to
receive a packet up to the specified size. The transport interface will either
reserve space for this packet or allocate a buffer to receive this packet such
that the packet can be received without stalling the physical transport.
The pkt_priv parameter is an opaque value provided by the client that is
returned in the notify_rx() callback.
Signal Consumption of Receive Buffer
------------------------------------
int glink_rx_done(void *handle, const void *ptr)
This function is called by the client to signal that they are done with the
receive buffer. The remote client is notified that it now owns the buffer again.
Set Control Signal Field
------------------------
glink_sigs_set(void *handle, uint32 *sig_value)
This function is called by the client to set a 32-bit control signal field.
Depending on the transport, it may take appropriate actions on the set bit-mask,
or transmit the entire 32-bit value to the remote host.
Get Local Control Signal Field
------------------------------
glink_sigs_local_get(void *handle, uint32 *sig_value)
This function is called by the client to retrieve the cached signals sent
using glink_sigs_set().
Get Remote Control Signal Field
-------------------------------
glink_sigs_remote_get(void *handle, uint32 *sig_value)
This function is called by the client to retrieve the cached remote signals
that were passed to notify_rx_sigs().
Register a Transport
--------------------
int glink_core_register_transport(struct glink_transport_if *if_ptr,
struct glink_core_transport_cfg *cfg)
Register a new transport with the G-Link Core. The if_ptr parameter is the
interface to the transport, and the cfg parameter is the configuration, which
contains the following information:
* The name of the transport
* The edge of the transport, i.e. remote processor name
* An array of the transport versions supported
* The maximum number of entries in the versions array
* The maximum number of channel identifiers supported
* The maximum number of intent identifiers supported
The implementation of this transport configuration structure is below.
struct glink_core_transport_cfg {
const char *name;
const char *edge;
const struct glink_core_version *versions;
size_t versions_count;
uint32_t max_cid;
uint32_t max_iid;
};
The initial state of a transport after it is registered is GLINK_XPRT_DOWN.
The possible states of a transport are as follows:
enum transport_state_e {
GLINK_XPRT_DOWN,
GLINK_XPRT_NEGOTIATING,
GLINK_XPRT_OPENED,
GLINK_XPRT_FAILED,
}
Unregister a Transport
----------------------
void glink_core_unregister_transport(struct glink_transport_if *if_ptr)
Unregister/destroy an existing transport. The transport's state is changed to
GLINK_XPRT_DOWN, and the following values are reset:
* the next local channel ID
* The local version index
* The remote version index
* The version negotiation completion flag
* The list of channels on the transport (list is deleted)
Driver parameters
=================
The G-Link core and G-Link Loopback Server modules both have a module parameter
called "debug_mask". The possible values are detailed in the "Config options"
section.
Config options
==============
G-Link supports several logging configurations. The following options are
available for the core and loopback client. They can be bitwise OR'ed together
to have multiple options at once.
QCOM_GLINK_INFO - The default option. Turn on only INFO messages
QCOM_GLINK_DEBUG - Turn on debug log messages - much more verbose logging to
aid in debugging.
QCOM_GLINK_PERF - Performance logging. This removes all other logging except
for logging messages that are created through a special
set of macros. These logs can be post-processed for
performance metrics.
The values of these options are as follows:
enum {
QCOM_GLINK_INFO = 1U << 0,
QCOM_GLINK_DEBUG = 1U << 1,
QCOM_GLINK_PERF = 1U << 2,
};
Dependencies
============
IPC Logging is a dependency of the G-Link core. The Transport Plug-ins will have
their own dependencies. The SMEM Native Transport depends on SMEM and the
interrupt subsystem.
DebugFS
=======
Several DebugFS nodes are exported under the glink directory for testing and
debugging. The directory structure below allows listing information by
subsystem, channel, and transport.
glink Directory
---------------
`-- debugfs
`-- glink
|-- channel
| |-- channels <- lists all of the channels in the system, their
| | state, the transport they are assigned to, etc.
| |-- SUBSYSTEM_NAME <- directory (such as "mpss")
| | `-- CHANNEL_NAME <- one directory per channel
| | |-- intents <- list of all open intents, their size, and
| | | their ID
| | `-- stats <- statistics for the channel (contents TBD)
`-- xprt
|-- xprts <- lists all of the transports in the system and basic
| transport-specific state
|-- XPRT_NAME <-- directory (such as "smem")
`-- XPRT_INFO <-- transport specific files
User space utilities
====================
A user space driver is provided which can export a character device for a single
channel based upon Device Tree configuration. The full DT schema is detailed in
Documentation/devicetree/bindings/arm/msm/glinkpkt.txt. The user space driver
implements the standard file operations of open, release, read, write, poll, and
mmap. An ioctl is defined to queue RX intents.
The file operations map to the G-Link Client API as follows:
open() -> glink_open() (Open a channel. Exported channels configured
through DT)
write() -> glink_tx() (Transmit data)
read() -> Receive data and send RX done notification (glink_rx_done())
release() -> glink_close() (Close a channel)
ioctl() -> glink_queue_rx_intent()
Other operations are:
poll() -> Poll waiting for the channel to become available
mmap() -> Prevent having to do a copy between kernel space and user space
for clients that need that performance.
A typical transmit and receive call flow is as follows:
1. G-Link user space driver opens the channel using open(), which returns a
file descriptor for the channel
2. An ioctl is sent to queue an RX intent on the channel
3. Data is transmitted on the channel using write()
4. The data is received using read(). read() also sends an RX done
notification.
Version/Feature Negotiation
===========================
To enable upgrading transports, G-Link supports a version number and feature
flags for each transport. The combination of the version number and feature
flags enable:
1. G-Link software updates to be rolled out to each processor
separately.
2. Individual features to be enabled or disabled on an edge-by-edge
basis.
Endpoints negotiate both the version and the feature flags when the transport
is opened; they cannot be changed after negotiation has been completed.
The version number represents any change in G-Link or the transport that
breaks compatibility between processors. Examples would be a change in the
shared data structures or changes to fundamental behavior in either the
transport or in G-Link Core. Each full implementation of G-Link must support a
minimum of the current version, the previous version, and the base negotiation
version called v0. For resource-constrained devices, this can be relaxed to
only support the latest version of the protocol and the v0 version.
The feature flags represent any changes in G-Link that are optional and
backwards-compatible. Feature flags can be version-specific, but to limit code
maintenance and documentation overhead, feature flags should not be re-used
unless the limit of 32 feature flags has been reached.
Negotiation Algorithm
---------------------
After a transport is registered with G-Link core, it should be configured with
the v0 transport settings. Once communication can be done without losing
messages, the link_up() call in the G-Link core should be made to start the
negotiation process. Both the local and remote sides will follow the same
negotiation state machines.
Since both sides follow the same sequence and both sides start once the link is
up, it is possible that both sides may start the negotiation sequence at the
same time resulting in a perceived race condition. However, both negotiation
sequences are independent and the transport is not considered opened until both
negotiation sequences are complete, so this is not a true race condition and
both sides will converge to the same version and feature set even if they
start with different versions and feature sets. Since this sequence is not
performance-critical, the extra complexity in the negotiation algorithm to
short-circuit the process is not deemed necessary.
Local-Initiated Negotiation Sequence
------------------------------------
The following local negotiation sequence is initiated and followed by each
side. The processor that is running this algorithm will be matched by the
remote processor following the Remote-Initiated Negotiation Sequence.
1. Set current version and feature variables to the maximum supported version
and feature set
2. Send Version Command (glink_transport_if::tx_cmd_version()) with local
version and feature set
3. If version is 0, then negotiation has failed and the transport should be
marked as having failed negotiation and the negotiation sequence
terminated.
4. When Version ACK is received (glink_core_if::rx_cmd_version_ack()):
a. Compare ACK version to the sent version and:
i. If equal, we are done with version negotiation
ii. Else set current version to the lower of:
1. Remote version number
2. Highest supported local version
b. Compare ACK features to the sent features and:
i. If equal, we are done with the negotiation sequence
ii. Else, call glink_core_version:: negotiate_features() for the
current version to set the features to either the bitwise AND of
the ACK features and the locally supported features or a lesser
feature set for cases where only certain combinations of features
are valid.
c. Go back to step 2 to send the updated version/feature set
Remote-Initiated Negotiation Sequence
-------------------------------------
The following remote negotiation sequence is followed by each side based upon
receiving a Version Command.
1. Receive Version Command (glink_core_if::rx_cmd_version())
2. Compare received version with the locally supported version and:
a. If equal, set ACK version to the received version
b. Else, set ACK version to the lower of:
i. Remote version number
ii. Highest supported local version
iii. Version 0 if no supported version less than or equal to
the remote version number can be found.
3. Compare received features with the locally supported features and:
a. If equal, set ACK features to the received features
b. Else, call glink_core_version:: negotiate_features() for the current
version to set the features to either the bitwise AND of the ACK
features and the locally supported features or a lesser feature set
for cases where only certain combinations of features are valid.
4. Send the Version ACK Command (glink_transport_if::tx_cmd_version_ack()).
Packets
=======
Packets are scheduled in a round-robin fashion from all active channels. Large
packets can be fragmented by the transport to ensure fairness between channels
and ensure latency.
Channel Migration
=================
The G-Link core has the capability of selecting the best transport available on
an edge-by-edge basis. The transport is selected based upon a pre-defined
transport priority and from optional transport selection information passed in
by the client in the glink_open_config structure.
Subsystem Restart (SSR)
=======================
In order to properly clean up channel state and recover buffer ownership
consistently across different physical transports, G-Link requires an
additional SSR notification system on top of the existing SSR framework. The
notification system is a star topology with the application processor as the
master. When a subsystem is restarted, all other subsystems are notified by the
application processor and must respond after cleaning up all affected channels
before the SSR event is allowed to continue.
The solution has four components:
1. Target-specific configuration for each subsystem, which consists of a list
of which subsystems should be notified in the event of SSR, specified in
Device Tree
2. SSR module that uses the G-Link Client API to isolate SSR functionality,
and handles calls to the SSR Framework, Device Tree parsing, etc.
3. SSR notification messages between the application processor and
other subsystems, which will be exchanged using G-Link.
4. SSR API:
a. glink_ssr(const char *subsystem_name) - G-Link Client API
b. ssr(struct glink_transport_if *if_ptr) - Transport Interface
1. Target-specific configuration using Device Tree
--------------------------------------------------
The target-specific configuration provides the G-Link SSR module with a list
of subsystems that should be notified in the event of SSR. This is necessary
to simplify handling of cases where there are multiple SoCs in one device -
there is no need to notify a subsystem on a second SoC of a restart in the
first SoC. The configuration also provides a mapping of the subsystem's name
in the SSR framework to its name as a G-Link edge, and allows the specification
of a transport for each notification. The figures below provide an example:
+----+ +------+ +-------------------+
|SSR |----->|G-Link|------->|Subsystem A |
|Name| |Name | |Subsystem B: xprt x|
+----+ +------+ |Subsystem C |
+-------------------+
+-------+ +------+ +--------------+
|"modem"|----->|"mpss"|------->|"wcnss" |
+-------+ +------+ |"lpass": "smd"|
|"dsps" |
+--------------+
The above configuration tells the G-Link SSR module to notify all subsystems
on G-Link edges "wcnss", "lpass", and "dsps" that the subsystem on edge "mpss"
has restarted, and to send the notifications to the "lpass" edge on the "smd"
transport.
2. G-Link SSR Module (glink_ssr)
--------------------------------
This module is a G-Link client which handles notifications from the SSR
framework on the application processor and triggers local clean-up in response
to these notifications by calling glink_ssr(const char *subsystem). This
module also sends notifications to any subsystems that need to be notified of
the SSR event, and ensures that they respond within the standard timeout (500
ms). If the subsystem fails to respond, it is restarted.
3. G-Link SSR Messages
----------------------
When an SSR event occurs, the application processor notifies all necessary
subsystems by sending a "do_cleanup" message. After the subsystem performs the
necessary clean-up, it sends back a "cleanup_done" message. If the
"cleanup_done" message for a given subsystem is not received within the
standard timeout (500 ms), the subsystem is restarted.
SSR "do_cleanup" Message
------------------------
+-----------------+-----------------------+------------------------------+
| Element | Type | Description |
+=================+=======================+==============================+
| version | uint32_t | G-Link SSR Protocol Version |
+-----------------+-----------------------+------------------------------+
| command | uint32_t | do_cleanup message (0) |
+-----------------+-----------------------+------------------------------+
| sequence_number | uint32_t | Sequence number |
+-----------------+-----------------------+------------------------------+
| name_len | uint32_t | Name length of the subsystem |
| | | being restarted |
+-----------------+-----------------------+------------------------------+
| name | char[GLINK_NAME_SIZE] | NULL-terminated name of the |
| | | subsystem being restarted |
| | | (GLINK_NAME_SIZE == 32) |
+-----------------+-----------------------+------------------------------+
SSR "cleanup_done" Message
--------------------------
+-----------------+-----------------------+------------------------------+
| Element | Type | Description |
+=================+=======================+==============================+
| version | uint32_t | G-Link SSR Protocol Version |
+-----------------+-----------------------+------------------------------+
| response | uint32_t | cleanup_done message (1) |
+-----------------+-----------------------+------------------------------+
| sequence_number | uint32_t | Sequence number |
+-----------------+-----------------------+------------------------------+
G-Link SSR Protocol Sequence Diagram
------------------------------------
+------+ +------+
+---------+ |G-Link| |G-Link| +----------+
|SSR | |SSR | |Client| +---------+ |Remote |
|Framework| |Module| |API | |Transport| |Processors|
+---------+ +------+ +------+ +---------+ +----------+
| | | | |
| SSR | | | |
| Notification | | | |
+--------------->| | | |
| | | | |
| | | | |
| | | | |
| | do_cleanup | | |
| +------------------------------------------------>|
| | | | |
| | glink_ssr(subsystem) | |
| +------------->| | |
| | | | |
| | | | |
| | | ssr(if_ptr) | |
| | +-------------->| |
| | | | |
| | | | |
| | | | |
| | | | cleanup_done |
| |<------------------------------------------------+
| | | | |
| | | | |
| | | | |
| ssr(subsystem)| | | |
|<---------------+ | | |
| | | | |
| +-----------+---------+ | | |
| |If no cleanup_done | | | |
| |response is received,| | | |
| |restart the subsystem| | | |
| +-----------+---------+ | | |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
+---------+ +------+ +------+ +---------+ +----------+
|SSR | |G-Link| |G-Link| |Transport| |Remote |
|Framework| |SSR | |Client| +---------+ |Processors|
+---------+ |Module| |API | +----------+
+------+ +------+
4. SSR API
----------
glink_ssr(const char *subsystem_name)
-------------------------------------
Called by the G-Link SSR Module, this function calls into the transport using
ssr(struct glink_transport_if *if_ptr) to allow the transport to perform any
necessary clean-up, and simulates receiving a remote close from the restarting
subsystem for all channels on the affected edge.
ssr(struct glink_transport_if *if_ptr)
--------------------------------------
The ssr() function is part of the Transport Interface, and as mentioned above is
used to perform any necessary clean-up on the transport.
Tracer Packet Framework
=======================
A tracer packet is a special type of packet that can be used to trace the timing
of events. This helps profile the latency experienced by a packet, and provides
granular information regarding the individual latencies that make up the overall
latency. The information obtained using the tracer packet can be used to
configure the Power Management Quality of Service (PM QoS) in the system in
order to achieve a client's desired packet latency. The tracer packet moves
through the system along with other normal traffic without any impact on the
normal traffic.
When a transport is registered with the local G-Link core, it performs a
transport-specific version and feature negotiation with the remote G-Link core.
Based on this negotiation, the transport reports its capability of supporting
tracer packets to the local G-Link core.
Once the transport has successfully completed the negotiation, the clients open
the G-Link channel over the concerned transport. After the channel is open, the
clients can exchange tracer packets over G-Link, in a way similar to normal
traffic.
When a tracer packet is exchanged over a G-Link channel, the G-Link core and the
transport log events related to packet exchange and their time.
1. Tracer Packet Format
-----------------------
The tracer packet contains a header and a payload. The header contains
identification and configuration information associated with a tracer packet.
The payload contains a series of event logs. The below diagram shows the layout
of the tracer packet:
Tracer Packet Header Layout
---------------------------
31 16 15 14 13 12 11 4 3 0
+-------------------------------+-----+---+----+------+---------+
| Packet Length (words) | CCL | Q | ID | Res. | Version |
+-------------------------------+-------------------------------+
| Client Event Cfg. | Packet Offset (words) |
| Bit Mask | |
+-------------------------------+-------------------------------+
| G-Link Event Config Bit Mask |
+---------------------------------------------------------------+
| Base Timestamp (MS 32 bits) |
+---------------------------------------------------------------+
| Base Timestamp (LS 32 bits) |
+---------------------------------------------------------------+
| Client Cookie |
+---------------------------------------------------------------+
Tracer Packet Payload Layout
----------------------------
31 16 15 0
+-------------------------------+-------------------------------+
| Reserved | Event 1 ID |
+-------------------------------+-------------------------------+
| Event 1 Timestamp (LS 32 bits) |
+---------------------------------------------------------------+
.
.
.
+-------------------------------+-------------------------------+
| Reserved | Event N ID |
+-------------------------------+-------------------------------+
| Event N Timestamp (LS 32 bits) |
+---------------------------------------------------------------+
Tracer Packet Header Fields
---------------------------
Version - This field contains the tracer packet version. The current version
of tracer packet supported by G-Link is 1. If a version of the tracer
packet is not supported by G-Link or its transport abstraction layer,
the tracer packet is still exchanged, but no events are logged.
Reserved - The reserved bit fields are set to 0 and can be used for future
extension of tracer packet functionality.
ID - The ID bit field indicates the presence or absence of the Source Processor
ID, Destination Processor ID and Transport ID fields in the tracer
packet. Currently this field is set to 0 and the concerned IDs are not
defined.
CoreSight ("Q" in the diagram above) - This bit field is used to indicate the
location of the log events. If this bit
field is set, the log events are logged
into CoreSight, otherwise the log events
are logged into the packet itself. If the
log events are logged into the packet,
then the number of events logged into the
packet depends on the size of the packet.
CCL - The tracer packet framework allows clients to differentiate multiple
tracer packets through a client-specified cookie. The Client Cookie Length
(CCL) bit field indicates the length of that cookie in units of words.
Packet Length - These 16 bits indicate the length of the tracer packet in units
of words.
Packet Offset - This field is used when events are logged into the packet. This
16-bit field indicates the offset into the packet, in units of
words, to log an event. Once an event is logged, this field is
updated with the appropriate offset to log future events.
Client Configuration Bit Mask - This bit-mask is used to enable/disable the
G-Link client-specific events. The procedure to
enable/disable client events is dependent upon
the client's implementation and is not included
in this document.
G-Link Configuration Bit Mask - This bit-mask is used to enable/disable the
G-Link-specific events. When a bit is set, the
concerned event logging is enabled.
Base Timestamp - The base timestamp contains the starting time of the tracer
packet exchange. The timestamp logged along with the event is
used as an offset from this base timestamp. This optimization
helps in reducing the log size of an event.
Client Cookie - The tracer packet framework allows clients to differentiate
multiple tracer packets through a client-specified cookie.
Tracer Packet Payload Fields
----------------------------
Event ID - The Event ID field uniquely identifies the G-Link and client-specific
tracer packet events. This field is present only when the events are
logged into the packet. The G-Link and client event IDs are assigned
a unique range. Refer to the table below for more information
regarding the event ID definition.
Reserved - The reserved field is set to 0 and can be used for future extension
of tracer packet functionality.
Event Timestamp - The Event Timestamp field contains the time at which the event
is logged. This field is used as an offset from the Base
Timestamp field in the header to calculate the actual event
timestamp. This field is present only when the events are
logged into the packet.
2. Tracer Packet Events
-----------------------
Each event has a uniquely defined ID. Since G-Link clients can use the tracer
packet framework, G-Link events and G-Link client events are defined in mutually
exclusive ranges. Since client events are client-context specific, the event
IDs can be reused among the clients. The ranges are detailed in the table below:
+--------------------------+-----------------------+
| Event Type | Range |
+==========================+=======================+
| G-Link | 1-255 |
+--------------------------+-----------------------+
| Client | 255 and above |
+--------------------------+-----------------------+
The G-Link specific events and their associated IDs are defined in the below
table:
+--------------------------+-----------------------+
| G-Link Event | ID |
+==========================+=======================+
| GLINK_CORE_TX | 1 |
+--------------------------+-----------------------+
| GLINK_QUEUE_TO_SCHEDULER | 2 |
+--------------------------+-----------------------+
| GLINK_SCHEDULER_TX | 3 |
+--------------------------+-----------------------+
| GLINK_XPRT_TX | 4 |
+--------------------------+-----------------------+
| GLINK_XPRT_RX | 5 |
+--------------------------+-----------------------+
| GLINK_CORE_RX | 6 |
+--------------------------+-----------------------+
3. Tracer Packet API
--------------------
tracer_pkt_init(void *data, size_t data_len, uint16_t client_event_cfg,
uint32_t glink_event_cfg, void *pkt_priv, size_t pkt_priv_len)
--------------------------------------------------------------------------
Initialize a buffer with the tracer packet header. The tracer packet header
includes the data passed in the parameters.
tracer_pkt_set_event_cfg(void *data, uint16_t client_event_cfg,
uint32_t glink_event_cfg)
---------------------------------------------------------------
Initialize a buffer with the event configuration mask passed in the parameters.
tracer_pkt_log_event(void *data, uint32_t event_id)
---------------------------------------------------
Log an event specific to the tracer packet. The event is logged either into
the tracer packet itself or a different tracing mechanism as configured.
tracer_pkt_calc_hex_dump_size(void *data, size_t data_len)
----------------------------------------------------------
Calculate the length of the buffer required to hold the hex dump of the tracer
packet.
tracer_pkt_hex_dump(void *buf, size_t buf_len, void *data, size_t data_len)
---------------------------------------------------------------------------
Dump the contents of the tracer packet into a buffer in a specific hexadecimal
format. The hex dump buffer can then be dumped through debugfs.
Known issues
============
No known issues.
To do
=====
Power Management
----------------
An internal power voting API will be defined to bring the transport out of power
collapse for SMUX and BAM DMUX-type systems. In addition, power for
request/response type systems can be optimized to prevent powering down
unnecessarily after sending a request only to power up immediately to process
the response.
Round-Robin Scheduling
----------------------
Add deficit round-robin schedule to ensure fairness between channels that have
a large number of small packets and channels that are sending the maximum
MTU-sized packets.
Transport Filter Internal API
-----------------------------
An internal transport filter API will be defined. This can be plugged into a
filter chain at the transport level to easily add data coding, encryption,
integrity hashes, etc.