From nobody Thu Nov 5 05:11:17 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=13.77.154.182 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793844082; b=sxcukLW6Tkm5G5smZXhQpVsw+MiYIjseSxCj6Dtd/tjhB8GB3UwAZPZxovrIO4l2DV+u dVfPJEHgTKoKSVR+IFNoyFtjLYcG76b2uA2aSSNFZMQKqMHRgD69NBdPJPGy8bPgcY37+ zf1N/iG0U+21WL8Gir/O1zA4bPcd2X0ePEVleCLuLvKFISlkBszFvD1+QchRoNkay8RUb VThansLNlEBKAtcAG7WRptsUaVKzpTZmhxeNtNaZ5VPCp3JaPfoAXdJwVWlViuQs+7etf JC5hMnO7Xpm4SyQxjB0Cal1sVxgl5BXN3KoqzXNxSSTWlyhMI2q/BxwBVX2+oJlRTTg== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793844082; h=DMARC-Filter:Received:DKIM-Filter:DKIM-Signature:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-Spam-Status:X-Spam-Checker-Version; bh=4Y6oHXTvDP3D/r6bj3sFJrcNWxTqMhnFq48JU9X+5bk=; b=1nf6yBT9Tl74gOLV0xIPKXO8Sn38AcBJ8fQXepd6rSmM7exp1FKyasaWTXjDusUSigS2 XMgRrarXEN4HD2rIew3ZUXL0XmkJuzzJHtXVJKSbuqU2dkCwDk9fMEjEnnoOgdo9dERJL En5iCzsRHlVXfE2zpDQCg8pz9gq6MAbclz5MNRkOWB2zhLJTJCplxnbIgfHb2HwLFU5Sm K6HLs+FRSY7RrmU95A2PEsWaDbLwnclwsJvELXC/+o7dNzheDhf+5blhLIw8qh9bvdUqQ 7/LS9W/ZfK1/FfF9JcqNIyZdL3ggnDCQItjwY5caAA8XVHwF2vu4a5b1eAjCFgDvHpw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=J4gTxX0+; arc=none smtp.remote-ip=13.77.154.182 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4B915606D825; Thu, 5 Nov 2026 02:01:21 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 4B915606D825 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=J4gTxX0+ Received: from mrdev.corp.microsoft.com (192-184-212-33.fiber.dynamic.sonic.net [192.184.212.33]) by linux.microsoft.com (Postfix) with ESMTPSA id A18E220B7167; Wed, 4 Nov 2026 18:01:04 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com A18E220B7167 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1793844065; bh=4Y6oHXTvDP3D/r6bj3sFJrcNWxTqMhnFq48JU9X+5bk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=J4gTxX0+ZbXK5cEVNQtpaKI3EU+BNGmawIBDDLeG5Gq8G8lKD15gMPCNV2+hKi1r/ KJdbXgEtgNCQzdRWP1rUOlTJVX0/wQ/mZ7FiBN++0I6IbjYF5ES/6mDk+0iCCVC06G zFbzE+I7wOULKPC5MZJR02Hn4zm/2ZaiEYFyLBrA= From: Mukesh R To: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux.dev, linux-arch@vger.kernel.org Cc: 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, mrathor@linux.microsoft.com Subject: [PATCH V4 1/5] mshv: Add gfp flags to deposit memory logic Date: Wed, 4 Nov 2026 18:01:02 -0800 Message-ID: <20261105020106.3496339-2-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20261105020106.3496339-1-mrathor@linux.microsoft.com> References: <20261105020106.3496339-1-mrathor@linux.microsoft.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently GFP_KERNEL is hard coded in hv_alloc_dep_pages(). Add gfp_flags parameter so it can be called in atomic context from map_pages callback in struct iommu_domain_ops and other places in future. A new function hv_deposit_memory_gfp() is added to facilitate this, so hv_deposit_memory() can continue to function as is. Signed-off-by: Mukesh R Reviewed-by: Easwar Hariharan --- drivers/hv/hv_proc.c | 20 +++++++++++--------- include/asm-generic/mshyperv.h | 14 +++++++++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/hv/hv_proc.c b/drivers/hv/hv_proc.c index f811bb5c42a1..8380e9050f46 100644 --- a/drivers/hv/hv_proc.c +++ b/drivers/hv/hv_proc.c @@ -22,7 +22,7 @@ * Returns: number of pages allocated or -ENOMEM */ static int hv_alloc_dep_pages(int node, u64 *pfna, u64 *lastpfnp, int num_= pages, - bool single) + bool single, gfp_t gfp_flags) { struct page *page; int num_allocd, count =3D 0; @@ -37,7 +37,6 @@ static int hv_alloc_dep_pages(int node, u64 *pfna, u64 *l= astpfnp, int num_pages, while (num_pages) { /* Find highest order we can actually allocate */ int order =3D 31 - __builtin_clz(num_pages); - gfp_t gfp_flags =3D GFP_KERNEL; =20 if (!single) gfp_flags |=3D __GFP_NOWARN; @@ -81,7 +80,8 @@ static int hv_alloc_dep_pages(int node, u64 *pfna, u64 *l= astpfnp, int num_pages, * so we must split that also. Lastly, HV_MAX_CONTIGUOUS_ALLOCATION_PAGES = is * not guaranteed to always be power of 2. */ -static int hv_call_deposit_pages(int node, u64 partition_id, bool contiguo= us) +static int hv_call_deposit_pages(int node, u64 partition_id, bool contiguo= us, + gfp_t gfp_flags) { struct hv_deposit_memory *hc_input; int i, rc, num_pages; @@ -98,14 +98,15 @@ static int hv_call_deposit_pages(int node, u64 partitio= n_id, bool contiguous) num_pages =3D HV_DEPOSIT_MAX; } =20 - hc_input =3D (struct hv_deposit_memory *)get_zeroed_page(GFP_KERNEL); + hc_input =3D (struct hv_deposit_memory *)get_zeroed_page(gfp_flags); if (hc_input =3D=3D NULL) return -ENOMEM; =20 hc_input->partition_id =3D partition_id; pfna =3D hc_input->gpa_page_list; =20 - rc =3D hv_alloc_dep_pages(node, pfna, &lastpfn, num_pages, contiguous); + rc =3D hv_alloc_dep_pages(node, pfna, &lastpfn, num_pages, contiguous, + gfp_flags); if (rc < 0) goto out_free; =20 @@ -159,7 +160,7 @@ static int hv_call_deposit_pages(int node, u64 partitio= n_id, bool contiguous) return rc; } =20 -int hv_deposit_memory_node(int node, u64 pt_id, u64 hv_status) +int hv_deposit_memory_node(int node, u64 pt_id, u64 hv_status, gfp_t gfp_f= lags) { int result =3D hv_result(hv_status); bool contiguous =3D false; @@ -179,7 +180,7 @@ int hv_deposit_memory_node(int node, u64 pt_id, u64 hv_= status) result =3D=3D HV_STATUS_INSUFFICIENT_CONTIGUOUS_ROOT_MEMORY) contiguous =3D true; =20 - return hv_call_deposit_pages(node, pt_id, contiguous); + return hv_call_deposit_pages(node, pt_id, contiguous, gfp_flags); } EXPORT_SYMBOL_GPL(hv_deposit_memory_node); =20 @@ -232,7 +233,7 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u3= 2 apic_id) break; } ret =3D hv_deposit_memory_node(node, hv_current_partition_id, - status); + status, GFP_KERNEL); } while (!ret); =20 return ret; @@ -266,7 +267,8 @@ int hv_call_create_vp(int node, u64 partition_id, u32 v= p_index, u32 flags) } break; } - ret =3D hv_deposit_memory_node(node, partition_id, status); + ret =3D hv_deposit_memory_node(node, partition_id, status, + GFP_KERNEL); =20 } while (!ret); =20 diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 4c9f3353074a..2584e693d7b3 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -346,7 +346,7 @@ static inline bool hv_parent_partition(void) } =20 bool hv_result_needs_memory(u64 status); -int hv_deposit_memory_node(int node, u64 partition_id, u64 status); +int hv_deposit_memory_node(int node, u64 partition_id, u64 status, gfp_t g= fp); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); int hv_call_notify_all_processors_started(void); bool hv_lp_exists(u32 lp_index); @@ -357,7 +357,8 @@ static inline bool hv_root_partition(void) { return fal= se; } static inline bool hv_l1vh_partition(void) { return false; } static inline bool hv_parent_partition(void) { return false; } static inline bool hv_result_needs_memory(u64 status) { return false; } -static inline int hv_deposit_memory_node(int node, u64 partition_id, u64 s= tatus) +static inline int hv_deposit_memory_node(int node, u64 partition_id, u64 s= tatus, + gfp_t gfp) { return -EOPNOTSUPP; } @@ -379,9 +380,16 @@ static inline int hv_call_create_vp(int node, u64 part= ition_id, u32 vp_index, u3 } #endif /* CONFIG_MSHV_ROOT */ =20 +/* Generic deposit function with no default numa node and GFP_KERNEL */ static inline int hv_deposit_memory(u64 partition_id, u64 status) { - return hv_deposit_memory_node(NUMA_NO_NODE, partition_id, status); + return hv_deposit_memory_node(NUMA_NO_NODE, partition_id, status, + GFP_KERNEL); +} + +static inline int hv_deposit_memory_gfp(u64 partition_id, u64 status, gfp_= t gfp) +{ + return hv_deposit_memory_node(NUMA_NO_NODE, partition_id, status, gfp); } =20 #if IS_ENABLED(CONFIG_HYPERV_VTL_MODE) --=20 2.51.2.vfs.0.1 From nobody Thu Nov 5 05:11:17 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=13.77.154.182 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793844082; b=MzWj5Qx30lhNUGj+QMVY5PEIjHgFA7PKKZIQKH76HvO1YOvm8pYwIgBf6YOnFOAOTE6O CnJErkRELSRk/2wSX5Vf1TOxRakjk4svowOZQ+1rX1NlypLEz9JTQd6p0WsX1jt6YQUTq HzfvA6snSGYuC4uvUhlOMPbqPsq5vJDAN5KFJPjwSYqPpXDQCttnktK46X02Eg4Qxp6tp 9OlUI/x+YJKWz5bfjeqfkCtOaL6S+sd7e83zTqOAXgCXuREgTDcyDl8f2me9MCdl0KZCJ uW2+h7WDc8ag9GvKnbvm+V32xfqAUT2w479bO6qXcEgG5IEHNGt/mVdHeqVDrKkAaRA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793844082; h=DMARC-Filter:Received:DKIM-Filter:DKIM-Signature:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-Spam-Status:X-Spam-Checker-Version; bh=0EHmjzbFR/yNJP1Nxx0dUmYDywoFEpvvyJwEhuOQ678=; b=TiyVt28vV2fHM9ZJneVVg5rGNfCMxm9GGyZGgNEeCYC+HiKmMFgYScTQFATgIwIUYqCX JRN/B1Tc1MkNSkZJSsWZDZNvJrPcOt0SG04c//0uhyPtJBXULtLodo6VNqQrvwkJL/S1R VeNuvYi5NWSrW9EXNyA6xbDd5I1VCoIqhU4Y0CcPAFbNnPK8UJa718EhMWAmGfeYZ6+S1 sRWNUHTYtHWUtV6DYpsvq0jw072+0dfkVXjVDriyTi4LrgOGrESauRpnYxlArA1rLD06h 2dXH8sRMD3PLI4G9VKPepRcXD70R7tpJU66MpLqq9uSerihHOVcayr71CwntTeebG9w== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b="gQ/70QIV"; arc=none smtp.remote-ip=13.77.154.182 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 23A8C6082BFA; Thu, 5 Nov 2026 02:01:22 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 23A8C6082BFA Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=gQ/70QIV Received: from mrdev.corp.microsoft.com (192-184-212-33.fiber.dynamic.sonic.net [192.184.212.33]) by linux.microsoft.com (Postfix) with ESMTPSA id ACEC120B7168; Wed, 4 Nov 2026 18:01:05 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com ACEC120B7168 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1793844066; bh=0EHmjzbFR/yNJP1Nxx0dUmYDywoFEpvvyJwEhuOQ678=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gQ/70QIVNFwhg9zK7Wyn4GFjEbn85pDKpy0w/LUnf6m4VXX+CcBjk95WwoCJlnD0P w8bah8MEu00otZuOHjTS7P8b91woR5wFAj+SCs9SM/K6MnJo7ptJ0fwPxR3XJ4/sZP ohsEgnD3iaS8h7sgamNDfu3qa9utDh8FNK9ryDQQ= From: Mukesh R To: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux.dev, linux-arch@vger.kernel.org Cc: 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, mrathor@linux.microsoft.com Subject: [PATCH V4 2/5] PCI: hv: Export hv_build_devid_type_pci() and change return type Date: Wed, 4 Nov 2026 18:01:03 -0800 Message-ID: <20261105020106.3496339-3-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20261105020106.3496339-1-mrathor@linux.microsoft.com> References: <20261105020106.3496339-1-mrathor@linux.microsoft.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" On Hyper-V, most hypercalls related to device domain management like attach device to a device domain, or to map/unmap regions, interrupts, etc need a device ID as a parameter. So, make hv_build_devid_type_pci() public and change return type to u64 to enforce its size. Signed-off-by: Mukesh R Reviewed-by: Souradeep Chakrabarti --- arch/x86/hyperv/irqdomain.c | 9 +++++---- arch/x86/include/asm/mshyperv.h | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c index b3ad50a874dc..8780573a4332 100644 --- a/arch/x86/hyperv/irqdomain.c +++ b/arch/x86/hyperv/irqdomain.c @@ -112,7 +112,7 @@ static int get_rid_cb(struct pci_dev *pdev, u16 alias, = void *data) return 0; } =20 -static union hv_device_id hv_build_devid_type_pci(struct pci_dev *pdev) +u64 hv_build_devid_type_pci(struct pci_dev *pdev) { int pos; union hv_device_id hv_devid; @@ -172,8 +172,9 @@ static union hv_device_id hv_build_devid_type_pci(struc= t pci_dev *pdev) } =20 out: - return hv_devid; + return hv_devid.as_uint64; } +EXPORT_SYMBOL_GPL(hv_build_devid_type_pci); =20 /* * hv_map_msi_interrupt() - Map the MSI IRQ in the hypervisor. @@ -196,7 +197,7 @@ int hv_map_msi_interrupt(struct irq_data *data, =20 msidesc =3D irq_data_get_msi_desc(data); pdev =3D msi_desc_to_pci_dev(msidesc); - hv_devid =3D hv_build_devid_type_pci(pdev); + hv_devid.as_uint64 =3D hv_build_devid_type_pci(pdev); cpu =3D cpumask_first(irq_data_get_effective_affinity_mask(data)); =20 return hv_map_interrupt(hv_devid, false, cpu, cfg->vector, @@ -271,7 +272,7 @@ static int hv_unmap_msi_interrupt(struct pci_dev *pdev, { union hv_device_id hv_devid; =20 - hv_devid =3D hv_build_devid_type_pci(pdev); + hv_devid.as_uint64 =3D hv_build_devid_type_pci(pdev); return hv_unmap_interrupt(hv_devid.as_uint64, irq_entry); } =20 diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyper= v.h index 20d947c2c758..24b19cd28a05 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -248,6 +248,12 @@ void hv_crash_asm_end(void); static inline void hv_root_crash_init(void) {} #endif /* CONFIG_MSHV_ROOT && CONFIG_CRASH_DUMP */ =20 +#ifdef CONFIG_PCI_MSI +u64 hv_build_devid_type_pci(struct pci_dev *pdev); +#else +static inline u64 hv_build_devid_type_pci(struct pci_dev *pdev) { return 0= ; } +#endif + #else /* CONFIG_HYPERV */ static inline void hyperv_init(void) {} static inline void hyperv_setup_mmu_ops(void) {} --=20 2.51.2.vfs.0.1 From nobody Thu Nov 5 05:11:17 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=13.77.154.182 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793844083; b=cLAdAazgXKtCX/Nd+KH9geh34A1rxfVhQYCTM0i+SWJm8AWKC4RSw1Iz6kt8KFWZHKh4 sHpogvSVLi/27tTcvgGq0CFyZTN/HLpmBjCSriuBOYxyc8Q//2gs0mlByx8A01NKKPvX9 k/qEELdONDBdvEFRSJvChRzbKSV3OWQojoq3USnppVNmjCx/UjqPCJ0mK/fXm0KOWyHz1 VdfMi0z/I2FaKZhk/n1eyGYW9gniESiF6aN6RK/ZhESwUwnrSUPqWRX9BXi+4jkDf94d5 tH+n9Jakr73kh2Sf71YuVI4NOEA9R1HVqWn7w/xFjqQWKTtZaahH3to2NoXZowivOFQ== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793844083; h=DMARC-Filter:Received:DKIM-Filter:DKIM-Signature:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-Spam-Status:X-Spam-Checker-Version; bh=VCBsFynuOrE8eD7Pz2S8sWwbPOUUcj3FdysVp5dx9ts=; b=Dt9/qZOekMqTiV9Lwbw2rX0xPjoohGVgUl8zCryUTRLSE6SsjJNwWrCWTrtVskS7ejbD cg7pUyEK+DUzJk/8V+bwCIba2msjuAbTzB40GkMYZ8yVvvE5uc7mJq2Ne3HwJrH2h4U0c QTZDdNLvbejJII92b3qiJI9y+9t6q+Y5Y5ZO8UJsy9BI+wI5mUpy9rkA3d/bB03zPCpMt bEhTn8uW43hB/C6GZqehezJAhbwWQVhoGfgiIjWubmcO/zsTpYTlE4boxOsS6bToknYS5 Y1h8C/GDsNnrrpVWhF3KPy2A9Lc51WOn7FWHusZwt0kj7I3e8bJqpxQaMvASq2rkCDw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=F9btZDhp; arc=none smtp.remote-ip=13.77.154.182 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 116A8606D829; Thu, 5 Nov 2026 02:01:23 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 116A8606D829 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=F9btZDhp Received: from mrdev.corp.microsoft.com (192-184-212-33.fiber.dynamic.sonic.net [192.184.212.33]) by linux.microsoft.com (Postfix) with ESMTPSA id AD3E320B7169; Wed, 4 Nov 2026 18:01:06 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com AD3E320B7169 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1793844067; bh=VCBsFynuOrE8eD7Pz2S8sWwbPOUUcj3FdysVp5dx9ts=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F9btZDhpmpksZ8VkM5u+rHtCl4ZJIbrll4SasJlVFien2zuv448KSS6E9tLLmmIOr KNWXwR/OPSMAgf/gnyZaUwQtT6Tby3M29vgcYjGWJ3tVzfZb/iT7P4Au6Aq4HOz0Y2 NjLdr4pFEhN82j5sGXyD3BSubG0sT9DZPbvKCxbs= From: Mukesh R To: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux.dev, linux-arch@vger.kernel.org Cc: 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, mrathor@linux.microsoft.com Subject: [PATCH V4 3/5] mshv: Import data structs around device domains from hyperv headers Date: Wed, 4 Nov 2026 18:01:04 -0800 Message-ID: <20261105020106.3496339-4-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20261105020106.3496339-1-mrathor@linux.microsoft.com> References: <20261105020106.3496339-1-mrathor@linux.microsoft.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Copy/import from Hyper-V public headers, definitions and hypercalls that are related to checking for device domain availibility, and mapping and unmapping of device pages. Signed-off-by: Mukesh R Reviewed-by: Easwar Hariharan --- include/hyperv/hvgdk_mini.h | 4 ++++ include/hyperv/hvhdk_mini.h | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h index 966945f0504f..606b16f43e16 100644 --- a/include/hyperv/hvgdk_mini.h +++ b/include/hyperv/hvgdk_mini.h @@ -327,6 +327,8 @@ union hv_hypervisor_version_info { /* stimer Direct Mode is available */ #define HV_STIMER_DIRECT_MODE_AVAILABLE BIT(19) =20 +#define HV_DEVICE_DOMAIN_AVAILABLE BIT(24) + /* * Implementation recommendations. Indicates which behaviors the hypervisor * recommends the OS implement for optimal performance. @@ -489,6 +491,8 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_AS= SIST_PAGE */ #define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0 #define HVCALL_CREATE_DEVICE_DOMAIN 0x00b1 #define HVCALL_ATTACH_DEVICE_DOMAIN 0x00b2 +#define HVCALL_MAP_DEVICE_GPA_PAGES 0x00b3 +#define HVCALL_UNMAP_DEVICE_GPA_PAGES 0x00b4 #define HVCALL_SIGNAL_EVENT_DIRECT 0x00c0 #define HVCALL_POST_MESSAGE_DIRECT 0x00c1 #define HVCALL_DISPATCH_VP 0x00c2 diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index 57b70efe60d4..bb42b81df848 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -549,6 +549,7 @@ union hv_device_id { /* HV_DEVICE_ID */ } __packed; =20 /* Device domain types */ +#define HV_DEVICE_DOMAIN_TYPE_S2 0 /* HV_DEVICE_DOMAIN_ID_TYPE_S2 */ #define HV_DEVICE_DOMAIN_TYPE_S1 1 /* HV_DEVICE_DOMAIN_ID_TYPE_S1 */ =20 /* ID for default domain and NULL domain */ @@ -657,6 +658,20 @@ struct hv_output_get_iommu_capabilities { u64 pgsize_bitmap; } __packed; =20 +struct hv_input_map_device_gpa_pages { /* HV_INPUT_MAP_DEVICE_GPA_PAGES */ + struct hv_input_device_domain device_domain; + union hv_input_vtl target_vtl; + u8 padding[3]; + u32 map_flags; + u64 target_device_va_base; + u64 gpa_page_list[]; +} __packed; + +struct hv_input_unmap_device_gpa_pages { /* HV_INPUT_UNMAP_DEVICE_GPA_PAG= ES */ + struct hv_input_device_domain device_domain; + u64 target_device_va_base; +} __packed; + /* HV_LOGICAL_DEVICE_PROPERTY_CODE */ enum hv_logical_device_property_code { HV_LOGICAL_DEVICE_PROPERTY_PVIOMMU =3D 10, --=20 2.51.2.vfs.0.1 From nobody Thu Nov 5 05:11:17 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=13.77.154.182 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793844084; b=v5WDMZ7LQVlvq/aC947ZLunwcP2cOdxrW1VdKiP+F/y4EMYFwb62gfjXZwtsWsMqXIp0 X9J/lYaOhj1w6DnYUnhUcs6IbCBdtRwwAPsd3boYdwE91ODIilLg/97PWTnl6geB1MHYr bPR/6jE3zX/40OqP5jCxt3/d3gdwLlULO6eE+WshfkFMvwQhEnWuTPhJZivFIlq0ioQFb 56RtaKo+yOIzAPAoceZJc4WVQNRifdRNWzjiA+hNvUmKmA3yVoozwGmPLwsd3zk7q1BgB VLLvIm3eDlY47XOcQJ5mUx5G96HdbxQu1R2Z8+YbVhouDciEcrGwC9EM3V+BoHXMGkw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793844084; h=DMARC-Filter:Received:DKIM-Filter:DKIM-Signature:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-Spam-Status:X-Spam-Checker-Version; bh=f7gzFtKMZJGCCgUHzye6RXzDrkC+83Rfcf2joF+exy8=; b=4D6aHa0GOw4VptS/XipjPpiHPddtXHvXHvwEnX48vdMGb8vnW0SVH1jzlIfxOcSFTuLM mgQyp2vu/Gmz9yllBBnMappM0Mu7bfbBtxh3bAJVP1b61bhuZdk9x3+QvG8rkSrxSkUFO vVb8SIjf8XvNPTDTwUlbL1l6hRS8E4Ya56EbzbU5quDmfZXqYVH1BiwMFdb6xs7OwF0Cf mMvmOlTtAgvlfAGPDd1gI3/Y4Kru/fsln3z1+0FQ+f0fW7XodDIC75nGEmzDDoLWwgsV0 KCTa9gELpqEmM43LiW6QsoXQyj8g/RexoVaQlhd7f97LZevRYBgbbtltVVGVCVjkX4w== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=CJt2N4Yo; arc=none smtp.remote-ip=13.77.154.182 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 5A08760A260D; Thu, 5 Nov 2026 02:01:24 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 5A08760A260D Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=CJt2N4Yo Received: from mrdev.corp.microsoft.com (192-184-212-33.fiber.dynamic.sonic.net [192.184.212.33]) by linux.microsoft.com (Postfix) with ESMTPSA id A36C420B716A; Wed, 4 Nov 2026 18:01:07 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com A36C420B716A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1793844068; bh=f7gzFtKMZJGCCgUHzye6RXzDrkC+83Rfcf2joF+exy8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CJt2N4YoIWRTliVGOS++r10H/i731xsXK4g86sYPNo6e958A5Qm0IqCs2oG3vuL9y 5YmurhDuCIz51VNleK/2AtQZO5GpAUNoHGLxwjo4URB955fUHDR8BkuRFS9G3VqgwJ ncgN29kAFX/Qillcv7vSfjkULdTH8tHsSecOJruY= From: Mukesh R To: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux.dev, linux-arch@vger.kernel.org Cc: 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, mrathor@linux.microsoft.com Subject: [PATCH V4 4/5] iommu/hyperv: Only call pviommu init on non-root VMs Date: Wed, 4 Nov 2026 18:01:05 -0800 Message-ID: <20261105020106.3496339-5-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20261105020106.3496339-1-mrathor@linux.microsoft.com> References: <20261105020106.3496339-1-mrathor@linux.microsoft.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Next patch introduces root iommu init. Since both can co-exist, set x86 iommu_init to pviommu init only on non-root VMs. Also, rename hv_iommu_init() to hv_pviommu_init(). Lastly, check for null iommu_init before assignement as suggested by one of the pviommu authors. Signed-off-by: Mukesh R Reviewed-by: Easwar Hariharan --- arch/x86/hyperv/hv_init.c | 3 ++- arch/x86/include/asm/mshyperv.h | 2 +- drivers/iommu/hyperv/hv-iommu-guest.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index b90e6f487533..ea50708c3ae6 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -580,7 +580,8 @@ void __init hyperv_init(void) x86_init.timers.setup_percpu_clockev =3D hv_stimer_setup_percpu_clockev; =20 #ifdef CONFIG_HYPERV_PVIOMMU - x86_init.iommu.iommu_init =3D hv_iommu_init; + if (!hv_root_partition()) + x86_init.iommu.iommu_init =3D hv_pviommu_init; #endif =20 hv_apic_init(); diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyper= v.h index 24b19cd28a05..c95d75ae645e 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -320,7 +320,7 @@ static inline void __mshv_vtl_return_call(struct mshv_v= tl_cpu_context *vtl0) {} #endif =20 #ifdef CONFIG_HYPERV_PVIOMMU -int __init hv_iommu_init(void); +int __init hv_pviommu_init(void); #endif =20 #include diff --git a/drivers/iommu/hyperv/hv-iommu-guest.c b/drivers/iommu/hyperv/h= v-iommu-guest.c index 876a22f21f1e..a40a8ceb3261 100644 --- a/drivers/iommu/hyperv/hv-iommu-guest.c +++ b/drivers/iommu/hyperv/hv-iommu-guest.c @@ -601,7 +601,7 @@ hv_init_iommu_device(struct hv_iommu_dev *hv_iommu, hv_iommu_device =3D hv_iommu; } =20 -int __init hv_iommu_init(void) +int __init hv_pviommu_init(void) { u64 status; int ret =3D 0; --=20 2.51.2.vfs.0.1 From nobody Thu Nov 5 05:11:17 2026 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=13.77.154.182 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793844086; b=MrtRX7z0CMx6V2VHv5/ep1IUVQpXF27GvdPoWZgUHyD7EGVUX8VJrkcenLAG2dXhRL2b gTe8ahSOdw/sp4qIkBpBF92x2EMt+Jros7DlvKJD2xrz6T1BizrWwdfVU2aBrQrVPTrwE 5mLbbQD0clmY7XwA19zwjFcIdFofiw5JZuWc7yi+zaZD8xX5Sj8CTUt4b++v70pw8iXw4 dhDM25zNBEBjOJ68GuVs0lEn5GyH7nax0oJBzk2JW8l47NnxRB3zGp1zez7A2FUHo+BWC 2Cl9UGK6DJnhxJjsexNkuo//lR3fQanAp+llLplmVjEndq5w8EDV6AyOPhai767JzJA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793844086; h=DMARC-Filter:Received:DKIM-Filter:DKIM-Signature:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-Spam-Status:X-Spam-Checker-Version; bh=oYrzJfqmmqJFvNW03XOLg+PyRcH3A1ss8Lwc/jedsYM=; b=wi33PGwYvvj2FkX77v48JFHPoFJJ64jPv3BOB8egst06VaMJ2erfOfPp5x8MVEzrRBh8 CSDEFN1mMsxTY/hvIpHsEZIIyCI7+KFm221MUf0TEp2XcKBKiSJy2CsNa6xaHwRxH5QtK wep22j7l4hXMUJHEJeVxHOrBRA96czu7IWR2HfXPnXeNy2nZfEWh565/wNQa98kr5n4WY WXTdd0W9KHRfQha5NXLXQ4OyhxobIDRoe0bc5cC8lyeb8j6Ssk3CAzFOMaPI955sERaNg JvnlrVqFkdQ4w2qaNIyRgGIyduGT/pRAlCP1y+Kub/JwKhAPh9zrKwnB0JMo6p47MzQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=niJMKsId; arc=none smtp.remote-ip=13.77.154.182 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3299D60A3801; Thu, 5 Nov 2026 02:01:26 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 3299D60A3801 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.a=rsa-sha256 header.s=default header.b=niJMKsId Received: from mrdev.corp.microsoft.com (192-184-212-33.fiber.dynamic.sonic.net [192.184.212.33]) by linux.microsoft.com (Postfix) with ESMTPSA id E356320B716B; Wed, 4 Nov 2026 18:01:08 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com E356320B716B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1793844069; bh=oYrzJfqmmqJFvNW03XOLg+PyRcH3A1ss8Lwc/jedsYM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=niJMKsIdY+FYtuD916jdXRSTaSXFLUjzZp1p3GBQpSLSoaAfFAa0+rK7lDF6p5TIy fO869nqvMzqkAAP+6BwxO89Tp9rcu4Bik9wAzk7sPev7wPzLwyBGcAKmbCW9lYh9Yc /0cmKd96TWsJ3VWUNJppU+ydgpcnzWhx/8eh3hy8= From: Mukesh R To: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org, iommu@lists.linux.dev, linux-arch@vger.kernel.org Cc: 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, mrathor@linux.microsoft.com Subject: [PATCH V4 5/5] x86/hyperv: Implement root partition IOMMU kernel only driver Date: Wed, 4 Nov 2026 18:01:06 -0800 Message-ID: <20261105020106.3496339-6-mrathor@linux.microsoft.com> X-Mailer: git-send-email 2.51.2.vfs.0.1 In-Reply-To: <20261105020106.3496339-1-mrathor@linux.microsoft.com> References: <20261105020106.3496339-1-mrathor@linux.microsoft.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 75cf8f6ae8cd..560d8a4f6e1d 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -8,6 +8,7 @@ #include #include #include +#include =20 #include #include @@ -103,6 +104,7 @@ void __init pci_iommu_alloc(void) gart_iommu_hole_init(); amd_iommu_detect(); detect_intel_iommu(); + hv_root_iommu_detect(); swiotlb_init(x86_swiotlb_enable, x86_swiotlb_flags); } =20 diff --git a/drivers/iommu/hyperv/Kconfig b/drivers/iommu/hyperv/Kconfig index f2f993f647c3..6ba5125ed501 100644 --- a/drivers/iommu/hyperv/Kconfig +++ b/drivers/iommu/hyperv/Kconfig @@ -13,3 +13,16 @@ config HYPERV_PVIOMMU Microsoft Hyper-V. Provides DMA remapping and IOTLB flush support to enable DMA isolation for PCI devices assigned to the guest. + +config HYPERV_ROOT_IOMMU + bool "Hyper-V IOMMU Device in root partition" + depends on HYPERV && X86 && PCI_MSI && MSHV_ROOT + select IOMMU_API + default HYPERV + help + This enables Hyper-V pseudo IOMMU device. When running as privileged + VM aka root on Microsoft Hyper-V hypervisor, this must be enabled + for doing any PCI passthru of devices to guest VMs. This applies to + both PFs and VFs. When enabling this, it is best to disable amd/intel + iommus via: intel_iommu=3Doff amd_iommu=3Doff as the hypervisor really + owns the iommu. diff --git a/drivers/iommu/hyperv/Makefile b/drivers/iommu/hyperv/Makefile index d3b2a259439c..04c66b4409a3 100644 --- a/drivers/iommu/hyperv/Makefile +++ b/drivers/iommu/hyperv/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IRQ_REMAP) +=3D hv-irq-remap-x86.o obj-$(CONFIG_HYPERV_PVIOMMU) +=3D hv-iommu-guest.o +obj-$(CONFIG_HYPERV_ROOT_IOMMU) +=3D 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 +#include +#include +#include +#include +#include +#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 ident= ity + * and null domains (dummy because we do not make hypercalls to create the= m). + */ +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 domain= s */ + +static bool hv_iommu_capable(struct device *dev, enum iommu_cap cap) +{ + switch (cap) { + case IOMMU_CAP_CACHE_COHERENCY: + return true; + default: + return false; + } +} + +static int hv_iommu_add_tree_mapping(struct hv_iommu_domain *hvdom, ulong = iova, + phys_addr_t paddr, size_t size, u32 flags, + gfp_t gfp) +{ + ulong irqflags; + struct hv_iommu_mapping *mapping; + + mapping =3D kzalloc_obj(struct hv_iommu_mapping, gfp); + if (!mapping) + return -ENOMEM; + + mapping->paddr =3D paddr; + mapping->iova.start =3D iova; + mapping->iova.last =3D iova + size - 1; + mapping->flags =3D flags; + + spin_lock_irqsave(&hvdom->mappings_lock, irqflags); + interval_tree_insert(&mapping->iova, &hvdom->mappings_tree); + spin_unlock_irqrestore(&hvdom->mappings_lock, irqflags); + + return 0; +} + +/* If both size and iova are 0, then last =3D ULONG_MAX will remove everyt= hing */ +static size_t hv_iommu_del_tree_mappings(struct hv_iommu_domain *hvdom, + ulong iova, size_t size) +{ + ulong flags; + size_t unmapped =3D 0; + ulong last =3D iova + size - 1; + struct hv_iommu_mapping *mapping =3D NULL; + struct interval_tree_node *node, *next; + + spin_lock_irqsave(&hvdom->mappings_lock, flags); + next =3D interval_tree_iter_first(&hvdom->mappings_tree, iova, last); + while (next) { + node =3D next; + mapping =3D container_of(node, struct hv_iommu_mapping, iova); + next =3D interval_tree_iter_next(node, iova, last); + + /* Splitting of a mapping is not supported at present */ + if (mapping->iova.start < iova) + break; + + unmapped +=3D mapping->iova.last - mapping->iova.start + 1; + + interval_tree_remove(node, &hvdom->mappings_tree); + kfree(mapping); + } + spin_unlock_irqrestore(&hvdom->mappings_lock, flags); + + return unmapped; +} + +/* Create a new device domain in the hypervisor */ +static int hv_iommu_create_hyp_devdom(struct hv_iommu_domain *hvdom) +{ + u64 status; + struct hv_input_device_domain *ddp; + struct hv_input_create_device_domain *input; + ulong flags; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + ddp =3D &input->device_domain; + ddp->partition_id =3D HV_PARTITION_ID_SELF; + ddp->domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + ddp->domain_id.id =3D hvdom->domid_num; + + input->create_device_domain_flags.forward_progress_required =3D 1; + input->create_device_domain_flags.inherit_owning_vtl =3D 0; + + status =3D hv_do_hypercall(HVCALL_CREATE_DEVICE_DOMAIN, input, NULL); + + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + return hv_result_to_errno(status); +} + +static struct iommu_domain *hv_iommu_domain_alloc_paging(struct device *de= v) +{ + struct hv_iommu_domain *hvdom; + int rc; + + if (!dev_is_pci(dev)) + return NULL; + + hvdom =3D kzalloc_obj(struct hv_iommu_domain); + if (hvdom =3D=3D NULL) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&hvdom->mappings_lock); + hvdom->mappings_tree =3D RB_ROOT_CACHED; + + rc =3D ida_alloc_range(&hv_iommu_domids, + HV_DEVICE_DOMAIN_ID_DEFAULT + 1, + HV_DEVICE_DOMAIN_ID_NULL - 1, GFP_KERNEL); + if (rc < 0) + goto out_err; + + hvdom->domid_num =3D rc; + hvdom->iommu_dom.pgsize_bitmap =3D HV_IOMMU_PGSIZES; + hvdom->iommu_dom.geometry.aperture_start =3D 0; + hvdom->iommu_dom.geometry.aperture_end =3D hv_iommu_max_iova; + hvdom->iommu_dom.geometry.force_aperture =3D true; + hvdom->iommu_dom.ops =3D &hv_paging_domain_ops; + + rc =3D hv_iommu_create_hyp_devdom(hvdom); + if (rc) + goto out_err1; + + return &hvdom->iommu_dom; + +out_err1: + ida_free(&hv_iommu_domids, hvdom->domid_num); +out_err: + kfree(hvdom); + return ERR_PTR(rc); +} + +static void hv_iommu_domain_free(struct iommu_domain *immdom) +{ + ulong flags; + u64 status; + struct hv_input_delete_device_domain *input; + struct hv_input_device_domain *ddp; + struct hv_iommu_domain *hvdom =3D to_hv_domain(immdom); + + /* Cleanup any remaining. 0 for size results in ULONG_MAX as the last */ + hv_iommu_del_tree_mappings(hvdom, 0, 0); + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + ddp =3D &input->device_domain; + memset(input, 0, sizeof(*input)); + + ddp->partition_id =3D HV_PARTITION_ID_SELF; + ddp->domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + ddp->domain_id.id =3D hvdom->domid_num; + + status =3D hv_do_hypercall(HVCALL_DELETE_DEVICE_DOMAIN, input, + NULL); + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + ida_free(&hv_iommu_domids, hvdom->domid_num); + kfree(hvdom); +} + +/* + * Attach a device to the default domain, or the null domain, or to a doma= in + * previously created in the hypervisor. + */ +static int hv_iommu_att_dev2dom(struct hv_iommu_domain *hvdom, + struct pci_dev *pdev) +{ + ulong flags; + u64 status; + struct hv_input_attach_device_domain *input; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + /* For null domain, hvdom->domid_num =3D=3D HV_DEVICE_DOMAIN_ID_NULL */ + input->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + input->device_domain.domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + input->device_domain.domain_id.id =3D hvdom->domid_num; + + input->device_id.as_uint64 =3D hv_build_devid_type_pci(pdev); + + status =3D hv_do_hypercall(HVCALL_ATTACH_DEVICE_DOMAIN, input, NULL); + local_irq_restore(flags); + + if (hv_result_success(status) || + hv_result(status) =3D=3D HV_STATUS_DEVICE_ALREADY_IN_DOMAIN) + return 0; + + hv_status_err(status, "\n"); + return hv_result_to_errno(status); +} + +static int hv_iommu_attach_dev(struct iommu_domain *immdom, struct device = *dev, + struct iommu_domain *old) +{ + struct pci_dev *pdev; + int rc; + struct hv_iommu_domain *hvdom =3D to_hv_domain(immdom); + + pdev =3D to_pci_dev(dev); + + rc =3D hv_iommu_att_dev2dom(hvdom, pdev); + if (rc) + WARN(1, "Failed to attach pdev:%s\n", pci_name(pdev)); + + return rc; +} + +static u64 hv_iommu_unmap_batch(u32 domid_num, ulong iova, u16 count) +{ + ulong flags; + struct hv_input_unmap_device_gpa_pages *input; + u64 status; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + input->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + input->device_domain.domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + input->device_domain.domain_id.id =3D domid_num; + input->target_device_va_base =3D iova; + + status =3D hv_do_rep_hypercall(HVCALL_UNMAP_DEVICE_GPA_PAGES, count, + 0, input, NULL); + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "iova:0x%lx count:0x%x\n", iova, count); + + return status; +} + +static size_t hv_iommu_hyp_unmap_pages(struct iommu_domain *immdom, ulong = iova, + size_t size) +{ + u64 status; + struct hv_iommu_domain *hvdom =3D to_hv_domain(immdom); + size_t tot_done =3D 0; + ulong npages =3D size >> HV_HYP_PAGE_SHIFT; + + while (npages) { + int done, count =3D min(npages, HV_REP_COUNT_MAX); + + status =3D hv_iommu_unmap_batch(hvdom->domid_num, iova, count); + + done =3D hv_repcomp(status); + tot_done +=3D done; + npages -=3D done; + iova +=3D done << HV_HYP_PAGE_SHIFT; + + if (!hv_result_success(status)) + break; + } + + return tot_done << HV_HYP_PAGE_SHIFT; +} + +static size_t hv_iommu_unmap_pages(struct iommu_domain *immdom, ulong iova, + size_t pgsize, size_t pgcount, + struct iommu_iotlb_gather *gather) +{ + struct hv_iommu_domain *hvdom =3D to_hv_domain(immdom); + size_t unmap_sz, size =3D pgsize * pgcount; + + /* For valid input, the hypervisor guarantees unmap will succeed */ + unmap_sz =3D hv_iommu_hyp_unmap_pages(immdom, iova, size); + if (unmap_sz !=3D size) + WARN(1, "Failed to unmap device gpa(%lx/%lx)\n", unmap_sz, + size); + + if (unmap_sz =3D=3D 0) + return 0; + + size =3D hv_iommu_del_tree_mappings(hvdom, iova, unmap_sz); + if (size !=3D unmap_sz) + pr_err("%s: could not delete tree mappings (%lx:%lx/%lx)\n", + __func__, iova, unmap_sz, size); + + return size; +} + +/* Return: must return exact status from the hypercall without changes */ +static u64 hv_iommu_map_pgs(struct hv_iommu_domain *hvdom, ulong iova, + phys_addr_t paddr, ulong npages, u32 map_flags) +{ + u64 status; + int i; + struct hv_input_map_device_gpa_pages *input; + unsigned long flags, pfn; + + local_irq_save(flags); + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + memset(input, 0, sizeof(*input)); + + input->device_domain.partition_id =3D HV_PARTITION_ID_SELF; + input->device_domain.domain_id.type =3D HV_DEVICE_DOMAIN_TYPE_S2; + input->device_domain.domain_id.id =3D hvdom->domid_num; + input->map_flags =3D map_flags; + input->target_device_va_base =3D iova; + + pfn =3D paddr >> HV_HYP_PAGE_SHIFT; + for (i =3D 0; i < npages; i++, pfn++) + input->gpa_page_list[i] =3D pfn; + + status =3D hv_do_rep_hypercall(HVCALL_MAP_DEVICE_GPA_PAGES, npages, 0, + input, NULL); + local_irq_restore(flags); + + if (!hv_result_success(status) && !hv_result_needs_memory(status)) + hv_status_err(status, "npgs:%lx paddr:%llx iova:%lx mf:0x%x\n", + npages, paddr, iova, map_flags); + return status; +} + +#define HV_MAP_DEVICE_GPA_BATCH_SIZE \ + ((HV_HYP_PAGE_SIZE - sizeof(struct hv_input_map_device_gpa_pages)) \ + / sizeof(u64)) + +/* NB: cond_resched() is in vfio_iommu_map() */ +static int hv_iommu_map_pages(struct iommu_domain *immdom, ulong iova, + phys_addr_t paddr, size_t pgsize, size_t pgcount, + int prot, gfp_t gfp, size_t *mapped) +{ + u32 map_flags; + int ret; + u64 status =3D 0; + ulong npages, sav_iova =3D iova, done =3D 0; + struct hv_iommu_domain *hvdom =3D to_hv_domain(immdom); + size_t size =3D pgsize * pgcount; + + map_flags =3D HV_MAP_GPA_READABLE; /* required */ + map_flags |=3D prot & IOMMU_WRITE ? HV_MAP_GPA_WRITABLE : 0; + + ret =3D hv_iommu_add_tree_mapping(hvdom, iova, paddr, size, map_flags, + gfp); + if (ret) + return ret; + + npages =3D size >> HV_HYP_PAGE_SHIFT; + while (done < npages) { + ulong completed, remain =3D npages - done; + + remain =3D min(remain, HV_MAP_DEVICE_GPA_BATCH_SIZE); + + status =3D hv_iommu_map_pgs(hvdom, iova, paddr, remain, + map_flags); + + completed =3D hv_repcomp(status); + done =3D done + completed; + iova =3D iova + (completed << HV_HYP_PAGE_SHIFT); + paddr =3D paddr + (completed << HV_HYP_PAGE_SHIFT); + + if (hv_result_needs_memory(status)) { + ret =3D hv_deposit_memory_gfp(hv_current_partition_id, + status, gfp); + if (ret) + break; + continue; + } + if (!hv_result_success(status)) + break; + } + + if (ret =3D=3D 0) + ret =3D hv_result_to_errno(status); + + if (hv_result_success(status)) { + if (mapped) + *mapped =3D size; + } else { + size_t done_size =3D done << HV_HYP_PAGE_SHIFT; + + hv_status_err(status, "pgs:%lx/%lx iova:%lx\n", done, npages, + sav_iova); + + hv_iommu_del_tree_mappings(hvdom, sav_iova, size); + + /* Hypervisor guarantees unmap will succeed here */ + hv_iommu_hyp_unmap_pages(immdom, sav_iova, done_size); + + if (mapped) + *mapped =3D 0; + } + + return ret; +} + +static phys_addr_t hv_iommu_iova_to_phys(struct iommu_domain *immdom, + dma_addr_t iova) +{ + unsigned long flags; + struct hv_iommu_mapping *mapping; + struct interval_tree_node *node; + u64 paddr =3D 0; + struct hv_iommu_domain *hvdom =3D to_hv_domain(immdom); + + spin_lock_irqsave(&hvdom->mappings_lock, flags); + node =3D interval_tree_iter_first(&hvdom->mappings_tree, iova, iova); + if (node) { + mapping =3D container_of(node, struct hv_iommu_mapping, iova); + paddr =3D mapping->paddr + (iova - mapping->iova.start); + } + spin_unlock_irqrestore(&hvdom->mappings_lock, flags); + + return paddr; +} + +static struct iommu_device *hv_iommu_probe_device(struct device *dev) +{ + if (!dev_is_pci(dev)) + return ERR_PTR(-ENODEV); + + return &hv_virt_iommu; +} + +static void hv_iommu_get_resv_regions(struct device *dev, + struct list_head *head) +{ + iommu_add_resv_x86_msi_region(head); +} + +static struct iommu_domain_ops hv_paging_domain_ops =3D { + .attach_dev =3D hv_iommu_attach_dev, + .map_pages =3D hv_iommu_map_pages, + .unmap_pages =3D hv_iommu_unmap_pages, + .iova_to_phys =3D hv_iommu_iova_to_phys, + .free =3D hv_iommu_domain_free, +}; + +static struct iommu_ops hv_iommu_ops =3D { + .capable =3D hv_iommu_capable, + .domain_alloc_paging =3D hv_iommu_domain_alloc_paging, + .probe_device =3D hv_iommu_probe_device, + .device_group =3D pci_device_group, + .get_resv_regions =3D hv_iommu_get_resv_regions, + .owner =3D THIS_MODULE, + .identity_domain =3D &hv_def_identity_dom.iommu_dom, + .blocked_domain =3D &hv_def_blocked_dom.iommu_dom, + .release_domain =3D &hv_def_blocked_dom.iommu_dom, +}; + +static const struct iommu_domain_ops hv_special_domain_ops =3D { + .attach_dev =3D hv_iommu_attach_dev, +}; + +static void __init hv_initialize_special_domains(void) +{ + hv_def_identity_dom.iommu_dom.type =3D IOMMU_DOMAIN_IDENTITY; + hv_def_identity_dom.iommu_dom.ops =3D &hv_special_domain_ops; + hv_def_identity_dom.iommu_dom.owner =3D &hv_iommu_ops; + hv_def_identity_dom.domid_num =3D HV_DEVICE_DOMAIN_ID_DEFAULT; /* 0 */ + + hv_def_blocked_dom.iommu_dom.type =3D IOMMU_DOMAIN_BLOCKED; + hv_def_blocked_dom.iommu_dom.ops =3D &hv_special_domain_ops; + hv_def_blocked_dom.iommu_dom.owner =3D &hv_iommu_ops; + hv_def_blocked_dom.domid_num =3D 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 ca= p, + * hv_root_iommu_detect() below checks for HV_DEVICE_DOMAIN_AVAILABLE which + * supersedes HV_IOMMU_CAP_PRESENT. As for the pgsize_bitmap cap, see comm= ent + * above HV_IOMMU_PGSIZES. + */ +static int hv_iommu_get_caps(struct hv_output_get_iommu_capabilities *caps) +{ + u64 status; + unsigned long flags; + struct hv_input_get_iommu_capabilities *input; + struct hv_output_get_iommu_capabilities *output; + + local_irq_save(flags); + + input =3D *this_cpu_ptr(hyperv_pcpu_input_arg); + output =3D *this_cpu_ptr(hyperv_pcpu_output_arg); + memset(input, 0, sizeof(*input)); + input->partition_id =3D HV_PARTITION_ID_SELF; + status =3D hv_do_hypercall(HVCALL_GET_IOMMU_CAPABILITIES, input, output); + *caps =3D *output; + + local_irq_restore(flags); + + if (!hv_result_success(status)) + hv_status_err(status, "\n"); + + return hv_result_to_errno(status); +} + +static int __init hv_root_iommu_init(void) +{ + int rc; + struct iommu_device *iommup =3D &hv_virt_iommu; + struct hv_output_get_iommu_capabilities caps; + + if (!hv_is_hyperv_initialized()) + return -ENODEV; + + rc =3D hv_iommu_get_caps(&caps); + if (rc) + return rc; + + hv_iommu_max_iova =3D 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 =3D iommu_device_sysfs_add(iommup, NULL, NULL, "%s", "hyperv-iommu"); + if (rc) { + pr_err("Hyper-V: iommu_device_sysfs_add failed: %d\n", rc); + return rc; + } + + rc =3D iommu_device_register(iommup, &hv_iommu_ops, NULL); + if (rc) { + pr_err("Hyper-V: iommu_device_register failed: %d\n", rc); + goto err_sysfs_remove; + } + + pr_info("Hyper-V IOMMU initialized\n"); + + return 0; + +err_sysfs_remove: + iommu_device_sysfs_remove(iommup); + return rc; +} + +void __init hv_root_iommu_detect(void) +{ + if (no_iommu || iommu_detected || hv_l1vh_partition()) + return; + + /* This is set for root partitions only, it is never set for non-root */ + if (!(ms_hyperv.misc_features & HV_DEVICE_DOMAIN_AVAILABLE)) + return; + + iommu_detected =3D 1; + x86_init.iommu.iommu_init =3D hv_root_iommu_init; + + pci_request_acs(); +} diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index 2584e693d7b3..ad02d6956ae3 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -28,6 +28,9 @@ =20 #define VTPM_BASE_ADDRESS 0xfed40000 =20 +#define HV_REP_COUNT_MAX \ + (HV_HYPERCALL_REP_COMP_MASK >> HV_HYPERCALL_REP_COMP_OFFSET) + enum hv_partition_type { HV_PARTITION_TYPE_GUEST, HV_PARTITION_TYPE_ROOT, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index e85a1a043eac..8aa752e8c4e1 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1823,4 +1823,10 @@ static inline unsigned long virt_to_hvpfn(void *addr) #define HVPFN_DOWN(x) ((x) >> HV_HYP_PAGE_SHIFT) #define page_to_hvpfn(page) (page_to_pfn(page) * NR_HV_HYP_PAGES_IN_PAGE) =20 +#ifdef CONFIG_HYPERV_ROOT_IOMMU +void __init hv_root_iommu_detect(void); +#else +static inline void hv_root_iommu_detect(void) { } +#endif /* CONFIG_HYPERV_ROOT_IOMMU */ + #endif /* _HYPERV_H */ --=20 2.51.2.vfs.0.1