#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "ofi_hmem.h"
#include "ofi.h"
#include "ofi_iov.h"
#if HAVE_GDRCOPY
#include <pthread.h>
#include <gdrapi.h>
struct gdrcopy_handle {
gdr_mh_t mh;
void *cuda_ptr;
void *user_ptr;
size_t length;
};
struct gdrcopy_ops {
gdr_t (*gdr_open)();
int (*gdr_close)(gdr_t g);
int (*gdr_pin_buffer)(gdr_t g, unsigned long addr, size_t size,
uint64_t p2p_token, uint32_t va_space,
gdr_mh_t *handle);
int (*gdr_unpin_buffer)(gdr_t g, gdr_mh_t handle);
int (*gdr_map)(gdr_t g, gdr_mh_t handle, void **va, size_t size);
int (*gdr_unmap)(gdr_t g, gdr_mh_t handle, void *va, size_t size);
int (*gdr_copy_to_mapping)(gdr_mh_t handle, void *map_d_ptr,
const void *h_ptr, size_t size);
int (*gdr_copy_from_mapping)(gdr_mh_t handle, void *map_d_ptr,
const void *h_ptr, size_t size);
};
enum gdrcopy_dir {
GDRCOPY_TO_DEVICE,
GDRCOPY_FROM_DEVICE,
};
static gdr_t global_gdr;
static pthread_spinlock_t global_gdr_lock;
#if ENABLE_GDRCOPY_DLOPEN
#include <dlfcn.h>
static void *gdrapi_handle;
static struct gdrcopy_ops global_gdrcopy_ops;
static int cuda_gdrcopy_dl_hmem_init(void)
{
gdrapi_handle = dlopen("libgdrapi.so", RTLD_NOW);
if (!gdrapi_handle) {
FI_INFO(&core_prov, FI_LOG_CORE,
"Failed to dlopen libgdrapi.so\n");
return -FI_ENOSYS;
}
global_gdrcopy_ops.gdr_open = dlsym(gdrapi_handle, "gdr_open");
if (!global_gdrcopy_ops.gdr_open) {
FI_WARN(&core_prov, FI_LOG_CORE, "Failed to find gdr_open\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_close = dlsym(gdrapi_handle, "gdr_close");
if (!global_gdrcopy_ops.gdr_close) {
FI_WARN(&core_prov, FI_LOG_CORE, "Failed to find gdr_close\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_pin_buffer = dlsym(gdrapi_handle, "gdr_pin_buffer");
if (!global_gdrcopy_ops.gdr_pin_buffer) {
FI_WARN(&core_prov, FI_LOG_CORE,
"Failed to find gdr_pin_buffer\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_unpin_buffer = dlsym(gdrapi_handle, "gdr_unpin_buffer");
if (!global_gdrcopy_ops.gdr_unpin_buffer) {
FI_WARN(&core_prov, FI_LOG_CORE,
"Failed to find gdr_unpin_buffer\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_map = dlsym(gdrapi_handle, "gdr_map");
if (!global_gdrcopy_ops.gdr_map) {
FI_WARN(&core_prov, FI_LOG_CORE,
"Failed to find gdr_map\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_unmap = dlsym(gdrapi_handle, "gdr_unmap");
if (!global_gdrcopy_ops.gdr_unmap) {
FI_WARN(&core_prov, FI_LOG_CORE,
"Failed to find gdr_unmap\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_copy_to_mapping = dlsym(gdrapi_handle, "gdr_copy_to_mapping");
if (!global_gdrcopy_ops.gdr_copy_to_mapping) {
FI_WARN(&core_prov, FI_LOG_CORE,
"Failed to find gdr_copy_to_mapping\n");
goto err_dlclose_gdrapi;
}
global_gdrcopy_ops.gdr_copy_from_mapping = dlsym(gdrapi_handle, "gdr_copy_from_mapping");
if (!global_gdrcopy_ops.gdr_copy_from_mapping) {
FI_WARN(&core_prov, FI_LOG_CORE,
"Failed to find gdr_copy_from_mapping\n");
goto err_dlclose_gdrapi;
}
return FI_SUCCESS;
err_dlclose_gdrapi:
memset(&global_gdrcopy_ops, 0, sizeof(global_gdrcopy_ops));
dlclose(gdrapi_handle);
return -FI_ENODATA;
}
static int cuda_gdrcopy_dl_hmem_cleanup(void)
{
dlclose(gdrapi_handle);
return FI_SUCCESS;
}
#else
static struct gdrcopy_ops global_gdrcopy_ops = {
.gdr_open = gdr_open,
.gdr_close = gdr_close,
.gdr_pin_buffer = gdr_pin_buffer,
.gdr_unpin_buffer = gdr_unpin_buffer,
.gdr_map = gdr_map,
.gdr_unmap = gdr_unmap,
.gdr_copy_to_mapping = gdr_copy_to_mapping,
.gdr_copy_from_mapping = gdr_copy_from_mapping
};
static int cuda_gdrcopy_dl_hmem_init(void)
{
return FI_SUCCESS;
}
static int cuda_gdrcopy_dl_hmem_cleanup(void)
{
return FI_SUCCESS;
}
#endif
int cuda_gdrcopy_hmem_init(void)
{
int err;
err = cuda_gdrcopy_dl_hmem_init();
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE,
"gdrcopy_dl_hmem_init failed!\n");
return -FI_ENOSYS;
}
assert(global_gdrcopy_ops.gdr_open);
global_gdr = global_gdrcopy_ops.gdr_open();
if (!global_gdr) {
FI_WARN(&core_prov, FI_LOG_CORE,
"gdr_open failed!\n");
err = -FI_ENOMEM;
goto error_exit;
}
err = pthread_spin_init(&global_gdr_lock, 0);
if (err) {
assert(global_gdrcopy_ops.gdr_close);
global_gdrcopy_ops.gdr_close(global_gdr);
err = -err;
goto error_exit;
}
return 0;
error_exit:
cuda_gdrcopy_dl_hmem_cleanup();
return err;
}
int cuda_gdrcopy_hmem_cleanup(void)
{
int err, ret = 0;
err = pthread_spin_destroy(&global_gdr_lock);
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE,
"destroy global_gdr_lock failed! err: %s\n",
strerror(err));
ret = err;
}
assert(global_gdrcopy_ops.gdr_close);
err = global_gdrcopy_ops.gdr_close(global_gdr);
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE,
"close global_gdr failed! err: %s\n",
strerror(err));
ret = err;
}
err = cuda_gdrcopy_dl_hmem_cleanup();
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE,
"cuda_gdrcopy_dl_hmem_cleaup() failed! err: %s\n",
strerror(err));
ret = err;
}
return ret;
}
void cuda_gdrcopy_impl(uint64_t handle, void *devptr,
void *hostptr, size_t len,
enum gdrcopy_dir dir)
{
ssize_t off;
struct gdrcopy_handle *gdrcopy;
void *gdrcopy_user_ptr;
assert(global_gdrcopy_ops.gdr_copy_to_mapping);
assert(handle);
gdrcopy = (struct gdrcopy_handle *)handle;
off = (char *)devptr - (char *)gdrcopy->cuda_ptr;
assert(off >= 0 && off + len <= gdrcopy->length);
gdrcopy_user_ptr = (char *)gdrcopy->user_ptr + off;
if (dir == GDRCOPY_TO_DEVICE) {
global_gdrcopy_ops.gdr_copy_to_mapping(gdrcopy->mh,
gdrcopy_user_ptr,
hostptr, len);
} else {
assert(dir == GDRCOPY_FROM_DEVICE);
global_gdrcopy_ops.gdr_copy_from_mapping(gdrcopy->mh,
hostptr,
gdrcopy_user_ptr,
len);
}
}
void cuda_gdrcopy_to_dev(uint64_t handle, void *devptr,
const void *hostptr, size_t len)
{
cuda_gdrcopy_impl(handle, devptr, (void *)hostptr, len,
GDRCOPY_TO_DEVICE);
}
void cuda_gdrcopy_from_dev(uint64_t handle, void *hostptr,
const void *devptr, size_t len)
{
cuda_gdrcopy_impl(handle, (void *)devptr, hostptr, len,
GDRCOPY_FROM_DEVICE);
}
static ssize_t cuda_gdrcopy_iov_buf(uint64_t handle,
const struct iovec *iov, size_t iov_count,
uint64_t iov_offset, void *buf,
size_t size, enum gdrcopy_dir dir)
{
uint64_t done = 0, len;
char *dev_buf;
size_t i;
for (i = 0; i < iov_count && size; i++) {
len = ofi_iov_bytes_to_copy(&iov[i], &size, &iov_offset, &dev_buf);
if (!len)
continue;
cuda_gdrcopy_impl(handle, dev_buf, (void *)((char *)buf + done), len, dir);
done += len;
}
return done;
}
ssize_t ofi_gdrcopy_to_cuda_iov(uint64_t handle, const struct iovec *iov,
size_t iov_count, uint64_t iov_offset,
const void *host, size_t len)
{
return cuda_gdrcopy_iov_buf(handle, iov, iov_count, iov_offset,
(void *) host, len, GDRCOPY_TO_DEVICE);
}
ssize_t ofi_gdrcopy_from_cuda_iov(uint64_t handle, void *host,
const struct iovec *iov, size_t iov_count,
uint64_t iov_offset, size_t len)
{
return cuda_gdrcopy_iov_buf(handle, iov, iov_count, iov_offset,
host, len, GDRCOPY_FROM_DEVICE);
}
int cuda_gdrcopy_dev_register(const void *buf, size_t len, uint64_t *handle)
{
int err;
uintptr_t regbgn, regend;
size_t reglen;
struct gdrcopy_handle *gdrcopy;
assert(global_gdr);
assert(global_gdrcopy_ops.gdr_pin_buffer);
assert(global_gdrcopy_ops.gdr_map);
regbgn = (uintptr_t)ofi_get_page_start(buf, GPU_PAGE_SIZE);
regend = (uintptr_t)buf + len;
reglen = ofi_get_aligned_size(regend - regbgn, GPU_PAGE_SIZE);
gdrcopy = malloc(sizeof(struct gdrcopy_handle));
if (!gdrcopy)
return -FI_ENOMEM;
assert(global_gdr);
pthread_spin_lock(&global_gdr_lock);
err = global_gdrcopy_ops.gdr_pin_buffer(global_gdr, regbgn,
reglen, 0, 0, &gdrcopy->mh);
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE,
"gdr_pin_buffer failed! error: %s ptr: %p len: %ld\n",
strerror(err), buf, len);
free(gdrcopy);
goto exit;
}
gdrcopy->cuda_ptr = (void *)regbgn;
gdrcopy->length = reglen;
err = global_gdrcopy_ops.gdr_map(global_gdr, gdrcopy->mh,
&gdrcopy->user_ptr, gdrcopy->length);
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE, "gdr_map failed! error: %s\n",
strerror(err));
global_gdrcopy_ops.gdr_unpin_buffer(global_gdr, gdrcopy->mh);
free(gdrcopy);
goto exit;
}
*handle = (uint64_t)gdrcopy;
exit:
pthread_spin_unlock(&global_gdr_lock);
return err;
}
int cuda_gdrcopy_dev_unregister(uint64_t handle)
{
int err;
struct gdrcopy_handle *gdrcopy;
assert(global_gdr);
assert(global_gdrcopy_ops.gdr_unmap);
assert(global_gdrcopy_ops.gdr_unpin_buffer);
gdrcopy = (struct gdrcopy_handle *)handle;
assert(gdrcopy);
pthread_spin_lock(&global_gdr_lock);
err = global_gdrcopy_ops.gdr_unmap(global_gdr, gdrcopy->mh,
gdrcopy->user_ptr, gdrcopy->length);
if (err) {
FI_WARN(&core_prov, FI_LOG_CORE,
"gdr_unmap failed! error: %s\n",
strerror(err));
goto exit;
}
err = global_gdrcopy_ops.gdr_unpin_buffer(global_gdr, gdrcopy->mh);
if (err) {
FI_WARN(&core_prov, FI_LOG_MR,
"gdr_unmap failed! error: %s\n",
strerror(err));
goto exit;
}
exit:
pthread_spin_unlock(&global_gdr_lock);
free(gdrcopy);
return err;
}
#else
int cuda_gdrcopy_hmem_init(void)
{
return -FI_ENOSYS;
}
int cuda_gdrcopy_hmem_cleanup(void)
{
return FI_SUCCESS;
}
void cuda_gdrcopy_to_dev(uint64_t devhandle, void *devptr,
const void *hostptr, size_t len)
{
}
void cuda_gdrcopy_from_dev(uint64_t devhandle, void *hostptr,
const void *devptr, size_t len)
{
}
int cuda_gdrcopy_dev_register(const void *buf, size_t len, uint64_t *handle)
{
return FI_SUCCESS;
}
int cuda_gdrcopy_dev_unregister(uint64_t handle)
{
return FI_SUCCESS;
}
ssize_t ofi_gdrcopy_to_cuda_iov(uint64_t handle, const struct iovec *iov,
size_t iov_count, uint64_t iov_offset,
const void *host, size_t len)
{
return -FI_ENOSYS;
}
ssize_t ofi_gdrcopy_from_cuda_iov(uint64_t handle, void *host,
const struct iovec *iov, size_t iov_count,
uint64_t iov_offset, size_t len)
{
return -FI_ENOSYS;
}
#endif