* [PATCH V4 1/9] mshv: Provide a way to get partition ID if running in a VMM process
From: Mukesh R @ 2026-07-18 2:19 UTC (permalink / raw)
To: hpa, robin.murphy, robh, wei.liu, mrathor, mhklinux, muislam,
namjain, magnuskulke, anbelski, linux-kernel, linux-hyperv, iommu,
linux-pci, linux-arch
Cc: kys, haiyangz, decui, longli, tglx, mingo, bp, dave.hansen, x86,
joro, will, lpieralisi, kwilczynski, bhelgaas, arnd, jacob.pan
In-Reply-To: <20260718021949.926306-1-mrathor@linux.microsoft.com>
Many PCI passthru related hypercalls require partition ID of the target
guest. Guests are actually managed by MSHV driver and the partition ID
is only maintained there. Add a field in the partition struct in MSHV
driver to save the tgid of the VMM process creating the partition, and
add a function there to retrieve partition ID if the current process is
a VMM process.
Signed-off-by: Mukesh R <mrathor@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
drivers/hv/mshv_root.h | 1 +
drivers/hv/mshv_root_main.c | 22 ++++++++++++++++++++++
include/asm-generic/mshyperv.h | 5 +++++
3 files changed, 28 insertions(+)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 1f086dcb7aa1..a85c24dcc701 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -138,6 +138,7 @@ struct mshv_partition {
struct mshv_girq_routing_table __rcu *pt_girq_tbl;
u64 isolation_type;
+ pid_t pt_vmm_tgid;
bool import_completed;
bool pt_initialized;
#if IS_ENABLED(CONFIG_DEBUG_FS)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index bd1359eb58dd..02c107458be9 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1908,6 +1908,27 @@ mshv_partition_release(struct inode *inode, struct file *filp)
return 0;
}
+/* Given a process tgid, return partition id if it is a VMM process */
+u64 mshv_current_partid(void)
+{
+ struct mshv_partition *pt;
+ int i;
+ u64 ret_ptid = HV_PARTITION_ID_INVALID;
+
+ rcu_read_lock();
+
+ hash_for_each_rcu(mshv_root.pt_htable, i, pt, pt_hnode) {
+ if (pt->pt_vmm_tgid == current->tgid) {
+ ret_ptid = pt->pt_id;
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+ return ret_ptid;
+}
+EXPORT_SYMBOL_GPL(mshv_current_partid);
+
static int
add_partition(struct mshv_partition *partition)
{
@@ -2073,6 +2094,7 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
goto cleanup_irq_srcu;
partition->pt_id = pt_id;
+ partition->pt_vmm_tgid = current->tgid;
ret = add_partition(partition);
if (ret)
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index bf601d67cecb..e8cbc4e3f7ad 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -350,6 +350,7 @@ 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);
int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags);
+u64 mshv_current_partid(void);
#else /* CONFIG_MSHV_ROOT */
static inline bool hv_root_partition(void) { return false; }
@@ -380,6 +381,10 @@ static inline int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u3
{
return -EOPNOTSUPP;
}
+static inline u64 mshv_current_partid(void)
+{
+ return HV_PARTITION_ID_INVALID;
+}
#endif /* CONFIG_MSHV_ROOT */
static inline int hv_deposit_memory(u64 partition_id, u64 status)
--
2.51.2.vfs.0.1
^ permalink raw reply related
* [PATCH V4 0/9] PCI passthru on Hyper-V
From: Mukesh R @ 2026-07-18 2:19 UTC (permalink / raw)
To: hpa, robin.murphy, robh, wei.liu, mrathor, mhklinux, muislam,
namjain, magnuskulke, anbelski, linux-kernel, linux-hyperv, iommu,
linux-pci, linux-arch
Cc: kys, haiyangz, decui, longli, tglx, mingo, bp, dave.hansen, x86,
joro, will, lpieralisi, kwilczynski, bhelgaas, arnd, jacob.pan
Implement passthru of PCI devices to unprivileged virtual machines
(VMs) when Linux is running as a privileged VM on Microsoft Hyper-V
hypervisor. This support is made to fit within the workings of VFIO
framework. This supports both full device passthru and SR-IOV based VFs.
At a high level, the hypervisor supports traditional paging iommu domains
that use explicit map and unmap hypercalls for mapping and unmapping
guest RAM into the iommu subsystem. Hyper-V also has a concept of direct
attach devices (DDA) whereby the iommu subsystem simply uses the guest
HW page table (ept/npt/..). This series adds support for the former.
This series rebased to: a4ffc59238be (origin/hyperv-next)
Testing:
o Most testing done on hyperv-next:a4ffc59238be using Cloud Hypervisor (51).
o All tests involved passthru of devices using MSIx.
o Tested on Baremetal dom0/root:
test 1: Mellanox ConnectX-6 Lx passthru
test 2: NVIDIA Tesla Tesla T4 GPU.
test 3: Both of above simultaneous passthru
o VF: Mellanox ConnectX-6 Lx passthru
This series strives to establish a base line. Some pending work items:
o arm64 : some delta to make this work on arm64 (in progress).
o Qemu and OpenVMM support (in progress).
o device sleep/wakeup.
o More stress testing with high end GPUs
Changes in V4:
o Completely remove direct device attach (DDA) support
o Remove first two patches that were submitted separately and are
already merged.
o Change hv_iommu_unmap_pages() to unmap in batches of HV_REP_COUNT_MAX,
otherwise if a physical range happens to be larger than that, there would
be errors from the hypercall.
o Use large page (2M) flag for mmio mappings as internal testing found
that to speed things up greatly.
o rename hv_no_movbl_pgs to hv_memreg in last patch.
Changes in V3:
o patch #8: fix compiler issues incase of !CONFIG_HYPERV. Also, do forward
declaration of struct pci_dev instead of including pci.h.
o patch #9: minor changes to comments. Pass hv_domain instead of
iommu_domain to hv_iommu_detach_dev() since that's what it needs. Set
device private to null if attach fails. Clam down number of PFNs passed
to hv_iommu_map_pgs().
Changes in V2:
o rebase to 5170a82e8921
o minor fixes for arm64 build
o drop patch 03: "x86/hyperv: add insufficient memory support in irqdomain.c"
as it that path is no longer used
o drop patch 08: "PCI: hv: rename hv_compose_msi_msg .. " and do it separately
outside this series.
o minor updates to commit messages
Changes in V1:
o patch 1: Don't tie hyperv-irq.c to CONFIG_HYPERV_IOMMU.
o patch 4: Redesigned to address security vulnerability found by copilot
with passing tgid as a parameter. Also, do tgid setting right
after setting pt_id.
o patch 5: Remove unused type parameter from mshv_device_ops.device_create
o patch 7: mshv_partition_ioctl_create_device cleanup on copy_to_user.
o patch 10: Add export of hv_build_devid_type_pci here to get rid of
patch 11.
o patch 12: Move functions to build device ids from patch 11 here for
the benefit of arm64. Rename file to: hyperv-iommu-root.c.
o patch 13: removed to be made part of interrupt part II of this support.
o patch 14: get rid of fast path to reduce review noise.
o New (last) patch to pin ram regions if device passthru to a VM.
Thanks,
-Mukesh
Mukesh R (9):
mshv: Provide a way to get partition ID if running in a VMM process
mshv: Add declarations and definitions for VFIO-MSHV bridge device
mshv: Introduce basic mshv bridge device for VFIO to build upon
mshv: Add ioctl support for MSHV-VFIO bridge device
mshv: Import data structs around device passthru from hyperv headers
PCI: hv: Export hv_build_devid_type_pci() and change return type
x86/hyperv: Implement Hyper-V virtual IOMMU
mshv: Populate mmio mappings for PCI passthru
mshv: Disable movable regions upfront if device passthru
arch/x86/hyperv/irqdomain.c | 9 +-
arch/x86/include/asm/mshyperv.h | 6 +
arch/x86/kernel/pci-dma.c | 2 +
drivers/hv/Makefile | 3 +-
drivers/hv/mshv_root.h | 24 +-
drivers/hv/mshv_root_hv_call.c | 101 +++-
drivers/hv/mshv_root_main.c | 259 +++++++++-
drivers/hv/mshv_vfio.c | 211 ++++++++
drivers/iommu/Kconfig | 1 +
drivers/iommu/hyperv/Kconfig | 15 +
drivers/iommu/hyperv/Makefile | 1 +
drivers/iommu/hyperv/hv-iommu-root.c | 704 +++++++++++++++++++++++++++
include/asm-generic/mshyperv.h | 8 +
include/hyperv/hvgdk_mini.h | 8 +
include/hyperv/hvhdk_mini.h | 67 +++
include/linux/hyperv.h | 6 +
include/uapi/linux/mshv.h | 31 ++
17 files changed, 1408 insertions(+), 48 deletions(-)
create mode 100644 drivers/hv/mshv_vfio.c
create mode 100644 drivers/iommu/hyperv/Kconfig
create mode 100644 drivers/iommu/hyperv/hv-iommu-root.c
--
2.51.2.vfs.0.1
^ permalink raw reply
* Re: [PATCH net-next v4] net: mana: Add handler for sriov configure
From: patchwork-bot+netdevbpf @ 2026-07-17 13:40 UTC (permalink / raw)
To: Haiyang Zhang
Cc: linux-hyperv, netdev, kys, haiyangz, wei.liu, decui, longli,
andrew+netdev, davem, edumazet, kuba, pabeni, horms, ernis,
dipayanroy, gargaditya, shradhagupta, linux-kernel, paulros
In-Reply-To: <20260710192735.2921300-1-haiyangz@linux.microsoft.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Fri, 10 Jul 2026 12:27:29 -0700 you wrote:
> From: Haiyang Zhang <haiyangz@microsoft.com>
>
> Add callback function for the pci_driver / sriov_configure.
>
> It asks the NIC to provide certain number of VFs, or disable
> VFs if the request is zero.
>
> [...]
Here is the summary with links:
- [net-next,v4] net: mana: Add handler for sriov configure
https://git.kernel.org/netdev/net-next/c/ce6b4d3216b6
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next] net: mana: Add debug knob to skip TX timeout recovery reset
From: patchwork-bot+netdevbpf @ 2026-07-17 11:30 UTC (permalink / raw)
To: Aditya Garg
Cc: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, kotaranov, horms, ernis, dipayanroy,
ssengar, gargaditya, linux-hyperv, netdev, linux-kernel,
linux-rdma
In-Reply-To: <20260710132229.2851441-1-gargaditya@linux.microsoft.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Fri, 10 Jul 2026 06:22:29 -0700 you wrote:
> Add a per-port debugfs boolean "tx_timeout_skip_reset" that, when
> enabled, makes mana_tx_timeout() log the TX timeout and return without
> queueing the per-port detach/attach recovery work.
>
> This is a debug-only aid for bringup and qualification: skipping the
> recovery reset keeps the device and queue state intact so a TX timeout
> can be correlated with hardware telemetry. The knob defaults to false,
> so production recovery behaviour is unchanged.
>
> [...]
Here is the summary with links:
- [net-next] net: mana: Add debug knob to skip TX timeout recovery reset
https://git.kernel.org/netdev/net-next/c/922cc43c6243
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH 00/32] x86/msr: Drop 32-bit MSR interfaces
From: Ingo Molnar @ 2026-07-17 9:38 UTC (permalink / raw)
To: Juergen Gross
Cc: Sean Christopherson, Arnd Bergmann, linux-kernel, linux-pm,
linux-edac@vger.kernel.org, x86, linux-acpi, kvm, linux-coco,
linux-pci, virtualization, linux-ide, dri-devel, linux-fbdev,
linux-crypto, open list:GPIO SUBSYSTEM, linux-hyperv, linux-hwmon,
linux-perf-users, linux-mtd, platform-driver-x86,
Rafael J . Wysocki, Daniel Lezcano, Zhang Rui,
lukasz.luba@arm.com, Jason Baron, Borislav Petkov, Tony Luck,
Yazen Ghannam, Len Brown, Pavel Machek, Thomas Gleixner,
Ingo Molnar, Dave Hansen, H. Peter Anvin, Paolo Bonzini,
Kirill A. Shutemov, Rick Edgecombe, Pu Wen, Bjorn Helgaas,
Ajay Kaher, Alexey Makhalov, Broadcom internal kernel review list,
Viresh Kumar, Reinette Chatre, Dave Martin, James Morse,
Babu Moger, Tony W Wang-oc, Damien Le Moal, Niklas Cassel,
Dave Airlie, Helge Deller, linux-geode, Olivia Mackall,
Herbert Xu, Linus Walleij, Bartosz Golaszewski,
Greg Kroah-Hartman, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Guenter Roeck, Peter Zijlstra,
Arnaldo Carvalho de Melo, Namhyung Kim, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Ian Rogers, Adrian Hunter,
James Clark, Josh Poimboeuf, Pawan Gupta, Vitaly Kuznetsov,
Andy Lutomirski, Boris Ostrovsky, Huang Rui, Mario Limonciello,
Perry Yuan, K Prateek Nayak, srinivas.pandruvada@linux.intel.com,
Artem Bityutskiy, Artem Bityutskiy, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Ashok Raj, Hans de Goede,
Ilpo Järvinen, Rajneesh Bhardwaj, David E Box, xen-devel
In-Reply-To: <99228803-b8b7-47a3-b77c-6fdf3b785730@suse.com>
* Juergen Gross <jgross@suse.com> wrote:
> On 02.07.26 12:07, Ingo Molnar wrote:
> >
> > * Sean Christopherson <seanjc@google.com> wrote:
> >
> > > > Note that the individual patches are IMO significantly easier to review
> > > > through the actual 32-bit => 64-bit variable assignment changes done
> > > > in isolation (which sometimes include minor cleanups), while
> > > > the Coccinelle semantic patch:
> > > >
> > > > { a(b,c) => c = a(b) }
> > > >
> > > > which changes both the function signature and the order of terms as
> > > > well, is just a single add-on treewide patch.
> > >
> > > Is the plan for subsystem maintainers to pick up the relevant patches,
> > > and then do the treewide change one release cycle later?
> >
> > I'll try to keep the patches in a single tree (tip:x86/msr)
> > in the hope of not prolonging the pain two cycles - but it's
> > of course fine for maintainers to pick up the patches too
> > (most of them are standalone), we'll sort it all out in the end.
>
> Ingo, would you be fine with me posting patch updates just as replies to the
> original patch emails? This would speed things up, as I wouldn't need to wait
> for more review input of all the patches before sending out new versions.
Sure, that works for me too. I've picked up a couple of -v2 patches
already.
Thanks,
Ingo
^ permalink raw reply
* RE: [PATCH v2] Drivers: hv: vmbus: add VTL2 redirect connection ID
From: Michael Kelley @ 2026-07-17 2:58 UTC (permalink / raw)
To: Hardik Garg, K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
Saurabh Sengar, Michael Kelley, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20260717001837.635756-1-hargar@linux.microsoft.com>
From: Hardik Garg <hargar@linux.microsoft.com> Sent: Thursday, July 16, 2026 5:19 PM
>
> VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
> connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
> while protocol version 5.0 and newer normally use
> VMBUS_MESSAGE_CONNECTION_ID_4.
>
> For a VTL2 kernel using VMBus protocol 5.0 or newer, the host
> may expect INITIATE_CONTACT on either the redirect connection ID or
> VMBUS_MESSAGE_CONNECTION_ID_4. There is no capability indication that
> identifies which ID is active, so the driver must determine it at runtime.
>
> During VMBus negotiation, the redirect ID is tried first because it is
> used by VTL2 configurations with VMBus redirection enabled. If the
> redirect ID is unavailable, the host rejects it synchronously with
> HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to the standard ID.
>
> Return a distinct error for an invalid Initiate Contact connection ID so
> this fallback does not mask other post-message failures or
> protocol-version rejections. Preserve the existing connection ID
> selection for older protocol versions or when running below VTL2.
>
> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
> ---
>
> Notes (v2-changelog):
> Changes in v2:
> - Replace the connection ID array and loop with a single redirect
> attempt, falling back to VMBUS_MESSAGE_CONNECTION_ID_4 only on
> -ENXIO.
> - Remove redundant connection-state and timeout checks and the
> unreachable return after the loop.
> - Simplify the commit message to describe runtime connection ID
> selection without relay and message-port implementation details.
> - Clarify the protocol 5.0 helper and VTL2 redirect comments, remove
> redundant comments, and use an exact VTL2 check.
>
> Link to v1: https://lore.kernel.org/r/20260714213838.3367103-1-
> hargar@linux.microsoft.com
>
> drivers/hv/connection.c | 47 +++++++++++++++++++++++----------------
> drivers/hv/hyperv_vmbus.h | 2 ++
> 2 files changed, 30 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
> index b5b322ce16df4..0fd50d4cb5739 100644
> --- a/drivers/hv/connection.c
> +++ b/drivers/hv/connection.c
> @@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO);
> MODULE_PARM_DESC(max_version,
> "Maximal VMBus protocol version which can be negotiated");
>
> -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
> + u32 version, u32 connection_id)
> {
> int ret = 0;
> struct vmbus_channel_initiate_contact *msg;
> @@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> msg->vmbus_version_requested = version;
>
> /*
> - * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
> - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
> - * and for subsequent messages, we must use the Message Connection ID
> - * field in the host-returned Version Response Message. And, with
> - * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
> - * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for
> - * compatibility.
> + * For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the
> + * caller-supplied connection_id for the Initiate Contact message so
> + * the caller can implement the required retry scheme. For subsequent
> + * messages, use the Message Connection ID field in the host-returned
> + * Version Response message. With VERSION_WIN10_V5 and higher, we don't
> + * use msg->interrupt_page, but tell the host explicitly that we still
> + * use VMBUS_MESSAGE_SINT(2) for compatibility.
> *
> * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
> */
> if (version >= VERSION_WIN10_V5) {
> msg->msg_sint = VMBUS_MESSAGE_SINT;
> msg->msg_vtl = ms_hyperv.vtl;
> - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
> + vmbus_connection.msg_conn_id = connection_id;
> } else {
> msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
> vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
> @@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> return ret;
> }
>
> +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> +{
> + int ret;
> +
> + /* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */
> + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) {
> + ret = vmbus_try_connection_id(msginfo, version,
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
> + if (ret != -ENXIO)
> + return ret;
> + }
> +
> + return vmbus_try_connection_id(msginfo, version,
> + VMBUS_MESSAGE_CONNECTION_ID_4);
> +}
> +
> /*
> * vmbus_connect - Sends a connect request on the partition service connection
> */
> @@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool
> can_sleep)
>
> switch (ret) {
> case HV_STATUS_INVALID_CONNECTION_ID:
> - /*
> - * See vmbus_negotiate_version(): VMBus protocol 5.0
> - * and higher require that we must use
> - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
> - * Contact message, but on old hosts that only
> - * support VMBus protocol 4.0 or lower, here we get
> - * HV_STATUS_INVALID_CONNECTION_ID and we should
> - * return an error immediately without retrying.
> - */
> + /* Allow INITIATE_CONTACT to try another connection ID. */
> hdr = buffer;
> if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
> - return -EINVAL;
> + return -ENXIO;
> /*
> * We could get this if we send messages too
> * frequently.
> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
> index 05a36854389af..87f8aca1a9fb5 100644
> --- a/drivers/hv/hyperv_vmbus.h
> +++ b/drivers/hv/hyperv_vmbus.h
> @@ -110,6 +110,8 @@ struct hv_input_post_message {
> enum {
> VMBUS_MESSAGE_CONNECTION_ID = 1,
> VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
> + /* VTL2 redirect connection ID for INITIATE_CONTACT. */
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
> VMBUS_MESSAGE_PORT_ID = 1,
> VMBUS_EVENT_CONNECTION_ID = 2,
> VMBUS_EVENT_PORT_ID = 2,
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH v2] Drivers: hv: vmbus: add VTL2 redirect connection ID
From: Hardik Garg @ 2026-07-17 0:18 UTC (permalink / raw)
To: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
Saurabh Sengar, Michael Kelley, linux-hyperv, linux-kernel
VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
while protocol version 5.0 and newer normally use
VMBUS_MESSAGE_CONNECTION_ID_4.
For a VTL2 kernel using VMBus protocol 5.0 or newer, the host
may expect INITIATE_CONTACT on either the redirect connection ID or
VMBUS_MESSAGE_CONNECTION_ID_4. There is no capability indication that
identifies which ID is active, so the driver must determine it at runtime.
During VMBus negotiation, the redirect ID is tried first because it is
used by VTL2 configurations with VMBus redirection enabled. If the
redirect ID is unavailable, the host rejects it synchronously with
HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to the standard ID.
Return a distinct error for an invalid Initiate Contact connection ID so
this fallback does not mask other post-message failures or
protocol-version rejections. Preserve the existing connection ID
selection for older protocol versions or when running below VTL2.
Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
---
Notes (v2-changelog):
Changes in v2:
- Replace the connection ID array and loop with a single redirect
attempt, falling back to VMBUS_MESSAGE_CONNECTION_ID_4 only on
-ENXIO.
- Remove redundant connection-state and timeout checks and the
unreachable return after the loop.
- Simplify the commit message to describe runtime connection ID
selection without relay and message-port implementation details.
- Clarify the protocol 5.0 helper and VTL2 redirect comments, remove
redundant comments, and use an exact VTL2 check.
Link to v1: https://lore.kernel.org/r/20260714213838.3367103-1-hargar@linux.microsoft.com
drivers/hv/connection.c | 47 +++++++++++++++++++++++----------------
drivers/hv/hyperv_vmbus.h | 2 ++
2 files changed, 30 insertions(+), 19 deletions(-)
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index b5b322ce16df4..0fd50d4cb5739 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO);
MODULE_PARM_DESC(max_version,
"Maximal VMBus protocol version which can be negotiated");
-int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
+static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
+ u32 version, u32 connection_id)
{
int ret = 0;
struct vmbus_channel_initiate_contact *msg;
@@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
msg->vmbus_version_requested = version;
/*
- * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
- * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
- * and for subsequent messages, we must use the Message Connection ID
- * field in the host-returned Version Response Message. And, with
- * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
- * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for
- * compatibility.
+ * For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the
+ * caller-supplied connection_id for the Initiate Contact message so
+ * the caller can implement the required retry scheme. For subsequent
+ * messages, use the Message Connection ID field in the host-returned
+ * Version Response message. With VERSION_WIN10_V5 and higher, we don't
+ * use msg->interrupt_page, but tell the host explicitly that we still
+ * use VMBUS_MESSAGE_SINT(2) for compatibility.
*
* On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
*/
if (version >= VERSION_WIN10_V5) {
msg->msg_sint = VMBUS_MESSAGE_SINT;
msg->msg_vtl = ms_hyperv.vtl;
- vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
+ vmbus_connection.msg_conn_id = connection_id;
} else {
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
@@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
return ret;
}
+int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
+{
+ int ret;
+
+ /* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */
+ if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) {
+ ret = vmbus_try_connection_id(msginfo, version,
+ VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
+ if (ret != -ENXIO)
+ return ret;
+ }
+
+ return vmbus_try_connection_id(msginfo, version,
+ VMBUS_MESSAGE_CONNECTION_ID_4);
+}
+
/*
* vmbus_connect - Sends a connect request on the partition service connection
*/
@@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
switch (ret) {
case HV_STATUS_INVALID_CONNECTION_ID:
- /*
- * See vmbus_negotiate_version(): VMBus protocol 5.0
- * and higher require that we must use
- * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
- * Contact message, but on old hosts that only
- * support VMBus protocol 4.0 or lower, here we get
- * HV_STATUS_INVALID_CONNECTION_ID and we should
- * return an error immediately without retrying.
- */
+ /* Allow INITIATE_CONTACT to try another connection ID. */
hdr = buffer;
if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
- return -EINVAL;
+ return -ENXIO;
/*
* We could get this if we send messages too
* frequently.
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 05a36854389af..87f8aca1a9fb5 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -110,6 +110,8 @@ struct hv_input_post_message {
enum {
VMBUS_MESSAGE_CONNECTION_ID = 1,
VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
+ /* VTL2 redirect connection ID for INITIATE_CONTACT. */
+ VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
VMBUS_MESSAGE_PORT_ID = 1,
VMBUS_EVENT_CONNECTION_ID = 2,
VMBUS_EVENT_PORT_ID = 2,
--
2.34.1
^ permalink raw reply related
* Re: [PATCH] Drivers: hv: vmbus: add VTL2 redirect connection ID
From: Hardik Garg @ 2026-07-16 22:04 UTC (permalink / raw)
To: Michael Kelley, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <SN6PR02MB415769F6F2645260C8D5778CD4C72@SN6PR02MB4157.namprd02.prod.outlook.com>
On 7/16/2026 10:03 AM, Michael Kelley wrote:
> From: Hardik Garg <hargar@linux.microsoft.com> Sent: Tuesday, July 14, 2026 2:39 PM
>> VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
>> connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
>> while protocol version 5.0 and newer normally use
>> VMBUS_MESSAGE_CONNECTION_ID_4.
>>
>> When the Linux VMBus driver runs at VTL2, the VMBus control plane may
>> be reached through a VMBus relay instead of the standard host endpoint.
>> In that setup the relay listens on the redirect message connection ID,
>> and an Initiate Contact message sent to the standard ID is not delivered
>> to the control plane.
>>
>> The connection ID selects the Hyper-V message port used to reach the
>> VMBus control plane. If Linux uses the wrong port, the host does not
>> receive the Initiate Contact message.
> This is a judgment call, but to me the previous two paragraphs are
> explaining VTL2 implementation details that aren't necessary for
> understanding this patch. Yes, in earlier discussions I asked questions
> about "why" all this funky behavior with connection IDs, but that's
> just me as a former Microsoft insider. :-) Details about VMBus relays and
> message ports are fairly obscure for people in the general Linux community.
> It would be sufficient for the commit message to simply state that VTL2
> may respond on either of two connection IDs. There's no separate indication
> of which one will respond in a given VM, so it is necessary to try at runtime
> to see which one works.
Good point, I will remove the extra details in v2.
>
>> For Linux running at VTL2 with VMBus protocol 5.0 or newer, try the
>> redirect connection ID first. In VTL2, the redirect connection ID is the
>> VMBus relay endpoint. If the relay is present, it accepts Initiate
>> Contact and completes the normal version-response handshake. Systems
>> without the relay reject the redirect ID synchronously with
>> HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to
>> VMBUS_MESSAGE_CONNECTION_ID_4.
> Is there a reason for trying the REDIRECT connection ID first? Or
> is the ordering of trying arbitrary? If the REDIRECT connection
> ID must be tried first, state that as a VTL2 requirement.
I will add it as a VTL2 requirement. Redirect Id is used first as it
is the configured VMBus control-plane endpoint when VTL2 redirection
is enabled.
>
>> Return a distinct error for an invalid Initiate Contact connection ID so
>> this fallback does not mask other post-message failures or
>> protocol-version rejections. For older protocol versions, and for Linux
>> below VTL2, keep the existing connection ID selection.
>>
>> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
>> ---
>> drivers/hv/connection.c | 76 ++++++++++++++++++++++++++++++++++++++++-------
>> drivers/hv/hyperv_vmbus.h | 2 ++
>> 2 files changed, 67 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
>> index 1fe3573ae52a4..eb871f87a819d 100644
>> --- a/drivers/hv/connection.c
>> +++ b/drivers/hv/connection.c
>> @@ -71,7 +71,19 @@ module_param(max_version, uint, S_IRUGO);
>> MODULE_PARM_DESC(max_version,
>> "Maximal VMBus protocol version which can be negotiated");
>>
>> -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> +/* Connection IDs to try for VTL2 VMBus protocol 5.0 and newer. */
>> +static const u32 connection_ids[] = {
>> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT,
>> + VMBUS_MESSAGE_CONNECTION_ID_4,
>> +};
> Using an array of connection IDs to iterate through matches the
> design of how multiple VMBus protocols versions are tried. But with
> only two entries in the array, this feels a bit over-engineered unless
> there is reason to expect that more connection ID values are coming
> in the reasonably near future. Absent an array, the code in the
> vmbus_negotiate_version() could just hard code to test the redirect
> connection ID first if running at VTL2, and if that fails with -ENXIO,
> fall through to the normal case.
I used the array to accommodate new connection-ids in the future but I agree
that we don't need it currently. I will simplify it in v2.
>> +
>> +/*
>> + * Send one CHANNELMSG_INITIATE_CONTACT attempt.
>> + * The caller supplies the message connection ID and owns retry/fallback
>> + * policy.
>> + */
>> +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
>> + u32 version, u32 connection_id)
>> {
>> int ret = 0;
>> struct vmbus_channel_initiate_contact *msg;
>> @@ -87,7 +99,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>>
>> /*
>> * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
>> - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
>> + * use connection_id for the Initiate Contact Message,
> This wording of this comment now feels a bit awkward. How about:
>
> "For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the caller supplied
> connection_id for the Initiate Contact Message so the caller can implement the
> necessary retry scheme. For subsequent messages, use the Message Connection ID
> field in the host-returned Version Response Message."
>
> followed by the rest of the existing text.
Will update the comment in V2
>
>
>> * and for subsequent messages, we must use the Message Connection ID
>> * field in the host-returned Version Response Message. And, with
>> * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
>> @@ -99,7 +111,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> if (version >= VERSION_WIN10_V5) {
>> msg->msg_sint = VMBUS_MESSAGE_SINT;
>> msg->msg_vtl = ms_hyperv.vtl;
>> - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
>> + vmbus_connection.msg_conn_id = connection_id;
>> } else {
>> msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
>> vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
>> @@ -161,6 +173,51 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> return ret;
>> }
>>
>> +/*
>> + * Negotiate the given VMBus protocol version with the host.
>> + * Protocol-specific connection ID policy is handled here so the single-try
>> + * helper stays simple.
>> + */
>> +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> +{
>> + int ret;
>> + size_t j;
>> +
>> + /*
>> + * The redirect ID is not a speculative endpoint. If the VMBus relay is
> What is a "speculative endpoint"? And per my comment above, I'd drop
> discussion of VMBus relay, and just phrase things in terms of trying the
> REDIRECT connection ID, and if that doesn't work, then try the normal one.
I will simplify the comment in V2
>
>
>> + * present, it accepts INITIATE_CONTACT and completes the normal version
>> + * response. Systems without the relay reject the ID synchronously, so
>> + * negotiation falls back to VMBUS_MESSAGE_CONNECTION_ID_4.
>> + */
>> + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >= 2) {
>> + for (j = 0; j < ARRAY_SIZE(connection_ids); j++) {
>> + ret = vmbus_try_connection_id(msginfo, version,
>> + connection_ids[j]);
>> + if (vmbus_connection.conn_state == CONNECTED)
>> + return 0;
>> +
>> + if (ret == -ETIMEDOUT)
>> + return ret;
>> +
>> + if (connection_ids[j] ==
>> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT &&
>> + ret == -ENXIO)
>> + continue;
>> +
>> + return ret;
>> + }
>> + return ret;
>> + }
> Per my earlier comment about not having an array of connection IDs, this
> code could be as simple as:
>
> if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >=2) {
> ret = vmbus_try_connection_id(msginfo, version,
> VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
> if (ret != -ENXIO)
> return ret;
> }
I will update this part as per the earlier feedback.
>> +
>> + /*
>> + * Non-redirect path. Protocol 5.0+ below VTL2 uses the standard
>> + * v5 connection ID; pre-v5 ignores the supplied ID and uses the
>> + * legacy connection ID.
>> + */
>> + return vmbus_try_connection_id(msginfo, version,
>> + VMBUS_MESSAGE_CONNECTION_ID_4);
>> +}
>> +
>> /*
>> * vmbus_connect - Sends a connect request on the partition service connection
>> */
>> @@ -455,17 +507,14 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
>> switch (ret) {
>> case HV_STATUS_INVALID_CONNECTION_ID:
>> /*
>> - * See vmbus_negotiate_version(): VMBus protocol 5.0
>> - * and higher require that we must use
>> - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
>> - * Contact message, but on old hosts that only
>> - * support VMBus protocol 4.0 or lower, here we get
>> - * HV_STATUS_INVALID_CONNECTION_ID and we should
>> - * return an error immediately without retrying.
>> + * Let negotiation distinguish an unusable
>> + * endpoint from other failures.
>> + *
>> + * Other messages keep retry behavior.
>> */
>> hdr = buffer;
>> if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
>> - return -EINVAL;
>> + return -ENXIO;
>> /*
>> * We could get this if we send messages too
>> * frequently.
>> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
>> index 49a72a4f3f6a7..951bdebdc0526 100644
>> --- a/drivers/hv/hyperv_vmbus.h
>> +++ b/drivers/hv/hyperv_vmbus.h
>> @@ -109,6 +109,8 @@ struct hv_input_post_message {
>> enum {
>> VMBUS_MESSAGE_CONNECTION_ID = 1,
>> VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
>> + /* VMBus relay port used for INITIATE_CONTACT probing. */
>> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
>> VMBUS_MESSAGE_PORT_ID = 1,
>> VMBUS_EVENT_CONNECTION_ID = 2,
>> VMBUS_EVENT_PORT_ID = 2,
>> --
>> 2.34.1
Thank you for the review, Michael.
Thanks,
Hardik
^ permalink raw reply
* Re: [PATCH] Drivers: hv: vmbus: add VTL2 redirect connection ID
From: Hardik Garg @ 2026-07-16 20:53 UTC (permalink / raw)
To: Saurabh Singh Sengar
Cc: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
linux-hyperv, linux-kernel
In-Reply-To: <alj3AOAXk47fQx4m@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
On 7/16/2026 8:21 AM, Saurabh Singh Sengar wrote:
> On Tue, Jul 14, 2026 at 09:38:38PM +0000, Hardik Garg wrote:
>> VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
>> connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
>> while protocol version 5.0 and newer normally use
>> VMBUS_MESSAGE_CONNECTION_ID_4.
>>
>> When the Linux VMBus driver runs at VTL2, the VMBus control plane may
>> be reached through a VMBus relay instead of the standard host endpoint.
>> In that setup the relay listens on the redirect message connection ID,
>> and an Initiate Contact message sent to the standard ID is not delivered
>> to the control plane.
>>
>> The connection ID selects the Hyper-V message port used to reach the
>> VMBus control plane. If Linux uses the wrong port, the host does not
>> receive the Initiate Contact message.
>>
>> For Linux running at VTL2 with VMBus protocol 5.0 or newer, try the
>> redirect connection ID first. In VTL2, the redirect connection ID is the
>> VMBus relay endpoint. If the relay is present, it accepts Initiate
>> Contact and completes the normal version-response handshake. Systems
>> without the relay reject the redirect ID synchronously with
>> HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to
>> VMBUS_MESSAGE_CONNECTION_ID_4.
>>
>> Return a distinct error for an invalid Initiate Contact connection ID so
>> this fallback does not mask other post-message failures or
>> protocol-version rejections. For older protocol versions, and for Linux
>> below VTL2, keep the existing connection ID selection.
>>
>> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
>> ---
>> drivers/hv/connection.c | 76 ++++++++++++++++++++++++++++++++++++++++-------
>> drivers/hv/hyperv_vmbus.h | 2 ++
>> 2 files changed, 67 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
>> index 1fe3573ae52a4..eb871f87a819d 100644
>> --- a/drivers/hv/connection.c
>> +++ b/drivers/hv/connection.c
>> @@ -71,7 +71,19 @@ module_param(max_version, uint, S_IRUGO);
>> MODULE_PARM_DESC(max_version,
>> "Maximal VMBus protocol version which can be negotiated");
>>
>> -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> +/* Connection IDs to try for VTL2 VMBus protocol 5.0 and newer. */
>> +static const u32 connection_ids[] = {
> Do we want to name it vtl2_connection_ids ?
I will simplify this part in V2 and remove the array.
>> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT,
>> + VMBUS_MESSAGE_CONNECTION_ID_4,
>> +};
>> +
>> +/*
>> + * Send one CHANNELMSG_INITIATE_CONTACT attempt.
>> + * The caller supplies the message connection ID and owns retry/fallback
>> + * policy.
>> + */
>> +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
>> + u32 version, u32 connection_id)
>> {
>> int ret = 0;
>> struct vmbus_channel_initiate_contact *msg;
>> @@ -87,7 +99,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>>
>> /*
>> * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
>> - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
>> + * use connection_id for the Initiate Contact Message,
>> * and for subsequent messages, we must use the Message Connection ID
>> * field in the host-returned Version Response Message. And, with
>> * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
>> @@ -99,7 +111,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> if (version >= VERSION_WIN10_V5) {
>> msg->msg_sint = VMBUS_MESSAGE_SINT;
>> msg->msg_vtl = ms_hyperv.vtl;
>> - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
>> + vmbus_connection.msg_conn_id = connection_id;
>> } else {
>> msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
>> vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
>> @@ -161,6 +173,51 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> return ret;
>> }
>>
>> +/*
>> + * Negotiate the given VMBus protocol version with the host.
>> + * Protocol-specific connection ID policy is handled here so the single-try
>> + * helper stays simple.
>> + */
>> +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>> +{
>> + int ret;
>> + size_t j;
>> +
>> + /*
>> + * The redirect ID is not a speculative endpoint. If the VMBus relay is
>> + * present, it accepts INITIATE_CONTACT and completes the normal version
>> + * response. Systems without the relay reject the ID synchronously, so
>> + * negotiation falls back to VMBUS_MESSAGE_CONNECTION_ID_4.
>> + */
>> + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >= 2) {
>> + for (j = 0; j < ARRAY_SIZE(connection_ids); j++) {
>> + ret = vmbus_try_connection_id(msginfo, version,
>> + connection_ids[j]);
>> + if (vmbus_connection.conn_state == CONNECTED)
>> + return 0;
> Do we need this ? ret is anyway 0 on success which we can return at the end ?
I will remove the redundant part.
>
>> +
>> + if (ret == -ETIMEDOUT)
>> + return ret;
>> +
>> + if (connection_ids[j] ==
>> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT &&
>> + ret == -ENXIO)
>> + continue;
>> +
>> + return ret;
>> + }
>> + return ret;
> Dead code
Will fix it in V2.
>
>> + }
>> +
>> + /*
>> + * Non-redirect path. Protocol 5.0+ below VTL2 uses the standard
>> + * v5 connection ID; pre-v5 ignores the supplied ID and uses the
>> + * legacy connection ID.
>> + */
>> + return vmbus_try_connection_id(msginfo, version,
>> + VMBUS_MESSAGE_CONNECTION_ID_4);
>> +}
>> +
>> /*
>> * vmbus_connect - Sends a connect request on the partition service connection
>> */
>> @@ -455,17 +507,14 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
>> switch (ret) {
>> case HV_STATUS_INVALID_CONNECTION_ID:
>> /*
>> - * See vmbus_negotiate_version(): VMBus protocol 5.0
>> - * and higher require that we must use
>> - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
>> - * Contact message, but on old hosts that only
>> - * support VMBus protocol 4.0 or lower, here we get
>> - * HV_STATUS_INVALID_CONNECTION_ID and we should
>> - * return an error immediately without retrying.
>> + * Let negotiation distinguish an unusable
>> + * endpoint from other failures.
>> + *
>> + * Other messages keep retry behavior.
>> */
>> hdr = buffer;
>> if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
>> - return -EINVAL;
>> + return -ENXIO;
>> /*
>> * We could get this if we send messages too
>> * frequently.
>> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
>> index 49a72a4f3f6a7..951bdebdc0526 100644
>> --- a/drivers/hv/hyperv_vmbus.h
>> +++ b/drivers/hv/hyperv_vmbus.h
>> @@ -109,6 +109,8 @@ struct hv_input_post_message {
>> enum {
>> VMBUS_MESSAGE_CONNECTION_ID = 1,
>> VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
>> + /* VMBus relay port used for INITIATE_CONTACT probing. */
> Do we want to mention specific for VTL2 in comment ?
Yes, that would be helpful. I will add it.
>
>> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
>> VMBUS_MESSAGE_PORT_ID = 1,
>> VMBUS_EVENT_CONNECTION_ID = 2,
>> VMBUS_EVENT_PORT_ID = 2,
>> --
>> 2.34.1
Thank you, Saurabh for the review.
Thanks,
Hardik
^ permalink raw reply
* RE: [PATCH 1/1] Drivers: hv: vmbus: Set DMA coherent mask for VMBus devices
From: Michael Kelley @ 2026-07-16 17:57 UTC (permalink / raw)
To: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, longli@microsoft.com,
linux-hyperv@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
In-Reply-To: <20260608020616.52852-1-mhklkml@zohomail.com>
From: Michael Kelley <mhklkml@zohomail.com> Sent: Sunday, June 7, 2026 7:06 PM
>
> In current code, the coherent_dma_mask for VMBus devices is not set, so
> it has the default value of 0, which essentially means "invalid". Because
> drivers for VMBus devices do not use dma_alloc_*() functions, the usual
> use of the coherent mask does not occur, and no errors result.
>
> However, a valid coherent_dma_mask may be needed even though the drivers
> don't use dma_alloc_*() functions. In a CoCo VM, the VMBus storvsc and
> netvsc drivers must bounce buffer DMA operations through the swiotlb
> because the Hyper-V host can't DMA into encrypted guest memory. If the
> kernel is built with CONFIG_SWIOTLB_DYNAMIC and the initial swiotlb size
> is small, swiotlb code may need to grow the swiotlb in response to a DMA
> mapping request. That growth first allocates a transient pool while the
> swiotlb is expanded in the background. The transient pool memory is
> allocated from the DMA atomic pools, and the allocation code checks for
> a valid coherent_dma_mask. With current code, this check fails, then the
> DMA mapping request from the storvsc or netvsc driver fails, and finally
> an I/O error occurs.
>
> Fix this problem by setting coherent_dma_mask for VMBus devices at the
> same time that dma_mask is set. Being a synthetic bus, VMBus does not
> have any restrictions on coherent DMA, so the coherent mask is set to
> the full 64 bits for all VMBus devices, just like with dma_mask.
>
> Signed-off-by: Michael Kelley <mhklinux@outlook.com>
Gentle ping: Anyone able to review this patch? There's a
Sashiko comment, but it's for an issue in an unrelated error path,
so I'm not planning to respin this patch for that comment.
Michael
> ---
> I have not provided a Fixes: tag because the scenario under which
> the error occurs is an artificial test case that I came across
> while stressing a unrelated patch set. The fix is valid for general
> goodness, but the likelihood of the problem occurring in the real
> world is extremely small. So I see little value in adding this
> patch to the stable kernel maintainers' workload. If someone wants
> to argue otherwise, I have no fundamental objection to adding the
> Fixes: tag.
>
> drivers/hv/vmbus_drv.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> index c9eeb2ec365d..26e8273bbddd 100644
> --- a/drivers/hv/vmbus_drv.c
> +++ b/drivers/hv/vmbus_drv.c
> @@ -2192,6 +2192,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
> child_device_obj->device.dma_parms = &child_device_obj->dma_parms;
> child_device_obj->device.dma_mask = &child_device_obj->dma_mask;
> dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64));
> + dma_set_coherent_mask(&child_device_obj->device, DMA_BIT_MASK(64));
>
> /*
> * Register with the LDM. This will kick off the driver/device
> --
> 2.25.1
>
^ permalink raw reply
* RE: RE: [PATCH 1/2] hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation
From: Michael Kelley @ 2026-07-16 17:37 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: linux-hyperv@vger.kernel.org, linux-rt-devel@lists.linux.dev,
K. Y. Srinivasan, Dexuan Cui, Haiyang Zhang, Jan Kiszka, Long Li,
Wei Liu
In-Reply-To: <20260715143252.gWEZfb5B@linutronix.de>
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Sent: Wednesday, July 15, 2026 7:33 AM
>
> On 2026-04-04 01:22:08 [+0000], Michael Kelley wrote:
> > Nit: For historical consistency, use "Drivers: hv: vmbus:" as the prefix for the
> > patch "Subject:" line. Same in Patch 2 of this series.
>
> okay.
>
> > Also, any reason not to have copied linux-kernel@vger.kernel.org? I know this
> > is pretty much just a Hyper-V thing, but I would have liked to see what the
> > Sashiko AI did with these two patches. :-)
>
> Can do.
>
> > > lockdep_hardirq_threaded() is supposed to be used within IRQ core code
> > > and not within drivers. It is not obvious from within the driver, that
> > > this is the only interrupt service routing and that it is not shared
> > > handler.
> >
> > I presume you meant "routine". And what do you mean by "the only interrupt
> > service routine"? And why is the lack of obviousness relevant here? I don't have
> > deep expertise in lockdep, but evidently there's some conclusion to reach and it
> > would have helped me to have it spelled out.
>
> You use lockdep_hardirq_threaded() which marks the hardirq as a thread
> for lockdep purposes. The reason is that the IRQ core will force-thread
> the handler and the whole routine will be threaded. It does invoke the
> function and the very begin and nothing else will be done.
>
> vmbus_isr() is using it before __vmbus_isr() is invoked and there can be
> other functions/ ISRs that are invoked afterwards which would be wrongly
> recognized.
> To make it more concrete: sysvec_hyperv_callback() invokes mshv_handler
> and vmbus_handler. Would vmbus_handler() be invoked first then it will
> "spill" this lockdep attribute into mshv_handler(). Nested locks in
> mshv_handler() would not be visible then.
> This override limits it only to __vmbus_isr() while the RT case above is
> using vmbus_irqd_wake().
>
> > >
> > > Replace lockdep_hardirq_threaded() with a lockdep annotation limiting
> > > threaded context on PREEMPT_RT to __vmbus_isr().
> >
> > Again, I'm not clear what "limiting threaded context" means. But see my
> > additional comment further down.
>
> Does the explanation above make it clear?
Yes, this makes sense. Thanks for the help.
>
> > >
> > > Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT")
> > > Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> > > ---
> > > drivers/hv/vmbus_drv.c | 5 ++++-
> > > 1 file changed, 4 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> > > index bc4fc1951ae1c..e44275370ac2a 100644
> > > --- a/drivers/hv/vmbus_drv.c
> > > +++ b/drivers/hv/vmbus_drv.c
> > > @@ -1407,8 +1407,11 @@ void vmbus_isr(void)
> > > if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
> > > vmbus_irqd_wake();
> > > } else {
> > > - lockdep_hardirq_threaded();
> >
> > I see two similar occurrences of LD_WAIT_CONFIG in the kernel:
> > __kfree_rcu_sheaf() and adjacent to printk_legacy_allow_spinlock_enter().
> > Both occurrences have a multi-line comment explaining the "why". I'd like
> > to see a similar comment here so that drive-by readers of the code have
> > some idea of what's going on. My suggestion is something like this:
> >
> > vmbus_isr() always runs at hard IRQ level -- the interrupt is not threaded. It
> > calls __vmbus_isr() here, which may obtain the spinlock_t sched_lock for
> > a VMBus channel in vmbus_chan_sched(). If CONFIG_PROVE_LOCKING=y,
> > lockdep complains because obtaining spinlock_t's is not permitted at hard
> > IRQ level in PREEMPT_RT configurations. However, the PREEMPT_RT path
> > is handled separately above, so there's actually not a problem. Tell
> > lockdep that acquiring the spinlock_t is valid by temporarily raising
> > the wait-type to LD_WAIT_CONFIG using the "fake" lock vmbus_map.
> > If lockdep is not enabled, the acquire & release of the fake lock are no-ops,
> > so performance is not impacted.
> >
> > Please review my suggested text and revise as appropriate, as I'm far
> > from an expert on any of this. The above is based on what I've learned
> > just now from a bit of research.
>
> What about?
> vmbus_isr is never force-threaded and always invoked at hard IRQ level.
> __vmbus_isr() below can acquire a spinlock_t which becomes a sleeping
> lock and must not be acquired in this context. Therefore on PREEMPT_RT
> this will be threaded via vmbus_irqd_wake(). On non-PREEMPT the
> annotation lets lockdep know that acquiring a spinlock_t is not an
> issue.
Yes, I'm good with this.
>
> > And thanks for jumping in and making all this better ....
>
> so I've been looking at __vmbus_isr() and vmbus_chan_sched() seems fine.
> vmbus_message_sched() on the handler invokes hv_stimer0_isr() which
> should not be done in the thread but in the hardirq. Is this the only
> timer in the system or some legacy thingy that is no longer used?
The stimer0 is not legacy, but having the stimer0 interrupt delivered as
a VMBus message *is* legacy. The legacy mechanism is used when running
on really old versions of Hyper-V. Modern versions of Hyper-V deliver stimer0
interrupts via a separate stimer0-specific hardirq. This linkage between stimer0
and VMBus creates separation problems in that it should conceptually be
possible to run on Hyper-V with stimer0 without using VMBus or vmbus_isr().
I will do some research on the history and supported versions of Hyper-V.
It might be possible to drop Linux support for running on these old versions
of Hyper-V, and remove this legacy mechanism for delivering stimer0
interrupts. That would be a nice cleanup of some historical baggage.
>
> And there is a lot of tasklet involved which mandates a
> local_bh_disable()/enable() in run_vmbus_irqd() around the function or
> everything gets pushed into ksoftirqd.
OK.
Michael
^ permalink raw reply
* RE: [PATCH] Drivers: hv: vmbus: add VTL2 redirect connection ID
From: Michael Kelley @ 2026-07-16 17:03 UTC (permalink / raw)
To: Hardik Garg, K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260714213838.3367103-1-hargar@linux.microsoft.com>
From: Hardik Garg <hargar@linux.microsoft.com> Sent: Tuesday, July 14, 2026 2:39 PM
>
> VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
> connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
> while protocol version 5.0 and newer normally use
> VMBUS_MESSAGE_CONNECTION_ID_4.
>
> When the Linux VMBus driver runs at VTL2, the VMBus control plane may
> be reached through a VMBus relay instead of the standard host endpoint.
> In that setup the relay listens on the redirect message connection ID,
> and an Initiate Contact message sent to the standard ID is not delivered
> to the control plane.
>
> The connection ID selects the Hyper-V message port used to reach the
> VMBus control plane. If Linux uses the wrong port, the host does not
> receive the Initiate Contact message.
This is a judgment call, but to me the previous two paragraphs are
explaining VTL2 implementation details that aren't necessary for
understanding this patch. Yes, in earlier discussions I asked questions
about "why" all this funky behavior with connection IDs, but that's
just me as a former Microsoft insider. :-) Details about VMBus relays and
message ports are fairly obscure for people in the general Linux community.
It would be sufficient for the commit message to simply state that VTL2
may respond on either of two connection IDs. There's no separate indication
of which one will respond in a given VM, so it is necessary to try at runtime
to see which one works.
>
> For Linux running at VTL2 with VMBus protocol 5.0 or newer, try the
> redirect connection ID first. In VTL2, the redirect connection ID is the
> VMBus relay endpoint. If the relay is present, it accepts Initiate
> Contact and completes the normal version-response handshake. Systems
> without the relay reject the redirect ID synchronously with
> HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to
> VMBUS_MESSAGE_CONNECTION_ID_4.
Is there a reason for trying the REDIRECT connection ID first? Or
is the ordering of trying arbitrary? If the REDIRECT connection
ID must be tried first, state that as a VTL2 requirement.
>
> Return a distinct error for an invalid Initiate Contact connection ID so
> this fallback does not mask other post-message failures or
> protocol-version rejections. For older protocol versions, and for Linux
> below VTL2, keep the existing connection ID selection.
>
> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
> ---
> drivers/hv/connection.c | 76 ++++++++++++++++++++++++++++++++++++++++-------
> drivers/hv/hyperv_vmbus.h | 2 ++
> 2 files changed, 67 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
> index 1fe3573ae52a4..eb871f87a819d 100644
> --- a/drivers/hv/connection.c
> +++ b/drivers/hv/connection.c
> @@ -71,7 +71,19 @@ module_param(max_version, uint, S_IRUGO);
> MODULE_PARM_DESC(max_version,
> "Maximal VMBus protocol version which can be negotiated");
>
> -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> +/* Connection IDs to try for VTL2 VMBus protocol 5.0 and newer. */
> +static const u32 connection_ids[] = {
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT,
> + VMBUS_MESSAGE_CONNECTION_ID_4,
> +};
Using an array of connection IDs to iterate through matches the
design of how multiple VMBus protocols versions are tried. But with
only two entries in the array, this feels a bit over-engineered unless
there is reason to expect that more connection ID values are coming
in the reasonably near future. Absent an array, the code in the
vmbus_negotiate_version() could just hard code to test the redirect
connection ID first if running at VTL2, and if that fails with -ENXIO,
fall through to the normal case.
> +
> +/*
> + * Send one CHANNELMSG_INITIATE_CONTACT attempt.
> + * The caller supplies the message connection ID and owns retry/fallback
> + * policy.
> + */
> +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
> + u32 version, u32 connection_id)
> {
> int ret = 0;
> struct vmbus_channel_initiate_contact *msg;
> @@ -87,7 +99,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>
> /*
> * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
> - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
> + * use connection_id for the Initiate Contact Message,
This wording of this comment now feels a bit awkward. How about:
"For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the caller supplied
connection_id for the Initiate Contact Message so the caller can implement the
necessary retry scheme. For subsequent messages, use the Message Connection ID
field in the host-returned Version Response Message."
followed by the rest of the existing text.
> * and for subsequent messages, we must use the Message Connection ID
> * field in the host-returned Version Response Message. And, with
> * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
> @@ -99,7 +111,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> if (version >= VERSION_WIN10_V5) {
> msg->msg_sint = VMBUS_MESSAGE_SINT;
> msg->msg_vtl = ms_hyperv.vtl;
> - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
> + vmbus_connection.msg_conn_id = connection_id;
> } else {
> msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
> vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
> @@ -161,6 +173,51 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> return ret;
> }
>
> +/*
> + * Negotiate the given VMBus protocol version with the host.
> + * Protocol-specific connection ID policy is handled here so the single-try
> + * helper stays simple.
> + */
> +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> +{
> + int ret;
> + size_t j;
> +
> + /*
> + * The redirect ID is not a speculative endpoint. If the VMBus relay is
What is a "speculative endpoint"? And per my comment above, I'd drop
discussion of VMBus relay, and just phrase things in terms of trying the
REDIRECT connection ID, and if that doesn't work, then try the normal one.
> + * present, it accepts INITIATE_CONTACT and completes the normal version
> + * response. Systems without the relay reject the ID synchronously, so
> + * negotiation falls back to VMBUS_MESSAGE_CONNECTION_ID_4.
> + */
> + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >= 2) {
> + for (j = 0; j < ARRAY_SIZE(connection_ids); j++) {
> + ret = vmbus_try_connection_id(msginfo, version,
> + connection_ids[j]);
> + if (vmbus_connection.conn_state == CONNECTED)
> + return 0;
> +
> + if (ret == -ETIMEDOUT)
> + return ret;
> +
> + if (connection_ids[j] ==
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT &&
> + ret == -ENXIO)
> + continue;
> +
> + return ret;
> + }
> + return ret;
> + }
Per my earlier comment about not having an array of connection IDs, this
code could be as simple as:
if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >=2) {
ret = vmbus_try_connection_id(msginfo, version,
VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
if (ret != -ENXIO)
return ret;
}
> +
> + /*
> + * Non-redirect path. Protocol 5.0+ below VTL2 uses the standard
> + * v5 connection ID; pre-v5 ignores the supplied ID and uses the
> + * legacy connection ID.
> + */
> + return vmbus_try_connection_id(msginfo, version,
> + VMBUS_MESSAGE_CONNECTION_ID_4);
> +}
> +
> /*
> * vmbus_connect - Sends a connect request on the partition service connection
> */
> @@ -455,17 +507,14 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
> switch (ret) {
> case HV_STATUS_INVALID_CONNECTION_ID:
> /*
> - * See vmbus_negotiate_version(): VMBus protocol 5.0
> - * and higher require that we must use
> - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
> - * Contact message, but on old hosts that only
> - * support VMBus protocol 4.0 or lower, here we get
> - * HV_STATUS_INVALID_CONNECTION_ID and we should
> - * return an error immediately without retrying.
> + * Let negotiation distinguish an unusable
> + * endpoint from other failures.
> + *
> + * Other messages keep retry behavior.
> */
> hdr = buffer;
> if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
> - return -EINVAL;
> + return -ENXIO;
> /*
> * We could get this if we send messages too
> * frequently.
> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
> index 49a72a4f3f6a7..951bdebdc0526 100644
> --- a/drivers/hv/hyperv_vmbus.h
> +++ b/drivers/hv/hyperv_vmbus.h
> @@ -109,6 +109,8 @@ struct hv_input_post_message {
> enum {
> VMBUS_MESSAGE_CONNECTION_ID = 1,
> VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
> + /* VMBus relay port used for INITIATE_CONTACT probing. */
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
> VMBUS_MESSAGE_PORT_ID = 1,
> VMBUS_EVENT_CONNECTION_ID = 2,
> VMBUS_EVENT_PORT_ID = 2,
> --
> 2.34.1
^ permalink raw reply
* Re: [PATCH] Drivers: hv: vmbus: add VTL2 redirect connection ID
From: Saurabh Singh Sengar @ 2026-07-16 15:21 UTC (permalink / raw)
To: Hardik Garg
Cc: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
linux-hyperv, linux-kernel
In-Reply-To: <20260714213838.3367103-1-hargar@linux.microsoft.com>
On Tue, Jul 14, 2026 at 09:38:38PM +0000, Hardik Garg wrote:
> VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message
> connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID,
> while protocol version 5.0 and newer normally use
> VMBUS_MESSAGE_CONNECTION_ID_4.
>
> When the Linux VMBus driver runs at VTL2, the VMBus control plane may
> be reached through a VMBus relay instead of the standard host endpoint.
> In that setup the relay listens on the redirect message connection ID,
> and an Initiate Contact message sent to the standard ID is not delivered
> to the control plane.
>
> The connection ID selects the Hyper-V message port used to reach the
> VMBus control plane. If Linux uses the wrong port, the host does not
> receive the Initiate Contact message.
>
> For Linux running at VTL2 with VMBus protocol 5.0 or newer, try the
> redirect connection ID first. In VTL2, the redirect connection ID is the
> VMBus relay endpoint. If the relay is present, it accepts Initiate
> Contact and completes the normal version-response handshake. Systems
> without the relay reject the redirect ID synchronously with
> HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to
> VMBUS_MESSAGE_CONNECTION_ID_4.
>
> Return a distinct error for an invalid Initiate Contact connection ID so
> this fallback does not mask other post-message failures or
> protocol-version rejections. For older protocol versions, and for Linux
> below VTL2, keep the existing connection ID selection.
>
> Signed-off-by: Hardik Garg <hargar@linux.microsoft.com>
> ---
> drivers/hv/connection.c | 76 ++++++++++++++++++++++++++++++++++++++++-------
> drivers/hv/hyperv_vmbus.h | 2 ++
> 2 files changed, 67 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
> index 1fe3573ae52a4..eb871f87a819d 100644
> --- a/drivers/hv/connection.c
> +++ b/drivers/hv/connection.c
> @@ -71,7 +71,19 @@ module_param(max_version, uint, S_IRUGO);
> MODULE_PARM_DESC(max_version,
> "Maximal VMBus protocol version which can be negotiated");
>
> -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> +/* Connection IDs to try for VTL2 VMBus protocol 5.0 and newer. */
> +static const u32 connection_ids[] = {
Do we want to name it vtl2_connection_ids ?
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT,
> + VMBUS_MESSAGE_CONNECTION_ID_4,
> +};
> +
> +/*
> + * Send one CHANNELMSG_INITIATE_CONTACT attempt.
> + * The caller supplies the message connection ID and owns retry/fallback
> + * policy.
> + */
> +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
> + u32 version, u32 connection_id)
> {
> int ret = 0;
> struct vmbus_channel_initiate_contact *msg;
> @@ -87,7 +99,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
>
> /*
> * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
> - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
> + * use connection_id for the Initiate Contact Message,
> * and for subsequent messages, we must use the Message Connection ID
> * field in the host-returned Version Response Message. And, with
> * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
> @@ -99,7 +111,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> if (version >= VERSION_WIN10_V5) {
> msg->msg_sint = VMBUS_MESSAGE_SINT;
> msg->msg_vtl = ms_hyperv.vtl;
> - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
> + vmbus_connection.msg_conn_id = connection_id;
> } else {
> msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
> vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
> @@ -161,6 +173,51 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> return ret;
> }
>
> +/*
> + * Negotiate the given VMBus protocol version with the host.
> + * Protocol-specific connection ID policy is handled here so the single-try
> + * helper stays simple.
> + */
> +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
> +{
> + int ret;
> + size_t j;
> +
> + /*
> + * The redirect ID is not a speculative endpoint. If the VMBus relay is
> + * present, it accepts INITIATE_CONTACT and completes the normal version
> + * response. Systems without the relay reject the ID synchronously, so
> + * negotiation falls back to VMBUS_MESSAGE_CONNECTION_ID_4.
> + */
> + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl >= 2) {
> + for (j = 0; j < ARRAY_SIZE(connection_ids); j++) {
> + ret = vmbus_try_connection_id(msginfo, version,
> + connection_ids[j]);
> + if (vmbus_connection.conn_state == CONNECTED)
> + return 0;
Do we need this ? ret is anyway 0 on success which we can return at the end ?
> +
> + if (ret == -ETIMEDOUT)
> + return ret;
> +
> + if (connection_ids[j] ==
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT &&
> + ret == -ENXIO)
> + continue;
> +
> + return ret;
> + }
> + return ret;
Dead code
> + }
> +
> + /*
> + * Non-redirect path. Protocol 5.0+ below VTL2 uses the standard
> + * v5 connection ID; pre-v5 ignores the supplied ID and uses the
> + * legacy connection ID.
> + */
> + return vmbus_try_connection_id(msginfo, version,
> + VMBUS_MESSAGE_CONNECTION_ID_4);
> +}
> +
> /*
> * vmbus_connect - Sends a connect request on the partition service connection
> */
> @@ -455,17 +507,14 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
> switch (ret) {
> case HV_STATUS_INVALID_CONNECTION_ID:
> /*
> - * See vmbus_negotiate_version(): VMBus protocol 5.0
> - * and higher require that we must use
> - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
> - * Contact message, but on old hosts that only
> - * support VMBus protocol 4.0 or lower, here we get
> - * HV_STATUS_INVALID_CONNECTION_ID and we should
> - * return an error immediately without retrying.
> + * Let negotiation distinguish an unusable
> + * endpoint from other failures.
> + *
> + * Other messages keep retry behavior.
> */
> hdr = buffer;
> if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
> - return -EINVAL;
> + return -ENXIO;
> /*
> * We could get this if we send messages too
> * frequently.
> diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
> index 49a72a4f3f6a7..951bdebdc0526 100644
> --- a/drivers/hv/hyperv_vmbus.h
> +++ b/drivers/hv/hyperv_vmbus.h
> @@ -109,6 +109,8 @@ struct hv_input_post_message {
> enum {
> VMBUS_MESSAGE_CONNECTION_ID = 1,
> VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
> + /* VMBus relay port used for INITIATE_CONTACT probing. */
Do we want to mention specific for VTL2 in comment ?
> + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
> VMBUS_MESSAGE_PORT_ID = 1,
> VMBUS_EVENT_CONNECTION_ID = 2,
> VMBUS_EVENT_PORT_ID = 2,
> --
> 2.34.1
- Saurabh
^ permalink raw reply
* Re: [PATCH net-next] net: mana: Add debug knob to skip TX timeout recovery reset
From: Simon Horman @ 2026-07-16 14:27 UTC (permalink / raw)
To: Aditya Garg
Cc: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, kotaranov, ernis, dipayanroy, ssengar,
gargaditya, linux-hyperv, netdev, linux-kernel, linux-rdma
In-Reply-To: <20260710132229.2851441-1-gargaditya@linux.microsoft.com>
On Fri, Jul 10, 2026 at 06:22:29AM -0700, Aditya Garg wrote:
> Add a per-port debugfs boolean "tx_timeout_skip_reset" that, when
> enabled, makes mana_tx_timeout() log the TX timeout and return without
> queueing the per-port detach/attach recovery work.
>
> This is a debug-only aid for bringup and qualification: skipping the
> recovery reset keeps the device and queue state intact so a TX timeout
> can be correlated with hardware telemetry. The knob defaults to false,
> so production recovery behaviour is unchanged.
>
> Signed-off-by: Aditya Garg <gargaditya@linux.microsoft.com>
> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> Reviewed-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH rdma-next] RDMA: Use ib_no_udata_io() in query_device callbacks
From: Leon Romanovsky @ 2026-07-16 8:21 UTC (permalink / raw)
To: Potnuri Bharat Teja, Jason Gunthorpe, Cheng Xu, Kai Shen,
Chengchang Tang, Junxian Huang, Abhijit Gangurde, Allen Hubbe,
Tatyana Nikolova, Long Li, Konstantin Taranov, Selvin Xavier,
Michal Kalderon, Nelson Escobar, Satish Kharat, Bryan Tan,
Vishnu Dasa, Broadcom internal kernel review list,
Dennis Dalessandro, Zhu Yanjun, Bernard Metzler, Leon Romanovsky
Cc: linux-rdma, linux-kernel, linux-hyperv
In-Reply-To: <20260714-convert-to-noio-udata-v1-1-f1f6b6c7c988@nvidia.com>
On Tue, 14 Jul 2026 14:46:58 +0300, Leon Romanovsky wrote:
> The query_device callbacks that neither accept driver-specific input nor
> return a driver-specific response open-code the empty udata handling as
> ib_is_udata_in_empty() on entry and ib_respond_empty_udata() on exit.
>
> ib_no_udata_io() already combines both steps, so replace the entry check
> with it and simply return 0 on success.
>
> [...]
Applied, thanks!
[1/1] RDMA: Use ib_no_udata_io() in query_device callbacks
https://git.kernel.org/rdma/rdma/c/b12985a0473a2f
Best regards,
--
Leon Romanovsky <leonro@nvidia.com>
^ permalink raw reply
* Re: [PATCH net-next 2/7] net: mana: fix HWC RQ/SQ buffer size swap
From: sashiko-bot @ 2026-07-16 3:30 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv
In-Reply-To: <20260715032942.3945317-3-longli@microsoft.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-After-Free and data corruption due to premature timeout return in mana_hwc_send_request without canceling hardware transaction.
- [High] Missing locking allows concurrent WQE ring corruption in mana_hwc_send_request.
--
--- Patch [2]: [PATCH net-next 2/7] net: mana: fix HWC RQ/SQ buffer size swap ---
commit a1baa394548fbd57903fe3f07805307884c7c9c0
Author: Long Li <longli@microsoft.com>
net: mana: fix HWC RQ/SQ buffer size swap
The HWC RQ receives responses and the SQ sends requests, but
mana_hwc_init_queues() sized the RQ with max_req_msg_size and the SQ with
max_resp_msg_size -- backwards. A response larger than the undersized RQ
buffer could overflow it, and mana_hwc_rx_event_handler() recovered the
RX slot index by dividing by the wrong size.
> diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> index 409e20caeccdc..3f011ebbe7b3e 100644
> --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
> +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
[ ... ]
> @@ -749,6 +749,7 @@ static int mana_hwc_init_queues(struct hw_channel_context *hwc, u16 q_depth,
>
> hwc->num_inflight_msg = q_depth;
> hwc->max_req_msg_size = max_req_msg_size;
> + hwc->max_resp_msg_size = max_resp_msg_size;
>
> return 0;
> out:
[Severity: High]
This isn't a bug introduced by this patch, but could this timeout path lead
to a use-after-free and data corruption?
If a caller invokes mana_hwc_send_request() and it times out, it frees the
msg_id slot and returns without canceling the hardware transaction:
mana_hwc_send_request() {
if (!wait_for_completion_timeout(&ctx->comp_event, ...)) {
err = -ETIMEDOUT;
goto out;
}
...
out:
mana_hwc_put_msg_index(hwc, msg_id);
}
If the caller provided a stack-allocated response buffer, its stack frame
will be popped when mana_hwc_send_request() returns. When the delayed
hardware response eventually arrives, will mana_hwc_handle_resp() blindly
accept it and overwrite the now-freed stack address?
mana_hwc_handle_resp() {
...
memcpy(ctx->output_buf, resp_msg, resp_len);
}
Alternatively, if another thread reuses the freed msg_id, could it receive
this delayed response instead of its own?
[Severity: High]
This is also a pre-existing issue, but does this code allow concurrent
WQE ring corruption?
Multiple threads can concurrently invoke mana_hwc_send_request(), such as
from parallel InfiniBand requests. This leads to mana_hwc_post_tx_wqe(),
which appears to call mana_gd_post_and_ring() locklessly:
mana_hwc_post_tx_wqe() {
...
err = mana_gd_post_and_ring(hwc_txq->gdma_wq, &req->wqe_req, NULL);
}
If multiple threads execute this concurrently, won't they read the same
wq->head in mana_gd_post_work_request(), overwrite the exact same WQE
slots, and corrupt the shared queue state?
mana_gd_post_work_request() {
wqe_ptr = mana_gd_get_wqe_ptr(wq, wq->head);
...
mana_gd_write_sgl(wq, wqe_ptr, wqe_req);
wq->head += wqe_size / GDMA_WQE_BU_SIZE;
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715032942.3945317-1-longli@microsoft.com?part=2
^ permalink raw reply
* Re: [PATCH net-next 5/7] net: mana: fix HWC teardown safety with setup_active flag and destroy ordering
From: sashiko-bot @ 2026-07-16 3:30 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv
In-Reply-To: <20260715032942.3945317-6-longli@microsoft.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing teardown synchronization for dynamically allocated service tasks leads to Use-After-Free during device removal.
--
--- Patch [5]: [PATCH net-next 5/7] net: mana: fix HWC teardown safety with setup_active flag and destroy ordering ---
commit d335b5db9ecdafc6371e77ac2bb32f6aa7467088
Author: Long Li <longli@microsoft.com>
net: mana: fix HWC teardown safety with setup_active flag and destroy ordering
Two teardown hazards let the hardware touch memory the driver freed.
First, if establish_channel fails, the caller had no reliable way to know
teardown was required and could free buffers while mappings were still live.
Second, destroy_channel freed TXQ/RXQ buffers while the HWC EQ was still on
the interrupt dispatch list.
Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)")
> diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> index 68236727aee8b..b26c2122ebf52 100644
> --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
> +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
[ ... ]
> @@ -878,11 +902,62 @@ void mana_hwc_destroy_channel(struct gdma_context *gc)
> if (!hwc)
> return;
>
> - /* gc->max_num_cqs is set in mana_hwc_init_event_handler(). If it's
> - * non-zero, the HWC worked and we should tear down the HWC here.
> + /* Tear down the HWC if setup_hwc previously activated MST entries.
> + * This is the definitive flag — unlike max_num_cqs which depends
> + * on the init EQE arriving.
> */
> - if (gc->max_num_cqs > 0)
> - mana_smc_teardown_hwc(&gc->shm_channel, false);
> + if (hwc->setup_active) {
> + int td_err = mana_smc_teardown_hwc(&gc->shm_channel, false);
[Severity: High]
This is a pre-existing issue, but is there a missing synchronization step for
dynamically allocated service tasks during device teardown?
When a hardware event (like GDMA_EQE_HWC_RESET_REQUEST) occurs, the EQ
interrupt handler calls mana_schedule_serv_work(), which allocates a
mana_serv_work structure and schedules it on the system workqueue:
drivers/net/ethernet/microsoft/mana/gdma_main.c:mana_schedule_serv_work() {
...
mns_wk = kzalloc(sizeof(*mns_wk), GFP_ATOMIC);
...
INIT_WORK(&mns_wk->serv_work, mana_serv_func);
schedule_work(&mns_wk->serv_work);
}
If a device unbind or module removal occurs concurrently, mana_gd_remove()
is called, which frees the context without flushing or canceling these
dynamically allocated tasks:
drivers/net/ethernet/microsoft/mana/gdma_main.c:mana_gd_remove() {
...
vfree(gc);
}
When the scheduled mana_serv_func() eventually executes, it accesses the
freed gdma_context (gc) and hw_channel_context (hwc):
drivers/net/ethernet/microsoft/mana/gdma_main.c:mana_serv_reset() {
...
struct gdma_context *gc = pci_get_drvdata(pdev);
struct hw_channel_context *hwc;
...
hwc = gc->hwc.driver_data;
}
Could this race condition result in a Use-After-Free during teardown?
> +
> + if (td_err) {
> + dev_err(gc->dev, "HWC teardown failed: %d, issuing FLR\n",
> + td_err);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715032942.3945317-1-longli@microsoft.com?part=5
^ permalink raw reply
* Re: [PATCH net-next 6/7] net: mana: support concurrent HWC requests with proper synchronization
From: sashiko-bot @ 2026-07-16 3:30 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv
In-Reply-To: <20260715032942.3945317-7-longli@microsoft.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Use-After-Free and waitqueue data corruption involving `gc->hwc_drain_waitq` due to flawed teardown logic and a race condition.
--
--- Patch [6]: [PATCH net-next 6/7] net: mana: support concurrent HWC requests with proper synchronization ---
commit c7a2831143a0268564f0b676313bc28bfee068e7
Author: Long Li <longli@microsoft.com>
net: mana: support concurrent HWC requests with proper synchronization
This patch replaces the depth-1 semaphore in the HWC with a waitqueue and
bitmap scheme to allow multiple in-flight requests. It introduces per-slot
synchronization, queue locks, and active sender tracking to serialize
channel teardown against concurrent requests.
> diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
> --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
> +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
[ ... ]
> @@ -367,9 +371,25 @@ static int mana_gd_detect_devices(struct pci_dev *pdev)
> int mana_gd_send_request(struct gdma_context *gc, u32 req_len, const void *req,
> u32 resp_len, void *resp)
> {
> - struct hw_channel_context *hwc = gc->hwc.driver_data;
> + struct hw_channel_context *hwc;
> + unsigned long flags;
> + int err;
> +
> + spin_lock_irqsave(&gc->hwc_lock, flags);
> + hwc = gc->hwc.driver_data;
> + if (!hwc) {
> + spin_unlock_irqrestore(&gc->hwc_lock, flags);
> + return -ENODEV;
> + }
> + atomic_inc(&hwc->active_senders);
> + spin_unlock_irqrestore(&gc->hwc_lock, flags);
> +
> + err = mana_hwc_send_request(hwc, req_len, req, resp_len, resp);
>
> - return mana_hwc_send_request(hwc, req_len, req, resp_len, resp);
> + if (atomic_dec_and_test(&hwc->active_senders))
> + wake_up(&gc->hwc_drain_waitq);
[Severity: High]
Could this cause a use-after-free of the gdma_context (gc) structure?
If atomic_dec_and_test() drops the last reference and returns true, it
immediately unblocks the wait_event() in mana_hwc_destroy_channel(). The
teardown thread can then proceed and the caller (mana_gd_remove) might free
gc before this sender thread executes the wake_up() call on
gc->hwc_drain_waitq.
> + return err;
> }
> diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
> +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
[ ... ]
> @@ -950,11 +1060,49 @@ void mana_hwc_destroy_channel(struct gdma_context *gc)
> hwc->setup_active = false;
> }
>
> + /* After SMC teardown, no more hardware events should arrive.
> + * Force-complete any remaining in-flight senders so they can
> + * exit and drop their refs.
> + */
> + if (hwc->caller_ctx) {
[ ... ]
> + }
> +
> + /* Wait for all sender threads to finish and drop their refs.
> + * After this, only slots held by timed-out senders whose
> + * handle_resp() never ran remain in the bitmap.
> + */
> + wait_event(gc->hwc_drain_waitq,
> + atomic_read(&hwc->active_senders) == 0);
[Severity: High]
What happens if the function level reset (FLR) fails during teardown?
Looking at mana_hwc_destroy_channel(), if pcie_flr() fails, it prints a
warning and returns early from the function. Because this early return
bypasses the newly added wait_event() logic above, any active sender
threads are abandoned.
When mana_gd_remove() subsequently frees gc, wouldn't these stranded
senders cause a use-after-free when they eventually time out and access
the freed gc->hwc_drain_waitq?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715032942.3945317-1-longli@microsoft.com?part=6
^ permalink raw reply
* Re: [PATCH net-next 3/7] net: mana: free HWC comp_buf after destroying the EQ
From: sashiko-bot @ 2026-07-16 3:30 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv
In-Reply-To: <20260715032942.3945317-4-longli@microsoft.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Use-after-free of TX and RX work queues during HWC channel destruction due to incorrect teardown ordering.
- [High] Data corruption and premature completion due to msg_id reuse after timeout in mana_hwc_send_request().
- [High] Use-after-free of gdma_context (gc) due to un-canceled service work during device removal.
--
--- Patch [3]: [PATCH net-next 3/7] net: mana: free HWC comp_buf after destroying the EQ ---
commit 0772412f368270fa1d8812b8355a2560f063690d
Author: Long Li <longli@microsoft.com>
Subject: net: mana: free HWC comp_buf after destroying the EQ
This patch resolves a use-after-free issue by deferring the release of
the hardware channel completion buffer until after the event queue has been
destroyed. This ensures no late interrupts can access the freed memory.
> diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
> +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
[ ... ]
> static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)
> {
[ ... ]
> if (hwc_cq->gdma_eq)
> mana_gd_destroy_queue(gc, hwc_cq->gdma_eq);
>
> + kfree(hwc_cq->comp_buf);
> kfree(hwc_cq);
> }
[Severity: High]
This isn't a bug introduced by this patch, but while looking at teardown
ordering, could there be a similar use-after-free with the TX and RX work
queues?
In mana_hwc_destroy_channel(), the work queues are freed before the
completion queue:
mana_hwc_destroy_channel() {
if (hwc->txq)
mana_hwc_destroy_wq(hwc, hwc->txq);
if (hwc->rxq)
mana_hwc_destroy_wq(hwc, hwc->rxq);
if (hwc->cq)
mana_hwc_destroy_cq(hwc->gdma_dev->gdma_context, hwc->cq);
}
If an interrupt fires before the CQ and EQ are destroyed, will the
completion handler still attempt to dereference the already freed work
queues?
mana_hwc_rx_event_handler() {
struct hw_channel_context *hwc = ctx;
struct hwc_wq *hwc_rxq = hwc->rxq;
...
rq = hwc_rxq->gdma_wq;
...
}
[Severity: High]
This is a pre-existing issue, but does mana_hwc_send_request() have a
race condition if a request times out?
If a timeout occurs, mana_hwc_send_request() releases the msg_id while the
hardware might still be processing it:
mana_hwc_send_request() {
...
err = -ETIMEDOUT;
goto out;
...
out:
mana_hwc_put_msg_index(hwc, msg_id);
}
Could another caller subsequently allocate the same msg_id and assign its
own stack buffer? If the hardware then completes the original delayed
request, would the handler overwrite the new caller's buffer with the old
response?
mana_hwc_handle_resp() {
ctx = hwc->caller_ctx + msg_id;
...
memcpy(ctx->output_buf, resp_msg, resp_len);
...
complete(&ctx->comp_event);
}
[Severity: High]
This is also a pre-existing issue, but could there be a use-after-free
during device removal if service work is pending?
In mana_gd_remove(), the gdma_context is freed without canceling pending
work on the system workqueue:
mana_gd_remove() {
...
mana_gd_cleanup_device(pdev);
xa_destroy(&gc->irq_contexts);
pci_iounmap(pdev, gc->bar0_va);
vfree(gc);
}
If the delayed work handler executes concurrently with or after device
removal, will it dereference the freed pointer?
mana_serv_reset() {
struct gdma_context *gc = pci_get_drvdata(pdev);
struct hw_channel_context *hwc;
...
hwc = gc->hwc.driver_data;
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715032942.3945317-1-longli@microsoft.com?part=3
^ permalink raw reply
* Re: [PATCH net-next 4/7] net: mana: validate hardware-supplied values in the HWC RX path
From: sashiko-bot @ 2026-07-16 3:30 UTC (permalink / raw)
To: Long Li; +Cc: linux-hyperv
In-Reply-To: <20260715032942.3945317-5-longli@microsoft.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Critical] A lack of synchronization between the request timeout path and the interrupt response handler leads to severe stack memory corruption and data leakage due to reused message IDs.
- [High] An unvalidated `inline_oob_size_div4` value read from shared DMA memory is used to calculate the `sge` pointer, leading to an out-of-bounds read and kernel panic.
--
--- Patch [4]: [PATCH net-next 4/7] net: mana: validate hardware-supplied values in the HWC RX path ---
commit 2b4c8935e9727b2b08f9773b13a4caaa94f11d99
Author: Long Li <longli@microsoft.com>
net: mana: validate hardware-supplied values in the HWC RX path
This commit adds validation for lengths and indices received from device DMA
in the hardware channel receive path. It verifies the SGE address matches
the posted slot, ensures the response covers the header, bounds checks the
message ID, and rejects oversized responses to prevent out of bounds access.
> diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> index 2239fdeda57cb..68236727aee8b 100644
> --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
> +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
> @@ -83,6 +83,17 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
> struct hwc_caller_ctx *ctx;
> int err;
>
> + /* Validate msg_id is in range before using it to index bitmap
> + * and caller_ctx array. Malicious firmware could send
> + * out-of-range msg_id causing out-of-bounds access.
> + */
> + if (msg_id >= hwc->num_inflight_msg) {
> + dev_err(hwc->dev, "hwc_rx: msg_id %u >= max %u\n",
> + msg_id, hwc->num_inflight_msg);
> + mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
> + return;
> + }
> +
> if (!test_bit(msg_id, hwc->inflight_msg_res.map)) {
> dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id);
> mana_hwc_post_rx_wqe(hwc->rxq, rx_req);
> @@ -90,6 +101,18 @@ static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len,
> }
>
> ctx = hwc->caller_ctx + msg_id;
> +
> + /* Reject responses larger than the RX DMA buffer — the SGE
> + * limits what hardware can DMA, so an oversized resp_len
> + * indicates a firmware bug. Fail rather than silently
> + * truncating.
> + */
> + if (resp_len > rx_req->buf_len) {
> + dev_err(hwc->dev, "HWC RX: resp_len %u > buf_len %u\n",
> + resp_len, rx_req->buf_len);
> + resp_len = 0;
> + }
> +
> err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len);
> if (err)
> goto out;
[Severity: Critical]
This isn't a bug introduced by this patch, but is there a race condition
between the request timeout path and this interrupt response handler?
If wait_for_completion_timeout() expires in mana_hwc_send_request(), the
function clears the message ID bit and returns. This leaves ctx->output_buf
dangling, pointing to the caller's stack frame.
If the hardware response arrives immediately after, or if the message ID is
reused by a new thread, could mana_hwc_handle_resp() read the ID and
execute memcpy() into the dangling output_buf? Since no lock is held to
verify the sender thread is still waiting, it seems this could lead to stack
memory corruption and cross-request data leakage.
[ ... ]
> @@ -261,19 +284,45 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id,
>
> sge = (struct gdma_sge *)(wqe + 8 + dma_oob->inline_oob_size_div4 * 4);
[Severity: High]
This is a pre-existing issue, but does this calculation lack bounds checking
for the sge pointer?
The inline_oob_size_div4 field is read directly from the untrusted DMA WQE,
multiplied by 4, and added to the WQE pointer. If the WQE is located at the
very end of the page-aligned DMA ring buffer (for example, at offset 4064
in a 4096-byte page), could this unchecked addition cause the resulting sge
pointer to exceed the queue buffer boundary?
This could lead to an out-of-bounds read and kernel panic when sge->address
is later dereferenced, which might be reachable by a malicious host in a
confidential VM environment.
> - /* Select the RX work request for virtual address and for reposting. */
> + /* Recover the originating RX slot from the SGE address. Of the three
> + * terms here only sge->address lives in device-accessible RQ memory;
> + * rq_base_addr and max_resp_msg_size are driver-private constants. An
> + * in-range but wrong/unaligned SGE (corrupted WQE, or a malicious host
> + * in a CVM) would otherwise truncate onto a neighbouring slot, letting
> + * us read a stale response that could complete the wrong, reused
> + * in-flight request. Require the index to be in range AND the address
> + * to exactly match the value the driver posted for that slot.
> + */
> rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle;
> rx_req_idx = (sge->address - rq_base_addr) / hwc->max_resp_msg_size;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260715032942.3945317-1-longli@microsoft.com?part=4
^ permalink raw reply
* RE: [RFC PATCH] x86/apic: Fix lost IRQ during forced vector migration on Hyper-V
From: Long Li @ 2026-07-16 1:17 UTC (permalink / raw)
To: Naman Jain, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86@kernel.org, H . Peter Anvin
Cc: Neeraj Upadhyay, linux-kernel@vger.kernel.org,
linux-hyperv@vger.kernel.org, Michael Kelley
In-Reply-To: <20260710054007.288807-1-namjain@linux.microsoft.com>
> On Hyper-V the MSI retarget hypercall is asynchronous. When a CPU is taken
> offline, chip_data_update() frees the outgoing CPU's vector inline because the
> deferred cleanup-vector mechanism is unavailable for an offline CPU. A device
> interrupt raised inside the stop_machine window can be posted to the
> outgoing CPU's old vIRR after that inline free, causing two problems:
>
> 1) The completion it carries is lost, as nothing drains the old vector
> once it is freed. If it was the last in-flight completion on an
> otherwise idle queue, the command times out and the controller is
> reset. This is the functional bug.
>
> 2) When the outgoing CPU re-enables interrupts during teardown, the late
> delivery finds VECTOR_UNUSED and reevaluate_vector() logs "No irq
> handler" - harmless but noisy.
>
> The native MSI path (msi_set_affinity()) handles this race with
> VECTOR_RETRIGGERED + irq_retrigger, but the Hyper-V MSI chip carries
> IRQCHIP_MOVE_DEFERRED and reaches the forced-migration else-branch in
> chip_data_update() instead, so it never gets that protection.
>
> Mirror that protection in the forced-migration path:
>
> - Issue __apic_send_IPI(newcpu, newvec) after installing the new mapping
> so a raced completion is drained on the new target. The retarget is
> asynchronous, so the outgoing IRR is not authoritative and the IPI is
> unconditional; a spurious or duplicate ISR is harmless to the MSI
> completion-draining handlers (NVMe, netdev), which find an empty queue.
> - Mark the freed slot VECTOR_RETRIGGERED so the late stray is absorbed by
> reevaluate_vector() rather than logged. The write is unconditional
> because apic_free_vector() leaves the slot pointing at this irq's stale
> desc, not an unused entry (unlike msi_set_affinity()).
>
> The migration runs under stop_machine with interrupts disabled on all CPUs,
> so any raced CQE is visible before the new CPU handles the retrigger.
>
> The guard is restricted to edge MSI vectors on Hyper-V: it requires
> X86_HYPER_MS_HYPERV, an external vector, a non-level trigger, and an
> attached msi_desc. Only the MSI retarget hypercall is asynchronous, so this is
> the sole path with the race; edge IOAPIC lines retarget synchronously via the
> RTE and IR-remapped interrupts via the IRTE, and neither is touched.
> Narrowing to MSI also keeps the unconditional retrigger confined to
> completion-draining handlers, which tolerate a phantom ISR, rather than
> arbitrary edge handlers. The slot marking and completion drain must happen
> where the vector is freed and the new mapping is installed, which is why the
> guard lives in chip_data_update() rather than the Hyper-V MSI chip, which
> cannot reach vector_irq[].
>
> Verified on a 64-CPU Azure VM: zero "No irq handler" events across 320 CPU
> hotplug passes with concurrent NVMe I/O, compared to ~460 per 5 minutes
> before the fix. No NVMe timeouts or controller resets observed.
>
> Fixes: e84cf6aa501c5 ("x86/apic/vector: Handle vector release on CPU unplug
> correctly")
> Cc: stable@vger.kernel.org
> Assisted-by: GitHub-Copilot:claude-opus-4.8
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
I think this patch is correct in that it retriggers interrupts unconditional on Hyper-V. This is needed because Hyper-V does MSI retarget asynchronously, unlike all other platforms.
As an alternative, this can also be fixed in fixup_irqs(). IRQ layer already has this logic in fixup_irqs() for dealing with a lost interrupt when programming the chip for retargeting, calling chip->irq_retrigger() unconditionally for Hyper-V in fixup_irqs() is another option.
Thanks,
Long
> ---
> arch/x86/kernel/apic/vector.c | 48 +++++++++++++++++++++++++++++++++++
> 1 file changed, 48 insertions(+)
>
> diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index
> bddc544653999..3577e9ca23ba6 100644
> --- a/arch/x86/kernel/apic/vector.c
> +++ b/arch/x86/kernel/apic/vector.c
> @@ -20,6 +20,7 @@
> #include <asm/i8259.h>
> #include <asm/desc.h>
> #include <asm/irq_remapping.h>
> +#include <asm/hypervisor.h>
>
> #include <asm/trace/irq_vectors.h>
>
> @@ -152,6 +153,7 @@ static void chip_data_update(struct irq_data *irqd,
> unsigned int newvec, unsigne
> struct apic_chip_data *apicd = apic_chip_data(irqd);
> struct irq_desc *desc = irq_data_to_desc(irqd);
> bool managed = irqd_affinity_is_managed(irqd);
> + bool hv_retrigger = false;
>
> lockdep_assert_held(&vector_lock);
>
> @@ -181,7 +183,46 @@ static void chip_data_update(struct irq_data *irqd,
> unsigned int newvec, unsigne
> apicd->prev_cpu = apicd->cpu;
> WARN_ON_ONCE(apicd->cpu == newcpu);
> } else {
> + /*
> + * The outgoing CPU cannot use the deferred cleanup-vector
> + * mechanism, so its vector is freed inline below. On Hyper-V
> the
> + * MSI retarget hypercall is asynchronous, so an interrupt
> raised
> + * inside the stop_machine window can be posted to the
> outgoing
> + * CPU's old vIRR after the free. Two complementary steps
> handle
> + * that (see also the retrigger at the end of the function):
> + *
> + * - Retrigger on the new target so a raced completion is
> drained
> + * there rather than lost. The retarget is asynchronous, so the
> + * outgoing IRR is not authoritative and the IPI is issued
> + * unconditionally; a spurious ISR is harmless to
> + * completion-draining handlers (they find an empty queue).
> + * - Mark the freed slot VECTOR_RETRIGGERED so a late stray
> is
> + * absorbed by reevaluate_vector() instead of logging "No irq
> + * handler" while the CPU still takes interrupts during
> + * teardown; __setup_vector_irq() resets it on re-online.
> + *
> + * This mirrors msi_set_affinity()'s protection, which the
> Hyper-V
> + * MSI chip bypasses via IRQCHIP_MOVE_DEFERRED. The
> guard is
> + * restricted to edge MSI vectors on Hyper-V (msi_desc
> present):
> + * only the MSI retarget hypercall is asynchronous, so edge
> IOAPIC
> + * lines (retargeted synchronously via the RTE) and level-
> triggered
> + * lines never see this race and must not be force-injected.
> + */
> + if (hypervisor_is_type(X86_HYPER_MS_HYPERV) &&
> + apicd->vector >= FIRST_EXTERNAL_VECTOR &&
> + !irqd_is_level_type(irqd) && irq_data_get_msi_desc(irqd))
> + hv_retrigger = true;
> apic_free_vector(apicd->cpu, apicd->vector, managed);
> + /*
> + * apic_free_vector() releases the matrix bit but leaves the
> + * outgoing CPU's vector_irq[] slot pointing at the stale desc, so
> + * the marker is written unconditionally here. (Unlike
> + * msi_set_affinity(), which marks a genuinely unused slot and
> + * therefore guards with IS_ERR_OR_NULL, the slot here still
> holds
> + * this irq's old desc.)
> + */
> + if (hv_retrigger)
> + per_cpu(vector_irq, apicd->cpu)[apicd->vector] =
> VECTOR_RETRIGGERED;
> }
>
> setnew:
> @@ -190,6 +231,13 @@ static void chip_data_update(struct irq_data *irqd,
> unsigned int newvec, unsigne
> BUG_ON(!IS_ERR_OR_NULL(per_cpu(vector_irq, newcpu)[newvec]));
> per_cpu(vector_irq, newcpu)[newvec] = desc;
> apic_update_irq_cfg(irqd, newvec, newcpu);
> + /*
> + * Drain any completion that raced onto the freed vIRR by retriggering
> + * on the new target (see the else-branch above). Issued after the new
> + * mapping is installed so the handler is present when it is serviced.
> + */
> + if (hv_retrigger)
> + __apic_send_IPI(newcpu, newvec);
> }
>
> static void vector_assign_managed_shutdown(struct irq_data *irqd)
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v9 0/8] mm/hmm: Add mmap lock-drop support for userfaultfd-backed mappings
From: Andrew Morton @ 2026-07-15 21:17 UTC (permalink / raw)
To: Stanislav Kinsburskii
Cc: airlied, akhilesh, corbet, dakr, david, decui, haiyangz, jgg,
kees, kys, leon, liam, lizhi.hou, ljs, longli, lyude,
maarten.lankhorst, mamin506, mhocko, mripard, nouveau, ogabbay,
oleg, rppt, shuah, simona, skhan, surenb, tzimmermann, vbabka,
wei.liu, dri-devel, intel-xe, linux-mm, linux-doc, linux-hyperv,
linux-kernel, linux-kselftest, linux-rdma
In-Reply-To: <178413903133.1155966.3904063656020521607.stgit@skinsburskii>
On Wed, 15 Jul 2026 11:15:50 -0700 Stanislav Kinsburskii <skinsburskii@gmail.com> wrote:
> This series extends the HMM framework to support userfaultfd-backed memory
> by allowing the mmap read lock to be dropped during hmm_range_fault().
Thanks, updated.
> Changes in v9:
>
> - Folded the fixups into the full 8-patch series instead of sending a
> separate fixup series.
> - Clarified that the HMM timeout bounds repeated HMM/mmu-notifier retry
> attempts, with the helper refreshing range->notifier_seq internally.
> - Kept nouveau’s explicit outer deadline because its retry loop runs in a GPU
> fault worker and cannot rely on fatal signals from the faulting process.
> - Converted amdxdna, and GPU SVM callers to pass the shole timeout budget to
> hmm_range_fault_unlocked_timeout().
These are all in your fixups for v8, so the v9 series made no
alteration to mm.git.
^ permalink raw reply
* [PATCH v9 8/8] drm/gpusvm: Use hmm_range_fault_unlocked_timeout() for range faults
From: Stanislav Kinsburskii @ 2026-07-15 18:16 UTC (permalink / raw)
To: airlied, akhilesh, akpm, corbet, dakr, david, decui, haiyangz,
jgg, kees, kys, leon, liam, lizhi.hou, ljs, longli, lyude,
maarten.lankhorst, mamin506, mhocko, mripard, nouveau, ogabbay,
oleg, rppt, shuah, simona, skhan, skinsburskii, surenb,
tzimmermann, vbabka, wei.liu, skinsburskii
Cc: dri-devel, intel-xe, linux-mm, linux-doc, linux-hyperv,
linux-kernel, linux-kselftest, linux-rdma
In-Reply-To: <178413903133.1155966.3904063656020521607.stgit@skinsburskii>
Several GPU SVM paths take mmap_read_lock() only to call hmm_range_fault()
and open-code mmu interval sequence setup before each HMM walk. They also
retry -EBUSY until HMM_RANGE_DEFAULT_TIMEOUT expires.
Use hmm_range_fault_unlocked_timeout() for those faults. The HMM helper now
owns mmap_lock acquisition and refreshes range->notifier_seq for its
internal retries, while GPU SVM keeps its existing driver-lock validation
with mmu_interval_read_retry() after a successful fault.
Pass HMM_RANGE_DEFAULT_TIMEOUT as the helper retry budget for each HMM
fault attempt. This scopes the timeout to repeated HMM notifier retries
while preserving the outer retry loops that restart when the interval is
invalidated before GPU SVM updates or consumes the mapping state.
Leave drm_gpusvm_check_pages() on hmm_range_fault() because that path is
called with the mmap lock already held by its caller.
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Stanislav Kinsburskii <skinsburskii@gmail.com>
---
drivers/gpu/drm/drm_gpusvm.c | 61 +++++-------------------------------------
1 file changed, 7 insertions(+), 54 deletions(-)
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index 958cb605aedd..de5bbfe58ee9 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -773,8 +773,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range,
.end = end,
.dev_private_owner = dev_private_owner,
};
- unsigned long timeout =
- jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ unsigned long timeout = msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
enum drm_gpusvm_scan_result state = DRM_GPUSVM_SCAN_UNPOPULATED, new_state;
unsigned long *pfns;
unsigned long npages = npages_in_range(start, end);
@@ -788,22 +787,7 @@ enum drm_gpusvm_scan_result drm_gpusvm_scan_mm(struct drm_gpusvm_range *range,
hmm_range.hmm_pfns = pfns;
retry:
- hmm_range.notifier_seq = mmu_interval_read_begin(notifier);
- mmap_read_lock(range->gpusvm->mm);
-
- while (true) {
- err = hmm_range_fault(&hmm_range);
- if (err == -EBUSY) {
- if (time_after(jiffies, timeout))
- break;
-
- hmm_range.notifier_seq =
- mmu_interval_read_begin(notifier);
- continue;
- }
- break;
- }
- mmap_read_unlock(range->gpusvm->mm);
+ err = hmm_range_fault_unlocked_timeout(&hmm_range, timeout);
if (err)
goto err_free;
@@ -1406,8 +1390,7 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
.dev_private_owner = ctx->device_private_page_owner,
};
void *zdd;
- unsigned long timeout =
- jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ unsigned long timeout = msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
unsigned long i, j;
unsigned long npages = npages_in_range(pages_start, pages_end);
unsigned long num_dma_mapped;
@@ -1422,9 +1405,6 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
struct dma_iova_state *state = &svm_pages->state;
retry:
- if (time_after(jiffies, timeout))
- return -EBUSY;
-
hmm_range.notifier_seq = mmu_interval_read_begin(notifier);
if (drm_gpusvm_pages_valid_unlocked(gpusvm, svm_pages))
goto set_seqno;
@@ -1439,21 +1419,7 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
}
hmm_range.hmm_pfns = pfns;
- while (true) {
- mmap_read_lock(mm);
- err = hmm_range_fault(&hmm_range);
- mmap_read_unlock(mm);
-
- if (err == -EBUSY) {
- if (time_after(jiffies, timeout))
- break;
-
- hmm_range.notifier_seq =
- mmu_interval_read_begin(notifier);
- continue;
- }
- break;
- }
+ err = hmm_range_fault_unlocked_timeout(&hmm_range, timeout);
mmput(mm);
if (err)
goto err_free;
@@ -1720,8 +1686,7 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
.end = drm_gpusvm_range_end(range),
.dev_private_owner = NULL,
};
- unsigned long timeout =
- jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ unsigned long timeout = msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
unsigned long *pfns;
unsigned long npages = npages_in_range(drm_gpusvm_range_start(range),
drm_gpusvm_range_end(range));
@@ -1736,24 +1701,12 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
return -ENOMEM;
hmm_range.hmm_pfns = pfns;
- while (!time_after(jiffies, timeout)) {
- hmm_range.notifier_seq = mmu_interval_read_begin(notifier);
- if (time_after(jiffies, timeout)) {
- err = -ETIME;
- break;
- }
-
- mmap_read_lock(mm);
- err = hmm_range_fault(&hmm_range);
- mmap_read_unlock(mm);
- if (err != -EBUSY)
- break;
- }
+ err = hmm_range_fault_unlocked_timeout(&hmm_range, timeout);
kvfree(pfns);
mmput(mm);
- return err;
+ return err == -EBUSY ? -ETIME : err;
}
EXPORT_SYMBOL_GPL(drm_gpusvm_range_evict);
^ permalink raw reply related
* [PATCH v9 7/8] accel/amdxdna: Use hmm_range_fault_unlocked_timeout() for range population
From: Stanislav Kinsburskii @ 2026-07-15 18:16 UTC (permalink / raw)
To: airlied, akhilesh, akpm, corbet, dakr, david, decui, haiyangz,
jgg, kees, kys, leon, liam, lizhi.hou, ljs, longli, lyude,
maarten.lankhorst, mamin506, mhocko, mripard, nouveau, ogabbay,
oleg, rppt, shuah, simona, skhan, skinsburskii, surenb,
tzimmermann, vbabka, wei.liu, skinsburskii
Cc: dri-devel, intel-xe, linux-mm, linux-doc, linux-hyperv,
linux-kernel, linux-kselftest, linux-rdma
In-Reply-To: <178413903133.1155966.3904063656020521607.stgit@skinsburskii>
aie2_populate_range() takes mmap_read_lock() only around hmm_range_fault().
It also open-codes the mmu interval sequence setup before each HMM walk and
retries -EBUSY until HMM_RANGE_DEFAULT_TIMEOUT expires.
Use hmm_range_fault_unlocked_timeout() instead. The HMM helper now owns
the mmap lock and refreshes mapp->range.notifier_seq for its internal
retries, so the driver only needs to call the helper and then validate the
sequence before marking the mapping populated.
Pass HMM_RANGE_DEFAULT_TIMEOUT as the helper retry budget for each HMM
population attempt. This scopes the timeout to repeated HMM notifier
retries while preserving the existing outer loop that moves between invalid
mappings and restarts when the interval is invalidated before the driver
updates its mapping state.
Keep returning -ETIME when the HMM retry budget expires, matching the
driver's existing timeout error convention.
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Stanislav Kinsburskii <skinsburskii@gmail.com>
---
drivers/accel/amdxdna/aie2_ctx.c | 23 ++++-------------------
1 file changed, 4 insertions(+), 19 deletions(-)
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
index 54486960cbf5..b5b4ca263002 100644
--- a/drivers/accel/amdxdna/aie2_ctx.c
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -1034,7 +1034,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo)
bool found;
int ret;
- timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ timeout = msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
again:
found = false;
down_write(&xdna->notifier_lock);
@@ -1061,24 +1061,9 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo)
return -EFAULT;
}
- mapp->range.notifier_seq = mmu_interval_read_begin(&mapp->notifier);
- mmap_read_lock(mm);
- ret = hmm_range_fault(&mapp->range);
- mmap_read_unlock(mm);
- if (ret) {
- if (time_after(jiffies, timeout)) {
- ret = -ETIME;
- goto put_mm;
- }
-
- if (ret == -EBUSY) {
- amdxdna_umap_put(mapp);
- mmput(mm);
- goto again;
- }
-
+ ret = hmm_range_fault_unlocked_timeout(&mapp->range, timeout);
+ if (ret)
goto put_mm;
- }
down_write(&xdna->notifier_lock);
if (mmu_interval_read_retry(&mapp->notifier, mapp->range.notifier_seq)) {
@@ -1096,7 +1081,7 @@ static int aie2_populate_range(struct amdxdna_gem_obj *abo)
put_mm:
amdxdna_umap_put(mapp);
mmput(mm);
- return ret;
+ return ret == -EBUSY ? -ETIME : ret;
}
int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq)
^ permalink raw reply related
* [PATCH v9 6/8] RDMA/umem: Use hmm_range_fault_unlocked_timeout() for ODP faults
From: Stanislav Kinsburskii @ 2026-07-15 18:16 UTC (permalink / raw)
To: airlied, akhilesh, akpm, corbet, dakr, david, decui, haiyangz,
jgg, kees, kys, leon, liam, lizhi.hou, ljs, longli, lyude,
maarten.lankhorst, mamin506, mhocko, mripard, nouveau, ogabbay,
oleg, rppt, shuah, simona, skhan, skinsburskii, surenb,
tzimmermann, vbabka, wei.liu, skinsburskii
Cc: dri-devel, intel-xe, linux-mm, linux-doc, linux-hyperv,
linux-kernel, linux-kselftest, linux-rdma
In-Reply-To: <178413903133.1155966.3904063656020521607.stgit@skinsburskii>
ib_umem_odp_map_dma_and_lock() takes mmap_read_lock() only around
hmm_range_fault(), then retries -EBUSY until HMM_RANGE_DEFAULT_TIMEOUT
expires.
Use hmm_range_fault_unlocked_timeout() instead. The HMM helper now owns
the mmap lock and refreshes range->notifier_seq for its internal retries.
ODP keeps using HMM_RANGE_DEFAULT_TIMEOUT for each HMM fault attempt,
while interval invalidation retries continue to be handled by the existing
outer loop.
ODP still validates the interval notifier sequence while holding umem_mutex
before DMA mapping pages.
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Stanislav Kinsburskii <skinsburskii@gmail.com>
---
drivers/infiniband/core/umem_odp.c | 18 +++++-------------
1 file changed, 5 insertions(+), 13 deletions(-)
diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c
index 404fa1cc3254..9cc21cd762d9 100644
--- a/drivers/infiniband/core/umem_odp.c
+++ b/drivers/infiniband/core/umem_odp.c
@@ -329,7 +329,7 @@ int ib_umem_odp_map_dma_and_lock(struct ib_umem_odp *umem_odp, u64 user_virt,
struct mm_struct *owning_mm = umem_odp->umem.owning_mm;
int pfn_index, dma_index, ret = 0, start_idx;
unsigned int page_shift, hmm_order, pfn_start_idx;
- unsigned long num_pfns, current_seq;
+ unsigned long num_pfns;
struct hmm_range range = {};
unsigned long timeout;
@@ -363,26 +363,18 @@ int ib_umem_odp_map_dma_and_lock(struct ib_umem_odp *umem_odp, u64 user_virt,
}
range.hmm_pfns = &(umem_odp->map.pfn_list[pfn_start_idx]);
- timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+ timeout = msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
retry:
- current_seq = range.notifier_seq =
- mmu_interval_read_begin(&umem_odp->notifier);
-
- mmap_read_lock(owning_mm);
- ret = hmm_range_fault(&range);
- mmap_read_unlock(owning_mm);
- if (unlikely(ret)) {
- if (ret == -EBUSY && !time_after(jiffies, timeout))
- goto retry;
+ ret = hmm_range_fault_unlocked_timeout(&range, timeout);
+ if (unlikely(ret))
goto out_put_mm;
- }
start_idx = (range.start - ib_umem_start(umem_odp)) >> page_shift;
dma_index = start_idx;
mutex_lock(&umem_odp->umem_mutex);
- if (mmu_interval_read_retry(&umem_odp->notifier, current_seq)) {
+ if (mmu_interval_read_retry(&umem_odp->notifier, range.notifier_seq)) {
mutex_unlock(&umem_odp->umem_mutex);
goto retry;
}
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox