Merge remote-tracking branch 'remotes/marcel/tags/rdma-pull-request' into staging

PVRDMA implementation

# gpg: Signature made Mon 19 Feb 2018 11:08:49 GMT
# gpg:                using RSA key 36D4C0F0CF2FE46D
# gpg: Good signature from "Marcel Apfelbaum <marcel@redhat.com>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: B1C6 3A57 F92E 08F2 640F  31F5 36D4 C0F0 CF2F E46D

* remotes/marcel/tags/rdma-pull-request:
  MAINTAINERS: add entry for hw/rdma
  hw/rdma: Implementation of PVRDMA device
  hw/rdma: PVRDMA commands and data-path ops
  hw/rdma: Implementation of generic rdma device layers
  hw/rdma: Definitions for rdma device and rdma resource manager
  hw/rdma: Add wrappers and macros
  include/standard-headers: add pvrdma related headers
  scripts/update-linux-headers: import pvrdma headers
  docs: add pvrdma device documentation.
  mem: add share parameter to memory-backend-ram

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2018-02-19 12:51:11 +00:00
43 changed files with 5570 additions and 54 deletions
+8
View File
@@ -2034,6 +2034,14 @@ F: block/replication.c
F: tests/test-replication.c
F: docs/block-replication.txt
PVRDMA
M: Yuval Shaia <yuval.shaia@oracle.com>
M: Marcel Apfelbaum <marcel@redhat.com>
S: Maintained
F: hw/rdma/*
F: hw/rdma/vmw/*
F: docs/pvrdma.txt
Build and test automation
-------------------------
Build and test automation
+2
View File
@@ -130,6 +130,8 @@ trace-events-subdirs += hw/block/dataplane
trace-events-subdirs += hw/char
trace-events-subdirs += hw/intc
trace-events-subdirs += hw/net
trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/audio
trace-events-subdirs += hw/misc
+1 -24
View File
@@ -31,7 +31,6 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
struct HostMemoryBackendFile {
HostMemoryBackend parent_obj;
bool share;
bool discard_data;
char *mem_path;
uint64_t align;
@@ -59,7 +58,7 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
path = object_get_canonical_path(OBJECT(backend));
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
path,
backend->size, fb->align, fb->share,
backend->size, fb->align, backend->share,
fb->mem_path, errp);
g_free(path);
}
@@ -86,25 +85,6 @@ static void set_mem_path(Object *o, const char *str, Error **errp)
fb->mem_path = g_strdup(str);
}
static bool file_memory_backend_get_share(Object *o, Error **errp)
{
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
return fb->share;
}
static void file_memory_backend_set_share(Object *o, bool value, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property value");
return;
}
fb->share = value;
}
static bool file_memory_backend_get_discard_data(Object *o, Error **errp)
{
return MEMORY_BACKEND_FILE(o)->discard_data;
@@ -171,9 +151,6 @@ file_backend_class_init(ObjectClass *oc, void *data)
bc->alloc = file_backend_memory_alloc;
oc->unparent = file_backend_unparent;
object_class_property_add_bool(oc, "share",
file_memory_backend_get_share, file_memory_backend_set_share,
&error_abort);
object_class_property_add_bool(oc, "discard-data",
file_memory_backend_get_discard_data, file_memory_backend_set_discard_data,
&error_abort);
+2 -2
View File
@@ -28,8 +28,8 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
}
path = object_get_canonical_path_component(OBJECT(backend));
memory_region_init_ram_nomigrate(&backend->mr, OBJECT(backend), path,
backend->size, errp);
memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path,
backend->size, backend->share, errp);
g_free(path);
}
+21
View File
@@ -368,6 +368,24 @@ static void set_id(Object *o, const char *str, Error **errp)
backend->id = g_strdup(str);
}
static bool host_memory_backend_get_share(Object *o, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
return backend->share;
}
static void host_memory_backend_set_share(Object *o, bool value, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
if (host_memory_backend_mr_inited(backend)) {
error_setg(errp, "cannot change property value");
return;
}
backend->share = value;
}
static void
host_memory_backend_class_init(ObjectClass *oc, void *data)
{
@@ -398,6 +416,9 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
host_memory_backend_get_policy,
host_memory_backend_set_policy, &error_abort);
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
object_class_property_add_bool(oc, "share",
host_memory_backend_get_share, host_memory_backend_set_share,
&error_abort);
}
static void host_memory_backend_finalize(Object *o)
Vendored
+5 -4
View File
@@ -1572,7 +1572,7 @@ disabled with --disable-FEATURE, default is enabled if available:
hax HAX acceleration support
hvf Hypervisor.framework acceleration support
whpx Windows Hypervisor Platform acceleration support
rdma RDMA-based migration support
rdma Enable RDMA-based migration and PVRDMA support
vde support for vde network
netmap support for netmap network
linux-aio Linux AIO support
@@ -2923,15 +2923,16 @@ if test "$rdma" != "no" ; then
#include <rdma/rdma_cma.h>
int main(void) { return 0; }
EOF
rdma_libs="-lrdmacm -libverbs"
rdma_libs="-lrdmacm -libverbs -libumad"
if compile_prog "" "$rdma_libs" ; then
rdma="yes"
libs_softmmu="$libs_softmmu $rdma_libs"
else
if test "$rdma" = "yes" ; then
error_exit \
" OpenFabrics librdmacm/libibverbs not present." \
" OpenFabrics librdmacm/libibverbs/libibumad not present." \
" Your options:" \
" (1) Fast: Install infiniband packages from your distro." \
" (1) Fast: Install infiniband packages (devel) from your distro." \
" (2) Cleanest: Install libraries from www.openfabrics.org" \
" (3) Also: Install softiwarp if you don't have RDMA hardware"
fi
+255
View File
@@ -0,0 +1,255 @@
Paravirtualized RDMA Device (PVRDMA)
====================================
1. Description
===============
PVRDMA is the QEMU implementation of VMware's paravirtualized RDMA device.
It works with its Linux Kernel driver AS IS, no need for any special guest
modifications.
While it complies with the VMware device, it can also communicate with bare
metal RDMA-enabled machines and does not require an RDMA HCA in the host, it
can work with Soft-RoCE (rxe).
It does not require the whole guest RAM to be pinned allowing memory
over-commit and, even if not implemented yet, migration support will be
possible with some HW assistance.
A project presentation accompany this document:
- http://events.linuxfoundation.org/sites/events/files/slides/lpc-2017-pvrdma-marcel-apfelbaum-yuval-shaia.pdf
2. Setup
========
2.1 Guest setup
===============
Fedora 27+ kernels work out of the box, older distributions
require updating the kernel to 4.14 to include the pvrdma driver.
However the libpvrdma library needed by User Level Software is still
not available as part of the distributions, so the rdma-core library
needs to be compiled and optionally installed.
Please follow the instructions at:
https://github.com/linux-rdma/rdma-core.git
2.2 Host Setup
==============
The pvrdma backend is an ibdevice interface that can be exposed
either by a Soft-RoCE(rxe) device on machines with no RDMA device,
or an HCA SRIOV function(VF/PF).
Note that ibdevice interfaces can't be shared between pvrdma devices,
each one requiring a separate instance (rxe or SRIOV VF).
2.2.1 Soft-RoCE backend(rxe)
===========================
A stable version of rxe is required, Fedora 27+ or a Linux
Kernel 4.14+ is preferred.
The rdma_rxe module is part of the Linux Kernel but not loaded by default.
Install the User Level library (librxe) following the instructions from:
https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home
Associate an ETH interface with rxe by running:
rxe_cfg add eth0
An rxe0 ibdevice interface will be created and can be used as pvrdma backend.
2.2.2 RDMA device Virtual Function backend
==========================================
Nothing special is required, the pvrdma device can work not only with
Ethernet Links, but also Infinibands Links.
All is needed is an ibdevice with an active port, for Mellanox cards
will be something like mlx5_6 which can be the backend.
2.2.3 QEMU setup
================
Configure QEMU with --enable-rdma flag, installing
the required RDMA libraries.
3. Usage
========
Currently the device is working only with memory backed RAM
and it must be mark as "shared":
-m 1G \
-object memory-backend-ram,id=mb1,size=1G,share \
-numa node,memdev=mb1 \
The pvrdma device is composed of two functions:
- Function 0 is a vmxnet Ethernet Device which is redundant in Guest
but is required to pass the ibdevice GID using its MAC.
Examples:
For an rxe backend using eth0 interface it will use its mac:
-device vmxnet3,addr=<slot>.0,multifunction=on,mac=<eth0 MAC>
For an SRIOV VF, we take the Ethernet Interface exposed by it:
-device vmxnet3,multifunction=on,mac=<RoCE eth MAC>
- Function 1 is the actual device:
-device pvrdma,addr=<slot>.1,backend-dev=<ibdevice>,backend-gid-idx=<gid>,backend-port=<port>
where the ibdevice can be rxe or RDMA VF (e.g. mlx5_4)
Note: Pay special attention that the GID at backend-gid-idx matches vmxnet's MAC.
The rules of conversion are part of the RoCE spec, but since manual conversion
is not required, spotting problems is not hard:
Example: GID: fe80:0000:0000:0000:7efe:90ff:fecb:743a
MAC: 7c:fe:90:cb:74:3a
Note the difference between the first byte of the MAC and the GID.
4. Implementation details
=========================
4.1 Overview
============
The device acts like a proxy between the Guest Driver and the host
ibdevice interface.
On configuration path:
- For every hardware resource request (PD/QP/CQ/...) the pvrdma will request
a resource from the backend interface, maintaining a 1-1 mapping
between the guest and host.
On data path:
- Every post_send/receive received from the guest will be converted into
a post_send/receive for the backend. The buffers data will not be touched
or copied resulting in near bare-metal performance for large enough buffers.
- Completions from the backend interface will result in completions for
the pvrdma device.
4.2 PCI BARs
============
PCI Bars:
BAR 0 - MSI-X
MSI-X vectors:
(0) Command - used when execution of a command is completed.
(1) Async - not in use.
(2) Completion - used when a completion event is placed in
device's CQ ring.
BAR 1 - Registers
--------------------------------------------------------
| VERSION | DSR | CTL | REQ | ERR | ICR | IMR | MAC |
--------------------------------------------------------
DSR - Address of driver/device shared memory used
for the command channel, used for passing:
- General info such as driver version
- Address of 'command' and 'response'
- Address of async ring
- Address of device's CQ ring
- Device capabilities
CTL - Device control operations (activate, reset etc)
IMG - Set interrupt mask
REQ - Command execution register
ERR - Operation status
BAR 2 - UAR
---------------------------------------------------------
| QP_NUM | SEND/RECV Flag || CQ_NUM | ARM/POLL Flag |
---------------------------------------------------------
- Offset 0 used for QP operations (send and recv)
- Offset 4 used for CQ operations (arm and poll)
4.3 Major flows
===============
4.3.1 Create CQ
===============
- Guest driver
- Allocates pages for CQ ring
- Creates page directory (pdir) to hold CQ ring's pages
- Initializes CQ ring
- Initializes 'Create CQ' command object (cqe, pdir etc)
- Copies the command to 'command' address
- Writes 0 into REQ register
- Device
- Reads the request object from the 'command' address
- Allocates CQ object and initialize CQ ring based on pdir
- Creates the backend CQ
- Writes operation status to ERR register
- Posts command-interrupt to guest
- Guest driver
- Reads the HW response code from ERR register
4.3.2 Create QP
===============
- Guest driver
- Allocates pages for send and receive rings
- Creates page directory(pdir) to hold the ring's pages
- Initializes 'Create QP' command object (max_send_wr,
send_cq_handle, recv_cq_handle, pdir etc)
- Copies the object to 'command' address
- Write 0 into REQ register
- Device
- Reads the request object from 'command' address
- Allocates the QP object and initialize
- Send and recv rings based on pdir
- Send and recv ring state
- Creates the backend QP
- Writes the operation status to ERR register
- Posts command-interrupt to guest
- Guest driver
- Reads the HW response code from ERR register
4.3.3 Post receive
==================
- Guest driver
- Initializes a wqe and place it on recv ring
- Write to qpn|qp_recv_bit (31) to QP offset in UAR
- Device
- Extracts qpn from UAR
- Walks through the ring and does the following for each wqe
- Prepares the backend CQE context to be used when
receiving completion from backend (wr_id, op_code, emu_cq_num)
- For each sge prepares backend sge
- Calls backend's post_recv
4.3.4 Process backend events
============================
- Done by a dedicated thread used to process backend events;
at initialization is attached to the device and creates
the communication channel.
- Thread main loop:
- Polls for completions
- Extracts QEMU _cq_num, wr_id and op_code from context
- Writes CQE to CQ ring
- Writes CQ number to device CQ
- Sends completion-interrupt to guest
- Deallocates context
- Acks the event to backend
5. Limitations
==============
- The device obviously is limited by the Guest Linux Driver features implementation
of the VMware device API.
- Memory registration mechanism requires mremap for every page in the buffer in order
to map it to a contiguous virtual address range. Since this is not the data path
it should not matter much. If the default max mr size is increased, be aware that
memory registration can take up to 0.5 seconds for 1GB of memory.
- The device requires target page size to be the same as the host page size,
otherwise it will fail to init.
- QEMU cannot map guest RAM from a file descriptor if a pvrdma device is attached,
so it can't work with huge pages. The limitation will be addressed in the future,
however QEMU allocates Guest RAM with MADV_HUGEPAGE so if there are enough huge
pages available, QEMU will use them. QEMU will fail to init if the requirements
are not met.
6. Performance
==============
By design the pvrdma device exits on each post-send/receive, so for small buffers
the performance is affected; however for medium buffers it will became close to
bare metal and from 1MB buffers and up it reaches bare metal performance.
(tested with 2 VMs, the pvrdma devices connected to 2 VFs of the same device)
All the above assumes no memory registration is done on data path.
+15 -11
View File
@@ -1285,7 +1285,7 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
uint16_t section);
static subpage_t *subpage_init(FlatView *fv, hwaddr base);
static void *(*phys_mem_alloc)(size_t size, uint64_t *align) =
static void *(*phys_mem_alloc)(size_t size, uint64_t *align, bool shared) =
qemu_anon_ram_alloc;
/*
@@ -1293,7 +1293,7 @@ static void *(*phys_mem_alloc)(size_t size, uint64_t *align) =
* Accelerators with unusual needs may need this. Hopefully, we can
* get rid of it eventually.
*/
void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align))
void phys_mem_set_alloc(void *(*alloc)(size_t, uint64_t *align, bool shared))
{
phys_mem_alloc = alloc;
}
@@ -1921,7 +1921,7 @@ static void dirty_memory_extend(ram_addr_t old_ram_size,
}
}
static void ram_block_add(RAMBlock *new_block, Error **errp)
static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared)
{
RAMBlock *block;
RAMBlock *last_block = NULL;
@@ -1944,7 +1944,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
}
} else {
new_block->host = phys_mem_alloc(new_block->max_length,
&new_block->mr->align);
&new_block->mr->align, shared);
if (!new_block->host) {
error_setg_errno(errp, errno,
"cannot set up guest memory '%s'",
@@ -2049,7 +2049,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
return NULL;
}
ram_block_add(new_block, &local_err);
ram_block_add(new_block, &local_err, share);
if (local_err) {
g_free(new_block);
error_propagate(errp, local_err);
@@ -2091,7 +2091,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
void (*resized)(const char*,
uint64_t length,
void *host),
void *host, bool resizeable,
void *host, bool resizeable, bool share,
MemoryRegion *mr, Error **errp)
{
RAMBlock *new_block;
@@ -2114,7 +2114,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
if (resizeable) {
new_block->flags |= RAM_RESIZEABLE;
}
ram_block_add(new_block, &local_err);
ram_block_add(new_block, &local_err, share);
if (local_err) {
g_free(new_block);
error_propagate(errp, local_err);
@@ -2126,12 +2126,15 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size,
RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host,
MemoryRegion *mr, Error **errp)
{
return qemu_ram_alloc_internal(size, size, NULL, host, false, mr, errp);
return qemu_ram_alloc_internal(size, size, NULL, host, false,
false, mr, errp);
}
RAMBlock *qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr, Error **errp)
RAMBlock *qemu_ram_alloc(ram_addr_t size, bool share,
MemoryRegion *mr, Error **errp)
{
return qemu_ram_alloc_internal(size, size, NULL, NULL, false, mr, errp);
return qemu_ram_alloc_internal(size, size, NULL, NULL, false,
share, mr, errp);
}
RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
@@ -2140,7 +2143,8 @@ RAMBlock *qemu_ram_alloc_resizeable(ram_addr_t size, ram_addr_t maxsz,
void *host),
MemoryRegion *mr, Error **errp)
{
return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true, mr, errp);
return qemu_ram_alloc_internal(size, maxsz, resized, NULL, true,
false, mr, errp);
}
static void reclaim_ramblock(RAMBlock *block)
+1
View File
@@ -18,6 +18,7 @@ devices-dirs-$(CONFIG_IPMI) += ipmi/
devices-dirs-$(CONFIG_SOFTMMU) += isa/
devices-dirs-$(CONFIG_SOFTMMU) += misc/
devices-dirs-$(CONFIG_SOFTMMU) += net/
devices-dirs-$(CONFIG_SOFTMMU) += rdma/
devices-dirs-$(CONFIG_SOFTMMU) += nvram/
devices-dirs-$(CONFIG_SOFTMMU) += pci/
devices-dirs-$(CONFIG_PCI) += pci-bridge/ pci-host/
+5
View File
@@ -0,0 +1,5 @@
ifeq ($(CONFIG_RDMA),y)
obj-$(CONFIG_PCI) += rdma_utils.o rdma_backend.o rdma_rm.o
obj-$(CONFIG_PCI) += vmw/pvrdma_dev_ring.o vmw/pvrdma_cmd.o \
vmw/pvrdma_qp_ops.o vmw/pvrdma_main.o
endif
File diff suppressed because it is too large Load Diff
+98
View File
@@ -0,0 +1,98 @@
/*
* RDMA device: Definitions of Backend Device functions
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef RDMA_BACKEND_H
#define RDMA_BACKEND_H
#include <qapi/error.h>
#include "rdma_rm_defs.h"
#include "rdma_backend_defs.h"
/* Add definition for QP0 and QP1 as there is no userspace enums for them */
enum ibv_special_qp_type {
IBV_QPT_SMI = 0,
IBV_QPT_GSI = 1,
};
static inline union ibv_gid *rdma_backend_gid(RdmaBackendDev *dev)
{
return &dev->gid;
}
static inline uint32_t rdma_backend_qpn(const RdmaBackendQP *qp)
{
return qp->ibqp ? qp->ibqp->qp_num : 0;
}
static inline uint32_t rdma_backend_mr_lkey(const RdmaBackendMR *mr)
{
return mr->ibmr ? mr->ibmr->lkey : 0;
}
static inline uint32_t rdma_backend_mr_rkey(const RdmaBackendMR *mr)
{
return mr->ibmr ? mr->ibmr->rkey : 0;
}
int rdma_backend_init(RdmaBackendDev *backend_dev,
RdmaDeviceResources *rdma_dev_res,
const char *backend_device_name, uint8_t port_num,
uint8_t backend_gid_idx, struct ibv_device_attr *dev_attr,
Error **errp);
void rdma_backend_fini(RdmaBackendDev *backend_dev);
void rdma_backend_register_comp_handler(void (*handler)(int status,
unsigned int vendor_err, void *ctx));
void rdma_backend_unregister_comp_handler(void);
int rdma_backend_query_port(RdmaBackendDev *backend_dev,
struct ibv_port_attr *port_attr);
int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd);
void rdma_backend_destroy_pd(RdmaBackendPD *pd);
int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, uint64_t addr,
size_t length, int access);
void rdma_backend_destroy_mr(RdmaBackendMR *mr);
int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq,
int cqe);
void rdma_backend_destroy_cq(RdmaBackendCQ *cq);
void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq);
int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type,
RdmaBackendPD *pd, RdmaBackendCQ *scq,
RdmaBackendCQ *rcq, uint32_t max_send_wr,
uint32_t max_recv_wr, uint32_t max_send_sge,
uint32_t max_recv_sge);
int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp,
uint8_t qp_type, uint32_t qkey);
int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp,
uint8_t qp_type, union ibv_gid *dgid,
uint32_t dqpn, uint32_t rq_psn, uint32_t qkey,
bool use_qkey);
int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type,
uint32_t sq_psn, uint32_t qkey, bool use_qkey);
void rdma_backend_destroy_qp(RdmaBackendQP *qp);
void rdma_backend_post_send(RdmaBackendDev *backend_dev,
RdmaBackendQP *qp, uint8_t qp_type,
struct ibv_sge *sge, uint32_t num_sge,
union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey,
void *ctx);
void rdma_backend_post_recv(RdmaBackendDev *backend_dev,
RdmaDeviceResources *rdma_dev_res,
RdmaBackendQP *qp, uint8_t qp_type,
struct ibv_sge *sge, uint32_t num_sge, void *ctx);
#endif
+62
View File
@@ -0,0 +1,62 @@
/*
* RDMA device: Definitions of Backend Device structures
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef RDMA_BACKEND_DEFS_H
#define RDMA_BACKEND_DEFS_H
#include <infiniband/verbs.h>
#include <qemu/thread.h>
typedef struct RdmaDeviceResources RdmaDeviceResources;
typedef struct RdmaBackendThread {
QemuThread thread;
QemuMutex mutex;
bool run;
} RdmaBackendThread;
typedef struct RdmaBackendDev {
struct ibv_device_attr dev_attr;
RdmaBackendThread comp_thread;
union ibv_gid gid;
PCIDevice *dev;
RdmaDeviceResources *rdma_dev_res;
struct ibv_device *ib_dev;
struct ibv_context *context;
struct ibv_comp_channel *channel;
uint8_t port_num;
uint8_t backend_gid_idx;
} RdmaBackendDev;
typedef struct RdmaBackendPD {
struct ibv_pd *ibpd;
} RdmaBackendPD;
typedef struct RdmaBackendMR {
struct ibv_pd *ibpd;
struct ibv_mr *ibmr;
} RdmaBackendMR;
typedef struct RdmaBackendCQ {
RdmaBackendDev *backend_dev;
struct ibv_cq *ibcq;
} RdmaBackendCQ;
typedef struct RdmaBackendQP {
struct ibv_pd *ibpd;
struct ibv_qp *ibqp;
} RdmaBackendQP;
#endif
+544
View File
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
/*
* RDMA device: Definitions of Resource Manager functions
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef RDMA_RM_H
#define RDMA_RM_H
#include <qapi/error.h>
#include "rdma_backend_defs.h"
#include "rdma_rm_defs.h"
int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr,
Error **errp);
void rdma_rm_fini(RdmaDeviceResources *dev_res);
int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev,
uint32_t *pd_handle, uint32_t ctx_handle);
RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle);
void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle);
int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle,
uint64_t guest_start, size_t guest_length, void *host_virt,
int access_flags, uint32_t *mr_handle, uint32_t *lkey,
uint32_t *rkey);
RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle);
void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle);
int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn,
uint32_t *uc_handle);
RdmaRmUC *rdma_rm_get_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle);
void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle);
int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev,
uint32_t cqe, uint32_t *cq_handle, void *opaque);
RdmaRmCQ *rdma_rm_get_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle);
void rdma_rm_req_notify_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle,
bool notify);
void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle);
int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle,
uint8_t qp_type, uint32_t max_send_wr,
uint32_t max_send_sge, uint32_t send_cq_handle,
uint32_t max_recv_wr, uint32_t max_recv_sge,
uint32_t recv_cq_handle, void *opaque, uint32_t *qpn);
RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn);
int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev,
uint32_t qp_handle, uint32_t attr_mask,
union ibv_gid *dgid, uint32_t dqpn,
enum ibv_qp_state qp_state, uint32_t qkey,
uint32_t rq_psn, uint32_t sq_psn);
void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle);
int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id,
void *ctx);
void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id);
void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id);
#endif
+104
View File
@@ -0,0 +1,104 @@
/*
* RDMA device: Definitions of Resource Manager structures
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef RDMA_RM_DEFS_H
#define RDMA_RM_DEFS_H
#include "rdma_backend_defs.h"
#define MAX_PORTS 1
#define MAX_PORT_GIDS 1
#define MAX_PORT_PKEYS 1
#define MAX_PKEYS 1
#define MAX_GIDS 2048
#define MAX_UCS 512
#define MAX_MR_SIZE (1UL << 27)
#define MAX_QP 1024
#define MAX_SGE 4
#define MAX_CQ 2048
#define MAX_MR 1024
#define MAX_PD 1024
#define MAX_QP_RD_ATOM 16
#define MAX_QP_INIT_RD_ATOM 16
#define MAX_AH 64
#define MAX_RMRESTBL_NAME_SZ 16
typedef struct RdmaRmResTbl {
char name[MAX_RMRESTBL_NAME_SZ];
QemuMutex lock;
unsigned long *bitmap;
size_t tbl_sz;
size_t res_sz;
void *tbl;
} RdmaRmResTbl;
typedef struct RdmaRmPD {
RdmaBackendPD backend_pd;
uint32_t ctx_handle;
} RdmaRmPD;
typedef struct RdmaRmCQ {
RdmaBackendCQ backend_cq;
void *opaque;
bool notify;
} RdmaRmCQ;
typedef struct RdmaRmUserMR {
uint64_t host_virt;
uint64_t guest_start;
size_t length;
} RdmaRmUserMR;
/* MR (DMA region) */
typedef struct RdmaRmMR {
RdmaBackendMR backend_mr;
RdmaRmUserMR user_mr;
uint32_t pd_handle;
uint32_t lkey;
uint32_t rkey;
} RdmaRmMR;
typedef struct RdmaRmUC {
uint64_t uc_handle;
} RdmaRmUC;
typedef struct RdmaRmQP {
RdmaBackendQP backend_qp;
void *opaque;
uint32_t qp_type;
uint32_t qpn;
uint32_t send_cq_handle;
uint32_t recv_cq_handle;
enum ibv_qp_state qp_state;
} RdmaRmQP;
typedef struct RdmaRmPort {
union ibv_gid gid_tbl[MAX_PORT_GIDS];
enum ibv_port_state state;
int *pkey_tbl; /* TODO: Not yet supported */
} RdmaRmPort;
typedef struct RdmaDeviceResources {
RdmaRmPort ports[MAX_PORTS];
RdmaRmResTbl pd_tbl;
RdmaRmResTbl mr_tbl;
RdmaRmResTbl uc_tbl;
RdmaRmResTbl qp_tbl;
RdmaRmResTbl cq_tbl;
RdmaRmResTbl cqe_ctx_tbl;
GHashTable *qp_hash; /* Keeps mapping between real and emulated */
} RdmaDeviceResources;
#endif
+51
View File
@@ -0,0 +1,51 @@
/*
* QEMU paravirtual RDMA - Generic RDMA backend
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "rdma_utils.h"
void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen)
{
void *p;
hwaddr len = plen;
if (!addr) {
pr_dbg("addr is NULL\n");
return NULL;
}
p = pci_dma_map(dev, addr, &len, DMA_DIRECTION_TO_DEVICE);
if (!p) {
pr_dbg("Fail in pci_dma_map, addr=0x%llx, len=%ld\n",
(long long unsigned int)addr, len);
return NULL;
}
if (len != plen) {
rdma_pci_dma_unmap(dev, p, len);
return NULL;
}
pr_dbg("0x%llx -> %p (len=%ld)\n", (long long unsigned int)addr, p, len);
return p;
}
void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len)
{
pr_dbg("%p\n", buffer);
if (buffer) {
pci_dma_unmap(dev, buffer, len, DMA_DIRECTION_TO_DEVICE, 0);
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* RDMA device: Debug utilities
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef RDMA_UTILS_H
#define RDMA_UTILS_H
#include <qemu/osdep.h>
#include <include/hw/pci/pci.h>
#include <include/sysemu/dma.h>
#define pr_info(fmt, ...) \
fprintf(stdout, "%s: %-20s (%3d): " fmt, "pvrdma", __func__, __LINE__,\
## __VA_ARGS__)
#define pr_err(fmt, ...) \
fprintf(stderr, "%s: Error at %-20s (%3d): " fmt, "pvrdma", __func__, \
__LINE__, ## __VA_ARGS__)
#ifdef PVRDMA_DEBUG
#define pr_dbg(fmt, ...) \
fprintf(stdout, "%s: %-20s (%3d): " fmt, "pvrdma", __func__, __LINE__,\
## __VA_ARGS__)
#else
#define pr_dbg(fmt, ...)
#endif
void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen);
void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len);
#endif
+5
View File
@@ -0,0 +1,5 @@
# See docs/tracing.txt for syntax documentation.
#hw/rdma/rdma_backend.c
create_ah_cache_hit(uint64_t subnet, uint64_t net_id) "subnet = 0x%"PRIx64" net_id = 0x%"PRIx64
create_ah_cache_miss(uint64_t subnet, uint64_t net_id) "subnet = 0x%"PRIx64" net_id = 0x%"PRIx64
+122
View File
@@ -0,0 +1,122 @@
/*
* QEMU VMWARE paravirtual RDMA device definitions
*
* Copyright (C) 2018 Oracle
* Copyright (C) 2018 Red Hat Inc
*
* Authors:
* Yuval Shaia <yuval.shaia@oracle.com>
* Marcel Apfelbaum <marcel@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#ifndef PVRDMA_PVRDMA_H
#define PVRDMA_PVRDMA_H
#include <hw/pci/pci.h>
#include <hw/pci/msix.h>
#include "../rdma_backend_defs.h"
#include "../rdma_rm_defs.h"
#include <standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h>
#include <standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h>
#include "pvrdma_dev_ring.h"
/* BARs */
#define RDMA_MSIX_BAR_IDX 0
#define RDMA_REG_BAR_IDX 1
#define RDMA_UAR_BAR_IDX 2
#define RDMA_BAR0_MSIX_SIZE (16 * 1024)
#define RDMA_BAR1_REGS_SIZE 256
#define RDMA_BAR2_UAR_SIZE (0x1000 * MAX_UCS) /* each uc gets page */
/* MSIX */
#define RDMA_MAX_INTRS 3
#define RDMA_MSIX_TABLE 0x0000
#define RDMA_MSIX_PBA 0x2000
/* Interrupts Vectors */
#define INTR_VEC_CMD_RING 0
#define INTR_VEC_CMD_ASYNC_EVENTS 1
#define INTR_VEC_CMD_COMPLETION_Q 2
/* HW attributes */
#define PVRDMA_HW_NAME "pvrdma"
#define PVRDMA_HW_VERSION 17
#define PVRDMA_FW_VERSION 14
typedef struct DSRInfo {
dma_addr_t dma;
struct pvrdma_device_shared_region *dsr;
union pvrdma_cmd_req *req;
union pvrdma_cmd_resp *rsp;
struct pvrdma_ring *async_ring_state;
PvrdmaRing async;
struct pvrdma_ring *cq_ring_state;
PvrdmaRing cq;
} DSRInfo;
typedef struct PVRDMADev {
PCIDevice parent_obj;
MemoryRegion msix;
MemoryRegion regs;
uint32_t regs_data[RDMA_BAR1_REGS_SIZE];
MemoryRegion uar;
uint32_t uar_data[RDMA_BAR2_UAR_SIZE];
DSRInfo dsr_info;
int interrupt_mask;
struct ibv_device_attr dev_attr;
uint64_t node_guid;
char *backend_device_name;
uint8_t backend_gid_idx;
uint8_t backend_port_num;
RdmaBackendDev backend_dev;
RdmaDeviceResources rdma_dev_res;
} PVRDMADev;
#define PVRDMA_DEV(dev) OBJECT_CHECK(PVRDMADev, (dev), PVRDMA_HW_NAME)
static inline int get_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t *val)
{
int idx = addr >> 2;
if (idx > RDMA_BAR1_REGS_SIZE) {
return -EINVAL;
}
*val = dev->regs_data[idx];
return 0;
}
static inline int set_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t val)
{
int idx = addr >> 2;
if (idx > RDMA_BAR1_REGS_SIZE) {
return -EINVAL;
}
dev->regs_data[idx] = val;
return 0;
}
static inline void post_interrupt(PVRDMADev *dev, unsigned vector)
{
PCIDevice *pci_dev = PCI_DEVICE(dev);
if (likely(!dev->interrupt_mask)) {
msix_notify(pci_dev, vector);
}
}
int execute_command(PVRDMADev *dev);
#endif

Some files were not shown because too many files have changed in this diff Show More