1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES 3 */ 4 #include "iommufd_private.h" 5 6 void iommufd_viommu_destroy(struct iommufd_object *obj) 7 { 8 struct iommufd_viommu *viommu = 9 container_of(obj, struct iommufd_viommu, obj); 10 11 if (viommu->ops && viommu->ops->destroy) 12 viommu->ops->destroy(viommu); 13 refcount_dec(&viommu->hwpt->common.obj.users); 14 xa_destroy(&viommu->vdevs); 15 } 16 17 int iommufd_viommu_alloc_ioctl(struct iommufd_ucmd *ucmd) 18 { 19 struct iommu_viommu_alloc *cmd = ucmd->cmd; 20 struct iommufd_hwpt_paging *hwpt_paging; 21 struct iommufd_viommu *viommu; 22 struct iommufd_device *idev; 23 const struct iommu_ops *ops; 24 int rc; 25 26 if (cmd->flags || cmd->type == IOMMU_VIOMMU_TYPE_DEFAULT) 27 return -EOPNOTSUPP; 28 29 idev = iommufd_get_device(ucmd, cmd->dev_id); 30 if (IS_ERR(idev)) 31 return PTR_ERR(idev); 32 33 ops = dev_iommu_ops(idev->dev); 34 if (!ops->viommu_alloc) { 35 rc = -EOPNOTSUPP; 36 goto out_put_idev; 37 } 38 39 hwpt_paging = iommufd_get_hwpt_paging(ucmd, cmd->hwpt_id); 40 if (IS_ERR(hwpt_paging)) { 41 rc = PTR_ERR(hwpt_paging); 42 goto out_put_idev; 43 } 44 45 if (!hwpt_paging->nest_parent) { 46 rc = -EINVAL; 47 goto out_put_hwpt; 48 } 49 50 viommu = ops->viommu_alloc(idev->dev, hwpt_paging->common.domain, 51 ucmd->ictx, cmd->type); 52 if (IS_ERR(viommu)) { 53 rc = PTR_ERR(viommu); 54 goto out_put_hwpt; 55 } 56 57 xa_init(&viommu->vdevs); 58 viommu->type = cmd->type; 59 viommu->ictx = ucmd->ictx; 60 viommu->hwpt = hwpt_paging; 61 refcount_inc(&viommu->hwpt->common.obj.users); 62 INIT_LIST_HEAD(&viommu->veventqs); 63 init_rwsem(&viommu->veventqs_rwsem); 64 /* 65 * It is the most likely case that a physical IOMMU is unpluggable. A 66 * pluggable IOMMU instance (if exists) is responsible for refcounting 67 * on its own. 68 */ 69 viommu->iommu_dev = __iommu_get_iommu_dev(idev->dev); 70 71 cmd->out_viommu_id = viommu->obj.id; 72 rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 73 if (rc) 74 goto out_abort; 75 iommufd_object_finalize(ucmd->ictx, &viommu->obj); 76 goto out_put_hwpt; 77 78 out_abort: 79 iommufd_object_abort_and_destroy(ucmd->ictx, &viommu->obj); 80 out_put_hwpt: 81 iommufd_put_object(ucmd->ictx, &hwpt_paging->common.obj); 82 out_put_idev: 83 iommufd_put_object(ucmd->ictx, &idev->obj); 84 return rc; 85 } 86 87 void iommufd_vdevice_destroy(struct iommufd_object *obj) 88 { 89 struct iommufd_vdevice *vdev = 90 container_of(obj, struct iommufd_vdevice, obj); 91 struct iommufd_viommu *viommu = vdev->viommu; 92 93 /* xa_cmpxchg is okay to fail if alloc failed xa_cmpxchg previously */ 94 xa_cmpxchg(&viommu->vdevs, vdev->id, vdev, NULL, GFP_KERNEL); 95 refcount_dec(&viommu->obj.users); 96 put_device(vdev->dev); 97 } 98 99 int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) 100 { 101 struct iommu_vdevice_alloc *cmd = ucmd->cmd; 102 struct iommufd_vdevice *vdev, *curr; 103 struct iommufd_viommu *viommu; 104 struct iommufd_device *idev; 105 u64 virt_id = cmd->virt_id; 106 int rc = 0; 107 108 /* virt_id indexes an xarray */ 109 if (virt_id > ULONG_MAX) 110 return -EINVAL; 111 112 viommu = iommufd_get_viommu(ucmd, cmd->viommu_id); 113 if (IS_ERR(viommu)) 114 return PTR_ERR(viommu); 115 116 idev = iommufd_get_device(ucmd, cmd->dev_id); 117 if (IS_ERR(idev)) { 118 rc = PTR_ERR(idev); 119 goto out_put_viommu; 120 } 121 122 if (viommu->iommu_dev != __iommu_get_iommu_dev(idev->dev)) { 123 rc = -EINVAL; 124 goto out_put_idev; 125 } 126 127 vdev = iommufd_object_alloc(ucmd->ictx, vdev, IOMMUFD_OBJ_VDEVICE); 128 if (IS_ERR(vdev)) { 129 rc = PTR_ERR(vdev); 130 goto out_put_idev; 131 } 132 133 vdev->id = virt_id; 134 vdev->dev = idev->dev; 135 get_device(idev->dev); 136 vdev->viommu = viommu; 137 refcount_inc(&viommu->obj.users); 138 139 curr = xa_cmpxchg(&viommu->vdevs, virt_id, NULL, vdev, GFP_KERNEL); 140 if (curr) { 141 rc = xa_err(curr) ?: -EEXIST; 142 goto out_abort; 143 } 144 145 cmd->out_vdevice_id = vdev->obj.id; 146 rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); 147 if (rc) 148 goto out_abort; 149 iommufd_object_finalize(ucmd->ictx, &vdev->obj); 150 goto out_put_idev; 151 152 out_abort: 153 iommufd_object_abort_and_destroy(ucmd->ictx, &vdev->obj); 154 out_put_idev: 155 iommufd_put_object(ucmd->ictx, &idev->obj); 156 out_put_viommu: 157 iommufd_put_object(ucmd->ictx, &viommu->obj); 158 return rc; 159 } 160