[PATCH v4 24/24] fpga: dfl: afu: add FPGA_PORT_DMA_MAP/UNMAP ioctls support

2018-02-13 Thread Wu Hao
DMA memory regions are required for Accelerated Function Unit (AFU) usage.
These two ioctls allow user space applications to map user memory regions
for dma, and unmap them after use. Iova is returned from driver to user
space application via FPGA_PORT_DMA_MAP ioctl. Application needs to unmap
it after use, otherwise, driver will unmap them in device file release
operation.

Each AFU has its own rb tree to keep track of its mapped DMA regions.

Ioctl interfaces:
* FPGA_PORT_DMA_MAP
  Do the dma mapping per user_addr and length which provided by user.
  Return iova in provided struct afu_port_dma_map.

* FPGA_PORT_DMA_UNMAP
  Unmap the dma region per iova provided by user.

Signed-off-by: Tim Whisonant 
Signed-off-by: Enno Luebbers 
Signed-off-by: Shiva Rao 
Signed-off-by: Christopher Rauer 
Signed-off-by: Xiao Guangrong 
Signed-off-by: Wu Hao 
---
v2: moved the code to drivers/fpga folder as suggested by Alan Tull.
switched to GPLv2 license.
fixed kbuild warnings.
v3: improved commit description and fixed coding style issues.
replaced fpga_pdata_to_pcidev with fpga_pdata_to_fpga_cdev
v4: rebase and fix SPDX license issue
---
 drivers/fpga/Makefile |   2 +-
 drivers/fpga/dfl-afu-dma-region.c | 463 ++
 drivers/fpga/dfl-afu-main.c   |  61 -
 drivers/fpga/dfl-afu.h|  31 ++-
 include/uapi/linux/fpga-dfl.h |  37 +++
 5 files changed, 591 insertions(+), 3 deletions(-)
 create mode 100644 drivers/fpga/dfl-afu-dma-region.c

diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 041e3cd1..dd454ab 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
 obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o
 
 dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
-dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o
+dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o
 
 # Drivers for FPGAs which implement DFL
 obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-afu-dma-region.c 
