static int tce_iommu_attach_group()

in vfio_iommu_spapr_tce.c [1241:1326]


static int tce_iommu_attach_group(void *iommu_data,
		struct iommu_group *iommu_group, enum vfio_group_type type)
{
	int ret = 0;
	struct tce_container *container = iommu_data;
	struct iommu_table_group *table_group;
	struct tce_iommu_group *tcegrp = NULL;

	if (type == VFIO_EMULATED_IOMMU)
		return -EINVAL;

	mutex_lock(&container->lock);

	/* pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
			iommu_group_id(iommu_group), iommu_group); */
	table_group = iommu_group_get_iommudata(iommu_group);
	if (!table_group) {
		ret = -ENODEV;
		goto unlock_exit;
	}

	if (tce_groups_attached(container) && (!table_group->ops ||
			!table_group->ops->take_ownership ||
			!table_group->ops->release_ownership)) {
		ret = -EBUSY;
		goto unlock_exit;
	}

	/* Check if new group has the same iommu_ops (i.e. compatible) */
	list_for_each_entry(tcegrp, &container->group_list, next) {
		struct iommu_table_group *table_group_tmp;

		if (tcegrp->grp == iommu_group) {
			pr_warn("tce_vfio: Group %d is already attached\n",
					iommu_group_id(iommu_group));
			ret = -EBUSY;
			goto unlock_exit;
		}
		table_group_tmp = iommu_group_get_iommudata(tcegrp->grp);
		if (table_group_tmp->ops->create_table !=
				table_group->ops->create_table) {
			pr_warn("tce_vfio: Group %d is incompatible with group %d\n",
					iommu_group_id(iommu_group),
					iommu_group_id(tcegrp->grp));
			ret = -EPERM;
			goto unlock_exit;
		}
	}

	tcegrp = kzalloc(sizeof(*tcegrp), GFP_KERNEL);
	if (!tcegrp) {
		ret = -ENOMEM;
		goto unlock_exit;
	}

	if (!table_group->ops || !table_group->ops->take_ownership ||
			!table_group->ops->release_ownership) {
		if (container->v2) {
			ret = -EPERM;
			goto free_exit;
		}
		ret = tce_iommu_take_ownership(container, table_group);
	} else {
		if (!container->v2) {
			ret = -EPERM;
			goto free_exit;
		}
		ret = tce_iommu_take_ownership_ddw(container, table_group);
		if (!tce_groups_attached(container) && !container->tables[0])
			container->def_window_pending = true;
	}

	if (!ret) {
		tcegrp->grp = iommu_group;
		list_add(&tcegrp->next, &container->group_list);
	}

free_exit:
	if (ret && tcegrp)
		kfree(tcegrp);

unlock_exit:
	mutex_unlock(&container->lock);

	return ret;
}