From b10380d783e61203fe0a33783a6d25259c697351 Mon Sep 17 00:00:00 2001 From: Serguei Sagalovitch Date: Fri, 8 Jan 2016 16:48:40 -0500 Subject: [PATCH] Skeleton for RDMA unit test v4 Added application and driver to serve as the starting point for RDMA unit test uility. v2: Added initial mmap support v3: Fixed logic to find correct ioctl handler v4: Fixed logic in mmap to find correct pages table Change-Id: Iaf97c0eb2acef2160d542c71afed58cf400414f7 Signed-off-by: Serguei Sagalovitch [ROCm/ROCR-Runtime commit: 47cef87a343a0a4d9a7c49282751707908e4ac1f] --- .../tests/rdma/simple/app/CMakeLists.txt | 12 + .../tests/rdma/simple/app/rdma_test.cpp | 264 ++++++++++ .../tests/rdma/simple/drv/Makefile | 54 ++ .../tests/rdma/simple/drv/amdp2ptest.c | 480 ++++++++++++++++++ .../tests/rdma/simple/drv/amdp2ptest.h | 71 +++ 5 files changed, 881 insertions(+) create mode 100644 projects/rocr-runtime/tests/rdma/simple/app/CMakeLists.txt create mode 100644 projects/rocr-runtime/tests/rdma/simple/app/rdma_test.cpp create mode 100644 projects/rocr-runtime/tests/rdma/simple/drv/Makefile create mode 100644 projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.c create mode 100644 projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.h diff --git a/projects/rocr-runtime/tests/rdma/simple/app/CMakeLists.txt b/projects/rocr-runtime/tests/rdma/simple/app/CMakeLists.txt new file mode 100644 index 0000000000..762420d461 --- /dev/null +++ b/projects/rocr-runtime/tests/rdma/simple/app/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required (VERSION 2.6) + +project (rdma_test) + +link_directories($ENV{ROOT_OF_ROOTS}/out/lib) + +include_directories($ENV{LIBHSAKMT_ROOT}/include) +include_directories(../drv) + +add_executable(rdma_test rdma_test.cpp) +target_link_libraries(rdma_test libhsakmt.so) + diff --git a/projects/rocr-runtime/tests/rdma/simple/app/rdma_test.cpp b/projects/rocr-runtime/tests/rdma/simple/app/rdma_test.cpp new file mode 100644 index 0000000000..79aaf8ae33 --- /dev/null +++ b/projects/rocr-runtime/tests/rdma/simple/app/rdma_test.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hsakmt.h" +#include "amdp2ptest.h" + +int rdma_fd = -1; + +void rdma_open() +{ + rdma_fd = open(AMDP2PTEST_DEVICE_PATH, O_RDWR); + + if (-1 == rdma_fd ) { + int ret = errno; + fprintf(stderr, "error opening driver (errno=%d/%s)\n", ret, strerror(ret)); + exit(EXIT_FAILURE); + } +} + +void rdma_close() +{ + int retcode = close(rdma_fd); + + if (-1 == retcode) { + fprintf(stderr, "error closing driver (errno=%d/%s)\n", retcode, strerror(retcode)); + exit(EXIT_FAILURE); + } + + rdma_fd = -1; +} + +int rdma_map(uint64_t gpu_ptr, size_t size, void **cpu_ptr) +{ + int ret = 0; + + *cpu_ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, rdma_fd, gpu_ptr); + + if (*cpu_ptr == NULL) { + int __errno = errno; + *cpu_ptr = NULL; + fprintf(stderr, "Can't BAR, error=%s(%d) size=%zu offset=%llx\n", + strerror(__errno), __errno, size, (long long unsigned)gpu_ptr); + ret = __errno; + } + + return ret; +} + +int rdma_unmap(void *cpu_ptr, size_t size) +{ + int ret = 0; + + int retcode = munmap(cpu_ptr, size); + + if (-1 == retcode) { + int __errno = errno; + fprintf(stderr, "can't unmap BAR, error=%s(%d) size=%zu\n", + strerror(__errno), __errno, size); + ret = __errno; + } + + return ret; +} + +void run_rdma_tests(HSAuint32 Node, HsaMemoryProperties *MemoryProperty) +{ + printf("Size 0x%lx (%ld MB)\n", MemoryProperty->SizeInBytes, + MemoryProperty->SizeInBytes / (1024 * 1024)); + printf("VirtualBaseAddress 0x%lx\n", MemoryProperty->VirtualBaseAddress); + + + void *cpu_ptr; + int ret = 0; + void *MemoryAddress = 0; + HSAuint64 SizeInBytes = 4096; + HsaMemFlags memFlags = {0}; + + memFlags.ui32.NonPaged = 1; + memFlags.ui32.CachePolicy = HSA_CACHING_WRITECOMBINED; + memFlags.ui32.NoSubstitute = 1; + memFlags.ui32.PageSize = HSA_PAGE_SIZE_4KB; +// memFlags.ui32.HostAccess = 1; + memFlags.ui32.CoarseGrain = 1; + + HSAKMT_STATUS status = hsaKmtAllocMemory(Node, + SizeInBytes, + memFlags, + &MemoryAddress); + + if (status != HSAKMT_STATUS_SUCCESS) + { + fprintf(stderr, "Failure to allocate memory. Status %d\n", status); + exit(EXIT_FAILURE); + } + + printf("Memory allocated. Address 0x%p\n", MemoryAddress); + + struct AMDRDMA_IOCTL_GET_PAGE_SIZE_PARAM get_page_size = {0}; + get_page_size.addr = (uint64_t) MemoryAddress; + get_page_size.length = SizeInBytes; + + ret = ioctl(rdma_fd, AMD2P2PTEST_IOCTL_GET_PAGE_SIZE, &get_page_size); + + if (ret < 0) + { + fprintf(stderr, + "AMD2P2PTEST_IOCTL_GET_PAGE_SIZE error (errno=%d/%s)\n", + ret, strerror(ret)); + exit(EXIT_FAILURE); + } + + printf("GPU Page size: 0x%ld\n", get_page_size.page_size); + + struct AMDRDMA_IOCTL_GET_PAGES_PARAM get_cpu_ptr = {0}; + get_cpu_ptr.addr = (uint64_t) MemoryAddress; + get_cpu_ptr.length = SizeInBytes; + + ret = ioctl(rdma_fd, AMD2P2PTEST_IOCTL_GET_PAGES, &get_cpu_ptr); + + if (ret < 0) + { + fprintf(stderr, "AMD2P2PTEST_IOCTL_GET_PAGES error (errno=%d/%s)\n", + ret, strerror(ret)); + exit(EXIT_FAILURE); + } + + + ret = rdma_map((uint64_t)MemoryAddress, 4096, &cpu_ptr); + + if (ret < 0) + { + exit(EXIT_FAILURE); + } + + printf("CPU Virtual address 0x%p\n", cpu_ptr); + + hsaKmtFreeMemory(MemoryAddress, SizeInBytes); +} + +int main(void) +{ + HsaVersionInfo VersionInfo; + + HSAKMT_STATUS status = hsaKmtOpenKFD(); + + if( status == HSAKMT_STATUS_SUCCESS) + { + status = hsaKmtGetVersion(&VersionInfo); + + if(status == HSAKMT_STATUS_SUCCESS) + { + printf("Kernel Interface Major Version: %d\n", VersionInfo.KernelInterfaceMajorVersion); + printf("Kernel Interface Minor Version: %d\n", VersionInfo.KernelInterfaceMinorVersion); + } + } + + rdma_open(); + + HsaSystemProperties SystemProperties = {0}; + status = hsaKmtAcquireSystemProperties(&SystemProperties); + + if(status != HSAKMT_STATUS_SUCCESS) + { + fprintf(stderr, "hsaKmtAcquireSystemProperties call failed. Error: %d\n", status); + exit(EXIT_FAILURE); + } + + printf("System properties: Number of nodes: %d\n", SystemProperties.NumNodes); + + for (HSAuint32 iNode = 0; iNode < SystemProperties.NumNodes; iNode++) + { + HsaNodeProperties NodeProperties = {0}; + status = hsaKmtGetNodeProperties(iNode, &NodeProperties); + + if(status != HSAKMT_STATUS_SUCCESS) + { + fprintf(stderr, "hsaKmtGetNodeProperties (Node = %d) call failed. Error: %d\n", + iNode, status); + exit(EXIT_FAILURE); + } + + printf("Node %d -> Number of Memory Banks = %d\n", iNode, + NodeProperties.NumMemoryBanks); + + HsaMemoryProperties* MemoryProperties = + new HsaMemoryProperties[NodeProperties.NumMemoryBanks]; + + status = hsaKmtGetNodeMemoryProperties(iNode, + NodeProperties.NumMemoryBanks, + MemoryProperties); + + if(status != HSAKMT_STATUS_SUCCESS) + { + fprintf(stderr, "hsaKmtGetNodeMemoryProperties (Node = %d) call failed. Error: %d\n", + iNode, status); + exit(EXIT_FAILURE); + } + + for (HSAuint32 iMemBank = 0; iMemBank < NodeProperties.NumMemoryBanks; iMemBank++) + { + printf("Heap type: %d\n", MemoryProperties[iMemBank].HeapType); + + if (MemoryProperties[iMemBank].HeapType == HSA_HEAPTYPE_FRAME_BUFFER_PUBLIC) + { + // We found local memory available for RDMA operation. + // Run some tests on it. + run_rdma_tests(iNode, &MemoryProperties[iMemBank]); + } + } + } + + + status = hsaKmtReleaseSystemProperties(); + + if(status != HSAKMT_STATUS_SUCCESS) + { + fprintf(stderr, "hsaKmtReleaseSystemProperties call failed. Error: %d\n", + status); + exit(EXIT_FAILURE); + } + + rdma_close(); + + status = hsaKmtCloseKFD(); + + if(status != HSAKMT_STATUS_SUCCESS) + { + fprintf(stderr, "hsaKmtCloseKFD call failed. Error: %d\n", status); + exit(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} + + diff --git a/projects/rocr-runtime/tests/rdma/simple/drv/Makefile b/projects/rocr-runtime/tests/rdma/simple/drv/Makefile new file mode 100644 index 0000000000..e6bea3edda --- /dev/null +++ b/projects/rocr-runtime/tests/rdma/simple/drv/Makefile @@ -0,0 +1,54 @@ + +ifeq ($(src),) + src := $$PWD +endif + + + +ifneq ($(KERNELRELEASE),) +# Specify default location of RDMA header +RDMA_HEADER_DIR := $(ROOT_OF_ROOTS)/kernel/drivers/gpu/drm/amd/include +ifeq ("$(wildcard $(RDMA_HEADER_DIR)/amd_rdma.h)","") +RDMA_HEADER_DIR := /usr/src/linux-headers-$(shell uname -r)/drivers/gpu/drm/amd/include +ifeq ("$(wildcard $(RDMA_HEADER_DIR)/amd_rdma.h)","") +$(error amd_rdma.h header is not found) +endif +endif + +$(info INFO: RDMA Header directory is $(RDMA_HEADER_DIR)) + +kver_major:=$(shell echo $(KERNELRELEASE) | awk -F '.' '// { print $$2;}' ) + +obj-m += amdp2ptest.o + +ccflags-y += -I $(RDMA_HEADER_DIR) + +else + +KDIR ?= /lib/modules/$(shell uname -r)/build +REL := $(subst ., , $(subst -, , $(shell uname -r))) +REL_MAJOR := $(word 1,$(REL)) +REL_MEDIUM := $(word 2,$(REL)) +REL_MINOR := $(word 3,$(REL)) + +all: default + + +default: + @ $(MAKE) -C $(KDIR) M=$$PWD modules + +install: + $(MAKE) -C $(KDIR) M=$$PWD modules_install + +help: + $(MAKE) -C $(KDIR) M=$$PWD help + +clean: + rm -rf *.o *.ko* *.mod.* .*.cmd Module.symvers modules.order .tmp_versions/ *~ core .depend TAGS + +TAGS: + find $(KERNELDIR) -follow -name \*.h -o -name \*.c |xargs etags + +.PHONY: clean all help install default linksyms + +endif diff --git a/projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.c b/projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.c new file mode 100644 index 0000000000..788185ae73 --- /dev/null +++ b/projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.c @@ -0,0 +1,480 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "amd_rdma.h" +#include "amdp2ptest.h" + + +MODULE_AUTHOR("serguei.sagalovitch@amd.com"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AMD RDMA basic API test kernel-mode driver"); +MODULE_VERSION("1.0"); + + +const struct amd_rdma_interface *rdma_interface; + + +struct va_pages_node { + struct list_head node; + struct amd_p2p_page_table *pages; +}; + + +struct amdp2ptest_pages_list { + struct list_head head; + struct mutex lock; +}; + + +#define MSG_INFO(fmt, args ...) \ + pr_info(AMDP2PTEST_DEVICE_NAME ": " fmt, ## args) +#define MSG_ERR(fmt, args ...) \ + pr_err(AMDP2PTEST_DEVICE_NAME ": " fmt, ## args) +#define MSG_warn(fmt, args ...) \ + pr_warn(AMDP2PTEST_DEVICE_NAME ": " fmt, ## args) + + + +static void free_callback(struct amd_p2p_page_table *page_table, + void *client_priv) +{ + struct va_pages_node *va_pages = NULL; + struct amdp2ptest_pages_list *list = client_priv; + struct list_head *p, *n; + + MSG_ERR("Free callback is called on va 0x%llx\n", page_table->va); + + list_for_each_safe(p, n, &list->head) { + va_pages = list_entry(p, struct va_pages_node, node); + + if (va_pages->pages == page_table) { + MSG_INFO("Found free page table to free\n"); + + mutex_lock(&list->lock); + list_del(&va_pages->node); + mutex_unlock(&list->lock); + kfree(va_pages); + + /* Note: Do not break from loop to allow test + * situation when "get_pages" would be called + * on the same memory several times + **/ + } + } +} + + + +static int amdp2ptest_open(struct inode *inode, struct file *filp) +{ + struct amdp2ptest_pages_list *list; + + MSG_INFO("Open driver\n"); + + list = kmalloc(sizeof(struct amdp2ptest_pages_list), GFP_KERNEL); + + if (!list) { + MSG_ERR("Can't alloc kernel memory to store list stucture\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&list->head); + mutex_init(&list->lock); + + filp->private_data = list; + + return 0; +} + + +static int amdp2ptest_release(struct inode *inode, struct file *filp) +{ + struct va_pages_node *va_pages = NULL; + int retcode; + struct amdp2ptest_pages_list *list = filp->private_data; + struct list_head *p, *n; + + MSG_INFO("Close driver\n"); + + list_for_each_safe(p, n, &list->head) { + va_pages = list_entry(p, struct va_pages_node, node); + MSG_INFO("Free pages: VA 0x%llx\n", va_pages->pages->va); + retcode = rdma_interface->put_pages(va_pages->pages); + + if (retcode != 0) + MSG_ERR("Could not put pages back: %d\n", retcode); + + mutex_lock(&list->lock); + list_del(&va_pages->node); + mutex_unlock(&list->lock); + kfree(va_pages); + } + + filp->private_data = NULL; + kfree(list); + return 0; +} + + +static int ioctl_get_page_size(struct file *filp, unsigned long arg) +{ + struct AMDRDMA_IOCTL_GET_PAGE_SIZE_PARAM params = {0}; + unsigned long page_size; + int result; + + MSG_INFO("AMD2P2PTEST_IOCTL_GET_PAGE_SIZE"); + + if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { + MSG_ERR("copy_from_user failed on pointer %p\n", + (void *)arg); + return -EFAULT; + } + + MSG_INFO("addr %llx, length %llx\n", params.addr, + params.length); + result = rdma_interface->get_page_size(params.addr, + params.length, + get_task_pid(current, PIDTYPE_PID), + &page_size); + + if (result) { + MSG_ERR("Could not get page size. %d", result); + return -EFAULT; + } + + params.page_size = page_size; + MSG_INFO("Page size %llx\n", params.page_size); + + if (result && copy_to_user((void *)arg, ¶ms, + sizeof(params))) { + MSG_ERR("copy_to_user failed on user pointer %p\n", + (void *)arg); + + return -EFAULT; + } + + return 0; +} + +static int ioctl_get_pages(struct file *filp, unsigned long arg) +{ + struct va_pages_node *va_pages = NULL; + struct amdp2ptest_pages_list *list = filp->private_data; + struct AMDRDMA_IOCTL_GET_PAGES_PARAM params = {0}; + int result; + struct amd_p2p_page_table *pages; + + MSG_INFO("AMD2P2PTEST_IOCTL_GET_PAGES"); + + if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { + MSG_ERR("copy_from_user failed on pointer %p\n", + (void *)arg); + return -EFAULT; + } + + + MSG_INFO("addr %llx, length %llx\n", params.addr, params.length); + + result = rdma_interface->get_pages(params.addr, params.length, + get_task_pid(current, PIDTYPE_PID), + 0, /* There is no dma_device for which + to get pages -> no IOMMU support + is needed */ + &pages, + free_callback, + list /* Pointer to the list */ + ); + + if (result) { + MSG_ERR("Could not get pages table. %d", result); + return -EFAULT; + } + + if (copy_to_user((void *)arg, ¶ms, sizeof(params))) { + MSG_ERR("copy_to_user failed on user pointer %p\n", + (void *)arg); + rdma_interface->put_pages(pages); + return -EFAULT; + } + + + va_pages = kmalloc(sizeof(struct va_pages_node), GFP_KERNEL); + + if (va_pages == 0) { + MSG_ERR("Can't alloc kernel memory\n"); + rdma_interface->put_pages(pages); + return -ENOMEM; + } + + memset(va_pages, 0, sizeof(struct va_pages_node)); + va_pages->pages = pages; + + mutex_lock(&list->lock); + list_add(&va_pages->node, &list->head); + mutex_unlock(&list->lock); + + return 0; +} + + +static int ioctl_put_pages(struct file *filp, unsigned long arg) +{ + struct va_pages_node *va_pages = NULL; + struct amdp2ptest_pages_list *list = filp->private_data; + struct AMDRDMA_IOCTL_PUT_PAGES_PARAM params = {0}; + struct list_head *p, *n; + int retcode; + + MSG_INFO("AMD2P2PTEST_IOCTL_PUT_PAGES"); + + if (copy_from_user(¶ms, (void *)arg, sizeof(params))) { + MSG_ERR("copy_from_user failed on pointer %p\n", + (void *)arg); + return -EFAULT; + } + + MSG_INFO("addr %llx, length %llx\n", params.addr, params.length); + + + list_for_each_safe(p, n, &list->head) { + va_pages = list_entry(p, struct va_pages_node, node); + + if (va_pages->pages->va == params.addr && + va_pages->pages->size == params.length) { + + retcode = rdma_interface->put_pages(va_pages->pages); + + if (retcode != 0) { + MSG_ERR("Could not put pages back: %d\n", + retcode); + } + + mutex_lock(&list->lock); + list_del(&va_pages->node); + mutex_unlock(&list->lock); + kfree(va_pages); + /* Note: Do not break from loop to allow test + * situation when "get_pages" would be called + * on the same memory several times + **/ + } + } + + return 0; +} + + +static const struct ioctl_handler_map { + int (*handler)(struct file *filp, unsigned long arg); + unsigned int cmd; +} handlers[] = { + { ioctl_get_page_size, AMD2P2PTEST_IOCTL_GET_PAGE_SIZE }, + { ioctl_get_pages, AMD2P2PTEST_IOCTL_GET_PAGES }, + { ioctl_put_pages, AMD2P2PTEST_IOCTL_PUT_PAGES }, + { NULL, 0 } +}; + + + +static long amdp2ptest_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int result = -EINVAL; + int i; + + for (i = 0; handlers[i].handler != NULL; i++) + if (cmd == handlers[i].cmd) { + result = handlers[i].handler(filp, arg); + break; + } + + return result; +} + + +static int amdp2ptest_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int i; + struct scatterlist *sg; + struct va_pages_node *va_pages = NULL; + struct amdp2ptest_pages_list *list = filp->private_data; + size_t size = vma->vm_end - vma->vm_start; + struct list_head *p, *n; + uint64_t gpu_va = vma->vm_pgoff << PAGE_SHIFT; + + MSG_INFO("Mapping to CPU user space\n"); + MSG_INFO("Begin vm_end 0x%lx, vm_start 0x%lx\n", vma->vm_end, + vma->vm_start); + MSG_INFO("vm_pgoff / pfn 0x%lx\n", vma->vm_pgoff); + MSG_INFO("gpu_va / phys. address 0x%llx\n", gpu_va); + + if (size != PAGE_SIZE) { + MSG_ERR("Mapping works now only per page size=%ld", PAGE_SIZE); + return -EINVAL; + } + + /* This is the first very simple version of getting CPU pointer for + * the single page. + * The logic is the following: + * - We get GPU VA address and enumerate list to find "get_pages" + * node for such range + * - Then we enumerate sg table to find the correct dma_address + * + * NOTE/TODO: Assumption is that the page size is 4KB to allow testing + * of the basic logic. Eventually more complex logic must be added. + */ + list_for_each_safe(p, n, &list->head) { + va_pages = list_entry(p, struct va_pages_node, node); + + if (va_pages->pages->va >= gpu_va && + (va_pages->pages->va + va_pages->pages->size) + < vma->vm_end) { + + MSG_INFO("Found node: va=0x%llx,size=0x%llx,nents %d\n", + va_pages->pages->va, + va_pages->pages->size, + va_pages->pages->pages->nents); + + for_each_sg(va_pages->pages->pages->sgl, sg, + va_pages->pages->pages->nents, i) { + if (va_pages->pages->va + (i * sg->length) == + gpu_va) { + + MSG_INFO("Found page[%d]: dma 0x%llx\n", + i, sg->dma_address); + + if (remap_pfn_range(vma, + vma->vm_start, + sg->dma_address, + size, + vma->vm_page_prot)) { + MSG_ERR("Failed remap_pfn()\n"); + return -EINVAL; + } + return 0; + } + } + } + } + + return -EINVAL; +} + + +/*---------------------------------------------------------------------------*/ + +static const struct file_operations amdp2ptest_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = amdp2ptest_unlocked_ioctl, + .open = amdp2ptest_open, + .release = amdp2ptest_release, + .mmap = amdp2ptest_mmap, +}; + + + +static struct miscdevice amdp2ptest_dev = { + /* + * We don't care what minor number we end up with, so tell the + * kernel to just pick one. + */ + .minor = MISC_DYNAMIC_MINOR, + /* + * Name ourselves /dev/hello. + */ + .name = AMDP2PTEST_DEVICE_NAME, + /* + * What functions to call when a program performs file + * operations on the device. + */ + .fops = &amdp2ptest_fops, + + /* Security attribute / access */ + .mode = S_IRWXU | S_IRWXG | S_IRWXO +}; + +static int __init amdp2ptest_init(void) +{ + int result; + + result = amdkfd_query_rdma_interface(&rdma_interface); + + if (result < 0) { + MSG_ERR("Can not get RDMA Interface (result = %d)\n", result); + return result; + } + + MSG_INFO("RDMA Interface %p\n", rdma_interface); + MSG_INFO(" get_pages %p\n", rdma_interface->get_pages); + MSG_INFO(" put_pages %p\n", rdma_interface->put_pages); + MSG_INFO(" is_gpu_address %p\n", rdma_interface->is_gpu_address); + MSG_INFO(" get_page_size %p\n", rdma_interface->get_page_size); + + + /* + * Create the device in the /sys/class/misc directory. + * Udev will automatically create the /dev/xxxxx device using + * the default rules. + */ + result = misc_register(&amdp2ptest_dev); + + if (result < 0) { + MSG_ERR("Can not register device (result = %d)\n", result); + return result; + } + + return 0; +} + + +/* Note: cleanup_module is never called if registering failed */ +static void __exit amdp2ptest_cleanup(void) +{ + MSG_INFO("Unregistering\n"); + + misc_deregister(&amdp2ptest_dev); +} + + +module_init(amdp2ptest_init); +module_exit(amdp2ptest_cleanup); + + diff --git a/projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.h b/projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.h new file mode 100644 index 0000000000..e37a1caf2e --- /dev/null +++ b/projects/rocr-runtime/tests/rdma/simple/drv/amdp2ptest.h @@ -0,0 +1,71 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef AMDP2PTEST_H_ +#define AMDP2PTEST_H_ + +#include + +#define AMDP2PTEST_IOCTL_MAGIC 'A' + + +#define AMDP2PTEST_DEVICE_NAME "amdp2ptest" +#define AMDP2PTEST_DEVICE_PATH "/dev/amdp2ptest" + +struct AMDRDMA_IOCTL_GET_PAGE_SIZE_PARAM { + /* Input parameters */ + uint64_t addr; + uint64_t length; + + /* Output parameters */ + uint64_t page_size; +}; + +struct AMDRDMA_IOCTL_GET_PAGES_PARAM { + /* Input parameters */ + uint64_t addr; + uint64_t length; + uint64_t is_local; /* 1 if this is the pointer to local + allocation */ + + /* Output parameters */ + uint64_t cpu_ptr; +}; + + +struct AMDRDMA_IOCTL_PUT_PAGES_PARAM { + /* Input parameters */ + uint64_t addr; + uint64_t length; +}; + + +#define AMD2P2PTEST_IOCTL_GET_PAGE_SIZE \ +_IOWR(AMDP2PTEST_IOCTL_MAGIC, 1, struct AMDRDMA_IOCTL_GET_PAGE_SIZE_PARAM *) + +#define AMD2P2PTEST_IOCTL_GET_PAGES \ +_IOWR(AMDP2PTEST_IOCTL_MAGIC, 2, struct AMDRDMA_IOCTL_GET_PAGES_PARAM *) + +#define AMD2P2PTEST_IOCTL_PUT_PAGES \ +_IOW(AMDP2PTEST_IOCTL_MAGIC, 3, struct AMDRDMA_IOCTL_PUT_PAGES_PARAM *) + + +#endif /* AMDP2PTEST_H */