Basic tests of establishing a dmabuf and revoking it. The selftest kernel
side provides a basic small dmabuf for this testing.

Reviewed-by: Nicolin Chen <[email protected]>
Reviewed-by: Kevin Tian <[email protected]>
Tested-by: Nicolin Chen <[email protected]>
Signed-off-by: Jason Gunthorpe <[email protected]>
---
 drivers/iommu/iommufd/iommufd_private.h       |  10 ++
 drivers/iommu/iommufd/iommufd_test.h          |  10 ++
 drivers/iommu/iommufd/pages.c                 |   4 +
 drivers/iommu/iommufd/selftest.c              | 143 ++++++++++++++++++
 tools/testing/selftests/iommu/iommufd.c       |  43 ++++++
 tools/testing/selftests/iommu/iommufd_utils.h |  44 ++++++
 6 files changed, 254 insertions(+)

diff --git a/drivers/iommu/iommufd/iommufd_private.h 
b/drivers/iommu/iommufd/iommufd_private.h
index a2786fee7603f5..ef2db82e3d7bf5 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -19,6 +19,8 @@ struct iommu_domain;
 struct iommu_group;
 struct iommu_option;
 struct iommufd_device;
+struct dma_buf_attachment;
+struct dma_buf_phys_vec;
 
 struct iommufd_sw_msi_map {
        struct list_head sw_msi_item;
@@ -713,6 +715,8 @@ bool iommufd_should_fail(void);
 int __init iommufd_test_init(void);
 void iommufd_test_exit(void);
 bool iommufd_selftest_is_mock_dev(struct device *dev);
+int iommufd_test_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+                                    struct dma_buf_phys_vec *phys);
 #else
 static inline void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
                                                 unsigned int ioas_id,
@@ -734,5 +738,11 @@ static inline bool iommufd_selftest_is_mock_dev(struct 
device *dev)
 {
        return false;
 }
+static inline int
+iommufd_test_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+                                struct dma_buf_phys_vec *phys)
+{
+       return -EOPNOTSUPP;
+}
 #endif
 #endif
diff --git a/drivers/iommu/iommufd/iommufd_test.h 
b/drivers/iommu/iommufd/iommufd_test.h
index 8fc618b2bcf967..9166c39eb0c8b2 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -29,6 +29,8 @@ enum {
        IOMMU_TEST_OP_PASID_REPLACE,
        IOMMU_TEST_OP_PASID_DETACH,
        IOMMU_TEST_OP_PASID_CHECK_HWPT,
+       IOMMU_TEST_OP_DMABUF_GET,
+       IOMMU_TEST_OP_DMABUF_REVOKE,
 };
 
 enum {
@@ -176,6 +178,14 @@ struct iommu_test_cmd {
                        __u32 hwpt_id;
                        /* @id is stdev_id */
                } pasid_check;
+               struct {
+                       __u32 length;
+                       __u32 open_flags;
+               } dmabuf_get;
+               struct {
+                       __s32 dmabuf_fd;
+                       __u32 revoked;
+               } dmabuf_revoke;
        };
        __u32 last;
 };
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index c73456833d04d6..dbe51ecb9a20f8 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -1465,6 +1465,10 @@ sym_vfio_pci_dma_buf_iommufd_map(struct 
dma_buf_attachment *attachment,
        typeof(&vfio_pci_dma_buf_iommufd_map) fn;
        int rc;
 
+       rc = iommufd_test_dma_buf_iommufd_map(attachment, phys);
+       if (rc != -EOPNOTSUPP)
+               return rc;
+
        if (!IS_ENABLED(CONFIG_VFIO_PCI_DMABUF))
                return -EOPNOTSUPP;
 
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index de178827a078a9..5d14dd0fb37d6a 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -5,6 +5,8 @@
  */
 #include <linux/anon_inodes.h>
 #include <linux/debugfs.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-resv.h>
 #include <linux/fault-inject.h>
 #include <linux/file.h>
 #include <linux/iommu.h>
@@ -2031,6 +2033,140 @@ void iommufd_selftest_destroy(struct iommufd_object 
*obj)
        }
 }
 
