in vfio_iommu_type1.c [829:949]
static int vfio_iommu_type1_pin_pages(void *iommu_data,
struct iommu_group *iommu_group,
unsigned long *user_pfn,
int npage, int prot,
unsigned long *phys_pfn)
{
struct vfio_iommu *iommu = iommu_data;
struct vfio_iommu_group *group;
int i, j, ret;
unsigned long remote_vaddr;
struct vfio_dma *dma;
bool do_accounting;
dma_addr_t iova;
if (!iommu || !user_pfn || !phys_pfn)
return -EINVAL;
/* Supported for v2 version only */
if (!iommu->v2)
return -EACCES;
mutex_lock(&iommu->lock);
/*
* Wait for all necessary vaddr's to be valid so they can be used in
* the main loop without dropping the lock, to avoid racing vs unmap.
*/
again:
if (iommu->vaddr_invalid_count) {
for (i = 0; i < npage; i++) {
iova = user_pfn[i] << PAGE_SHIFT;
ret = vfio_find_dma_valid(iommu, iova, PAGE_SIZE, &dma);
if (ret < 0)
goto pin_done;
if (ret == WAITED)
goto again;
}
}
/* Fail if notifier list is empty */
if (!iommu->notifier.head) {
ret = -EINVAL;
goto pin_done;
}
/*
* If iommu capable domain exist in the container then all pages are
* already pinned and accounted. Accounting should be done if there is no
* iommu capable domain in the container.
*/
do_accounting = list_empty(&iommu->domain_list);
for (i = 0; i < npage; i++) {
struct vfio_pfn *vpfn;
iova = user_pfn[i] << PAGE_SHIFT;
dma = vfio_find_dma(iommu, iova, PAGE_SIZE);
if (!dma) {
ret = -EINVAL;
goto pin_unwind;
}
if ((dma->prot & prot) != prot) {
ret = -EPERM;
goto pin_unwind;
}
vpfn = vfio_iova_get_vfio_pfn(dma, iova);
if (vpfn) {
phys_pfn[i] = vpfn->pfn;
continue;
}
remote_vaddr = dma->vaddr + (iova - dma->iova);
ret = vfio_pin_page_external(dma, remote_vaddr, &phys_pfn[i],
do_accounting);
if (ret)
goto pin_unwind;
ret = vfio_add_to_pfn_list(dma, iova, phys_pfn[i]);
if (ret) {
if (put_pfn(phys_pfn[i], dma->prot) && do_accounting)
vfio_lock_acct(dma, -1, true);
goto pin_unwind;
}
if (iommu->dirty_page_tracking) {
unsigned long pgshift = __ffs(iommu->pgsize_bitmap);
/*
* Bitmap populated with the smallest supported page
* size
*/
bitmap_set(dma->bitmap,
(iova - dma->iova) >> pgshift, 1);
}
}
ret = i;
group = vfio_iommu_find_iommu_group(iommu, iommu_group);
if (!group->pinned_page_dirty_scope) {
group->pinned_page_dirty_scope = true;
iommu->num_non_pinned_groups--;
}
goto pin_done;
pin_unwind:
phys_pfn[i] = 0;
for (j = 0; j < i; j++) {
dma_addr_t iova;
iova = user_pfn[j] << PAGE_SHIFT;
dma = vfio_find_dma(iommu, iova, PAGE_SIZE);
vfio_unpin_page_external(dma, iova, do_accounting);
phys_pfn[j] = 0;
}
pin_done:
mutex_unlock(&iommu->lock);
return ret;
}