Re: [PATCH v2 13/40] vfio: Add support for Shared Virtual Addressing

Jean-Philippe Brucker
2018-05-21T14:51:29Z

On 17/05/18 16:58, Jonathan Cameron wrote:
>> +static int vfio_iommu_bind_group(struct vfio_iommu *iommu,
>> + struct vfio_group *group,
>> + struct vfio_mm *vfio_mm)
>> +{
>> + int ret;
>> + bool enabled_sva = false;
>> + struct vfio_iommu_sva_bind_data data = {
>> + .vfio_mm = vfio_mm,
>> + .iommu = iommu,
>> + .count = 0,
>> + };
>> +
>> + if (!group->sva_enabled) {
>> + ret = iommu_group_for_each_dev(group->iommu_group, NULL,
>> + vfio_iommu_sva_init);
>> + if (ret)
>> + return ret;
>> +
>> + group->sva_enabled = enabled_sva = true;
>> + }
>> +
>> + ret = iommu_group_for_each_dev(group->iommu_group, &data,
>> + vfio_iommu_sva_bind_dev);
>> + if (ret && data.count > 1)
>
> Are we safe to run this even if data.count == 1? I assume that at
> that point we would always not have an iommu domain associated with the
> device so the initial check would error out.

If data.count == 1, then the first bind didn't succeed. But it's not
necessarily a domain missing, failure to bind may come from various
places. If this vfio_mm was already bound to another device then it
contains a valid PASID and it's safe to call unbind(). Otherwise we call
it with PASID -1 (VFIO_INVALID_PASID) and that's a bit of a grey area.
-1 is currently invalid everywhere, but in the future someone might
implement 32 bits of PASIDs, in which case a bond between this dev and
PASID -1 might exist. But I think it's safe for now, and whoever
redefines VFIO_INVALID_PASID when such implementation appears will also
fix this case.

> Just be nice to get rid of the special casing in this error path as then
> could just do it all under if (ret) and make it visually clearer these
> are different aspects of the same error path.

[...]
>> static long vfio_iommu_type1_ioctl(void *iommu_data,
>> unsigned int cmd, unsigned long arg)
>> {
>> @@ -1728,6 +2097,44 @@ static long vfio_iommu_type1_ioctl(void *iommu_data,
>>
>> return copy_to_user((void __user *)arg, &unmap, minsz) ?
>> -EFAULT : 0;
>> +
>> + } else if (cmd == VFIO_IOMMU_BIND) {
>> + struct vfio_iommu_type1_bind bind;
>> +
>> + minsz = offsetofend(struct vfio_iommu_type1_bind, flags);
>> +
>> + if (copy_from_user(&bind, (void __user *)arg, minsz))
>> + return -EFAULT;
>> +
>> + if (bind.argsz < minsz)
>> + return -EINVAL;
>> +
>> + switch (bind.flags) {
>> + case VFIO_IOMMU_BIND_PROCESS:
>> + return vfio_iommu_type1_bind_process(iommu, (void *)arg,
>> + &bind);
>
> Can we combine these two blocks given it is only this case statement that is different?

That would be nicer, though I don't know yet what's needed for vSVA (by
Yi Liu on Cc), which will add statements to the switches.

Thanks,
Jean

>
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + } else if (cmd == VFIO_IOMMU_UNBIND) {
>> + struct vfio_iommu_type1_bind bind;
>> +
>> + minsz = offsetofend(struct vfio_iommu_type1_bind, flags);
>> +
>> + if (copy_from_user(&bind, (void __user *)arg, minsz))
>> + return -EFAULT;
>> +
>> + if (bind.argsz < minsz)
>> + return -EINVAL;
>> +
>> + switch (bind.flags) {
>> + case VFIO_IOMMU_BIND_PROCESS:
>> + return vfio_iommu_type1_unbind_process(iommu, (void *)arg,
>> + &bind);
>> + default:
>> + return -EINVAL;
>> + }
>> }