mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
To: Mukesh R <mrathor@linux.microsoft.com>
Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
	iommu@lists.linux.dev, linux-arch@vger.kernel.org,
	easwar.hariharan@linux.microsoft.com, jgg@nvidia.com,
	jacob.pan@linux.microsoft.com, haiyangz@microsoft.com,
	wei.liu@kernel.org, decui@microsoft.com, tglx@kernel.org,
	mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
	x86@kernel.org, hpa@zytor.com, joro@8bytes.org, will@kernel.org,
	robin.murphy@arm.com, arnd@arndb.de
Subject: Re: [PATCH V4 5/5] x86/hyperv: Implement root partition IOMMU kernel only driver
Date: Fri, 6 Nov 2026 13:54:19 -0800	[thread overview]
Message-ID: <f2579646-7f77-4dd2-b7ce-ccf702828610@linux.microsoft.com> (raw)
In-Reply-To: <20261105020106.3496339-6-mrathor@linux.microsoft.com>

On 11/4/2026 18:01, Mukesh R wrote:
> Add a new file to implement a kernel only virtual IOMMU that works
> on Microsoft Hyper-V hypervisor (aka MSHV) in privileged VMs aka
> root VMs. The hypervisor claims the IOMMU upon boot, and this driver
> communicates with it for creating and deleting paging domains, attaching
> of devices, mapping and unmapping of pages, etc. During boot, hypervisor
> automatically creates identity and blocked domains, so there is no need
> to do hypercalls to create them. This is a kernel only driver and only
> supported on baremetal root (and not L1VH root) without any guest passthru
> support. Support for guest device passthru will be added incrementally.
> 
> Signed-off-by: Mukesh R <mrathor@linux.microsoft.com>
> ---
>  arch/x86/kernel/pci-dma.c            |   2 +
>  drivers/iommu/hyperv/Kconfig         |  13 +
>  drivers/iommu/hyperv/Makefile        |   1 +
>  drivers/iommu/hyperv/hv-iommu-root.c | 613 +++++++++++++++++++++++++++
>  include/asm-generic/mshyperv.h       |   3 +
>  include/linux/hyperv.h               |   6 +
>  6 files changed, 638 insertions(+)
>  create mode 100644 drivers/iommu/hyperv/hv-iommu-root.c
> 

<snip>

