Improve the mock DMA-buf to have multiple physical ranges and add a
method to compare the values loaded into the iommu_domain with the
allocated page array.

Signed-off-by: Jason Gunthorpe <[email protected]>
---
 drivers/iommu/iommufd/iommufd_test.h          |   7 ++
 drivers/iommu/iommufd/selftest.c              | 107 +++++++++++++++---
 tools/testing/selftests/iommu/iommufd.c       |  43 +++++++
 tools/testing/selftests/iommu/iommufd_utils.h |  17 +++
 4 files changed, 160 insertions(+), 14 deletions(-)

diff --git a/drivers/iommu/iommufd/iommufd_test.h 
b/drivers/iommu/iommufd/iommufd_test.h
index 73e73e1ec15837..dae7d808b7bade 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -31,6 +31,7 @@ enum {
        IOMMU_TEST_OP_PASID_CHECK_HWPT,
        IOMMU_TEST_OP_DMABUF_GET,
        IOMMU_TEST_OP_DMABUF_REVOKE,
+       IOMMU_TEST_OP_MD_CHECK_DMABUF,
 };
 
 enum {
@@ -194,6 +195,12 @@ struct iommu_test_cmd {
                        __s32 dmabuf_fd;
                        __u32 revoked;
                } dmabuf_revoke;
+               struct {
+                       __s32 dmabuf_fd;
+                       __aligned_u64 iova;
+                       __aligned_u64 length;
+                       __aligned_u64 offset;
+               } check_dmabuf;
        };
        __u32 last;
 };
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index 06820a50d5d24c..e924281840a07e 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -1957,16 +1957,19 @@ void iommufd_selftest_destroy(struct iommufd_object 
*obj)
 }
 
 struct iommufd_test_dma_buf {
-       void *memory;
        size_t length;
+       unsigned int npages;
        bool revoked;
+       struct page *pages[] __counted_by(npages);
 };
 
 static void iommufd_test_dma_buf_release(struct dma_buf *dmabuf)
 {
        struct iommufd_test_dma_buf *priv = dmabuf->priv;
+       unsigned int i;
 
-       kfree(priv->memory);
+       for (i = 0; i < priv->npages; i++)
+               __free_page(priv->pages[i]);
        kfree(priv);
 }
 
@@ -1981,19 +1984,22 @@ iommufd_dma_pal_map_phys(struct dma_buf_attachment 
*attachment)
        if (priv->revoked)
                return ERR_PTR(-ENODEV);
 
-       phys = kvmalloc(struct_size(phys, phys, 1), GFP_KERNEL);
+       phys = kvmalloc(struct_size(phys, phys, priv->npages), GFP_KERNEL);
        if (!phys)
                return ERR_PTR(-ENOMEM);
 
-       phys->length = 1;
-       phys->phys[0].paddr = virt_to_phys(priv->memory);
-       phys->phys[0].len = priv->length;
+       phys->length = priv->npages;
+       for (unsigned int i = 0; i < priv->npages; i++) {
+               phys->phys[i].paddr = page_to_phys(priv->pages[i]);
+               phys->phys[i].len = PAGE_SIZE;
+       }
        return phys;
 }
 
 static void iommufd_dma_pal_unmap_phys(struct dma_buf_attachment *attach,
                                       struct dma_buf_phys_list *phys)
 {
+       kfree(phys);
 }
 
 static const struct dma_buf_mapping_pal_exp_ops iommufd_test_dma_buf_pal_ops = 
{
@@ -2022,21 +2028,27 @@ static int iommufd_test_dmabuf_get(struct iommufd_ucmd 
*ucmd,
        DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
        struct iommufd_test_dma_buf *priv;
        struct dma_buf *dmabuf;
+       size_t i;
        int rc;
 
-       len = ALIGN(len, PAGE_SIZE);
-       if (len == 0 || len > PAGE_SIZE * 512)
+       unsigned int npages;
+
+       if (len == 0 || len % PAGE_SIZE || len > PAGE_SIZE * 512)
                return -EINVAL;
 
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       npages = len >> PAGE_SHIFT;
+       priv = kzalloc(struct_size(priv, pages, npages), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        priv->length = len;
-       priv->memory = kzalloc(len, GFP_KERNEL);
-       if (!priv->memory) {
-               rc = -ENOMEM;
-               goto err_free;
+       priv->npages = npages;
+       for (i = 0; i < npages; i++) {
+               priv->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+               if (!priv->pages[i]) {
+                       rc = -ENOMEM;
+                       goto err_free;
+               }
        }
 
        exp_info.ops = &iommufd_test_dmabuf_ops;
@@ -2053,7 +2065,11 @@ static int iommufd_test_dmabuf_get(struct iommufd_ucmd 
*ucmd,
        return dma_buf_fd(dmabuf, open_flags);
 
 err_free:
-       kfree(priv->memory);
+       for (unsigned int i = 0; i < npages; i++) {
+               if (!priv->pages[i])
+                       break;
+               __free_page(priv->pages[i]);
+       }
        kfree(priv);
        return rc;
 }
@@ -2085,6 +2101,64 @@ static int iommufd_test_dmabuf_revoke(struct 
iommufd_ucmd *ucmd, int fd,
        return rc;
 }
 
+static int iommufd_test_md_check_dmabuf(struct iommufd_ucmd *ucmd,
+                                       unsigned int mockpt_id, int dmabuf_fd,
+                                       unsigned long iova, size_t length,
+                                       unsigned long offset)
+{
+       struct iommufd_hw_pagetable *hwpt;
+       struct iommufd_test_dma_buf *priv;
+       struct mock_iommu_domain *mock;
+       struct dma_buf *dmabuf;
+       unsigned int page_size;
+       unsigned long end;
+       size_t i;
+       int rc;
+
+       hwpt = get_md_pagetable(ucmd, mockpt_id, &mock);
+       if (IS_ERR(hwpt))
+               return PTR_ERR(hwpt);
+
+       dmabuf = dma_buf_get(dmabuf_fd);
+       if (IS_ERR(dmabuf)) {
+               rc = PTR_ERR(dmabuf);
+               goto out_put_hwpt;
+       }
+
+       if (dmabuf->ops != &iommufd_test_dmabuf_ops) {
+               rc = -EINVAL;
+               goto out_put_dmabuf;
+       }
+
+       priv = dmabuf->priv;
+       page_size = 1 << __ffs(mock->domain.pgsize_bitmap);
+       if (iova % page_size || length % page_size || offset % page_size ||
+           check_add_overflow(offset, length, &end) || end > priv->length) {
+               rc = -EINVAL;
+               goto out_put_dmabuf;
+       }
+
+       for (i = 0; i < length; i += page_size) {
+               phys_addr_t expected =
+                       page_to_phys(priv->pages[(offset + i) / PAGE_SIZE]) +
+                       ((offset + i) % PAGE_SIZE);
+               phys_addr_t io_phys =
+                       mock->domain.ops->iova_to_phys(&mock->domain, iova + i);
+
+               if (io_phys != expected) {
+                       rc = -EINVAL;
+                       goto out_put_dmabuf;
+               }
+       }
+       rc = 0;
+
+out_put_dmabuf:
+       dma_buf_put(dmabuf);
+out_put_hwpt:
+       iommufd_put_object(ucmd->ictx, &hwpt->obj);
+       return rc;
+}
+
 int iommufd_test(struct iommufd_ucmd *ucmd)
 {
        struct iommu_test_cmd *cmd = ucmd->cmd;
@@ -2170,6 +2244,11 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
                return iommufd_test_dmabuf_revoke(ucmd,
                                                  cmd->dmabuf_revoke.dmabuf_fd,
                                                  cmd->dmabuf_revoke.revoked);
+       case IOMMU_TEST_OP_MD_CHECK_DMABUF:
+               return iommufd_test_md_check_dmabuf(
+                       ucmd, cmd->id, cmd->check_dmabuf.dmabuf_fd,
+                       cmd->check_dmabuf.iova, cmd->check_dmabuf.length,
+                       cmd->check_dmabuf.offset);
        default:
                return -EOPNOTSUPP;
        }
diff --git a/tools/testing/selftests/iommu/iommufd.c 
b/tools/testing/selftests/iommu/iommufd.c
index dadad277f4eb2e..2673f9f153392f 100644
--- a/tools/testing/selftests/iommu/iommufd.c
+++ b/tools/testing/selftests/iommu/iommufd.c
@@ -1580,10 +1580,53 @@ TEST_F(iommufd_ioas, dmabuf_simple)
        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);
+       if (variant->mock_domains)
+               test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, buf_size, 0);
 
        close(dfd);
 }
 
+TEST_F(iommufd_ioas, dmabuf_multi_page)
+{
+       __u64 iova;
+       int dfd;
+
+       /* Single page */
+       test_cmd_get_dmabuf(PAGE_SIZE, &dfd);
+       test_ioctl_ioas_map_file(dfd, 0, PAGE_SIZE, &iova);
+       if (variant->mock_domains)
+               test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE, 0);
+       close(dfd);
+
+       /* Many pages - exercises batch filling across multiple phys entries */
+       test_cmd_get_dmabuf(PAGE_SIZE * 64, &dfd);
+       test_ioctl_ioas_map_file(dfd, 0, PAGE_SIZE * 64, &iova);
+       if (variant->mock_domains)
+               test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE * 64,
+                                     0);
+       close(dfd);
+
+       /* Sub-range from the middle - exercises seeking into the phys array */
+       test_cmd_get_dmabuf(PAGE_SIZE * 16, &dfd);
+       test_ioctl_ioas_map_file(dfd, PAGE_SIZE * 4, PAGE_SIZE * 8, &iova);
+       if (variant->mock_domains)
+               test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE * 8,
+                                     PAGE_SIZE * 4);
+       close(dfd);
+
+       /* Multiple sub-ranges from the same dmabuf */
+       test_cmd_get_dmabuf(PAGE_SIZE * 16, &dfd);
+       test_ioctl_ioas_map_file(dfd, 0, PAGE_SIZE * 4, &iova);
+       if (variant->mock_domains)
+               test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE * 4,
+                                     0);
+       test_ioctl_ioas_map_file(dfd, PAGE_SIZE * 8, PAGE_SIZE * 4, &iova);
+       if (variant->mock_domains)
+               test_cmd_check_dmabuf(self->hwpt_id, dfd, iova, PAGE_SIZE * 4,
+                                     PAGE_SIZE * 8);
+       close(dfd);
+}
+
 TEST_F(iommufd_ioas, dmabuf_revoke)
 {
        size_t buf_size = PAGE_SIZE*4;
diff --git a/tools/testing/selftests/iommu/iommufd_utils.h 
b/tools/testing/selftests/iommu/iommufd_utils.h
index 5502751d500c89..35fd91d354f998 100644
--- a/tools/testing/selftests/iommu/iommufd_utils.h
+++ b/tools/testing/selftests/iommu/iommufd_utils.h
@@ -593,6 +593,23 @@ static int _test_cmd_revoke_dmabuf(int fd, int dmabuf_fd, 
bool revoked)
 #define test_cmd_revoke_dmabuf(dmabuf_fd, revoke) \
        ASSERT_EQ(0, _test_cmd_revoke_dmabuf(self->fd, dmabuf_fd, revoke))
 
+#define test_cmd_check_dmabuf(_hwpt_id, _dmabuf_fd, _iova, _length, _offset) \
+       ({                                                                   \
+               struct iommu_test_cmd check_cmd = {                          \
+                       .size = sizeof(check_cmd),                           \
+                       .op = IOMMU_TEST_OP_MD_CHECK_DMABUF,                 \
+                       .id = _hwpt_id,                                      \
+                       .check_dmabuf = { .dmabuf_fd = _dmabuf_fd,           \
+                                         .iova = _iova,                     \
+                                         .length = _length,                 \
+                                         .offset = _offset },               \
+               };                                                           \
+               ASSERT_EQ(0, ioctl(self->fd,                                 \
+                                  _IOMMU_TEST_CMD(                          \
+                                          IOMMU_TEST_OP_MD_CHECK_DMABUF),   \
+                                  &check_cmd));                             \
+       })
+
 static int _test_ioctl_destroy(int fd, unsigned int id)
 {
        struct iommu_destroy cmd = {
-- 
2.43.0

Reply via email to