aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/core/umem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/infiniband/core/umem.c')
-rw-r--r--drivers/infiniband/core/umem.c72
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(&current->mm->mmap_sem); 155 down_write(&current->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}
294EXPORT_SYMBOL(ib_umem_page_count); 320EXPORT_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 */
332int 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}
354EXPORT_SYMBOL(ib_umem_copy_from);