>  obj-$(CONFIG_HYPERV_PVIOMMU) += hv-iommu-guest.o
> +obj-$(CONFIG_HYPERV_ROOT_IOMMU) += hv-iommu-root.o
> diff --git a/drivers/iommu/hyperv/hv-iommu-root.c b/drivers/iommu/hyperv/hv-iommu-root.c
> new file mode 100644
> index 000000000000..e0e3e9d4385e
> --- /dev/null
> +++ b/drivers/iommu/hyperv/hv-iommu-root.c
> @@ -0,0 +1,613 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hyper-V root vIOMMU driver.
> + * Copyright (C) 2026, Microsoft, Inc.
> + */
> +#include <linux/pci.h>
> +#include <linux/dma-map-ops.h>
> +#include <linux/interval_tree.h>
> +#include <linux/hyperv.h>
> +#include <asm/iommu.h>
> +#include <asm/mshyperv.h>
> +#include "../dma-iommu.h"
> +
> +static dma_addr_t hv_iommu_max_iova;
> +static struct iommu_domain_ops hv_paging_domain_ops;
> +
> +/* IOMMU device that we export to the world. MSHV supports max of one */
> +static struct iommu_device hv_virt_iommu;
> +
> +struct hv_iommu_domain {
> +	struct iommu_domain iommu_dom;
> +	u32 domid_num;			      /* as opposed to domain_id.type */
> +	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_iommu_domain, iommu_dom)
> +
> +struct hv_iommu_mapping {
> +	phys_addr_t paddr;
> +	struct interval_tree_node iova;
> +	u32 flags;
> +};
> +
> +/*
> + * By default, during boot the hypervisor creates one Stage 2 (S2) default
> + * domain. It has two types:
> + *   S2 default: access to entire root partition memory. This for the host
> + *		 maps to IOMMU_DOMAIN_IDENTITY in the iommu subsystem, and
> + *		 is called HV_DEVICE_DOMAIN_ID_S2_DEFAULT in the hypervisor.
> + *		 (Linux renamed to : HV_DEVICE_DOMAIN_ID_DEFAULT)
> + *   S2 NULL: Blocks everything, ie, IOMMU_DOMAIN_BLOCKED in linux.
> + */
> +
> +/*
> + * Create dummy domains to correspond to hypervisor prebuilt default identity
> + * and null domains (dummy because we do not make hypercalls to create them).
> + */
> +static struct hv_iommu_domain hv_def_identity_dom;
> +static struct hv_iommu_domain hv_def_blocked_dom;
> +
> +/*
> + * The hypervisor owns the IOMMU. As such it will choose the best page size
> + * and whenever possible use larger page sizes automatically. The map gpa
> + * hypercall to map the iova however currently uses only 4K pfns as input.
> + * So we specify 4K as the IOMMU PAGE_SIZE.
> + */
> +#define HV_IOMMU_PGSIZES SZ_4K
> +
> +static DEFINE_IDA(hv_iommu_domids);   /* unique numeric ids for new domains */
> +
> +static bool hv_iommu_capable(struct device *dev, enum iommu_cap cap)
> +{
> +	switch (cap) {
> +	case IOMMU_CAP_CACHE_COHERENCY:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}

Seems like this could match the guest driver and just be: return cap == IOMMU_CAP_CACHE_COHERENCY.

<snip>

> +
> +static struct iommu_domain_ops hv_paging_domain_ops = {
> +	.attach_dev = hv_iommu_attach_dev,
> +	.map_pages = hv_iommu_map_pages,
> +	.unmap_pages = hv_iommu_unmap_pages,
> +	.iova_to_phys = hv_iommu_iova_to_phys,
> +	.free = hv_iommu_domain_free,
> +};
> +
> +static struct iommu_ops hv_iommu_ops = {
> +	.capable	    = hv_iommu_capable,
> +	.domain_alloc_paging	= hv_iommu_domain_alloc_paging,
> +	.probe_device	    = hv_iommu_probe_device,
> +	.device_group	    = pci_device_group,
> +	.get_resv_regions   = hv_iommu_get_resv_regions,
> +	.owner		    = THIS_MODULE,
> +	.identity_domain    = &hv_def_identity_dom.iommu_dom,
> +	.blocked_domain     = &hv_def_blocked_dom.iommu_dom,
> +	.release_domain     = &hv_def_blocked_dom.iommu_dom,
> +};
> +
> +static const struct iommu_domain_ops hv_special_domain_ops = {
> +	.attach_dev = hv_iommu_attach_dev,
> +};
> +
> +static void __init hv_initialize_special_domains(void)
> +{
> +	hv_def_identity_dom.iommu_dom.type = IOMMU_DOMAIN_IDENTITY;
> +	hv_def_identity_dom.iommu_dom.ops = &hv_special_domain_ops;
> +	hv_def_identity_dom.iommu_dom.owner = &hv_iommu_ops;
> +	hv_def_identity_dom.domid_num = HV_DEVICE_DOMAIN_ID_DEFAULT; /* 0 */
> +
> +	hv_def_blocked_dom.iommu_dom.type = IOMMU_DOMAIN_BLOCKED;
> +	hv_def_blocked_dom.iommu_dom.ops = &hv_special_domain_ops;
> +	hv_def_blocked_dom.iommu_dom.owner = &hv_iommu_ops;
> +	hv_def_blocked_dom.domid_num = HV_DEVICE_DOMAIN_ID_NULL;    /* INTMAX */
> +}
> +

A net negative 9 lines to initialize these with .field notation, as suggested in v0: https://lore.kernel.org/all/4f7cadc5-817c-4163-bac8-1c02a7c6a862@linux.microsoft.com/

Wasn't super hard to do the 1 forward declaration needed...

diff --git a/drivers/iommu/hyperv/hv-iommu-root.c b/drivers/iommu/hyperv/hv-iommu-root.c
index e0e3e9d4385e2..4e6bf94413458 100644
--- a/drivers/iommu/hyperv/hv-iommu-root.c
+++ b/drivers/iommu/hyperv/hv-iommu-root.c
@@ -12,6 +12,7 @@
 #include "../dma-iommu.h"
 
 static dma_addr_t hv_iommu_max_iova;
+static struct iommu_ops hv_iommu_ops;
 static struct iommu_domain_ops hv_paging_domain_ops;
 
 /* IOMMU device that we export to the world. MSHV supports max of one */
@@ -32,23 +33,6 @@ struct hv_iommu_mapping {
 	u32 flags;
 };
 
-/*
- * By default, during boot the hypervisor creates one Stage 2 (S2) default
- * domain. It has two types:
- *   S2 default: access to entire root partition memory. This for the host
- *		 maps to IOMMU_DOMAIN_IDENTITY in the iommu subsystem, and
- *		 is called HV_DEVICE_DOMAIN_ID_S2_DEFAULT in the hypervisor.
- *		 (Linux renamed to : HV_DEVICE_DOMAIN_ID_DEFAULT)
- *   S2 NULL: Blocks everything, ie, IOMMU_DOMAIN_BLOCKED in linux.
- */
-
-/*
- * Create dummy domains to correspond to hypervisor prebuilt default identity
- * and null domains (dummy because we do not make hypercalls to create them).
- */
-static struct hv_iommu_domain hv_def_identity_dom;
-static struct hv_iommu_domain hv_def_blocked_dom;
-
 /*
  * The hypervisor owns the IOMMU. As such it will choose the best page size
  * and whenever possible use larger page sizes automatically. The map gpa
@@ -488,6 +472,35 @@ static void hv_iommu_get_resv_regions(struct device *dev,
 	iommu_add_resv_x86_msi_region(head);
 }
 
+static const struct iommu_domain_ops hv_special_domain_ops = {
+	.attach_dev = hv_iommu_attach_dev,
+};
+
+/*
+ * By default, during boot the hypervisor creates one Stage 2 (S2) default
+ * domain. It has two types:
+ *   S2 default: access to entire root partition memory. This for the host
+ *		 maps to IOMMU_DOMAIN_IDENTITY in the iommu subsystem, and
+ *		 is called HV_DEVICE_DOMAIN_ID_S2_DEFAULT in the hypervisor.
+ *		 (Linux renamed to : HV_DEVICE_DOMAIN_ID_DEFAULT)
+ *   S2 NULL: Blocks everything, ie, IOMMU_DOMAIN_BLOCKED in linux.
+ * Create dummy domains to correspond to hypervisor prebuilt default identity
+ * and null domains (dummy because we do not make hypercalls to create them).
+ */
+static struct hv_iommu_domain hv_def_identity_dom = {
+	.iommu_dom.type = IOMMU_DOMAIN_IDENTITY,
+	.iommu_dom.ops = &hv_special_domain_ops,
+	.iommu_dom.owner = &hv_iommu_ops,
+	.domid_num = HV_DEVICE_DOMAIN_ID_DEFAULT, /* 0 */
+};
+
+static struct hv_iommu_domain hv_def_blocked_dom = {
+	.iommu_dom.type = IOMMU_DOMAIN_BLOCKED,
+	.iommu_dom.ops = &hv_special_domain_ops,
+	.iommu_dom.owner = &hv_iommu_ops,
+	.domid_num = HV_DEVICE_DOMAIN_ID_NULL,    /* INTMAX */
+};
+
 static struct iommu_domain_ops hv_paging_domain_ops = {
 	.attach_dev = hv_iommu_attach_dev,
 	.map_pages = hv_iommu_map_pages,
@@ -508,23 +521,6 @@ static struct iommu_ops hv_iommu_ops = {
 	.release_domain     = &hv_def_blocked_dom.iommu_dom,
 };
 
-static const struct iommu_domain_ops hv_special_domain_ops = {
-	.attach_dev = hv_iommu_attach_dev,
-};
-
-static void __init hv_initialize_special_domains(void)
-{
-	hv_def_identity_dom.iommu_dom.type = IOMMU_DOMAIN_IDENTITY;
-	hv_def_identity_dom.iommu_dom.ops = &hv_special_domain_ops;
-	hv_def_identity_dom.iommu_dom.owner = &hv_iommu_ops;
-	hv_def_identity_dom.domid_num = HV_DEVICE_DOMAIN_ID_DEFAULT; /* 0 */
-
-	hv_def_blocked_dom.iommu_dom.type = IOMMU_DOMAIN_BLOCKED;
-	hv_def_blocked_dom.iommu_dom.ops = &hv_special_domain_ops;
-	hv_def_blocked_dom.iommu_dom.owner = &hv_iommu_ops;
-	hv_def_blocked_dom.domid_num = HV_DEVICE_DOMAIN_ID_NULL;    /* INTMAX */
-}
-
 /*
  * In general, the hypervisor does not support the capabilities hypercall for
  * root partitions, an exception was made for max_iova_width. For IOMMU cap,
@@ -571,11 +567,6 @@ static int __init hv_root_iommu_init(void)
 
 	hv_iommu_max_iova = DMA_BIT_MASK(caps.max_iova_width);
 
-	/* This must come before iommu_device_register() because the latter
-	 * calls into the hooks.
-	 */
-	hv_initialize_special_domains();
-
 	rc = iommu_device_sysfs_add(iommup, NULL, NULL, "%s", "hyperv-iommu");
 	if (rc) {
 		pr_err("Hyper-V: iommu_device_sysfs_add failed: %d\n", rc);


<snip>

Thanks,
Easwar (he/him)

      reply	other threads:[~2026-11-06 21:54 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-05  2:01 [PATCH V4 0/5] Hyper-V: root VM iommu " Mukesh R
2026-11-05  2:01 ` [PATCH V4 1/5] mshv: Add gfp flags to deposit memory logic Mukesh R
2026-11-05  4:51   ` Easwar Hariharan
2026-11-05  2:01 ` [PATCH V4 2/5] PCI: hv: Export hv_build_devid_type_pci() and change return type Mukesh R
2026-11-06 19:39   ` Easwar Hariharan
2026-11-05  2:01 ` [PATCH V4 3/5] mshv: Import data structs around device domains from hyperv headers Mukesh R
2026-11-05  2:01 ` [PATCH V4 4/5] iommu/hyperv: Only call pviommu init on non-root VMs Mukesh R
2026-11-05  4:34   ` Easwar Hariharan
2026-11-05  2:01 ` [PATCH V4 5/5] x86/hyperv: Implement root partition IOMMU kernel only driver Mukesh R
2026-11-06 21:54   ` Easwar Hariharan [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=f2579646-7f77-4dd2-b7ce-ccf702828610@linux.microsoft.com \
    --to=easwar.hariharan@linux.microsoft.com \
    --cc=arnd@arndb.de \
    --cc=bp@alien8.de \
    --cc=dave.hansen@linux.intel.com \
    --cc=decui@microsoft.com \
    --cc=haiyangz@microsoft.com \
    --cc=hpa@zytor.com \
    --cc=iommu@lists.linux.dev \
    --cc=jacob.pan@linux.microsoft.com \
    --cc=jgg@nvidia.com \
    --cc=joro@8bytes.org \
    --cc=linux-arch@vger.kernel.org \
    --cc=linux-hyperv@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=mrathor@linux.microsoft.com \
    --cc=robin.murphy@arm.com \
    --cc=tglx@kernel.org \
    --cc=wei.liu@kernel.org \
    --cc=will@kernel.org \
    --cc=x86@kernel.org \
    /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®