diff options
-rw-r--r-- | drivers/vfio/vfio_iommu_type1.c | 126 |
1 files changed, 115 insertions, 11 deletions
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 45657e2b1ff7..3c082451ab1a 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c | |||
@@ -102,6 +102,13 @@ struct vfio_pfn { | |||
102 | atomic_t ref_count; | 102 | atomic_t ref_count; |
103 | }; | 103 | }; |
104 | 104 | ||
105 | struct vfio_regions { | ||
106 | struct list_head list; | ||
107 | dma_addr_t iova; | ||
108 | phys_addr_t phys; | ||
109 | size_t len; | ||
110 | }; | ||
111 | |||
105 | #define IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu) \ | 112 | #define IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu) \ |
106 | (!list_empty(&iommu->domain_list)) | 113 | (!list_empty(&iommu->domain_list)) |
107 | 114 | ||
@@ -660,11 +667,102 @@ unpin_exit: | |||
660 | return i > npage ? npage : (i > 0 ? i : -EINVAL); | 667 | return i > npage ? npage : (i > 0 ? i : -EINVAL); |
661 | } | 668 | } |
662 | 669 | ||
670 | static long vfio_sync_unpin(struct vfio_dma *dma, struct vfio_domain *domain, | ||
671 | struct list_head *regions) | ||
672 | { | ||
673 | long unlocked = 0; | ||
674 | struct vfio_regions *entry, *next; | ||
675 | |||
676 | iommu_tlb_sync(domain->domain); | ||
677 | |||
678 | list_for_each_entry_safe(entry, next, regions, list) { | ||
679 | unlocked += vfio_unpin_pages_remote(dma, | ||
680 | entry->iova, | ||
681 | entry->phys >> PAGE_SHIFT, | ||
682 | entry->len >> PAGE_SHIFT, | ||
683 | false); | ||
684 | list_del(&entry->list); | ||
685 | kfree(entry); | ||
686 | } | ||
687 | |||
688 | cond_resched(); | ||
689 | |||
690 | return unlocked; | ||
691 | } | ||
692 | |||
693 | /* | ||
694 | * Generally, VFIO needs to unpin remote pages after each IOTLB flush. | ||
695 | * Therefore, when using IOTLB flush sync interface, VFIO need to keep track | ||
696 | * of these regions (currently using a list). | ||
697 | * | ||
698 | * This value specifies maximum number of regions for each IOTLB flush sync. | ||
699 | */ | ||
700 | #define VFIO_IOMMU_TLB_SYNC_MAX 512 | ||
701 | |||
702 | static size_t unmap_unpin_fast(struct vfio_domain *domain, | ||
703 | struct vfio_dma *dma, dma_addr_t *iova, | ||
704 | size_t len, phys_addr_t phys, long *unlocked, | ||
705 | struct list_head *unmapped_list, | ||
706 | int *unmapped_cnt) | ||
707 | { | ||
708 | size_t unmapped = 0; | ||
709 | struct vfio_regions *entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
710 | |||
711 | if (entry) { | ||
712 | unmapped = iommu_unmap_fast(domain->domain, *iova, len); | ||
713 | |||
714 | if (!unmapped) { | ||
715 | kfree(entry); | ||
716 | } else { | ||
717 | iommu_tlb_range_add(domain->domain, *iova, unmapped); | ||
718 | entry->iova = *iova; | ||
719 | entry->phys = phys; | ||
720 | entry->len = unmapped; | ||
721 | list_add_tail(&entry->list, unmapped_list); | ||
722 | |||
723 | *iova += unmapped; | ||
724 | (*unmapped_cnt)++; | ||
725 | } | ||
726 | } | ||
727 | |||
728 | /* | ||
729 | * Sync if the number of fast-unmap regions hits the limit | ||
730 | * or in case of errors. | ||
731 | */ | ||
732 | if (*unmapped_cnt >= VFIO_IOMMU_TLB_SYNC_MAX || !unmapped) { | ||
733 | *unlocked += vfio_sync_unpin(dma, domain, | ||
734 | unmapped_list); | ||
735 | *unmapped_cnt = 0; | ||
736 | } | ||
737 | |||
738 | return unmapped; | ||
739 | } | ||
740 | |||
741 | static size_t unmap_unpin_slow(struct vfio_domain *domain, | ||
742 | struct vfio_dma *dma, dma_addr_t *iova, | ||
743 | size_t len, phys_addr_t phys, | ||
744 | long *unlocked) | ||
745 | { | ||
746 | size_t unmapped = iommu_unmap(domain->domain, *iova, len); | ||
747 | |||
748 | if (unmapped) { | ||
749 | *unlocked += vfio_unpin_pages_remote(dma, *iova, | ||
750 | phys >> PAGE_SHIFT, | ||
751 | unmapped >> PAGE_SHIFT, | ||
752 | false); | ||
753 | *iova += unmapped; | ||
754 | cond_resched(); | ||
755 | } | ||
756 | return unmapped; | ||
757 | } | ||
758 | |||
663 | static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, | 759 | static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, |
664 | bool do_accounting) | 760 | bool do_accounting) |
665 | { | 761 | { |
666 | dma_addr_t iova = dma->iova, end = dma->iova + dma->size; | 762 | dma_addr_t iova = dma->iova, end = dma->iova + dma->size; |
667 | struct vfio_domain *domain, *d; | 763 | struct vfio_domain *domain, *d; |
764 | LIST_HEAD(unmapped_region_list); | ||
765 | int unmapped_region_cnt = 0; | ||
668 | long unlocked = 0; | 766 | long unlocked = 0; |
669 | 767 | ||
670 | if (!dma->size) | 768 | if (!dma->size) |
@@ -710,20 +808,26 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, | |||
710 | break; | 808 | break; |
711 | } | 809 | } |
712 | 810 | ||
713 | unmapped = iommu_unmap(domain->domain, iova, len); | 811 | /* |
714 | if (WARN_ON(!unmapped)) | 812 | * First, try to use fast unmap/unpin. In case of failure, |
715 | break; | 813 | * switch to slow unmap/unpin path. |
716 | 814 | */ | |
717 | unlocked += vfio_unpin_pages_remote(dma, iova, | 815 | unmapped = unmap_unpin_fast(domain, dma, &iova, len, phys, |
718 | phys >> PAGE_SHIFT, | 816 | &unlocked, &unmapped_region_list, |
719 | unmapped >> PAGE_SHIFT, | 817 | &unmapped_region_cnt); |
720 | false); | 818 | if (!unmapped) { |
721 | iova += unmapped; | 819 | unmapped = unmap_unpin_slow(domain, dma, &iova, len, |
722 | 820 | phys, &unlocked); | |
723 | cond_resched(); | 821 | if (WARN_ON(!unmapped)) |
822 | break; | ||
823 | } | ||
724 | } | 824 | } |
725 | 825 | ||
726 | dma->iommu_mapped = false; | 826 | dma->iommu_mapped = false; |
827 | |||
828 | if (unmapped_region_cnt) | ||
829 | unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list); | ||
830 | |||
727 | if (do_accounting) { | 831 | if (do_accounting) { |
728 | vfio_lock_acct(dma->task, -unlocked, NULL); | 832 | vfio_lock_acct(dma->task, -unlocked, NULL); |
729 | return 0; | 833 | return 0; |