.. | .. |
---|
47 | 47 | #include "usnic_uiom.h" |
---|
48 | 48 | #include "usnic_uiom_interval_tree.h" |
---|
49 | 49 | |
---|
50 | | -static struct workqueue_struct *usnic_uiom_wq; |
---|
51 | | - |
---|
52 | 50 | #define USNIC_UIOM_PAGE_CHUNK \ |
---|
53 | 51 | ((PAGE_SIZE - offsetof(struct usnic_uiom_chunk, page_list)) /\ |
---|
54 | 52 | ((void *) &((struct usnic_uiom_chunk *) 0)->page_list[1] - \ |
---|
55 | 53 | (void *) &((struct usnic_uiom_chunk *) 0)->page_list[0])) |
---|
56 | | - |
---|
57 | | -static void usnic_uiom_reg_account(struct work_struct *work) |
---|
58 | | -{ |
---|
59 | | - struct usnic_uiom_reg *umem = container_of(work, |
---|
60 | | - struct usnic_uiom_reg, work); |
---|
61 | | - |
---|
62 | | - down_write(&umem->mm->mmap_sem); |
---|
63 | | - umem->mm->locked_vm -= umem->diff; |
---|
64 | | - up_write(&umem->mm->mmap_sem); |
---|
65 | | - mmput(umem->mm); |
---|
66 | | - kfree(umem); |
---|
67 | | -} |
---|
68 | 54 | |
---|
69 | 55 | static int usnic_uiom_dma_fault(struct iommu_domain *domain, |
---|
70 | 56 | struct device *dev, |
---|
.. | .. |
---|
89 | 75 | for_each_sg(chunk->page_list, sg, chunk->nents, i) { |
---|
90 | 76 | page = sg_page(sg); |
---|
91 | 77 | pa = sg_phys(sg); |
---|
92 | | - if (!PageDirty(page) && dirty) |
---|
93 | | - set_page_dirty_lock(page); |
---|
94 | | - put_page(page); |
---|
| 78 | + unpin_user_pages_dirty_lock(&page, 1, dirty); |
---|
95 | 79 | usnic_dbg("pa: %pa\n", &pa); |
---|
96 | 80 | } |
---|
97 | 81 | kfree(chunk); |
---|
.. | .. |
---|
99 | 83 | } |
---|
100 | 84 | |
---|
101 | 85 | static int usnic_uiom_get_pages(unsigned long addr, size_t size, int writable, |
---|
102 | | - int dmasync, struct list_head *chunk_list) |
---|
| 86 | + int dmasync, struct usnic_uiom_reg *uiomr) |
---|
103 | 87 | { |
---|
| 88 | + struct list_head *chunk_list = &uiomr->chunk_list; |
---|
104 | 89 | struct page **page_list; |
---|
105 | 90 | struct scatterlist *sg; |
---|
106 | 91 | struct usnic_uiom_chunk *chunk; |
---|
.. | .. |
---|
114 | 99 | int flags; |
---|
115 | 100 | dma_addr_t pa; |
---|
116 | 101 | unsigned int gup_flags; |
---|
| 102 | + struct mm_struct *mm; |
---|
117 | 103 | |
---|
118 | 104 | /* |
---|
119 | 105 | * If the combination of the addr and size requested for this memory |
---|
.. | .. |
---|
136 | 122 | |
---|
137 | 123 | npages = PAGE_ALIGN(size + (addr & ~PAGE_MASK)) >> PAGE_SHIFT; |
---|
138 | 124 | |
---|
139 | | - down_write(¤t->mm->mmap_sem); |
---|
| 125 | + uiomr->owning_mm = mm = current->mm; |
---|
| 126 | + mmap_read_lock(mm); |
---|
140 | 127 | |
---|
141 | | - locked = npages + current->mm->pinned_vm; |
---|
| 128 | + locked = atomic64_add_return(npages, ¤t->mm->pinned_vm); |
---|
142 | 129 | lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; |
---|
143 | 130 | |
---|
144 | 131 | if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) { |
---|
.. | .. |
---|
154 | 141 | ret = 0; |
---|
155 | 142 | |
---|
156 | 143 | while (npages) { |
---|
157 | | - ret = get_user_pages_longterm(cur_base, |
---|
158 | | - min_t(unsigned long, npages, |
---|
159 | | - PAGE_SIZE / sizeof(struct page *)), |
---|
160 | | - gup_flags, page_list, NULL); |
---|
| 144 | + ret = pin_user_pages(cur_base, |
---|
| 145 | + min_t(unsigned long, npages, |
---|
| 146 | + PAGE_SIZE / sizeof(struct page *)), |
---|
| 147 | + gup_flags | FOLL_LONGTERM, |
---|
| 148 | + page_list, NULL); |
---|
161 | 149 | |
---|
162 | 150 | if (ret < 0) |
---|
163 | 151 | goto out; |
---|
.. | .. |
---|
166 | 154 | off = 0; |
---|
167 | 155 | |
---|
168 | 156 | while (ret) { |
---|
169 | | - chunk = kmalloc(sizeof(*chunk) + |
---|
170 | | - sizeof(struct scatterlist) * |
---|
171 | | - min_t(int, ret, USNIC_UIOM_PAGE_CHUNK), |
---|
| 157 | + chunk = kmalloc(struct_size(chunk, page_list, |
---|
| 158 | + min_t(int, ret, USNIC_UIOM_PAGE_CHUNK)), |
---|
172 | 159 | GFP_KERNEL); |
---|
173 | 160 | if (!chunk) { |
---|
174 | 161 | ret = -ENOMEM; |
---|
.. | .. |
---|
194 | 181 | } |
---|
195 | 182 | |
---|
196 | 183 | out: |
---|
197 | | - if (ret < 0) |
---|
| 184 | + if (ret < 0) { |
---|
198 | 185 | usnic_uiom_put_pages(chunk_list, 0); |
---|
199 | | - else |
---|
200 | | - current->mm->pinned_vm = locked; |
---|
| 186 | + atomic64_sub(npages, ¤t->mm->pinned_vm); |
---|
| 187 | + } else |
---|
| 188 | + mmgrab(uiomr->owning_mm); |
---|
201 | 189 | |
---|
202 | | - up_write(¤t->mm->mmap_sem); |
---|
| 190 | + mmap_read_unlock(mm); |
---|
203 | 191 | free_page((unsigned long) page_list); |
---|
204 | 192 | return ret; |
---|
205 | 193 | } |
---|
.. | .. |
---|
293 | 281 | size = pa_end - pa_start + PAGE_SIZE; |
---|
294 | 282 | usnic_dbg("va 0x%lx pa %pa size 0x%zx flags 0x%x", |
---|
295 | 283 | va_start, &pa_start, size, flags); |
---|
296 | | - err = iommu_map(pd->domain, va_start, pa_start, |
---|
297 | | - size, flags); |
---|
| 284 | + err = iommu_map_atomic(pd->domain, va_start, |
---|
| 285 | + pa_start, size, flags); |
---|
298 | 286 | if (err) { |
---|
299 | 287 | usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n", |
---|
300 | 288 | va_start, &pa_start, size, err); |
---|
.. | .. |
---|
310 | 298 | size = pa - pa_start + PAGE_SIZE; |
---|
311 | 299 | usnic_dbg("va 0x%lx pa %pa size 0x%zx flags 0x%x\n", |
---|
312 | 300 | va_start, &pa_start, size, flags); |
---|
313 | | - err = iommu_map(pd->domain, va_start, pa_start, |
---|
314 | | - size, flags); |
---|
| 301 | + err = iommu_map_atomic(pd->domain, va_start, |
---|
| 302 | + pa_start, size, flags); |
---|
315 | 303 | if (err) { |
---|
316 | 304 | usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n", |
---|
317 | 305 | va_start, &pa_start, size, err); |
---|
.. | .. |
---|
379 | 367 | uiomr->pd = pd; |
---|
380 | 368 | |
---|
381 | 369 | err = usnic_uiom_get_pages(addr, size, writable, dmasync, |
---|
382 | | - &uiomr->chunk_list); |
---|
| 370 | + uiomr); |
---|
383 | 371 | if (err) { |
---|
384 | 372 | usnic_err("Failed get_pages vpn [0x%lx,0x%lx] err %d\n", |
---|
385 | 373 | vpn_start, vpn_last, err); |
---|
.. | .. |
---|
426 | 414 | out_put_pages: |
---|
427 | 415 | usnic_uiom_put_pages(&uiomr->chunk_list, 0); |
---|
428 | 416 | spin_unlock(&pd->lock); |
---|
| 417 | + mmdrop(uiomr->owning_mm); |
---|
429 | 418 | out_free_uiomr: |
---|
430 | 419 | kfree(uiomr); |
---|
431 | 420 | return ERR_PTR(err); |
---|
432 | 421 | } |
---|
433 | 422 | |
---|
434 | | -void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr, |
---|
435 | | - struct ib_ucontext *ucontext) |
---|
| 423 | +static void __usnic_uiom_release_tail(struct usnic_uiom_reg *uiomr) |
---|
436 | 424 | { |
---|
437 | | - struct task_struct *task; |
---|
438 | | - struct mm_struct *mm; |
---|
439 | | - unsigned long diff; |
---|
| 425 | + mmdrop(uiomr->owning_mm); |
---|
| 426 | + kfree(uiomr); |
---|
| 427 | +} |
---|
440 | 428 | |
---|
| 429 | +static inline size_t usnic_uiom_num_pages(struct usnic_uiom_reg *uiomr) |
---|
| 430 | +{ |
---|
| 431 | + return PAGE_ALIGN(uiomr->length + uiomr->offset) >> PAGE_SHIFT; |
---|
| 432 | +} |
---|
| 433 | + |
---|
| 434 | +void usnic_uiom_reg_release(struct usnic_uiom_reg *uiomr) |
---|
| 435 | +{ |
---|
441 | 436 | __usnic_uiom_reg_release(uiomr->pd, uiomr, 1); |
---|
442 | 437 | |
---|
443 | | - task = get_pid_task(ucontext->tgid, PIDTYPE_PID); |
---|
444 | | - if (!task) |
---|
445 | | - goto out; |
---|
446 | | - mm = get_task_mm(task); |
---|
447 | | - put_task_struct(task); |
---|
448 | | - if (!mm) |
---|
449 | | - goto out; |
---|
450 | | - |
---|
451 | | - diff = PAGE_ALIGN(uiomr->length + uiomr->offset) >> PAGE_SHIFT; |
---|
452 | | - |
---|
453 | | - /* |
---|
454 | | - * We may be called with the mm's mmap_sem already held. This |
---|
455 | | - * can happen when a userspace munmap() is the call that drops |
---|
456 | | - * the last reference to our file and calls our release |
---|
457 | | - * method. If there are memory regions to destroy, we'll end |
---|
458 | | - * up here and not be able to take the mmap_sem. In that case |
---|
459 | | - * we defer the vm_locked accounting to the system workqueue. |
---|
460 | | - */ |
---|
461 | | - if (ucontext->closing) { |
---|
462 | | - if (!down_write_trylock(&mm->mmap_sem)) { |
---|
463 | | - INIT_WORK(&uiomr->work, usnic_uiom_reg_account); |
---|
464 | | - uiomr->mm = mm; |
---|
465 | | - uiomr->diff = diff; |
---|
466 | | - |
---|
467 | | - queue_work(usnic_uiom_wq, &uiomr->work); |
---|
468 | | - return; |
---|
469 | | - } |
---|
470 | | - } else |
---|
471 | | - down_write(&mm->mmap_sem); |
---|
472 | | - |
---|
473 | | - mm->pinned_vm -= diff; |
---|
474 | | - up_write(&mm->mmap_sem); |
---|
475 | | - mmput(mm); |
---|
476 | | -out: |
---|
477 | | - kfree(uiomr); |
---|
| 438 | + atomic64_sub(usnic_uiom_num_pages(uiomr), &uiomr->owning_mm->pinned_vm); |
---|
| 439 | + __usnic_uiom_release_tail(uiomr); |
---|
478 | 440 | } |
---|
479 | 441 | |
---|
480 | 442 | struct usnic_uiom_pd *usnic_uiom_alloc_pd(void) |
---|
.. | .. |
---|
602 | 564 | return -EPERM; |
---|
603 | 565 | } |
---|
604 | 566 | |
---|
605 | | - usnic_uiom_wq = create_workqueue(drv_name); |
---|
606 | | - if (!usnic_uiom_wq) { |
---|
607 | | - usnic_err("Unable to alloc wq for drv %s\n", drv_name); |
---|
608 | | - return -ENOMEM; |
---|
609 | | - } |
---|
610 | | - |
---|
611 | 567 | return 0; |
---|
612 | | -} |
---|
613 | | - |
---|
614 | | -void usnic_uiom_fini(void) |
---|
615 | | -{ |
---|
616 | | - flush_workqueue(usnic_uiom_wq); |
---|
617 | | - destroy_workqueue(usnic_uiom_wq); |
---|
618 | 568 | } |
---|