| @@ -23,18 +23,25 @@ static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, · 22 unmodified lines |
| 23 | static bool tid_rb_invalidate(struct mmu_interval_notifier *mni, | 23 | static bool tid_rb_invalidate(struct mmu_interval_notifier *mni, |
| 24 | const struct mmu_notifier_range *range, | 24 | const struct mmu_notifier_range *range, |
| 25 | unsigned long cur_seq); | 25 | unsigned long cur_seq); |
| | 26 | static bool tid_cover_invalidate(struct mmu_interval_notifier *mni, |
| | 27 | const struct mmu_notifier_range *range, |
| | 28 | unsigned long cur_seq); |
| 26 | static int program_rcvarray(struct hfi1_filedata *fd, struct tid_user_buf *, | 29 | static int program_rcvarray(struct hfi1_filedata *fd, struct tid_user_buf *, |
| 27 | struct tid_group *grp, | 30 | struct tid_group *grp, |
| 28 | unsigned int start, u16 count, | 31 | unsigned int start, u16 count, |
| 29 | u32 *tidlist, unsigned int *tididx, | 32 | u32 *tidlist, unsigned int *tididx, |
| 30 | unsigned int *pmapped); | 33 | unsigned int *pmapped); |
| 31 | static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, | 34 | static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo); |
| 32 | struct tid_group **grp); | 35 | static void __clear_tid_node(struct hfi1_filedata *fd, |
| | 36 | struct tid_rb_node *node); |
| 33 | static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node); | 37 | static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node); |
| 34 | | 38 | |
| 35 | static const struct mmu_interval_notifier_ops tid_mn_ops = { | 39 | static const struct mmu_interval_notifier_ops tid_mn_ops = { |
| 36 | .invalidate = tid_rb_invalidate, | 40 | .invalidate = tid_rb_invalidate, |
| 37 | }; | 41 | }; |
| | 42 | static const struct mmu_interval_notifier_ops tid_cover_ops = { |
| | 43 | .invalidate = tid_cover_invalidate, |
| | 44 | }; |
| 38 | | 45 | |
| 39 | /* | 46 | /* |
| 40 | * Initialize context and file private data needed for Expected | 47 | * Initialize context and file private data needed for Expected |
| @@ -253,53 +260,65 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd, · 212 unmodified lines |
| 253 | tididx = 0, mapped, mapped_pages = 0; | 260 | tididx = 0, mapped, mapped_pages = 0; |
| 254 | u32 *tidlist = NULL; | 261 | u32 *tidlist = NULL; |
| 255 | struct tid_user_buf *tidbuf; | 262 | struct tid_user_buf *tidbuf; |
| | 263 | unsigned long mmu_seq = 0; |
| 256 | | 264 | |
| 257 | if (!PAGE_ALIGNED(tinfo->vaddr)) | 265 | if (!PAGE_ALIGNED(tinfo->vaddr)) |
| 258 | return -EINVAL; | 266 | return -EINVAL; |
| | 267 | if (tinfo->length == 0) |
| | 268 | return -EINVAL; |
| 259 | | 269 | |
| 260 | tidbuf = kzalloc(sizeof(*tidbuf), GFP_KERNEL); | 270 | tidbuf = kzalloc(sizeof(*tidbuf), GFP_KERNEL); |
| 261 | if (!tidbuf) | 271 | if (!tidbuf) |
| 262 | return -ENOMEM; | 272 | return -ENOMEM; |
| 263 | | 273 | |
| | 274 | mutex_init(&tidbuf->cover_mutex); |
| 264 | tidbuf->vaddr = tinfo->vaddr; | 275 | tidbuf->vaddr = tinfo->vaddr; |
| 265 | tidbuf->length = tinfo->length; | 276 | tidbuf->length = tinfo->length; |
| 266 | tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets), | 277 | tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets), |
| 267 | GFP_KERNEL); | 278 | GFP_KERNEL); |
| 268 | if (!tidbuf->psets) { | 279 | if (!tidbuf->psets) { |
| 269 | kfree(tidbuf); | 280 | ret = -ENOMEM; |
| 270 | return -ENOMEM; | 281 | goto fail_release_mem; |
| | 282 | } |
| | 283 | |
| | 284 | if (fd->use_mn) { |
| | 285 | ret = mmu_interval_notifier_insert( |
| | 286 | &tidbuf->notifier, current->mm, |
| | 287 | tidbuf->vaddr, tidbuf->npages * PAGE_SIZE, |
| | 288 | &tid_cover_ops); |
| | 289 | if (ret) |
| | 290 | goto fail_release_mem; |
| | 291 | mmu_seq = mmu_interval_read_begin(&tidbuf->notifier); |
| 271 | } | 292 | } |
| 272 | | 293 | |
| 273 | pinned = pin_rcv_pages(fd, tidbuf); | 294 | pinned = pin_rcv_pages(fd, tidbuf); |
| 274 | if (pinned <= 0) { | 295 | if (pinned <= 0) { |
| 275 | kfree(tidbuf->psets); | 296 | ret = (pinned < 0) ? pinned : -ENOSPC; |
| 276 | kfree(tidbuf); | 297 | goto fail_unpin; |
| 277 | return pinned; | | |
| 278 | } | 298 | } |
| 279 | | 299 | |
| 280 | /* Find sets of physically contiguous pages */ | 300 | /* Find sets of physically contiguous pages */ |
| 281 | tidbuf->n_psets = find_phys_blocks(tidbuf, pinned); | 301 | tidbuf->n_psets = find_phys_blocks(tidbuf, pinned); |
| 282 | | 302 | |
| 283 | /* | 303 | /* Reserve the number of expected tids to be used. */ |
| 284 | * We don't need to access this under a lock since tid_used is per | | |
| 285 | * process and the same process cannot be in hfi1_user_exp_rcv_clear() | | |
| 286 | * and hfi1_user_exp_rcv_setup() at the same time. | | |
| 287 | */ | | |
| 288 | spin_lock(&fd->tid_lock); | 304 | spin_lock(&fd->tid_lock); |
| 289 | if (fd->tid_used + tidbuf->n_psets > fd->tid_limit) | 305 | if (fd->tid_used + tidbuf->n_psets > fd->tid_limit) |
| 290 | pageset_count = fd->tid_limit - fd->tid_used; | 306 | pageset_count = fd->tid_limit - fd->tid_used; |
| 291 | else | 307 | else |
| 292 | pageset_count = tidbuf->n_psets; | 308 | pageset_count = tidbuf->n_psets; |
| | 309 | fd->tid_used += pageset_count; |
| 293 | spin_unlock(&fd->tid_lock); | 310 | spin_unlock(&fd->tid_lock); |
| 294 | | 311 | |
| 295 | if (!pageset_count) | 312 | if (!pageset_count) { |
| 296 | goto bail; | 313 | ret = -ENOSPC; |
| | 314 | goto fail_unreserve; |
| | 315 | } |
| 297 | | 316 | |
| 298 | ngroups = pageset_count / dd->rcv_entries.group_size; | 317 | ngroups = pageset_count / dd->rcv_entries.group_size; |
| 299 | tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL); | 318 | tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL); |
| 300 | if (!tidlist) { | 319 | if (!tidlist) { |
| 301 | ret = -ENOMEM; | 320 | ret = -ENOMEM; |
| 302 | goto nomem; | 321 | goto fail_unreserve; |
| 303 | } | 322 | } |
| 304 | | 323 | |
| 305 | tididx = 0; | 324 | tididx = 0; |
| @@ -395,43 +414,78 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd, · 89 unmodified lines |
| 395 | } | 414 | } |
| 396 | unlock: | 415 | unlock: |
| 397 | mutex_unlock(&uctxt->exp_mutex); | 416 | mutex_unlock(&uctxt->exp_mutex); |
| 398 | nomem: | | |
| 399 | hfi1_cdbg(TID, "total mapped: tidpairs:%u pages:%u (%d)", tididx, | 417 | hfi1_cdbg(TID, "total mapped: tidpairs:%u pages:%u (%d)", tididx, |
| 400 | mapped_pages, ret); | 418 | mapped_pages, ret); |
| 401 | if (tididx) { | 419 | |
| 402 | spin_lock(&fd->tid_lock); | 420 | /* fail if nothing was programmed, set error if none provided */ |
| 403 | fd->tid_used += tididx; | 421 | if (tididx == 0) { |
| 404 | spin_unlock(&fd->tid_lock); | 422 | if (ret >= 0) |
| 405 | tinfo->tidcnt = tididx; | 423 | ret = -ENOSPC; |
| 406 | tinfo->length = mapped_pages * PAGE_SIZE; | 424 | goto fail_unreserve; |
| 407 | | 425 | } |
| 408 | if (copy_to_user(u64_to_user_ptr(tinfo->tidlist), | 426 | |
| 409 | tidlist, sizeof(tidlist[0]) * tididx)) { | 427 | /* adjust reserved tid_used to actual count */ |
| 410 | /* | 428 | spin_lock(&fd->tid_lock); |
| 411 | * On failure to copy to the user level, we need to undo | 429 | fd->tid_used -= pageset_count - tididx; |
| 412 | * everything done so far so we don't leak resources. | 430 | spin_unlock(&fd->tid_lock); |
| 413 | */ | 431 | |
| 414 | tinfo->tidlist = (unsigned long)&tidlist; | 432 | /* unpin all pages not covered by a TID */ |
| 415 | hfi1_user_exp_rcv_clear(fd, tinfo); | 433 | unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages, pinned - mapped_pages, |
| 416 | tinfo->tidlist = 0; | 434 | false); |
| 417 | ret = -EFAULT; | 435 | |
| 418 | goto bail; | 436 | if (fd->use_mn) { |
| | 437 | /* check for an invalidate during setup */ |
| | 438 | bool fail = false; |
| | 439 | |
| | 440 | mutex_lock(&tidbuf->cover_mutex); |
| | 441 | fail = mmu_interval_read_retry(&tidbuf->notifier, mmu_seq); |
| | 442 | mutex_unlock(&tidbuf->cover_mutex); |
| | 443 | |
| | 444 | if (fail) { |
| | 445 | ret = -EBUSY; |
| | 446 | goto fail_unprogram; |
| 419 | } | 447 | } |
| 420 | } | 448 | } |
| 421 | | 449 | |
| 422 | /* | 450 | tinfo->tidcnt = tididx; |
| 423 | * If not everything was mapped (due to insufficient RcvArray entries, | 451 | tinfo->length = mapped_pages * PAGE_SIZE; |
| 424 | * for example), unpin all unmapped pages so we can pin them nex time. | 452 | |
| 425 | */ | 453 | if (copy_to_user(u64_to_user_ptr(tinfo->tidlist), |
| 426 | if (mapped_pages != pinned) | 454 | tidlist, sizeof(tidlist[0]) * tididx)) { |
| 427 | unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages, | 455 | ret = -EFAULT; |
| 428 | (pinned - mapped_pages), false); | 456 | goto fail_unprogram; |
| 429 | bail: | 457 | } |
| | 458 | |
| | 459 | if (fd->use_mn) |
| | 460 | mmu_interval_notifier_remove(&tidbuf->notifier); |
| | 461 | kfree(tidbuf->pages); |
| 430 | kfree(tidbuf->psets); | 462 | kfree(tidbuf->psets); |
| | 463 | kfree(tidbuf); |
| 431 | kfree(tidlist); | 464 | kfree(tidlist); |
| | 465 | return 0; |
| | 466 | |
| | 467 | fail_unprogram: |
| | 468 | /* unprogram, unmap, and unpin all allocated TIDs */ |
| | 469 | tinfo->tidlist = (unsigned long)tidlist; |
| | 470 | hfi1_user_exp_rcv_clear(fd, tinfo); |
| | 471 | tinfo->tidlist = 0; |
| | 472 | pinned = 0; /* nothing left to unpin */ |
| | 473 | pageset_count = 0; /* nothing left reserved */ |
| | 474 | fail_unreserve: |
| | 475 | spin_lock(&fd->tid_lock); |
| | 476 | fd->tid_used -= pageset_count; |
| | 477 | spin_unlock(&fd->tid_lock); |
| | 478 | fail_unpin: |
| | 479 | if (fd->use_mn) |
| | 480 | mmu_interval_notifier_remove(&tidbuf->notifier); |
| | 481 | if (pinned > 0) |
| | 482 | unpin_rcv_pages(fd, tidbuf, NULL, 0, pinned, false); |
| | 483 | fail_release_mem: |
| 432 | kfree(tidbuf->pages); | 484 | kfree(tidbuf->pages); |
| | 485 | kfree(tidbuf->psets); |
| 433 | kfree(tidbuf); | 486 | kfree(tidbuf); |
| 434 | return ret > 0 ? 0 : ret; | 487 | kfree(tidlist); |
| | 488 | return ret; |
| 435 | } | 489 | } |
| 436 | | 490 | |
| 437 | int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd, | 491 | int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd, |
| @@ -452,7 +506,7 @@ int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd, · 14 unmodified lines |
| 452 | | 506 | |
| 453 | mutex_lock(&uctxt->exp_mutex); | 507 | mutex_lock(&uctxt->exp_mutex); |
| 454 | for (tididx = 0; tididx < tinfo->tidcnt; tididx++) { | 508 | for (tididx = 0; tididx < tinfo->tidcnt; tididx++) { |
| 455 | ret = unprogram_rcvarray(fd, tidinfo[tididx], NULL); | 509 | ret = unprogram_rcvarray(fd, tidinfo[tididx]); |
| 456 | if (ret) { | 510 | if (ret) { |
| 457 | hfi1_cdbg(TID, "Failed to unprogram rcv array %d", | 511 | hfi1_cdbg(TID, "Failed to unprogram rcv array %d", |
| 458 | ret); | 512 | ret); |
| @@ -706,6 +760,7 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd, · 247 unmodified lines |
| 706 | } | 760 | } |
| 707 | | 761 | |
| 708 | node->fdata = fd; | 762 | node->fdata = fd; |
| | 763 | mutex_init(&node->invalidate_mutex); |
| 709 | node->phys = page_to_phys(pages[0]); | 764 | node->phys = page_to_phys(pages[0]); |
| 710 | node->npages = npages; | 765 | node->npages = npages; |
| 711 | node->rcventry = rcventry; | 766 | node->rcventry = rcventry; |
| @@ -721,11 +776,6 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd, · 9 unmodified lines |
| 721 | &tid_mn_ops); | 776 | &tid_mn_ops); |
| 722 | if (ret) | 777 | if (ret) |
| 723 | goto out_unmap; | 778 | goto out_unmap; |
| 724 | /* | | |
| 725 | * FIXME: This is in the wrong order, the notifier should be | | |
| 726 | * established before the pages are pinned by pin_rcv_pages. | | |
| 727 | */ | | |
| 728 | mmu_interval_read_begin(&node->notifier); | | |
| 729 | } | 779 | } |
| 730 | fd->entry_to_rb[node->rcventry - uctxt->expected_base] = node; | 780 | fd->entry_to_rb[node->rcventry - uctxt->expected_base] = node; |
| 731 | | 781 | |
| @@ -745,8 +795,7 @@ out_unmap: · 13 unmodified lines |
| 745 | return -EFAULT; | 795 | return -EFAULT; |
| 746 | } | 796 | } |
| 747 | | 797 | |
| 748 | static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, | 798 | static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo) |
| 749 | struct tid_group **grp) | | |
| 750 | { | 799 | { |
| 751 | struct hfi1_ctxtdata *uctxt = fd->uctxt; | 800 | struct hfi1_ctxtdata *uctxt = fd->uctxt; |
| 752 | struct hfi1_devdata *dd = uctxt->dd; | 801 | struct hfi1_devdata *dd = uctxt->dd; |
| @@ -769,9 +818,6 @@ static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, · 16 unmodified lines |
| 769 | if (!node || node->rcventry != (uctxt->expected_base + rcventry)) | 818 | if (!node || node->rcventry != (uctxt->expected_base + rcventry)) |
| 770 | return -EBADF; | 819 | return -EBADF; |
| 771 | | 820 | |
| 772 | if (grp) | | |
| 773 | *grp = node->grp; | | |
| 774 | | | |
| 775 | if (fd->use_mn) | 821 | if (fd->use_mn) |
| 776 | mmu_interval_notifier_remove(&node->notifier); | 822 | mmu_interval_notifier_remove(&node->notifier); |
| 777 | cacheless_tid_rb_remove(fd, node); | 823 | cacheless_tid_rb_remove(fd, node); |
| @@ -779,23 +825,34 @@ static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, · 1 unmodified line |
| 779 | return 0; | 825 | return 0; |
| 780 | } | 826 | } |
| 781 | | 827 | |
| 782 | static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node) | 828 | static void __clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node) |
| 783 | { | 829 | { |
| 784 | struct hfi1_ctxtdata *uctxt = fd->uctxt; | 830 | struct hfi1_ctxtdata *uctxt = fd->uctxt; |
| 785 | struct hfi1_devdata *dd = uctxt->dd; | 831 | struct hfi1_devdata *dd = uctxt->dd; |
| 786 | | 832 | |
| | 833 | mutex_lock(&node->invalidate_mutex); |
| | 834 | if (node->freed) |
| | 835 | goto done; |
| | 836 | node->freed = true; |
| | 837 | |
| 787 | trace_hfi1_exp_tid_unreg(uctxt->ctxt, fd->subctxt, node->rcventry, | 838 | trace_hfi1_exp_tid_unreg(uctxt->ctxt, fd->subctxt, node->rcventry, |
| 788 | node->npages, | 839 | node->npages, |
| 789 | node->notifier.interval_tree.start, node->phys, | 840 | node->notifier.interval_tree.start, node->phys, |
| 790 | node->dma_addr); | 841 | node->dma_addr); |
| 791 | | 842 | |
| 792 | /* | 843 | /* Make sure device has seen the write before pages are unpinned */ |
| 793 | * Make sure device has seen the write before we unpin the | | |
| 794 | * pages. | | |
| 795 | */ | | |
| 796 | hfi1_put_tid(dd, node->rcventry, PT_INVALID_FLUSH, 0, 0); | 844 | hfi1_put_tid(dd, node->rcventry, PT_INVALID_FLUSH, 0, 0); |
| 797 | | 845 | |
| 798 | unpin_rcv_pages(fd, NULL, node, 0, node->npages, true); | 846 | unpin_rcv_pages(fd, NULL, node, 0, node->npages, true); |
| | 847 | done: |
| | 848 | mutex_unlock(&node->invalidate_mutex); |
| | 849 | } |
| | 850 | |
| | 851 | static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node) |
| | 852 | { |
| | 853 | struct hfi1_ctxtdata *uctxt = fd->uctxt; |
| | 854 | |
| | 855 | __clear_tid_node(fd, node); |
| 799 | | 856 | |
| 800 | node->grp->used--; | 857 | node->grp->used--; |
| 801 | node->grp->map &= ~(1 << (node->rcventry - node->grp->base)); | 858 | node->grp->map &= ~(1 << (node->rcventry - node->grp->base)); |
| @@ -854,10 +911,16 @@ static bool tid_rb_invalidate(struct mmu_interval_notifier *mni, · 52 unmodified lines |
| 854 | if (node->freed) | 911 | if (node->freed) |
| 855 | return true; | 912 | return true; |
| 856 | | 913 | |
| | 914 | /* take action only if unmapping */ |
| | 915 | if (range->event != MMU_NOTIFY_UNMAP) |
| | 916 | return true; |
| | 917 | |
| 857 | trace_hfi1_exp_tid_inval(uctxt->ctxt, fdata->subctxt, | 918 | trace_hfi1_exp_tid_inval(uctxt->ctxt, fdata->subctxt, |
| 858 | node->notifier.interval_tree.start, | 919 | node->notifier.interval_tree.start, |
| 859 | node->rcventry, node->npages, node->dma_addr); | 920 | node->rcventry, node->npages, node->dma_addr); |
| 860 | node->freed = true; | 921 | |
| | 922 | /* clear the hardware rcvarray entry */ |
| | 923 | __clear_tid_node(fdata, node); |
| 861 | | 924 | |
| 862 | spin_lock(&fdata->invalid_lock); | 925 | spin_lock(&fdata->invalid_lock); |
| 863 | if (fdata->invalid_tid_idx < uctxt->expected_count) { | 926 | if (fdata->invalid_tid_idx < uctxt->expected_count) { |
| @@ -887,6 +950,23 @@ static bool tid_rb_invalidate(struct mmu_interval_notifier *mni, · 23 unmodified lines |
| 887 | return true; | 950 | return true; |
| 888 | } | 951 | } |
| 889 | | 952 | |
| | 953 | static bool tid_cover_invalidate(struct mmu_interval_notifier *mni, |
| | 954 | const struct mmu_notifier_range *range, |
| | 955 | unsigned long cur_seq) |
| | 956 | { |
| | 957 | struct tid_user_buf *tidbuf = |
| | 958 | container_of(mni, struct tid_user_buf, notifier); |
| | 959 | |
| | 960 | /* take action only if unmapping */ |
| | 961 | if (range->event == MMU_NOTIFY_UNMAP) { |
| | 962 | mutex_lock(&tidbuf->cover_mutex); |
| | 963 | mmu_interval_set_seq(mni, cur_seq); |
| | 964 | mutex_unlock(&tidbuf->cover_mutex); |
| | 965 | } |
| | 966 | |
| | 967 | return true; |
| | 968 | } |
| | 969 | |
| 890 | static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, | 970 | static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, |
| 891 | struct tid_rb_node *tnode) | 971 | struct tid_rb_node *tnode) |
| 892 | { | 972 | { |