b/drivers/fpga/dfl-afu-dma-region.c
new file mode 100644
index 000..e0cfa86
--- /dev/null
+++ b/drivers/fpga/dfl-afu-dma-region.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Accelerated Function Unit (AFU) DMA Region Management
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ *   Wu Hao 
+ *   Xiao Guangrong 
+ */
+
+#include 
+#include 
+#include 
+
+#include "dfl-afu.h"
+
+static void put_all_pages(struct page **pages, int npages)
+{
+   int i;
+
+   for (i = 0; i < npages; i++)
+   if (pages[i])
+   put_page(pages[i]);
+}
+
+void afu_dma_region_init(struct feature_platform_data *pdata)
+{
+   struct fpga_afu *afu = fpga_pdata_get_private(pdata);
+
+   afu->dma_regions = RB_ROOT;
+}
+
+/**
+ * afu_dma_adjust_locked_vm - adjust locked memory
+ * @dev: port device
+ * @npages: number of pages
+ * @incr: increase or decrease locked memory
+ *
+ * Increase or decrease the locked memory size with npages input.
+ *
+ * Return 0 on success.
+ * Return -ENOMEM if locked memory size is over the limit and no CAP_IPC_LOCK.
+ */
+static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr)
+{
+   unsigned long locked, lock_limit;
+   int ret = 0;
+
+   /* the task is exiting. */
+   if (!current->mm)
+   return 0;
+
+   down_write(>mm->mmap_sem);
+
+   if (incr) {
+   locked = current->mm->locked_vm + npages;
+   lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+
+   if (locked > lock_limit && !capable(CAP_IPC_LOCK))
+   ret = -ENOMEM;
+   else
+   current->mm->locked_vm += npages;
+   } else {
+   if (WARN_ON_ONCE(npages > current->mm->locked_vm))
+   npages = current->mm->locked_vm;
+   current->mm->locked_vm -= npages;
+   }
+
+   dev_dbg(dev, "[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n", current->pid,
+   incr ? '+' : '-', npages << PAGE_SHIFT,
+   current->mm->locked_vm << PAGE_SHIFT, rlimit(RLIMIT_MEMLOCK),
+   ret ? "- execeeded" : "");
+
+   up_write(>mm->mmap_sem);
+
+   return ret;
+}
+
+/**
+ * afu_dma_pin_pages - pin pages of given dma memory region
+ * @pdata: feature device platform data
+ * @region: dma memory region to be pinned
+ *
+ * Pin all the pages of given fpga_afu_dma_region.
+ * Return 0 for success or negative error code.
+ */
+static int afu_dma_pin_pages(struct feature_platform_data *pdata,
+struct fpga_afu_dma_region *region)
+{
+   int npages = 

[PATCH v4 24/24] fpga: dfl: afu: add FPGA_PORT_DMA_MAP/UNMAP ioctls support

2018-02-13 Thread Wu Hao
DMA memory regions are required for Accelerated Function Unit (AFU) usage.
These two ioctls allow user space applications to map user memory regions
for dma, and unmap them after use. Iova is returned from driver to user
space application via FPGA_PORT_DMA_MAP ioctl. Application needs to unmap
it after use, otherwise, driver will unmap them in device file release
operation.

Each AFU has its own rb tree to keep track of its mapped DMA regions.

Ioctl interfaces:
* FPGA_PORT_DMA_MAP
  Do the dma mapping per user_addr and length which provided by user.
  Return iova in provided struct afu_port_dma_map.

* FPGA_PORT_DMA_UNMAP
  Unmap the dma region per iova provided by user.

Signed-off-by: Tim Whisonant 
Signed-off-by: Enno Luebbers 
Signed-off-by: Shiva Rao 
Signed-off-by: Christopher Rauer 
Signed-off-by: Xiao Guangrong 
Signed-off-by: Wu Hao 
---
v2: moved the code to drivers/fpga folder as suggested by Alan Tull.
switched to GPLv2 license.
fixed kbuild warnings.
v3: improved commit description and fixed coding style issues.
replaced fpga_pdata_to_pcidev with fpga_pdata_to_fpga_cdev
v4: rebase and fix SPDX license issue
---
 drivers/fpga/Makefile |   2 +-
 drivers/fpga/dfl-afu-dma-region.c | 463 ++
 drivers/fpga/dfl-afu-main.c   |  61 -
 drivers/fpga/dfl-afu.h|  31 ++-
 include/uapi/linux/fpga-dfl.h |  37 +++
 5 files changed, 591 insertions(+), 3 deletions(-)
 create mode 100644 drivers/fpga/dfl-afu-dma-region.c

diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 041e3cd1..dd454ab 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
 obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o
 
 dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
-dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o
+dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o
 
 # Drivers for FPGAs which implement DFL
 obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-afu-dma-region.c 
b/drivers/fpga/dfl-afu-dma-region.c
new file mode 100644
index 000..e0cfa86
--- /dev/null
+++ b/drivers/fpga/dfl-afu-dma-region.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Accelerated Function Unit (AFU) DMA Region Management
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ *   Wu Hao 
+ *   Xiao Guangrong 
+ */
+
+#include 
+#include 
+#include 
+
+#include "dfl-afu.h"
+
+static void put_all_pages(struct page **pages, int npages)
+{
+   int i;
+
+   for (i = 0; i < npages; i++)
+   if (pages[i])
+   put_page(pages[i]);
+}
+
+void afu_dma_region_init(struct feature_platform_data *pdata)
+{
+   struct fpga_afu *afu = fpga_pdata_get_private(pdata);
+
+   afu->dma_regions = RB_ROOT;
+}
+
+/**
+ * afu_dma_adjust_locked_vm - adjust locked memory
+ * @dev: port device
+ * @npages: number of pages
+ * @incr: increase or decrease locked memory
+ *
+ * Increase or decrease the locked memory size with npages input.
+ *
+ * Return 0 on success.
+ * Return -ENOMEM if locked memory size is over the limit and no CAP_IPC_LOCK.
+ */
+static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr)
+{
+   unsigned long locked, lock_limit;
+   int ret = 0;
+
+   /* the task is exiting. */
+   if (!current->mm)
+   return 0;
+
+   down_write(>mm->mmap_sem);
+
+   if (incr) {
+   locked = current->mm->locked_vm + npages;
+   lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+
+   if (locked > lock_limit && !capable(CAP_IPC_LOCK))
+   ret = -ENOMEM;
+   else
+   current->mm->locked_vm += npages;
+   } else {
+   if (WARN_ON_ONCE(npages > current->mm->locked_vm))
+   npages = current->mm->locked_vm;
+   current->mm->locked_vm -= npages;
+   }
+
+   dev_dbg(dev, "[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n", current->pid,
+   incr ? '+' : '-', npages << PAGE_SHIFT,
+   current->mm->locked_vm << PAGE_SHIFT, rlimit(RLIMIT_MEMLOCK),
+   ret ? "- execeeded" : "");
+
+   up_write(>mm->mmap_sem);
+
+   return ret;
+}
+
+/**
+ * afu_dma_pin_pages - pin pages of given dma memory region
+ * @pdata: feature device platform data
+ * @region: dma memory region to be pinned
+ *
+ * Pin all the pages of given fpga_afu_dma_region.
+ * Return 0 for success or negative error code.
+ */
+static int afu_dma_pin_pages(struct feature_platform_data *pdata,
+struct fpga_afu_dma_region *region)
+{
+   int npages = region->length >> PAGE_SHIFT;
+   struct device *dev = >dev->dev;
+   int ret, pinned;
+
+   ret = afu_dma_adjust_locked_vm(dev, npages, true);
+   if (ret)
+   return ret;
+
+