mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 07:47:50 +00:00
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: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user