static int vfio_iommu_type1_dma_rw_chunk()

in vfio_iommu_type1.c [3027:3087]


static int vfio_iommu_type1_dma_rw_chunk(struct vfio_iommu *iommu,
					 dma_addr_t user_iova, void *data,
					 size_t count, bool write,
					 size_t *copied)
{
	struct mm_struct *mm;
	unsigned long vaddr;
	struct vfio_dma *dma;
	bool kthread = current->mm == NULL;
	size_t offset;
	int ret;

	*copied = 0;

	ret = vfio_find_dma_valid(iommu, user_iova, 1, &dma);
	if (ret < 0)
		return ret;

	if ((write && !(dma->prot & IOMMU_WRITE)) ||
			!(dma->prot & IOMMU_READ))
		return -EPERM;

	mm = get_task_mm(dma->task);

	if (!mm)
		return -EPERM;

	if (kthread)
		kthread_use_mm(mm);
	else if (current->mm != mm)
		goto out;

	offset = user_iova - dma->iova;

	if (count > dma->size - offset)
		count = dma->size - offset;

	vaddr = dma->vaddr + offset;

	if (write) {
		*copied = copy_to_user((void __user *)vaddr, data,
					 count) ? 0 : count;
		if (*copied && iommu->dirty_page_tracking) {
			unsigned long pgshift = __ffs(iommu->pgsize_bitmap);
			/*
			 * Bitmap populated with the smallest supported page
			 * size
			 */
			bitmap_set(dma->bitmap, offset >> pgshift,
				   ((offset + *copied - 1) >> pgshift) -
				   (offset >> pgshift) + 1);
		}
	} else
		*copied = copy_from_user(data, (void __user *)vaddr,
					   count) ? 0 : count;
	if (kthread)
		kthread_unuse_mm(mm);
out:
	mmput(mm);
	return *copied ? 0 : -EFAULT;
}