summaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorSuravee Suthikulpanit <suravee.suthikulpanit@amd.com>2018-03-21 14:46:19 -0400
committerAlex Williamson <alex.williamson@redhat.com>2018-03-21 14:46:19 -0400
commit6bd06f5a486c06023a618a86e8153b91d26f75f4 (patch)
tree1a3eb894fd10a6b101644824c79eb91a934d96b2 /drivers/vfio
parentc698ca5278934c0ae32297a8725ced2e27585d7f (diff)
vfio/type1: Adopt fast IOTLB flush interface when unmap IOVAs
VFIO IOMMU type1 currently upmaps IOVA pages synchronously, which requires IOTLB flushing for every unmapping. This results in large IOTLB flushing overhead when handling pass-through devices has a large number of mapped IOVAs. This can be avoided by using the new IOTLB flushing interface. Cc: Alex Williamson <alex.williamson@redhat.com> Cc: Joerg Roedel <joro@8bytes.org> Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> [aw - use LIST_HEAD] Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/vfio_iommu_type1.c126
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
105struct 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
670static 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
702static 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
741static 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
663static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma, 759static 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;