Commit d2041f1f authored by Jason Gunthorpe's avatar Jason Gunthorpe
Browse files

iommufd/selftest: Add some tests for the dmabuf flow

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

Link: https://patch.msgid.link/r/9-v2-b2c110338e3f+5c2-iommufd_dmabuf_jgg@nvidia.com


Reviewed-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Reviewed-by: default avatarKevin Tian <kevin.tian@intel.com>
Tested-by: default avatarNicolin Chen <nicolinc@nvidia.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
parent 44ebaa17
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
+10 −0
Original line number Diff line number Diff line
@@ -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;
};
+4 −0
Original line number Diff line number Diff line
@@ -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;

+143 −0
Original line number Diff line number Diff line
@@ -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;
	}
+43 −0
Original line number Diff line number Diff line
@@ -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;
Loading