| .. | .. |
|---|
| 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 | } |
|---|
| .. | .. |
|---|
| 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 | } |
|---|