aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/fpga/Makefile2
-rw-r--r--drivers/fpga/dfl-afu-dma-region.c463
-rw-r--r--drivers/fpga/dfl-afu-main.c61
-rw-r--r--drivers/fpga/dfl-afu.h31
-rw-r--r--include/uapi/linux/fpga-dfl.h37
5 files changed, 591 insertions, 3 deletions
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index a44d50dd0b70..7a2d73ba7122 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -38,7 +38,7 @@ obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
38obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o 38obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o
39 39
40dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o 40dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
41dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o 41dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o
42 42
43# Drivers for FPGAs which implement DFL 43# Drivers for FPGAs which implement DFL
44obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o 44obj-$(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 000000000000..0e81d33af856
--- /dev/null
+++ b/drivers/fpga/dfl-afu-dma-region.c
@@ -0,0 +1,463 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Driver for FPGA Accelerated Function Unit (AFU) DMA Region Management
4 *
5 * Copyright (C) 2017-2018 Intel Corporation, Inc.
6 *
7 * Authors:
8 * Wu Hao <hao.wu@intel.com>
9 * Xiao Guangrong <guangrong.xiao@linux.intel.com>
10 */
11
12#include <linux/dma-mapping.h>
13#include <linux/sched/signal.h>
14#include <linux/uaccess.h>
15
16#include "dfl-afu.h"
17
18static void put_all_pages(struct page **pages, int npages)
19{
20 int i;
21
22 for (i = 0; i < npages; i++)
23 if (pages[i])
24 put_page(pages[i]);
25}
26
27void afu_dma_region_init(struct dfl_feature_platform_data *pdata)
28{
29 struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
30
31 afu->dma_regions = RB_ROOT;
32}
33
34/**
35 * afu_dma_adjust_locked_vm - adjust locked memory
36 * @dev: port device
37 * @npages: number of pages
38 * @incr: increase or decrease locked memory
39 *
40 * Increase or decrease the locked memory size with npages input.
41 *
42 * Return 0 on success.
43 * Return -ENOMEM if locked memory size is over the limit and no CAP_IPC_LOCK.
44 */
45static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr)
46{
47 unsigned long locked, lock_limit;
48 int ret = 0;
49
50 /* the task is exiting. */
51 if (!current->mm)
52 return 0;
53
54 down_write(&current->mm->mmap_sem);
55
56 if (incr) {
57 locked = current->mm->locked_vm + npages;
58 lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
59
60 if (locked > lock_limit && !capable(CAP_IPC_LOCK))
61 ret = -ENOMEM;
62 else
63 current->mm->locked_vm += npages;
64 } else {
65 if (WARN_ON_ONCE(npages > current->mm->locked_vm))
66 npages = current->mm->locked_vm;
67 current->mm->locked_vm -= npages;
68 }
69
70 dev_dbg(dev, "[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n", current->pid,
71 incr ? '+' : '-', npages << PAGE_SHIFT,
72 current->mm->locked_vm << PAGE_SHIFT, rlimit(RLIMIT_MEMLOCK),
73 ret ? "- execeeded" : "");
74
75 up_write(&current->mm->mmap_sem);
76
77 return ret;
78}
79
80/**
81 * afu_dma_pin_pages - pin pages of given dma memory region
82 * @pdata: feature device platform data
83 * @region: dma memory region to be pinned
84 *
85 * Pin all the pages of given dfl_afu_dma_region.
86 * Return 0 for success or negative error code.
87 */
88static int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata,
89 struct dfl_afu_dma_region *region)
90{
91 int npages = region->length >> PAGE_SHIFT;
92 struct device *dev = &pdata->dev->dev;
93 int ret, pinned;
94
95 ret = afu_dma_adjust_locked_vm(dev, npages, true);
96 if (ret)
97 return ret;
98
99 region->pages = kcalloc(npages, sizeof(struct page *), GFP_KERNEL);
100 if (!region->pages) {
101 ret = -ENOMEM;
102 goto unlock_vm;
103 }
104
105 pinned = get_user_pages_fast(region->user_addr, npages, 1,
106 region->pages);
107 if (pinned < 0) {
108 ret = pinned;
109 goto put_pages;
110 } else if (pinned != npages) {
111 ret = -EFAULT;
112 goto free_pages;
113 }
114
115 dev_dbg(dev, "%d pages pinned\n", pinned);
116
117 return 0;
118
119put_pages:
120 put_all_pages(region->pages, pinned);
121free_pages:
122 kfree(region->pages);
123unlock_vm:
124 afu_dma_adjust_locked_vm(dev, npages, false);
125 return ret;
126}
127
128/**
129 * afu_dma_unpin_pages - unpin pages of given dma memory region
130 * @pdata: feature device platform data
131 * @region: dma memory region to be unpinned
132 *
133 * Unpin all the pages of given dfl_afu_dma_region.
134 * Return 0 for success or negative error code.
135 */
136static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata,
137 struct dfl_afu_dma_region *region)
138{
139 long npages = region->length >> PAGE_SHIFT;
140 struct device *dev = &pdata->dev->dev;
141
142 put_all_pages(region->pages, npages);
143 kfree(region->pages);
144 afu_dma_adjust_locked_vm(dev, npages, false);
145
146 dev_dbg(dev, "%ld pages unpinned\n", npages);
147}
148
149/**
150 * afu_dma_check_continuous_pages - check if pages are continuous
151 * @region: dma memory region
152 *
153 * Return true if pages of given dma memory region have continuous physical
154 * address, otherwise return false.
155 */
156static bool afu_dma_check_continuous_pages(struct dfl_afu_dma_region *region)
157{
158 int npages = region->length >> PAGE_SHIFT;
159 int i;
160
161 for (i = 0; i < npages - 1; i++)
162 if (page_to_pfn(region->pages[i]) + 1 !=
163 page_to_pfn(region->pages[i + 1]))
164 return false;
165
166 return true;
167}
168
169/**
170 * dma_region_check_iova - check if memory area is fully contained in the region
171 * @region: dma memory region
172 * @iova: address of the dma memory area
173 * @size: size of the dma memory area
174 *
175 * Compare the dma memory area defined by @iova and @size with given dma region.
176 * Return true if memory area is fully contained in the region, otherwise false.
177 */
178static bool dma_region_check_iova(struct dfl_afu_dma_region *region,
179 u64 iova, u64 size)
180{
181 if (!size && region->iova != iova)
182 return false;
183
184 return (region->iova <= iova) &&
185 (region->length + region->iova >= iova + size);
186}
187
188/**
189 * afu_dma_region_add - add given dma region to rbtree
190 * @pdata: feature device platform data
191 * @region: dma region to be added
192 *
193 * Return 0 for success, -EEXIST if dma region has already been added.
194 *
195 * Needs to be called with pdata->lock heold.