From: Jacob Pan <jacob.pan@linux.microsoft.com>
To: iommu@lists.linux.dev
Cc: Jason Gunthorpe <jgg@ziepe.ca>,
Nicolin Chen <nicolinc@nvidia.com>,
Kevin Tian <kevin.tian@intel.com>, Will Deacon <will@kernel.org>,
Robin Murphy <robin.murphy@arm.com>, Wei Liu <wei.liu@kernel.org>,
"K . Y . Srinivasan" <kys@microsoft.com>,
Haiyang Zhang <haiyangz@microsoft.com>,
Dexuan Cui <decui@microsoft.com>, Long Li <longli@microsoft.com>,
linux-hyperv@vger.kernel.org, Joerg Roedel <joro@8bytes.org>,
Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>,
Vasant Hegde <vasant.hegde@amd.com>,
Arnd Bergmann <arnd@arndb.de>,
linux-arch@vger.kernel.org, linux-kernel@vger.kernel.org,
"Aneesh Kumar K . V" <aneesh.kumar@kernel.org>,
Mukesh Rathor <mukeshrathor@microsoft.com>,
John Starks <John.Starks@microsoft.com>,
Souradeep Chakrabarti <schakrabarti@microsoft.com>,
Yu Zhang <zhangyu1@linux.microsoft.com>,
Easwar Hariharan <eahariha@linux.microsoft.com>,
Alex Williamson <alex@shazbot.org>
Subject: [PATCH RFC 4/9] iommufd/selftest: Add hypervisor external attach backend
Date: Fri, 25 Sep 2026 12:07:37 -0700 [thread overview]
Message-ID: <20260925190742.1575380-5-jacob.pan@linux.microsoft.com> (raw)
In-Reply-To: <20260925190742.1575380-1-jacob.pan@linux.microsoft.com>
Teach the mock IOMMU driver to allocate an hypervisor vIOMMU from a
partition fd and to allocate an external domain for external attach HWPT
testing.
Require a matching vDEVICE during external-domain attach so the selftest
backend exercises the vIOMMU/vDEVICE/HWPT relationship used by the
external attach flow.
Assisted-by: GPT-5.6 Sol
Signed-off-by: Jacob Pan <jacob.pan@linux.microsoft.com>
---
drivers/iommu/iommufd/selftest.c | 120 ++++++++++++++++++++++++++++---
1 file changed, 112 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index ee706f18f7e9..b264114b6552 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -28,6 +28,7 @@ static struct dentry *dbgfs_root;
static struct platform_device *selftest_iommu_dev;
static const struct iommu_ops mock_ops;
static struct iommu_domain_ops domain_nested_ops;
+static struct iommu_domain_ops domain_direct_ops;
size_t iommufd_test_memory_limit = 65536;
@@ -142,9 +143,21 @@ to_mock_nested(struct iommu_domain *domain)
return container_of(domain, struct mock_iommu_domain_nested, domain);
}
+struct mock_iommu_domain_direct {
+ struct iommu_domain domain;
+ struct mock_viommu *mock_viommu;
+};
+
+static inline struct mock_iommu_domain_direct *
+to_mock_direct(struct iommu_domain *domain)
+{
+ return container_of(domain, struct mock_iommu_domain_direct, domain);
+}
+
struct mock_viommu {
struct iommufd_viommu core;
struct mock_iommu_domain *s2_parent;
+ struct file *vm_file;
struct mock_hw_queue *hw_queue[IOMMU_TEST_HW_QUEUE_MAX];
struct mutex queue_mutex;
@@ -231,6 +244,12 @@ static int mock_domain_nop_attach(struct iommu_domain *domain,
if (rc)
return rc;
}
+ } else if (domain->type == IOMMU_DOMAIN_EXTERNAL) {
+ new_viommu = to_mock_direct(domain)->mock_viommu;
+ rc = iommufd_viommu_get_vdev_id(&new_viommu->core, dev,
+ &vdev_id);
+ if (rc)
+ return rc;
}
if (new_viommu != mdev->viommu) {
down_write(&mdev->viommu_rwsem);
@@ -607,7 +626,15 @@ static void mock_viommu_destroy(struct iommufd_viommu *viommu)
if (mock_viommu->mmap_offset)
iommufd_viommu_destroy_mmap(&mock_viommu->core,
mock_viommu->mmap_offset);
- free_pages((unsigned long)mock_viommu->page, 1);
+ if (mock_viommu->page)
+ free_pages((unsigned long)mock_viommu->page, 1);
+ if (mock_viommu->vm_file) {
+ pr_info("iommufd selftest: MSHV viommu destroy drops pinned vm_file=%p\n",
+ mock_viommu->vm_file);
+ fput(mock_viommu->vm_file);
+ pr_info("iommufd selftest: MSHV viommu dropped pinned vm_file=%p\n",
+ mock_viommu->vm_file);
+ }
mutex_destroy(&mock_viommu->queue_mutex);
/* iommufd core frees mock_viommu and viommu */
@@ -630,6 +657,40 @@ mock_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
return &mock_nested->domain;
}
+static struct iommu_domain *
+mock_viommu_alloc_domain_external(struct iommufd_viommu *viommu, u32 flags,
+ const struct iommu_user_data *user_data)
+{
+ struct mock_iommu_domain_direct *mock_direct;
+ struct iommu_hwpt_external data = {};
+ int rc;
+
+ if (viommu->type != IOMMU_VIOMMU_TYPE_HYPERVISOR)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (flags)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (user_data->type != IOMMU_HWPT_DATA_EXTERNAL)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ rc = iommu_copy_struct_from_user(&data, user_data,
+ IOMMU_HWPT_DATA_EXTERNAL, flags);
+ if (rc)
+ return ERR_PTR(rc);
+ if (data.flags || data.__reserved)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ mock_direct = kzalloc_obj(*mock_direct);
+ if (!mock_direct)
+ return ERR_PTR(-ENOMEM);
+
+ mock_direct->domain.ops = &domain_direct_ops;
+ mock_direct->domain.type = IOMMU_DOMAIN_EXTERNAL;
+ mock_direct->mock_viommu = to_mock_viommu(viommu);
+ pr_info("iommufd selftest: external attach domain allocated viommu_id=%u\n",
+ viommu->obj.id);
+ return &mock_direct->domain;
+}
+
static int mock_viommu_cache_invalidate(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array)
{
@@ -777,6 +838,7 @@ static int mock_hw_queue_init_phys(struct iommufd_hw_queue *hw_queue, u32 index,
static struct iommufd_viommu_ops mock_viommu_ops = {
.destroy = mock_viommu_destroy,
.alloc_domain_nested = mock_viommu_alloc_domain_nested,
+ .alloc_domain_external = mock_viommu_alloc_domain_external,
.cache_invalidate = mock_viommu_cache_invalidate,
.get_hw_queue_size = mock_viommu_get_hw_queue_size,
.hw_queue_init_phys = mock_hw_queue_init_phys,
@@ -785,7 +847,8 @@ static struct iommufd_viommu_ops mock_viommu_ops = {
static size_t mock_get_viommu_size(struct device *dev,
enum iommu_viommu_type viommu_type)
{
- if (viommu_type != IOMMU_VIOMMU_TYPE_SELFTEST)
+ if (viommu_type != IOMMU_VIOMMU_TYPE_SELFTEST &&
+ viommu_type != IOMMU_VIOMMU_TYPE_HYPERVISOR)
return 0;
return VIOMMU_STRUCT_SIZE(struct mock_viommu, core);
}
@@ -797,10 +860,32 @@ static int mock_viommu_init(struct iommufd_viommu *viommu,
struct mock_iommu_device *mock_iommu = container_of(
viommu->iommu_dev, struct mock_iommu_device, iommu_dev);
struct mock_viommu *mock_viommu = to_mock_viommu(viommu);
- struct iommu_viommu_selftest data;
+ struct iommu_viommu_selftest data = {};
int rc;
- if (user_data) {
+ if (viommu->type == IOMMU_VIOMMU_TYPE_HYPERVISOR) {
+ struct iommu_viommu_hypervisor hypervisor = {};
+
+ if (!user_data)
+ return -EINVAL;
+
+ rc = iommu_copy_struct_from_user(&hypervisor, user_data,
+ IOMMU_VIOMMU_TYPE_HYPERVISOR,
+ vm_fd);
+ if (rc)
+ return rc;
+ if (hypervisor.flags || hypervisor.__reserved)
+ return -EOPNOTSUPP;
+
+ mock_viommu->vm_file = fget(hypervisor.vm_fd);
+ if (!mock_viommu->vm_file)
+ return -EBADF;
+ pr_info("iommufd selftest: MSHV viommu init type=%u vm_fd=%d vm_file=%p has_hwpt_paging=%u\n",
+ viommu->type, hypervisor.vm_fd, mock_viommu->vm_file,
+ !!parent_domain);
+ pr_info("iommufd selftest: MSHV viommu pins vm_file=%p, userspace close waits for viommu destroy\n",
+ mock_viommu->vm_file);
+ } else if (user_data) {
rc = iommu_copy_struct_from_user(
&data, user_data, IOMMU_VIOMMU_TYPE_SELFTEST, out_data);
if (rc)
@@ -809,8 +894,10 @@ static int mock_viommu_init(struct iommufd_viommu *viommu,
/* Allocate two pages */
mock_viommu->page =
(u32 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
- if (!mock_viommu->page)
- return -ENOMEM;
+ if (!mock_viommu->page) {
+ rc = -ENOMEM;
+ goto err_put_vm_file;
+ }
rc = iommufd_viommu_alloc_mmap(&mock_viommu->core,
__pa(mock_viommu->page),
@@ -832,7 +919,8 @@ static int mock_viommu_init(struct iommufd_viommu *viommu,
refcount_inc(&mock_iommu->users);
mutex_init(&mock_viommu->queue_mutex);
- mock_viommu->s2_parent = to_mock_domain(parent_domain);
+ if (parent_domain)
+ mock_viommu->s2_parent = to_mock_domain(parent_domain);
viommu->ops = &mock_viommu_ops;
return 0;
@@ -842,6 +930,11 @@ static int mock_viommu_init(struct iommufd_viommu *viommu,
mock_viommu->mmap_offset);
err_free_page:
free_pages((unsigned long)mock_viommu->page, 1);
+err_put_vm_file:
+ if (mock_viommu->vm_file) {
+ fput(mock_viommu->vm_file);
+ mock_viommu->vm_file = NULL;
+ }
return rc;
}
@@ -870,6 +963,11 @@ static void mock_domain_free_nested(struct iommu_domain *domain)
kfree(to_mock_nested(domain));
}
+static void mock_domain_free_direct(struct iommu_domain *domain)
+{
+ kfree(to_mock_direct(domain));
+}
+
static int
mock_domain_cache_invalidate_user(struct iommu_domain *domain,
struct iommu_user_data_array *array)
@@ -925,6 +1023,11 @@ static struct iommu_domain_ops domain_nested_ops = {
.set_dev_pasid = mock_domain_set_dev_pasid_nop,
};
+static struct iommu_domain_ops domain_direct_ops = {
+ .free = mock_domain_free_direct,
+ .attach_dev = mock_domain_nop_attach,
+};
+
static inline struct iommufd_hw_pagetable *
__get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id, u32 hwpt_type)
{
@@ -1817,7 +1920,8 @@ iommufd_get_hwpt(struct iommufd_ucmd *ucmd, u32 id)
if (IS_ERR(pt_obj))
return ERR_CAST(pt_obj);
- if (pt_obj->type != IOMMUFD_OBJ_HWPT_NESTED &&
+ if (pt_obj->type != IOMMUFD_OBJ_HWPT_EXTERNAL &&
+ pt_obj->type != IOMMUFD_OBJ_HWPT_NESTED &&
pt_obj->type != IOMMUFD_OBJ_HWPT_PAGING) {
iommufd_put_object(ucmd->ictx, pt_obj);
return ERR_PTR(-EINVAL);
--
2.43.0
next prev parent reply other threads:[~2026-09-25 19:07 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-25 19:07 [RFC PATCH 0/9] iommu/iommufd: Add hypervisor external attach Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 1/9] iommu: Introduce external attach domain type Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 2/9] iommufd: Introduce hypervisor vIOMMU type Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 3/9] iommufd: Add external HWPT support Jacob Pan
2026-09-25 19:07 ` Jacob Pan [this message]
2026-09-25 19:07 ` [PATCH RFC 5/9] mshv: Add partition file identity helper Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 6/9] mshv: Add prepare callback for external device attach Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 7/9] iommu/hyperv: Split root IOMMU declarations Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 8/9] iommu/hyperv: Add fd-backed vIOMMU support Jacob Pan
2026-09-25 19:07 ` [PATCH RFC 9/9] iommu/hyperv: Add IOMMUFD external domains Jacob Pan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260925190742.1575380-5-jacob.pan@linux.microsoft.com \
--to=jacob.pan@linux.microsoft.com \
--cc=John.Starks@microsoft.com \
--cc=alex@shazbot.org \
--cc=aneesh.kumar@kernel.org \
--cc=arnd@arndb.de \
--cc=decui@microsoft.com \
--cc=eahariha@linux.microsoft.com \
--cc=haiyangz@microsoft.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=kys@microsoft.com \
--cc=linux-arch@vger.kernel.org \
--cc=linux-hyperv@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=longli@microsoft.com \
--cc=mukeshrathor@microsoft.com \
--cc=nicolinc@nvidia.com \
--cc=robin.murphy@arm.com \
--cc=schakrabarti@microsoft.com \
--cc=suravee.suthikulpanit@amd.com \
--cc=vasant.hegde@amd.com \
--cc=wei.liu@kernel.org \
--cc=will@kernel.org \
--cc=zhangyu1@linux.microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®