+struct iommufd_test_dma_buf {
+       void *memory;
+       size_t length;
+       bool revoked;
+};
+
+static int iommufd_test_dma_buf_attach(struct dma_buf *dmabuf,
+                                      struct dma_buf_attachment *attachment)
+{
+       return 0;
+}
+
+static void iommufd_test_dma_buf_detach(struct dma_buf *dmabuf,
+                                       struct dma_buf_attachment *attachment)
+{
+}
+
+static struct sg_table *
+iommufd_test_dma_buf_map(struct dma_buf_attachment *attachment,
+                        enum dma_data_direction dir)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static void iommufd_test_dma_buf_unmap(struct dma_buf_attachment *attachment,
+                                      struct sg_table *sgt,
+                                      enum dma_data_direction dir)
+{
+}
+
+static void iommufd_test_dma_buf_release(struct dma_buf *dmabuf)
+{
+       struct iommufd_test_dma_buf *priv = dmabuf->priv;
+
+       kfree(priv->memory);
+       kfree(priv);
+}
+
+static const struct dma_buf_ops iommufd_test_dmabuf_ops = {
+       .attach = iommufd_test_dma_buf_attach,
+       .detach = iommufd_test_dma_buf_detach,
+       .map_dma_buf = iommufd_test_dma_buf_map,
+       .release = iommufd_test_dma_buf_release,
+       .unmap_dma_buf = iommufd_test_dma_buf_unmap,
+};
+
+int iommufd_test_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+                                    struct dma_buf_phys_vec *phys)
+{
+       struct iommufd_test_dma_buf *priv = attachment->dmabuf->priv;
+
+       dma_resv_assert_held(attachment->dmabuf->resv);
+
+       if (attachment->dmabuf->ops != &iommufd_test_dmabuf_ops)
+               return -EOPNOTSUPP;
+
+       if (priv->revoked)
+               return -ENODEV;
+
+       phys->paddr = virt_to_phys(priv->memory);
+       phys->len = priv->length;
+       return 0;
+}
+
+static int iommufd_test_dmabuf_get(struct iommufd_ucmd *ucmd,
+                                  unsigned int open_flags,
+                                  size_t len)
+{
+       DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+       struct iommufd_test_dma_buf *priv;
+       struct dma_buf *dmabuf;
+       int rc;
+
+       len = ALIGN(len, PAGE_SIZE);
+       if (len == 0 || len > PAGE_SIZE * 512)
+               return -EINVAL;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->length = len;
+       priv->memory = kzalloc(len, GFP_KERNEL);
+       if (!priv->memory) {
+               rc = -ENOMEM;
+               goto err_free;
+       }
+
+       exp_info.ops = &iommufd_test_dmabuf_ops;
+       exp_info.size = len;
+       exp_info.flags = open_flags;
+       exp_info.priv = priv;
+
+       dmabuf = dma_buf_export(&exp_info);
+       if (IS_ERR(dmabuf)) {
+               rc = PTR_ERR(dmabuf);
+               goto err_free;
+       }
+
+       return dma_buf_fd(dmabuf, open_flags);
+
+err_free:
+       kfree(priv->memory);
+       kfree(priv);
+       return rc;
+}
+
+static int iommufd_test_dmabuf_revoke(struct iommufd_ucmd *ucmd, int fd,
+                                     bool revoked)
+{
+       struct iommufd_test_dma_buf *priv;
+       struct dma_buf *dmabuf;
+       int rc = 0;
+
+       dmabuf = dma_buf_get(fd);
+       if (IS_ERR(dmabuf))
+               return PTR_ERR(dmabuf);
+
+       if (dmabuf->ops != &iommufd_test_dmabuf_ops) {
+               rc = -EOPNOTSUPP;
+               goto err_put;
+       }
+
+       priv = dmabuf->priv;
+       dma_resv_lock(dmabuf->resv, NULL);
+       priv->revoked = revoked;
+       dma_buf_move_notify(dmabuf);
+       dma_resv_unlock(dmabuf->resv);
+
+err_put:
+       dma_buf_put(dmabuf);
+       return rc;
+}
+
 int iommufd_test(struct iommufd_ucmd *ucmd)
 {
        struct iommu_test_cmd *cmd = ucmd->cmd;
@@ -2109,6 +2245,13 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
                return iommufd_test_pasid_detach(ucmd, cmd);
        case IOMMU_TEST_OP_PASID_CHECK_HWPT:
                return iommufd_test_pasid_check_hwpt(ucmd, cmd);
+       case IOMMU_TEST_OP_DMABUF_GET:
+               return iommufd_test_dmabuf_get(ucmd, cmd->dmabuf_get.open_flags,
+                                              cmd->dmabuf_get.length);
+       case IOMMU_TEST_OP_DMABUF_REVOKE:
+               return iommufd_test_dmabuf_revoke(ucmd,
+                                                 cmd->dmabuf_revoke.dmabuf_fd,
+                                                 cmd->dmabuf_revoke.revoked);
        default:
                return -EOPNOTSUPP;
        }
diff --git a/tools/testing/selftests/iommu/iommufd.c 
b/tools/testing/selftests/iommu/iommufd.c
index 3eebf5e3b974f4..de348d6412791b 100644
--- a/tools/testing/selftests/iommu/iommufd.c
+++ b/tools/testing/selftests/iommu/iommufd.c
@@ -1574,6 +1574,49 @@ TEST_F(iommufd_ioas, copy_sweep)
        test_ioctl_destroy(dst_ioas_id);
 }
 
