439 lines
15 KiB
Plaintext
439 lines
15 KiB
Plaintext
|
Coherent Accelerator Interface (CXL)
|
||
|
====================================
|
||
|
|
||
|
Introduction
|
||
|
============
|
||
|
|
||
|
The coherent accelerator interface is designed to allow the
|
||
|
coherent connection of accelerators (FPGAs and other devices) to a
|
||
|
POWER system. These devices need to adhere to the Coherent
|
||
|
Accelerator Interface Architecture (CAIA).
|
||
|
|
||
|
IBM refers to this as the Coherent Accelerator Processor Interface
|
||
|
or CAPI. In the kernel it's referred to by the name CXL to avoid
|
||
|
confusion with the ISDN CAPI subsystem.
|
||
|
|
||
|
Coherent in this context means that the accelerator and CPUs can
|
||
|
both access system memory directly and with the same effective
|
||
|
addresses.
|
||
|
|
||
|
|
||
|
Hardware overview
|
||
|
=================
|
||
|
|
||
|
POWER8 FPGA
|
||
|
+----------+ +---------+
|
||
|
| | | |
|
||
|
| CPU | | AFU |
|
||
|
| | | |
|
||
|
| | | |
|
||
|
| | | |
|
||
|
+----------+ +---------+
|
||
|
| PHB | | |
|
||
|
| +------+ | PSL |
|
||
|
| | CAPP |<------>| |
|
||
|
+---+------+ PCIE +---------+
|
||
|
|
||
|
The POWER8 chip has a Coherently Attached Processor Proxy (CAPP)
|
||
|
unit which is part of the PCIe Host Bridge (PHB). This is managed
|
||
|
by Linux by calls into OPAL. Linux doesn't directly program the
|
||
|
CAPP.
|
||
|
|
||
|
The FPGA (or coherently attached device) consists of two parts.
|
||
|
The POWER Service Layer (PSL) and the Accelerator Function Unit
|
||
|
(AFU). The AFU is used to implement specific functionality behind
|
||
|
the PSL. The PSL, among other things, provides memory address
|
||
|
translation services to allow each AFU direct access to userspace
|
||
|
memory.
|
||
|
|
||
|
The AFU is the core part of the accelerator (eg. the compression,
|
||
|
crypto etc function). The kernel has no knowledge of the function
|
||
|
of the AFU. Only userspace interacts directly with the AFU.
|
||
|
|
||
|
The PSL provides the translation and interrupt services that the
|
||
|
AFU needs. This is what the kernel interacts with. For example, if
|
||
|
the AFU needs to read a particular effective address, it sends
|
||
|
that address to the PSL, the PSL then translates it, fetches the
|
||
|
data from memory and returns it to the AFU. If the PSL has a
|
||
|
translation miss, it interrupts the kernel and the kernel services
|
||
|
the fault. The context to which this fault is serviced is based on
|
||
|
who owns that acceleration function.
|
||
|
|
||
|
|
||
|
AFU Modes
|
||
|
=========
|
||
|
|
||
|
There are two programming modes supported by the AFU. Dedicated
|
||
|
and AFU directed. AFU may support one or both modes.
|
||
|
|
||
|
When using dedicated mode only one MMU context is supported. In
|
||
|
this mode, only one userspace process can use the accelerator at
|
||
|
time.
|
||
|
|
||
|
When using AFU directed mode, up to 16K simultaneous contexts can
|
||
|
be supported. This means up to 16K simultaneous userspace
|
||
|
applications may use the accelerator (although specific AFUs may
|
||
|
support fewer). In this mode, the AFU sends a 16 bit context ID
|
||
|
with each of its requests. This tells the PSL which context is
|
||
|
associated with each operation. If the PSL can't translate an
|
||
|
operation, the ID can also be accessed by the kernel so it can
|
||
|
determine the userspace context associated with an operation.
|
||
|
|
||
|
|
||
|
MMIO space
|
||
|
==========
|
||
|
|
||
|
A portion of the accelerator MMIO space can be directly mapped
|
||
|
from the AFU to userspace. Either the whole space can be mapped or
|
||
|
just a per context portion. The hardware is self describing, hence
|
||
|
the kernel can determine the offset and size of the per context
|
||
|
portion.
|
||
|
|
||
|
|
||
|
Interrupts
|
||
|
==========
|
||
|
|
||
|
AFUs may generate interrupts that are destined for userspace. These
|
||
|
are received by the kernel as hardware interrupts and passed onto
|
||
|
userspace by a read syscall documented below.
|
||
|
|
||
|
Data storage faults and error interrupts are handled by the kernel
|
||
|
driver.
|
||
|
|
||
|
|
||
|
Work Element Descriptor (WED)
|
||
|
=============================
|
||
|
|
||
|
The WED is a 64-bit parameter passed to the AFU when a context is
|
||
|
started. Its format is up to the AFU hence the kernel has no
|
||
|
knowledge of what it represents. Typically it will be the
|
||
|
effective address of a work queue or status block where the AFU
|
||
|
and userspace can share control and status information.
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
User API
|
||
|
========
|
||
|
|
||
|
1. AFU character devices
|
||
|
|
||
|
For AFUs operating in AFU directed mode, two character device
|
||
|
files will be created. /dev/cxl/afu0.0m will correspond to a
|
||
|
master context and /dev/cxl/afu0.0s will correspond to a slave
|
||
|
context. Master contexts have access to the full MMIO space an
|
||
|
AFU provides. Slave contexts have access to only the per process
|
||
|
MMIO space an AFU provides.
|
||
|
|
||
|
For AFUs operating in dedicated process mode, the driver will
|
||
|
only create a single character device per AFU called
|
||
|
/dev/cxl/afu0.0d. This will have access to the entire MMIO space
|
||
|
that the AFU provides (like master contexts in AFU directed).
|
||
|
|
||
|
The types described below are defined in include/uapi/misc/cxl.h
|
||
|
|
||
|
The following file operations are supported on both slave and
|
||
|
master devices.
|
||
|
|
||
|
A userspace library libcxl is available here:
|
||
|
https://github.com/ibm-capi/libcxl
|
||
|
This provides a C interface to this kernel API.
|
||
|
|
||
|
open
|
||
|
----
|
||
|
|
||
|
Opens the device and allocates a file descriptor to be used with
|
||
|
the rest of the API.
|
||
|
|
||
|
A dedicated mode AFU only has one context and only allows the
|
||
|
device to be opened once.
|
||
|
|
||
|
An AFU directed mode AFU can have many contexts, the device can be
|
||
|
opened once for each context that is available.
|
||
|
|
||
|
When all available contexts are allocated the open call will fail
|
||
|
and return -ENOSPC.
|
||
|
|
||
|
Note: IRQs need to be allocated for each context, which may limit
|
||
|
the number of contexts that can be created, and therefore
|
||
|
how many times the device can be opened. The POWER8 CAPP
|
||
|
supports 2040 IRQs and 3 are used by the kernel, so 2037 are
|
||
|
left. If 1 IRQ is needed per context, then only 2037
|
||
|
contexts can be allocated. If 4 IRQs are needed per context,
|
||
|
then only 2037/4 = 509 contexts can be allocated.
|
||
|
|
||
|
|
||
|
ioctl
|
||
|
-----
|
||
|
|
||
|
CXL_IOCTL_START_WORK:
|
||
|
Starts the AFU context and associates it with the current
|
||
|
process. Once this ioctl is successfully executed, all memory
|
||
|
mapped into this process is accessible to this AFU context
|
||
|
using the same effective addresses. No additional calls are
|
||
|
required to map/unmap memory. The AFU memory context will be
|
||
|
updated as userspace allocates and frees memory. This ioctl
|
||
|
returns once the AFU context is started.
|
||
|
|
||
|
Takes a pointer to a struct cxl_ioctl_start_work:
|
||
|
|
||
|
struct cxl_ioctl_start_work {
|
||
|
__u64 flags;
|
||
|
__u64 work_element_descriptor;
|
||
|
__u64 amr;
|
||
|
__s16 num_interrupts;
|
||
|
__s16 reserved1;
|
||
|
__s32 reserved2;
|
||
|
__u64 reserved3;
|
||
|
__u64 reserved4;
|
||
|
__u64 reserved5;
|
||
|
__u64 reserved6;
|
||
|
};
|
||
|
|
||
|
flags:
|
||
|
Indicates which optional fields in the structure are
|
||
|
valid.
|
||
|
|
||
|
work_element_descriptor:
|
||
|
The Work Element Descriptor (WED) is a 64-bit argument
|
||
|
defined by the AFU. Typically this is an effective
|
||
|
address pointing to an AFU specific structure
|
||
|
describing what work to perform.
|
||
|
|
||
|
amr:
|
||
|
Authority Mask Register (AMR), same as the powerpc
|
||
|
AMR. This field is only used by the kernel when the
|
||
|
corresponding CXL_START_WORK_AMR value is specified in
|
||
|
flags. If not specified the kernel will use a default
|
||
|
value of 0.
|
||
|
|
||
|
num_interrupts:
|
||
|
Number of userspace interrupts to request. This field
|
||
|
is only used by the kernel when the corresponding
|
||
|
CXL_START_WORK_NUM_IRQS value is specified in flags.
|
||
|
If not specified the minimum number required by the
|
||
|
AFU will be allocated. The min and max number can be
|
||
|
obtained from sysfs.
|
||
|
|
||
|
reserved fields:
|
||
|
For ABI padding and future extensions
|
||
|
|
||
|
CXL_IOCTL_GET_PROCESS_ELEMENT:
|
||
|
Get the current context id, also known as the process element.
|
||
|
The value is returned from the kernel as a __u32.
|
||
|
|
||
|
|
||
|
mmap
|
||
|
----
|
||
|
|
||
|
An AFU may have an MMIO space to facilitate communication with the
|
||
|
AFU. If it does, the MMIO space can be accessed via mmap. The size
|
||
|
and contents of this area are specific to the particular AFU. The
|
||
|
size can be discovered via sysfs.
|
||
|
|
||
|
In AFU directed mode, master contexts are allowed to map all of
|
||
|
the MMIO space and slave contexts are allowed to only map the per
|
||
|
process MMIO space associated with the context. In dedicated
|
||
|
process mode the entire MMIO space can always be mapped.
|
||
|
|
||
|
This mmap call must be done after the START_WORK ioctl.
|
||
|
|
||
|
Care should be taken when accessing MMIO space. Only 32 and 64-bit
|
||
|
accesses are supported by POWER8. Also, the AFU will be designed
|
||
|
with a specific endianness, so all MMIO accesses should consider
|
||
|
endianness (recommend endian(3) variants like: le64toh(),
|
||
|
be64toh() etc). These endian issues equally apply to shared memory
|
||
|
queues the WED may describe.
|
||
|
|
||
|
|
||
|
read
|
||
|
----
|
||
|
|
||
|
Reads events from the AFU. Blocks if no events are pending
|
||
|
(unless O_NONBLOCK is supplied). Returns -EIO in the case of an
|
||
|
unrecoverable error or if the card is removed.
|
||
|
|
||
|
read() will always return an integral number of events.
|
||
|
|
||
|
The buffer passed to read() must be at least 4K bytes.
|
||
|
|
||
|
The result of the read will be a buffer of one or more events,
|
||
|
each event is of type struct cxl_event, of varying size.
|
||
|
|
||
|
struct cxl_event {
|
||
|
struct cxl_event_header header;
|
||
|
union {
|
||
|
struct cxl_event_afu_interrupt irq;
|
||
|
struct cxl_event_data_storage fault;
|
||
|
struct cxl_event_afu_error afu_error;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
The struct cxl_event_header is defined as:
|
||
|
|
||
|
struct cxl_event_header {
|
||
|
__u16 type;
|
||
|
__u16 size;
|
||
|
__u16 process_element;
|
||
|
__u16 reserved1;
|
||
|
};
|
||
|
|
||
|
type:
|
||
|
This defines the type of event. The type determines how
|
||
|
the rest of the event is structured. These types are
|
||
|
described below and defined by enum cxl_event_type.
|
||
|
|
||
|
size:
|
||
|
This is the size of the event in bytes including the
|
||
|
struct cxl_event_header. The start of the next event can
|
||
|
be found at this offset from the start of the current
|
||
|
event.
|
||
|
|
||
|
process_element:
|
||
|
Context ID of the event.
|
||
|
|
||
|
reserved field:
|
||
|
For future extensions and padding.
|
||
|
|
||
|
If the event type is CXL_EVENT_AFU_INTERRUPT then the event
|
||
|
structure is defined as:
|
||
|
|
||
|
struct cxl_event_afu_interrupt {
|
||
|
__u16 flags;
|
||
|
__u16 irq; /* Raised AFU interrupt number */
|
||
|
__u32 reserved1;
|
||
|
};
|
||
|
|
||
|
flags:
|
||
|
These flags indicate which optional fields are present
|
||
|
in this struct. Currently all fields are mandatory.
|
||
|
|
||
|
irq:
|
||
|
The IRQ number sent by the AFU.
|
||
|
|
||
|
reserved field:
|
||
|
For future extensions and padding.
|
||
|
|
||
|
If the event type is CXL_EVENT_DATA_STORAGE then the event
|
||
|
structure is defined as:
|
||
|
|
||
|
struct cxl_event_data_storage {
|
||
|
__u16 flags;
|
||
|
__u16 reserved1;
|
||
|
__u32 reserved2;
|
||
|
__u64 addr;
|
||
|
__u64 dsisr;
|
||
|
__u64 reserved3;
|
||
|
};
|
||
|
|
||
|
flags:
|
||
|
These flags indicate which optional fields are present in
|
||
|
this struct. Currently all fields are mandatory.
|
||
|
|
||
|
address:
|
||
|
The address that the AFU unsuccessfully attempted to
|
||
|
access. Valid accesses will be handled transparently by the
|
||
|
kernel but invalid accesses will generate this event.
|
||
|
|
||
|
dsisr:
|
||
|
This field gives information on the type of fault. It is a
|
||
|
copy of the DSISR from the PSL hardware when the address
|
||
|
fault occurred. The form of the DSISR is as defined in the
|
||
|
CAIA.
|
||
|
|
||
|
reserved fields:
|
||
|
For future extensions
|
||
|
|
||
|
If the event type is CXL_EVENT_AFU_ERROR then the event structure
|
||
|
is defined as:
|
||
|
|
||
|
struct cxl_event_afu_error {
|
||
|
__u16 flags;
|
||
|
__u16 reserved1;
|
||
|
__u32 reserved2;
|
||
|
__u64 error;
|
||
|
};
|
||
|
|
||
|
flags:
|
||
|
These flags indicate which optional fields are present in
|
||
|
this struct. Currently all fields are Mandatory.
|
||
|
|
||
|
error:
|
||
|
Error status from the AFU. Defined by the AFU.
|
||
|
|
||
|
reserved fields:
|
||
|
For future extensions and padding
|
||
|
|
||
|
|
||
|
2. Card character device (powerVM guest only)
|
||
|
|
||
|
In a powerVM guest, an extra character device is created for the
|
||
|
card. The device is only used to write (flash) a new image on the
|
||
|
FPGA accelerator. Once the image is written and verified, the
|
||
|
device tree is updated and the card is reset to reload the updated
|
||
|
image.
|
||
|
|
||
|
open
|
||
|
----
|
||
|
|
||
|
Opens the device and allocates a file descriptor to be used with
|
||
|
the rest of the API. The device can only be opened once.
|
||
|
|
||
|
ioctl
|
||
|
-----
|
||
|
|
||
|
CXL_IOCTL_DOWNLOAD_IMAGE:
|
||
|
CXL_IOCTL_VALIDATE_IMAGE:
|
||
|
Starts and controls flashing a new FPGA image. Partial
|
||
|
reconfiguration is not supported (yet), so the image must contain
|
||
|
a copy of the PSL and AFU(s). Since an image can be quite large,
|
||
|
the caller may have to iterate, splitting the image in smaller
|
||
|
chunks.
|
||
|
|
||
|
Takes a pointer to a struct cxl_adapter_image:
|
||
|
struct cxl_adapter_image {
|
||
|
__u64 flags;
|
||
|
__u64 data;
|
||
|
__u64 len_data;
|
||
|
__u64 len_image;
|
||
|
__u64 reserved1;
|
||
|
__u64 reserved2;
|
||
|
__u64 reserved3;
|
||
|
__u64 reserved4;
|
||
|
};
|
||
|
|
||
|
flags:
|
||
|
These flags indicate which optional fields are present in
|
||
|
this struct. Currently all fields are mandatory.
|
||
|
|
||
|
data:
|
||
|
Pointer to a buffer with part of the image to write to the
|
||
|
card.
|
||
|
|
||
|
len_data:
|
||
|
Size of the buffer pointed to by data.
|
||
|
|
||
|
len_image:
|
||
|
Full size of the image.
|
||
|
|
||
|
|
||
|
Sysfs Class
|
||
|
===========
|
||
|
|
||
|
A cxl sysfs class is added under /sys/class/cxl to facilitate
|
||
|
enumeration and tuning of the accelerators. Its layout is
|
||
|
described in Documentation/ABI/testing/sysfs-class-cxl
|
||
|
|
||
|
|
||
|
Udev rules
|
||
|
==========
|
||
|
|
||
|
The following udev rules could be used to create a symlink to the
|
||
|
most logical chardev to use in any programming mode (afuX.Yd for
|
||
|
dedicated, afuX.Ys for afu directed), since the API is virtually
|
||
|
identical for each:
|
||
|
|
||
|
SUBSYSTEM=="cxl", ATTRS{mode}=="dedicated_process", SYMLINK="cxl/%b"
|
||
|
SUBSYSTEM=="cxl", ATTRS{mode}=="afu_directed", \
|
||
|
KERNEL=="afu[0-9]*.[0-9]*s", SYMLINK="cxl/%b"
|