]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
iommufd/selftest: Add IOPF support for mock device
authorLu Baolu <baolu.lu@linux.intel.com>
Tue, 2 Jul 2024 06:34:43 +0000 (14:34 +0800)
committerJason Gunthorpe <jgg@nvidia.com>
Tue, 9 Jul 2024 16:54:32 +0000 (13:54 -0300)
Extend the selftest mock device to support generating and responding to
an IOPF. Also add an ioctl interface to userspace applications to trigger
the IOPF on the mock device. This would allow userspace applications to
test the IOMMUFD's handling of IOPFs without having to rely on any real
hardware.

Link: https://lore.kernel.org/r/20240702063444.105814-10-baolu.lu@linux.intel.com
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/iommu/iommufd/iommufd_test.h
drivers/iommu/iommufd/selftest.c

index e854d3f672051b5223e0fec8af741abf03bbffbd..acbbba1c66716e9fb67273436d8f39967b127bfa 100644 (file)
@@ -22,6 +22,7 @@ enum {
        IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS,
        IOMMU_TEST_OP_DIRTY,
        IOMMU_TEST_OP_MD_CHECK_IOTLB,
+       IOMMU_TEST_OP_TRIGGER_IOPF,
 };
 
 enum {
@@ -127,6 +128,13 @@ struct iommu_test_cmd {
                        __u32 id;
                        __u32 iotlb;
                } check_iotlb;
+               struct {
+                       __u32 dev_id;
+                       __u32 pasid;
+                       __u32 grpid;
+                       __u32 perm;
+                       __u64 addr;
+               } trigger_iopf;
        };
        __u32 last;
 };
index 7a2199470f3121da91e060bca82315a6944e37b8..1133f1b2362fdfe63c35090d204d426d08ec3fd4 100644 (file)
@@ -504,6 +504,8 @@ static bool mock_domain_capable(struct device *dev, enum iommu_cap cap)
        return false;
 }
 
+static struct iopf_queue *mock_iommu_iopf_queue;
+
 static struct iommu_device mock_iommu_device = {
 };
 
@@ -514,6 +516,29 @@ static struct iommu_device *mock_probe_device(struct device *dev)
        return &mock_iommu_device;
 }
 
+static void mock_domain_page_response(struct device *dev, struct iopf_fault *evt,
+                                     struct iommu_page_response *msg)
+{
+}
+
+static int mock_dev_enable_feat(struct device *dev, enum iommu_dev_features feat)
+{
+       if (feat != IOMMU_DEV_FEAT_IOPF || !mock_iommu_iopf_queue)
+               return -ENODEV;
+
+       return iopf_queue_add_device(mock_iommu_iopf_queue, dev);
+}
+
+static int mock_dev_disable_feat(struct device *dev, enum iommu_dev_features feat)
+{
+       if (feat != IOMMU_DEV_FEAT_IOPF || !mock_iommu_iopf_queue)
+               return -ENODEV;
+
+       iopf_queue_remove_device(mock_iommu_iopf_queue, dev);
+
+       return 0;
+}
+
 static const struct iommu_ops mock_ops = {
        /*
         * IOMMU_DOMAIN_BLOCKED cannot be returned from def_domain_type()
@@ -529,6 +554,10 @@ static const struct iommu_ops mock_ops = {
        .capable = mock_domain_capable,
        .device_group = generic_device_group,
        .probe_device = mock_probe_device,
+       .page_response = mock_domain_page_response,
+       .dev_enable_feat = mock_dev_enable_feat,
+       .dev_disable_feat = mock_dev_disable_feat,
+       .user_pasid_table = true,
        .default_domain_ops =
                &(struct iommu_domain_ops){
                        .free = mock_domain_free,
@@ -1375,6 +1404,31 @@ out_put:
        return rc;
 }
 
+static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd,
+                                    struct iommu_test_cmd *cmd)
+{
+       struct iopf_fault event = { };
+       struct iommufd_device *idev;
+
+       idev = iommufd_get_device(ucmd, cmd->trigger_iopf.dev_id);
+       if (IS_ERR(idev))
+               return PTR_ERR(idev);
+
+       event.fault.prm.flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
+       if (cmd->trigger_iopf.pasid != IOMMU_NO_PASID)
+               event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
+       event.fault.type = IOMMU_FAULT_PAGE_REQ;
+       event.fault.prm.addr = cmd->trigger_iopf.addr;
+       event.fault.prm.pasid = cmd->trigger_iopf.pasid;
+       event.fault.prm.grpid = cmd->trigger_iopf.grpid;
+       event.fault.prm.perm = cmd->trigger_iopf.perm;
+
+       iommu_report_device_fault(idev->dev, &event);
+       iommufd_put_object(ucmd->ictx, &idev->obj);
+
+       return 0;
+}
+
 void iommufd_selftest_destroy(struct iommufd_object *obj)
 {
        struct selftest_obj *sobj = container_of(obj, struct selftest_obj, obj);
@@ -1450,6 +1504,8 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
                                          cmd->dirty.page_size,
                                          u64_to_user_ptr(cmd->dirty.uptr),
                                          cmd->dirty.flags);
+       case IOMMU_TEST_OP_TRIGGER_IOPF:
+               return iommufd_test_trigger_iopf(ucmd, cmd);
        default:
                return -EOPNOTSUPP;
        }
@@ -1491,6 +1547,9 @@ int __init iommufd_test_init(void)
                                  &iommufd_mock_bus_type.nb);
        if (rc)
                goto err_sysfs;
+
+       mock_iommu_iopf_queue = iopf_queue_alloc("mock-iopfq");
+
        return 0;
 
 err_sysfs:
@@ -1506,6 +1565,11 @@ err_dbgfs:
 
 void iommufd_test_exit(void)
 {
+       if (mock_iommu_iopf_queue) {
+               iopf_queue_free(mock_iommu_iopf_queue);
+               mock_iommu_iopf_queue = NULL;
+       }
+
        iommu_device_sysfs_remove(&mock_iommu_device);
        iommu_device_unregister_bus(&mock_iommu_device,
                                    &iommufd_mock_bus_type.bus,