+TEST_F(iommufd_ioas, dmabuf_simple)
+{
+       size_t buf_size = PAGE_SIZE*4;
+       __u64 iova;
+       int dfd;
+
+       test_cmd_get_dmabuf(buf_size, &dfd);
+       test_err_ioctl_ioas_map_file(EINVAL, dfd, 0, 0, &iova);
+       test_err_ioctl_ioas_map_file(EINVAL, dfd, buf_size, buf_size, &iova);
+       test_err_ioctl_ioas_map_file(EINVAL, dfd, 0, buf_size + 1, &iova);
+       test_ioctl_ioas_map_file(dfd, 0, buf_size, &iova);
+
+       close(dfd);
+}
+
+TEST_F(iommufd_ioas, dmabuf_revoke)
+{
+       size_t buf_size = PAGE_SIZE*4;
+       __u32 hwpt_id;
+       __u64 iova;
+       __u64 iova2;
+       int dfd;
+
+       test_cmd_get_dmabuf(buf_size, &dfd);
+       test_ioctl_ioas_map_file(dfd, 0, buf_size, &iova);
+       test_cmd_revoke_dmabuf(dfd, true);
+
+       if (variant->mock_domains)
+               test_cmd_hwpt_alloc(self->device_id, self->ioas_id, 0,
+                                   &hwpt_id);
+
+       test_err_ioctl_ioas_map_file(ENODEV, dfd, 0, buf_size, &iova2);
+
+       test_cmd_revoke_dmabuf(dfd, false);
+       test_ioctl_ioas_map_file(dfd, 0, buf_size, &iova2);
+
+       /* Restore the iova back */
+       test_ioctl_ioas_unmap(iova, buf_size);
+       test_ioctl_ioas_map_fixed_file(dfd, 0, buf_size, iova);
+
+       close(dfd);
+}
+
 FIXTURE(iommufd_mock_domain)
 {
        int fd;
diff --git a/tools/testing/selftests/iommu/iommufd_utils.h 
b/tools/testing/selftests/iommu/iommufd_utils.h
index 772ca1db6e5971..f6d1678b310e1f 100644
--- a/tools/testing/selftests/iommu/iommufd_utils.h
+++ b/tools/testing/selftests/iommu/iommufd_utils.h
@@ -548,6 +548,39 @@ static int _test_cmd_destroy_access_pages(int fd, unsigned 
int access_id,
        EXPECT_ERRNO(_errno, _test_cmd_destroy_access_pages(              \
                                     self->fd, access_id, access_pages_id))
 
+static int _test_cmd_get_dmabuf(int fd, size_t len, int *out_fd)
+{
+       struct iommu_test_cmd cmd = {
+               .size = sizeof(cmd),
+               .op = IOMMU_TEST_OP_DMABUF_GET,
+               .dmabuf_get = { .length = len, .open_flags = O_CLOEXEC },
+       };
+
+       *out_fd = ioctl(fd, IOMMU_TEST_CMD, &cmd);
+       if (*out_fd < 0)
+               return -1;
+       return 0;
+}
+#define test_cmd_get_dmabuf(len, out_fd) \
+       ASSERT_EQ(0, _test_cmd_get_dmabuf(self->fd, len, out_fd))
+
+static int _test_cmd_revoke_dmabuf(int fd, int dmabuf_fd, bool revoked)
+{
+       struct iommu_test_cmd cmd = {
+               .size = sizeof(cmd),
+               .op = IOMMU_TEST_OP_DMABUF_REVOKE,
+               .dmabuf_revoke = { .dmabuf_fd = dmabuf_fd, .revoked = revoked },
+       };
+       int ret;
+
+       ret = ioctl(fd, IOMMU_TEST_CMD, &cmd);
+       if (ret < 0)
+               return -1;
+       return 0;
+}
+#define test_cmd_revoke_dmabuf(dmabuf_fd, revoke) \
+       ASSERT_EQ(0, _test_cmd_revoke_dmabuf(self->fd, dmabuf_fd, revoke))
+
 static int _test_ioctl_destroy(int fd, unsigned int id)
 {
        struct iommu_destroy cmd = {
@@ -718,6 +751,17 @@ static int _test_ioctl_ioas_map_file(int fd, unsigned int 
ioas_id, int mfd,
                          self->fd, ioas_id, mfd, start, length, iova_p,     \
                          IOMMU_IOAS_MAP_WRITEABLE | IOMMU_IOAS_MAP_READABLE))
 
+#define test_ioctl_ioas_map_fixed_file(mfd, start, length, iova)          \
+       ({                                                                \
+               __u64 __iova = iova;                                      \
+               ASSERT_EQ(0, _test_ioctl_ioas_map_file(                   \
+                                    self->fd, self->ioas_id, mfd, start, \
+                                    length, &__iova,                     \
+                                    IOMMU_IOAS_MAP_FIXED_IOVA |          \
+                                            IOMMU_IOAS_MAP_WRITEABLE |   \
+                                            IOMMU_IOAS_MAP_READABLE));   \
+       })
+
 static int _test_ioctl_set_temp_memory_limit(int fd, unsigned int limit)
 {
        struct iommu_test_cmd memlimit_cmd = {
-- 
2.43.0

Reply via email to