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 9/9] iommu/hyperv: Add IOMMUFD external domains
Date: Fri, 25 Sep 2026 12:07:42 -0700 [thread overview]
Message-ID: <20260925190742.1575380-10-jacob.pan@linux.microsoft.com> (raw)
In-Reply-To: <20260925190742.1575380-1-jacob.pan@linux.microsoft.com>
Allocate IOMMU_DOMAIN_EXTERNAL domains under Hyper-V vIOMMUs and use
HVCALL_ATTACH_DEVICE to assign devices to an MSHV partition.
Resolve the userspace-assigned vDEVICE virt_id during attach and use it
as the Hyper-V logical device ID. External domains retain their vIOMMU
so attach preparation can pin guest memory before atomically replacing
the previous device assignment.
Keep paging domains backed by normal Hyper-V device domains.
Assisted-by: GPT-5.6 Sol
Signed-off-by: Jacob Pan <jacob.pan@linux.microsoft.com>
---
drivers/iommu/hyperv/hv-iommu-iommufd.c | 44 ++++++++++++
drivers/iommu/hyperv/hv-iommu-root.c | 89 +++++++++++++++++++++++++
drivers/iommu/hyperv/hv-iommu.h | 5 ++
3 files changed, 138 insertions(+)
diff --git a/drivers/iommu/hyperv/hv-iommu-iommufd.c b/drivers/iommu/hyperv/hv-iommu-iommufd.c
index 486a85c0d1e8..e595f53ddc66 100644
--- a/drivers/iommu/hyperv/hv-iommu-iommufd.c
+++ b/drivers/iommu/hyperv/hv-iommu-iommufd.c
@@ -33,10 +33,54 @@ static void hv_iommu_viommu_destroy(struct iommufd_viommu *viommu)
fput(hv_viommu->vm_file);
}
+static struct iommu_domain *
+hv_iommu_alloc_domain_external(struct iommufd_viommu *viommu, u32 flags,
+ const struct iommu_user_data *user_data)
+{
+ struct hv_iommu_viommu *hv_viommu = to_hv_iommu_viommu(viommu);
+ struct iommu_hwpt_external external = {};
+ struct hv_domain *hvdom;
+ int rc;
+
+ if (viommu->type != IOMMU_VIOMMU_TYPE_HYPERVISOR)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (flags)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (!user_data || user_data->type != IOMMU_HWPT_DATA_EXTERNAL)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ rc = iommu_copy_struct_from_user(&external, user_data,
+ IOMMU_HWPT_DATA_EXTERNAL, flags);
+ if (rc)
+ return ERR_PTR(rc);
+ if (external.flags || external.__reserved)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ hvdom = kzalloc_obj(*hvdom, GFP_KERNEL_ACCOUNT);
+ if (!hvdom)
+ return ERR_PTR(-ENOMEM);
+
+ hvdom->iommu_dom.type = IOMMU_DOMAIN_EXTERNAL;
+ hvdom->iommu_dom.ops = &hv_iommu_external_domain_ops;
+ hvdom->iommu_dom.pgsize_bitmap = HV_IOMMU_PGSIZES;
+ hvdom->partid = hv_viommu->partid;
+ hvdom->viommu = viommu;
+
+ return &hvdom->iommu_dom;
+}
+
static const struct iommufd_viommu_ops hv_iommu_hypervisor_viommu_ops = {
.destroy = hv_iommu_viommu_destroy,
+ .alloc_domain_external = hv_iommu_alloc_domain_external,
};
+int hv_iommufd_prepare_attach(struct iommufd_viommu *viommu)
+{
+ struct hv_iommu_viommu *hv_viommu = to_hv_iommu_viommu(viommu);
+
+ return mshv_partition_file_prepare_attach(hv_viommu->vm_file);
+}
+
size_t hv_iommufd_get_viommu_size(struct device *dev,
enum iommu_viommu_type viommu_type)
{
diff --git a/drivers/iommu/hyperv/hv-iommu-root.c b/drivers/iommu/hyperv/hv-iommu-root.c
index d6424b9ea0cd..b87152489630 100644
--- a/drivers/iommu/hyperv/hv-iommu-root.c
+++ b/drivers/iommu/hyperv/hv-iommu-root.c
@@ -7,6 +7,7 @@
#include <linux/dma-map-ops.h>
#include <linux/interval_tree.h>
#include <linux/hyperv.h>
+#include <linux/iommufd.h>
#include "hv-iommu.h"
#include <asm/iommu.h>
#include <asm/mshyperv.h>
@@ -59,6 +60,13 @@ static bool hv_special_domain(struct hv_domain *hvdom)
return hvdom == &hv_def_identity_dom || hvdom == &hv_def_blocked_dom;
}
+static u64 hv_iommu_host_device_id(struct pci_dev *pdev)
+{
+ u64 devid = hv_pci_vmbus_device_id(pdev);
+
+ return devid ? devid : hv_build_devid_type_pci(pdev);
+}
+
static atomic_t hv_unique_id; /* unique numeric id for a new domain */
static bool hv_iommu_capable(struct device *dev, enum iommu_cap cap)
@@ -205,6 +213,11 @@ static void hv_iommu_domain_free(struct iommu_domain *immdom)
if (hv_special_domain(hvdom))
return;
+ if (immdom->type == IOMMU_DOMAIN_EXTERNAL) {
+ kfree(hvdom);
+ return;
+ }
+
/* Cleanup any remaining. 0 for size results in ULONG_MAX as the last */
hv_iommu_del_tree_mappings(hvdom, 0, 0);
@@ -269,6 +282,10 @@ static int hv_iommu_attach_dev(struct iommu_domain *immdom, struct device *dev,
pdev = to_pci_dev(dev);
+ /*
+ * HVCALL_ATTACH_DEVICE_DOMAIN atomically replaces any existing
+ * assignment, leaving @old intact if the new attachment fails.
+ */
rc = hv_iommu_att_dev2dom(hvdom_new, pdev);
if (rc)
WARN(1, "Failed to attach pdev:%s\n", pci_name(pdev));
@@ -276,6 +293,73 @@ static int hv_iommu_attach_dev(struct iommu_domain *immdom, struct device *dev,
return rc;
}
+static int hv_iommu_external_attach_device(struct pci_dev *pdev, u64 partid,
+ unsigned long vdev_id)
+{
+ struct hv_input_attach_device *input;
+ union hv_device_id host_devid;
+ unsigned long flags;
+ u64 status;
+ int rc;
+
+ if (partid == HV_PARTITION_ID_INVALID)
+ return -EINVAL;
+
+ host_devid.as_uint64 = hv_iommu_host_device_id(pdev);
+
+ do {
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(input, 0, sizeof(*input));
+
+ input->partition_id = partid;
+ input->device_id = host_devid;
+ input->attdev_flags.logical_id = 1;
+ input->logical_devid = vdev_id;
+
+ status = hv_do_hypercall(HVCALL_ATTACH_DEVICE, input, NULL);
+ local_irq_restore(flags);
+
+ if (hv_result(status) == HV_STATUS_INSUFFICIENT_MEMORY) {
+ rc = hv_call_deposit_pages(NUMA_NO_NODE, partid, 1);
+ if (rc)
+ return rc;
+ }
+ } while (hv_result(status) == HV_STATUS_INSUFFICIENT_MEMORY);
+
+ if (!hv_result_success(status))
+ hv_status_err(status, "\n");
+
+ return hv_result_to_errno(status);
+}
+
+static int hv_iommu_external_attach_dev(struct iommu_domain *immdom,
+ struct device *dev,
+ struct iommu_domain *old)
+{
+ struct hv_domain *hvdom_new = to_hv_domain(immdom);
+ unsigned long vdev_id;
+ int rc;
+
+ if (!dev_is_pci(dev))
+ return -EINVAL;
+
+ rc = iommufd_viommu_get_vdev_id(hvdom_new->viommu, dev, &vdev_id);
+ if (rc)
+ return rc;
+
+ rc = hv_iommufd_prepare_attach(hvdom_new->viommu);
+ if (rc)
+ return rc;
+
+ /*
+ * HVCALL_ATTACH_DEVICE atomically replaces any existing assignment,
+ * leaving @old intact if the new attachment fails.
+ */
+ return hv_iommu_external_attach_device(to_pci_dev(dev),
+ hvdom_new->partid, vdev_id);
+}
+
static u64 hv_iommu_unmap_batch(u32 domid_num, ulong iova, u16 count)
{
ulong flags;
@@ -527,6 +611,11 @@ static struct iommu_domain_ops hv_paging_domain_ops = {
.free = hv_iommu_domain_free,
};
+const struct iommu_domain_ops hv_iommu_external_domain_ops = {
+ .attach_dev = hv_iommu_external_attach_dev,
+ .free = hv_iommu_domain_free,
+};
+
static struct iommu_ops hv_iommu_ops = {
.capable = hv_iommu_capable,
.domain_alloc_paging = hv_iommu_domain_alloc_paging,
diff --git a/drivers/iommu/hyperv/hv-iommu.h b/drivers/iommu/hyperv/hv-iommu.h
index 6fdbe56a4b1d..647fead81533 100644
--- a/drivers/iommu/hyperv/hv-iommu.h
+++ b/drivers/iommu/hyperv/hv-iommu.h
@@ -20,16 +20,21 @@ struct iommu_user_data;
struct hv_domain {
struct iommu_domain iommu_dom;
u32 domid_num; /* as opposed to domain_id.type */
+ u64 partid; /* partition id for external attach */
+ struct iommufd_viommu *viommu;
spinlock_t mappings_lock; /* protects mappings_tree */
struct rb_root_cached mappings_tree; /* iova to pa lookup tree */
};
#define to_hv_domain(d) container_of(d, struct hv_domain, iommu_dom)
+extern const struct iommu_domain_ops hv_iommu_external_domain_ops;
+
size_t hv_iommufd_get_viommu_size(struct device *dev,
enum iommu_viommu_type viommu_type);
int hv_iommufd_viommu_init(struct iommufd_viommu *viommu,
struct iommu_domain *parent_domain,
const struct iommu_user_data *user_data);
+int hv_iommufd_prepare_attach(struct iommufd_viommu *viommu);
#endif /* __HYPERV_IOMMU_H */
--
2.43.0
prev parent reply other threads:[~2026-09-25 19:08 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 ` [PATCH RFC 4/9] iommufd/selftest: Add hypervisor external attach backend Jacob Pan
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 ` Jacob Pan [this message]
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-10-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.