static long tce_iommu_build_v2()

in vfio_iommu_spapr_tce.c [548:605]


static long tce_iommu_build_v2(struct tce_container *container,
		struct iommu_table *tbl,
		unsigned long entry, unsigned long tce, unsigned long pages,
		enum dma_data_direction direction)
{
	long i, ret = 0;
	unsigned long hpa;
	enum dma_data_direction dirtmp;

	for (i = 0; i < pages; ++i) {
		struct mm_iommu_table_group_mem_t *mem = NULL;
		__be64 *pua = IOMMU_TABLE_USERSPACE_ENTRY(tbl, entry + i);

		ret = tce_iommu_prereg_ua_to_hpa(container,
				tce, tbl->it_page_shift, &hpa, &mem);
		if (ret)
			break;

		if (!tce_page_is_contained(container->mm, hpa,
				tbl->it_page_shift)) {
			ret = -EPERM;
			break;
		}

		/* Preserve offset within IOMMU page */
		hpa |= tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
		dirtmp = direction;

		/* The registered region is being unregistered */
		if (mm_iommu_mapped_inc(mem))
			break;

		ret = iommu_tce_xchg_no_kill(container->mm, tbl, entry + i,
				&hpa, &dirtmp);
		if (ret) {
			/* dirtmp cannot be DMA_NONE here */
			tce_iommu_unuse_page_v2(container, tbl, entry + i);
			pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n",
					__func__, entry << tbl->it_page_shift,
					tce, ret);
			break;
		}

		if (dirtmp != DMA_NONE)
			tce_iommu_unuse_page_v2(container, tbl, entry + i);

		*pua = cpu_to_be64(tce);

		tce += IOMMU_PAGE_SIZE(tbl);
	}

	if (ret)
		tce_iommu_clear(container, tbl, entry, i);
	else
		iommu_tce_kill(tbl, entry, pages);

	return ret;
}