static long tce_iommu_create_window()

in vfio_iommu_spapr_tce.c [644:707]


static long tce_iommu_create_window(struct tce_container *container,
		__u32 page_shift, __u64 window_size, __u32 levels,
		__u64 *start_addr)
{
	struct tce_iommu_group *tcegrp;
	struct iommu_table_group *table_group;
	struct iommu_table *tbl = NULL;
	long ret, num;

	num = tce_iommu_find_free_table(container);
	if (num < 0)
		return num;

	/* Get the first group for ops::create_table */
	tcegrp = list_first_entry(&container->group_list,
			struct tce_iommu_group, next);
	table_group = iommu_group_get_iommudata(tcegrp->grp);
	if (!table_group)
		return -EFAULT;

	if (!(table_group->pgsizes & (1ULL << page_shift)))
		return -EINVAL;

	if (!table_group->ops->set_window || !table_group->ops->unset_window ||
			!table_group->ops->get_table_size ||
			!table_group->ops->create_table)
		return -EPERM;

	/* Create TCE table */
	ret = tce_iommu_create_table(container, table_group, num,
			page_shift, window_size, levels, &tbl);
	if (ret)
		return ret;

	BUG_ON(!tbl->it_ops->free);

	/*
	 * Program the table to every group.
	 * Groups have been tested for compatibility at the attach time.
	 */
	list_for_each_entry(tcegrp, &container->group_list, next) {
		table_group = iommu_group_get_iommudata(tcegrp->grp);

		ret = table_group->ops->set_window(table_group, num, tbl);
		if (ret)
			goto unset_exit;
	}

	container->tables[num] = tbl;

	/* Return start address assigned by platform in create_table() */
	*start_addr = tbl->it_offset << tbl->it_page_shift;

	return 0;

unset_exit:
	list_for_each_entry(tcegrp, &container->group_list, next) {
		table_group = iommu_group_get_iommudata(tcegrp->grp);
		table_group->ops->unset_window(table_group, num);
	}
	tce_iommu_free_table(container, tbl);

	return ret;
}