@@ -970,7 +970,8 @@ static long verify_bitmap_size(unsigned long npages, unsigned long bitmap_size)
}
static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
- struct vfio_iommu_type1_dma_unmap *unmap)
+ struct vfio_iommu_type1_dma_unmap *unmap,
+ unsigned long *bitmap)
{
uint64_t mask;
struct vfio_dma *dma, *dma_last = NULL;
@@ -1045,6 +1046,15 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
if (dma->task->mm != current->mm)
break;
+ if ((unmap->flags & VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP) &&
+ (dma_last != dma))
+ vfio_iova_dirty_bitmap(iommu, dma->iova, dma->size,
+ unmap->bitmap_pgsize, unmap->iova,
+ bitmap);
+ else
+ vfio_remove_unpinned_from_pfn_list(dma, true);
+
+
if (!RB_EMPTY_ROOT(&dma->pfn_list)) {
struct vfio_iommu_type1_dma_unmap nb_unmap;
@@ -1070,6 +1080,7 @@ static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
&nb_unmap);
goto again;
}
+
unmapped += dma->size;
vfio_remove_dma(iommu, dma);
}
@@ -2401,22 +2412,60 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
} else if (cmd == VFIO_IOMMU_UNMAP_DMA) {
struct vfio_iommu_type1_dma_unmap unmap;
- long ret;
+ unsigned long *bitmap = NULL;
+ long ret, bsize;
minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size);
- if (copy_from_user(&unmap, (void __user *)arg, minsz))
+ if (copy_from_user(&unmap, (void __user *)arg, sizeof(unmap)))
return -EFAULT;
- if (unmap.argsz < minsz || unmap.flags)
+ if (unmap.argsz < minsz ||
+ unmap.flags & ~VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP)
return -EINVAL;
- ret = vfio_dma_do_unmap(iommu, &unmap);
+ if (unmap.flags & VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP) {
+ unsigned long pgshift = __ffs(unmap.bitmap_pgsize);
+ uint64_t iommu_pgmask =
+ ((uint64_t)1 << __ffs(vfio_pgsize_bitmap(iommu))) - 1;
+
+ if (((unmap.bitmap_pgsize - 1) & iommu_pgmask) !=
+ (unmap.bitmap_pgsize - 1))
+ return -EINVAL;
+
+ bsize = verify_bitmap_size(unmap.size >> pgshift,
+ unmap.bitmap_size);
+ if (bsize < 0)
+ return bsize;
+
+ bitmap = kmalloc(bsize, GFP_KERNEL);
+ if (!bitmap)
+ return -ENOMEM;
+
+ if (copy_from_user(bitmap, (void __user *)unmap.bitmap,
+ bsize)) {
+ ret = -EFAULT;
+ goto unmap_exit;
+ }
+ }
+
+ ret = vfio_dma_do_unmap(iommu, &unmap, bitmap);
if (ret)
- return ret;
+ goto unmap_exit;
- return copy_to_user((void __user *)arg, &unmap, minsz) ?
+ if (unmap.flags & VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP) {
+ if (copy_to_user((void __user *)unmap.bitmap, bitmap,
+ bsize)) {
+ ret = -EFAULT;
+ goto unmap_exit;
+ }
+ }
+
+ ret = copy_to_user((void __user *)arg, &unmap, minsz) ?
-EFAULT : 0;
+unmap_exit:
+ kfree(bitmap);
+ return ret;
} else if (cmd == VFIO_IOMMU_DIRTY_PAGES) {
struct vfio_iommu_type1_dirty_bitmap range;
uint32_t mask = VFIO_IOMMU_DIRTY_PAGES_FLAG_START |
@@ -958,12 +958,24 @@ struct vfio_iommu_type1_dma_map {
* field. No guarantee is made to the user that arbitrary unmaps of iova
* or size different from those used in the original mapping call will
* succeed.
+ * VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP should be set to get dirty bitmap
+ * before unmapping IO virtual addresses. When this flag is set, user should
+ * allocate memory to get bitmap, clear the bitmap memory by setting zero and
+ * should set size of allocated memory in bitmap_size field. One bit in bitmap
+ * represents per page , page of user provided page size in 'bitmap_pgsize',
+ * consecutively starting from iova offset. Bit set indicates page at that
+ * offset from iova is dirty. Bitmap of pages in the range of unmapped size is
+ * returned in bitmap.
*/
struct vfio_iommu_type1_dma_unmap {
__u32 argsz;
__u32 flags;
+#define VFIO_DMA_UNMAP_FLAG_GET_DIRTY_BITMAP (1 << 0)
__u64 iova; /* IO virtual address */
__u64 size; /* Size of mapping (bytes) */
+ __u64 bitmap_pgsize; /* page size for bitmap */
+ __u64 bitmap_size; /* in bytes */
+ void __user *bitmap; /* one bit per page */
};
#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)