summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;