diff options
Diffstat (limited to 'drivers/infiniband/core/umem.c')
-rw-r--r-- | drivers/infiniband/core/umem.c | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c index df0c4f605a21..aec7a6aa2951 100644 --- a/drivers/infiniband/core/umem.c +++ b/drivers/infiniband/core/umem.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/hugetlb.h> | 39 | #include <linux/hugetlb.h> |
40 | #include <linux/dma-attrs.h> | 40 | #include <linux/dma-attrs.h> |
41 | #include <linux/slab.h> | 41 | #include <linux/slab.h> |
42 | #include <rdma/ib_umem_odp.h> | ||
42 | 43 | ||
43 | #include "uverbs.h" | 44 | #include "uverbs.h" |
44 | 45 | ||
@@ -69,6 +70,10 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d | |||
69 | 70 | ||
70 | /** | 71 | /** |
71 | * ib_umem_get - Pin and DMA map userspace memory. | 72 | * ib_umem_get - Pin and DMA map userspace memory. |
73 | * | ||
74 | * If access flags indicate ODP memory, avoid pinning. Instead, stores | ||
75 | * the mm for future page fault handling in conjunction with MMU notifiers. | ||
76 | * | ||
72 | * @context: userspace context to pin memory for | 77 | * @context: userspace context to pin memory for |
73 | * @addr: userspace virtual address to start at | 78 | * @addr: userspace virtual address to start at |
74 | * @size: length of region to pin | 79 | * @size: length of region to pin |
@@ -103,17 +108,30 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, | |||
103 | 108 | ||
104 | umem->context = context; | 109 | umem->context = context; |
105 | umem->length = size; | 110 | umem->length = size; |
106 | umem->offset = addr & ~PAGE_MASK; | 111 | umem->address = addr; |
107 | umem->page_size = PAGE_SIZE; | 112 | umem->page_size = PAGE_SIZE; |
108 | umem->pid = get_task_pid(current, PIDTYPE_PID); | 113 | umem->pid = get_task_pid(current, PIDTYPE_PID); |
109 | /* | 114 | /* |
110 | * We ask for writable memory if any access flags other than | 115 | * We ask for writable memory if any of the following |
111 | * "remote read" are set. "Local write" and "remote write" | 116 | * access flags are set. "Local write" and "remote write" |
112 | * obviously require write access. "Remote atomic" can do | 117 | * obviously require write access. "Remote atomic" can do |
113 | * things like fetch and add, which will modify memory, and | 118 | * things like fetch and add, which will modify memory, and |
114 | * "MW bind" can change permissions by binding a window. | 119 | * "MW bind" can change permissions by binding a window. |
115 | */ | 120 | */ |
116 | umem->writable = !!(access & ~IB_ACCESS_REMOTE_READ); | 121 | umem->writable = !!(access & |
122 | (IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE | | ||
123 | IB_ACCESS_REMOTE_ATOMIC | IB_ACCESS_MW_BIND)); | ||
124 | |||
125 | if (access & IB_ACCESS_ON_DEMAND) { | ||
126 | ret = ib_umem_odp_get(context, umem); | ||
127 | if (ret) { | ||
128 | kfree(umem); | ||
129 | return ERR_PTR(ret); | ||
130 | } | ||
131 | return umem; | ||
132 | } | ||
133 | |||
134 | umem->odp_data = NULL; | ||
117 | 135 | ||
118 | /* We assume the memory is from hugetlb until proved otherwise */ | 136 | /* We assume the memory is from hugetlb until proved otherwise */ |
119 | umem->hugetlb = 1; | 137 | umem->hugetlb = 1; |
@@ -132,7 +150,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr, | |||
132 | if (!vma_list) | 150 | if (!vma_list) |
133 | umem->hugetlb = 0; | 151 | umem->hugetlb = 0; |
134 | 152 | ||
135 | npages = PAGE_ALIGN(size + umem->offset) >> PAGE_SHIFT; | 153 | npages = ib_umem_num_pages(umem); |
136 | 154 | ||
137 | down_write(¤t->mm->mmap_sem); | 155 | down_write(¤t->mm->mmap_sem); |
138 | 156 | ||
@@ -235,6 +253,11 @@ void ib_umem_release(struct ib_umem *umem) | |||
235 | struct task_struct *task; | 253 | struct task_struct *task; |
236 | unsigned long diff; | 254 | unsigned long diff; |
237 | 255 | ||
256 | if (umem->odp_data) { | ||
257 | ib_umem_odp_release(umem); | ||
258 | return; | ||
259 | } | ||
260 | |||
238 | __ib_umem_release(umem->context->device, umem, 1); | 261 | __ib_umem_release(umem->context->device, umem, 1); |
239 | 262 | ||
240 | task = get_pid_task(umem->pid, PIDTYPE_PID); | 263 | task = get_pid_task(umem->pid, PIDTYPE_PID); |
@@ -246,7 +269,7 @@ void ib_umem_release(struct ib_umem *umem) | |||
246 | if (!mm) | 269 | if (!mm) |
247 | goto out; | 270 | goto out; |
248 | 271 | ||
249 | diff = PAGE_ALIGN(umem->length + umem->offset) >> PAGE_SHIFT; | 272 | diff = ib_umem_num_pages(umem); |
250 | 273 | ||
251 | /* | 274 | /* |
252 | * We may be called with the mm's mmap_sem already held. This | 275 | * We may be called with the mm's mmap_sem already held. This |
@@ -283,6 +306,9 @@ int ib_umem_page_count(struct ib_umem *umem) | |||
283 | int n; | 306 | int n; |
284 | struct scatterlist *sg; | 307 | struct scatterlist *sg; |
285 | 308 | ||
309 | if (umem->odp_data) | ||
310 | return ib_umem_num_pages(umem); | ||
311 | |||
286 | shift = ilog2(umem->page_size); | 312 | shift = ilog2(umem->page_size); |
287 | 313 | ||
288 | n = 0; | 314 | n = 0; |
@@ -292,3 +318,37 @@ int ib_umem_page_count(struct ib_umem *umem) | |||
292 | return n; | 318 | return n; |
293 | } | 319 | } |
294 | EXPORT_SYMBOL(ib_umem_page_count); | 320 | EXPORT_SYMBOL(ib_umem_page_count); |
321 | |||
322 | /* | ||
323 | * Copy from the given ib_umem's pages to the given buffer. | ||
324 | * | ||
325 | * umem - the umem to copy from | ||
326 | * offset - offset to start copying from | ||
327 | * dst - destination buffer | ||
328 | * length - buffer length | ||
329 | * | ||
330 | * Returns 0 on success, or an error code. | ||
331 | */ | ||
332 | int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset, | ||
333 | size_t length) | ||
334 | { | ||
335 | size_t end = offset + length; | ||
336 | int ret; | ||
337 | |||
338 | if (offset > umem->length || length > umem->length - offset) { | ||
339 | pr_err("ib_umem_copy_from not in range. offset: %zd umem length: %zd end: %zd\n", | ||
340 | offset, umem->length, end); | ||
341 | return -EINVAL; | ||
342 | } | ||
343 | |||
344 | ret = sg_pcopy_to_buffer(umem->sg_head.sgl, umem->nmap, dst, length, | ||
345 | offset + ib_umem_offset(umem)); | ||
346 | |||
347 | if (ret < 0) | ||
348 | return ret; | ||
349 | else if (ret != length) | ||
350 | return -EINVAL; | ||
351 | else | ||
352 | return 0; | ||
353 | } | ||
354 | EXPORT_SYMBOL(ib_umem_copy_from); | ||