iommufd: Add IOMMU_HWPT_ALLOC
authorJason Gunthorpe <jgg@nvidia.com>
Mon, 17 Jul 2023 18:12:13 +0000 (15:12 -0300)
committerJason Gunthorpe <jgg@nvidia.com>
Wed, 26 Jul 2023 13:20:31 +0000 (10:20 -0300)
This allows userspace to manually create HWPTs on IOAS's and then use
those HWPTs as inputs to iommufd_device_attach/replace().

Following series will extend this to allow creating iommu_domains with
driver specific parameters.

Link: https://lore.kernel.org/r/17-v8-6659224517ea+532-iommufd_alloc_jgg@nvidia.com
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/iommufd/hw_pagetable.c
drivers/iommu/iommufd/iommufd_private.h
drivers/iommu/iommufd/main.c
include/uapi/linux/iommufd.h

index 2087b51..cf2c150 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
  */
 #include <linux/iommu.h>
+#include <uapi/linux/iommufd.h>
 
 #include "iommufd_private.h"
 
@@ -131,3 +132,48 @@ out_abort:
        iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
        return ERR_PTR(rc);
 }
+
+int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
+{
+       struct iommu_hwpt_alloc *cmd = ucmd->cmd;
+       struct iommufd_hw_pagetable *hwpt;
+       struct iommufd_device *idev;
+       struct iommufd_ioas *ioas;
+       int rc;
+
+       if (cmd->flags || cmd->__reserved)
+               return -EOPNOTSUPP;
+
+       idev = iommufd_get_device(ucmd, cmd->dev_id);
+       if (IS_ERR(idev))
+               return PTR_ERR(idev);
+
+       ioas = iommufd_get_ioas(ucmd->ictx, cmd->pt_id);
+       if (IS_ERR(ioas)) {
+               rc = PTR_ERR(ioas);
+               goto out_put_idev;
+       }
+
+       mutex_lock(&ioas->mutex);
+       hwpt = iommufd_hw_pagetable_alloc(ucmd->ictx, ioas, idev, false);
+       if (IS_ERR(hwpt)) {
+               rc = PTR_ERR(hwpt);
+               goto out_unlock;
+       }
+
+       cmd->out_hwpt_id = hwpt->obj.id;
+       rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+       if (rc)
+               goto out_hwpt;
+       iommufd_object_finalize(ucmd->ictx, &hwpt->obj);
+       goto out_unlock;
+
+out_hwpt:
+       iommufd_object_abort_and_destroy(ucmd->ictx, &hwpt->obj);
+out_unlock:
+       mutex_unlock(&ioas->mutex);
+       iommufd_put_object(&ioas->obj);
+out_put_idev:
+       iommufd_put_object(&idev->obj);
+       return rc;
+}
index 9394f0e..dba7301 100644 (file)
@@ -262,6 +262,7 @@ struct iommufd_hw_pagetable *
 iommufd_hw_pagetable_detach(struct iommufd_device *idev);
 void iommufd_hw_pagetable_destroy(struct iommufd_object *obj);
 void iommufd_hw_pagetable_abort(struct iommufd_object *obj);
+int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd);
 
 static inline void iommufd_hw_pagetable_put(struct iommufd_ctx *ictx,
                                            struct iommufd_hw_pagetable *hwpt)
@@ -298,6 +299,14 @@ struct iommufd_device {
        bool enforce_cache_coherency;
 };
 
+static inline struct iommufd_device *
+iommufd_get_device(struct iommufd_ucmd *ucmd, u32 id)
+{
+       return container_of(iommufd_get_object(ucmd->ictx, id,
+                                              IOMMUFD_OBJ_DEVICE),
+                           struct iommufd_device, obj);
+}
+
 void iommufd_device_destroy(struct iommufd_object *obj);
 
 struct iommufd_access {
index 56cdf3c..94c498b 100644 (file)
@@ -265,6 +265,7 @@ static int iommufd_option(struct iommufd_ucmd *ucmd)
 
 union ucmd_buffer {
        struct iommu_destroy destroy;
+       struct iommu_hwpt_alloc hwpt;
        struct iommu_ioas_alloc alloc;
        struct iommu_ioas_allow_iovas allow_iovas;
        struct iommu_ioas_copy ioas_copy;
@@ -296,6 +297,8 @@ struct iommufd_ioctl_op {
        }
 static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
        IOCTL_OP(IOMMU_DESTROY, iommufd_destroy, struct iommu_destroy, id),
+       IOCTL_OP(IOMMU_HWPT_ALLOC, iommufd_hwpt_alloc, struct iommu_hwpt_alloc,
+                __reserved),
        IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl,
                 struct iommu_ioas_alloc, out_ioas_id),
        IOCTL_OP(IOMMU_IOAS_ALLOW_IOVAS, iommufd_ioas_allow_iovas,
index 98ebba8..8245c01 100644 (file)
@@ -45,6 +45,7 @@ enum {
        IOMMUFD_CMD_IOAS_UNMAP,
        IOMMUFD_CMD_OPTION,
        IOMMUFD_CMD_VFIO_IOAS,
+       IOMMUFD_CMD_HWPT_ALLOC,
 };
 
 /**
@@ -344,4 +345,29 @@ struct iommu_vfio_ioas {
        __u16 __reserved;
 };
 #define IOMMU_VFIO_IOAS _IO(IOMMUFD_TYPE, IOMMUFD_CMD_VFIO_IOAS)
+
+/**
+ * struct iommu_hwpt_alloc - ioctl(IOMMU_HWPT_ALLOC)
+ * @size: sizeof(struct iommu_hwpt_alloc)
+ * @flags: Must be 0
+ * @dev_id: The device to allocate this HWPT for
+ * @pt_id: The IOAS to connect this HWPT to
+ * @out_hwpt_id: The ID of the new HWPT
+ * @__reserved: Must be 0
+ *
+ * Explicitly allocate a hardware page table object. This is the same object
+ * type that is returned by iommufd_device_attach() and represents the
+ * underlying iommu driver's iommu_domain kernel object.
+ *
+ * A HWPT will be created with the IOVA mappings from the given IOAS.
+ */
+struct iommu_hwpt_alloc {
+       __u32 size;
+       __u32 flags;
+       __u32 dev_id;
+       __u32 pt_id;
+       __u32 out_hwpt_id;
+       __u32 __reserved;
+};
+#define IOMMU_HWPT_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HWPT_ALLOC)
 #endif