[RFC PATCH 3/3] vfio/type1: Introduce pfn_list mutex

From: Alex Williamson
Date: Thu Jan 16 2020 - 13:18:24 EST


We can promote external page {un}pinning to a reader lock, allowing
concurrency since these don't change the vfio_iommu state. We do need
to protect the vpfn list per vfio_dma in place of that serialization
though.

Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
---
drivers/vfio/vfio_iommu_type1.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index e78067cc74b3..ea63306c16f7 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -90,6 +90,7 @@ struct vfio_dma {
bool iommu_mapped;
bool lock_cap; /* capable(CAP_IPC_LOCK) */
struct task_struct *task;
+ struct mutex pfn_list_lock;
struct rb_root pfn_list; /* Ex-user pinned pfn list */
};

@@ -539,7 +540,7 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,
if (!iommu->v2)
return -EACCES;

- down_write(&iommu->lock);
+ down_read(&iommu->lock);

/* Fail if notifier list is empty */
if (!iommu->notifier.head) {
@@ -570,8 +571,11 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,
goto pin_unwind;
}

+ mutex_lock(&dma->pfn_list_lock);
+
vpfn = vfio_iova_get_vfio_pfn(dma, iova);
if (vpfn) {
+ mutex_unlock(&dma->pfn_list_lock);
phys_pfn[i] = vpfn->pfn;
continue;
}
@@ -579,14 +583,19 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,
remote_vaddr = dma->vaddr + iova - dma->iova;
ret = vfio_pin_page_external(dma, remote_vaddr, &phys_pfn[i],
do_accounting);
- if (ret)
+ if (ret) {
+ mutex_unlock(&dma->pfn_list_lock);
goto pin_unwind;
+ }

ret = vfio_add_to_pfn_list(dma, iova, phys_pfn[i]);
if (ret) {
vfio_unpin_page_external(dma, iova, do_accounting);
+ mutex_unlock(&dma->pfn_list_lock);
goto pin_unwind;
}
+
+ mutex_unlock(&dma->pfn_list_lock);
}

ret = i;
@@ -599,11 +608,13 @@ static int vfio_iommu_type1_pin_pages(void *iommu_data,

iova = user_pfn[j] << PAGE_SHIFT;
dma = vfio_find_dma(iommu, iova, PAGE_SIZE);
+ mutex_lock(&dma->pfn_list_lock);
vfio_unpin_page_external(dma, iova, do_accounting);
+ mutex_unlock(&dma->pfn_list_lock);
phys_pfn[j] = 0;
}
pin_done:
- up_write(&iommu->lock);
+ up_read(&iommu->lock);
return ret;
}

@@ -622,7 +633,7 @@ static int vfio_iommu_type1_unpin_pages(void *iommu_data,
if (!iommu->v2)
return -EACCES;

- down_write(&iommu->lock);
+ down_read(&iommu->lock);

do_accounting = !IS_IOMMU_CAP_DOMAIN_IN_CONTAINER(iommu);
for (i = 0; i < npage; i++) {
@@ -633,11 +644,13 @@ static int vfio_iommu_type1_unpin_pages(void *iommu_data,
dma = vfio_find_dma(iommu, iova, PAGE_SIZE);
if (!dma)
goto unpin_exit;
+ mutex_lock(&dma->pfn_list_lock);
vfio_unpin_page_external(dma, iova, do_accounting);
+ mutex_unlock(&dma->pfn_list_lock);
}

unpin_exit:
- up_write(&iommu->lock);
+ up_read(&iommu->lock);
return i > npage ? npage : (i > 0 ? i : -EINVAL);
}

@@ -1109,6 +1122,7 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
dma->iova = iova;
dma->vaddr = vaddr;
dma->prot = prot;
+ mutex_init(&dma->pfn_list_lock);

/*
* We need to be able to both add to a task's locked memory and test