* [PATCH V1 04/13] mshv: Provide a way to get partition id if running in a VMM process
From: Mukesh R @ 2026-04-22 2:32 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
In-Reply-To: <20260422023239.1171963-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 current process
is a VMM process.
Signed-off-by: Mukesh R <mrathor@linux.microsoft.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 V1 01/13] iommu/hyperv: rename hyperv-iommu.c to hyperv-irq.c
From: Mukesh R @ 2026-04-22 2:32 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
In-Reply-To: <20260422023239.1171963-1-mrathor@linux.microsoft.com>
This file actually implements irq remapping, so rename to more appropriate
hyperv-irq.c. A new file to implement hyperv iommu will be introduced
later. Also, it should not be tied to HYPERV_IOMMU, but to CONFIG_HYPERV
and IRQ_REMAP. The file already has #ifdef CONFIG_IRQ_REMAP.
Signed-off-by: Mukesh R <mrathor@linux.microsoft.com>
---
MAINTAINERS | 2 +-
drivers/iommu/Makefile | 2 +-
drivers/iommu/{hyperv-iommu.c => hyperv-irq.c} | 2 +-
drivers/iommu/irq_remapping.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
rename drivers/iommu/{hyperv-iommu.c => hyperv-irq.c} (99%)
diff --git a/MAINTAINERS b/MAINTAINERS
index d1cc0e12fe1f..f803a6a38fee 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11914,7 +11914,7 @@ F: drivers/clocksource/hyperv_timer.c
F: drivers/hid/hid-hyperv.c
F: drivers/hv/
F: drivers/input/serio/hyperv-keyboard.c
-F: drivers/iommu/hyperv-iommu.c
+F: drivers/iommu/hyperv-irq.c
F: drivers/net/ethernet/microsoft/
F: drivers/net/hyperv/
F: drivers/pci/controller/pci-hyperv-intf.c
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 0275821f4ef9..335ea77cced6 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -30,7 +30,7 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
-obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
+obj-$(CONFIG_HYPERV) += hyperv-irq.o
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o
diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv-irq.c
similarity index 99%
rename from drivers/iommu/hyperv-iommu.c
rename to drivers/iommu/hyperv-irq.c
index 479103261ae6..cc49c7cbc434 100644
--- a/drivers/iommu/hyperv-iommu.c
+++ b/drivers/iommu/hyperv-irq.c
@@ -331,4 +331,4 @@ static const struct irq_domain_ops hyperv_root_ir_domain_ops = {
.free = hyperv_root_irq_remapping_free,
};
-#endif
+#endif /* CONFIG_IRQ_REMAP */
diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c
index c2443659812a..41bf65e4ea88 100644
--- a/drivers/iommu/irq_remapping.c
+++ b/drivers/iommu/irq_remapping.c
@@ -108,7 +108,7 @@ int __init irq_remapping_prepare(void)
else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
amd_iommu_irq_ops.prepare() == 0)
remap_ops = &amd_iommu_irq_ops;
- else if (IS_ENABLED(CONFIG_HYPERV_IOMMU) &&
+ else if (IS_ENABLED(CONFIG_HYPERV) &&
hyperv_irq_remap_ops.prepare() == 0)
remap_ops = &hyperv_irq_remap_ops;
else
--
2.51.2.vfs.0.1
^ permalink raw reply related
* [PATCH V1 02/13] x86/hyperv: cosmetic changes in irqdomain.c for readability
From: Mukesh R @ 2026-04-22 2:32 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
In-Reply-To: <20260422023239.1171963-1-mrathor@linux.microsoft.com>
Make cosmetic changes:
o Rename struct pci_dev *dev to *pdev since there are cases of
struct device *dev in the file and all over the kernel
o Rename hv_build_pci_dev_id to hv_build_devid_type_pci in anticipation
of building different types of device ids
o Fix checkpatch.pl issues with return and extraneous printk
o Replace spaces with tabs
o Rename struct hv_devid *xxx to struct hv_devid *hv_devid given code
paths involve many types of device ids
o Fix indentation in a large if block by using goto.
There are no functional changes.
Reviewed-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
Signed-off-by: Mukesh R <mrathor@linux.microsoft.com>
---
arch/x86/hyperv/irqdomain.c | 198 +++++++++++++++++++-----------------
1 file changed, 104 insertions(+), 94 deletions(-)
diff --git a/arch/x86/hyperv/irqdomain.c b/arch/x86/hyperv/irqdomain.c
index 365e364268d9..b3ad50a874dc 100644
--- a/arch/x86/hyperv/irqdomain.c
+++ b/arch/x86/hyperv/irqdomain.c
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
-
/*
* Irqdomain for Linux to run as the root partition on Microsoft Hypervisor.
*
@@ -14,8 +13,8 @@
#include <linux/irqchip/irq-msi-lib.h>
#include <asm/mshyperv.h>
-static int hv_map_interrupt(union hv_device_id device_id, bool level,
- int cpu, int vector, struct hv_interrupt_entry *entry)
+static int hv_map_interrupt(union hv_device_id hv_devid, bool level,
+ int cpu, int vector, struct hv_interrupt_entry *ret_entry)
{
struct hv_input_map_device_interrupt *input;
struct hv_output_map_device_interrupt *output;
@@ -32,7 +31,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level,
intr_desc = &input->interrupt_descriptor;
memset(input, 0, sizeof(*input));
input->partition_id = hv_current_partition_id;
- input->device_id = device_id.as_uint64;
+ input->device_id = hv_devid.as_uint64;
intr_desc->interrupt_type = HV_X64_INTERRUPT_TYPE_FIXED;
intr_desc->vector_count = 1;
intr_desc->target.vector = vector;
@@ -44,7 +43,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level,
intr_desc->target.vp_set.valid_bank_mask = 0;
intr_desc->target.vp_set.format = HV_GENERIC_SET_SPARSE_4K;
- nr_bank = cpumask_to_vpset(&(intr_desc->target.vp_set), cpumask_of(cpu));
+ nr_bank = cpumask_to_vpset(&intr_desc->target.vp_set, cpumask_of(cpu));
if (nr_bank < 0) {
local_irq_restore(flags);
pr_err("%s: unable to generate VP set\n", __func__);
@@ -61,7 +60,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level,
status = hv_do_rep_hypercall(HVCALL_MAP_DEVICE_INTERRUPT, 0, var_size,
input, output);
- *entry = output->interrupt_entry;
+ *ret_entry = output->interrupt_entry;
local_irq_restore(flags);
@@ -71,21 +70,19 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level,
return hv_result_to_errno(status);
}
-static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *old_entry)
+static int hv_unmap_interrupt(u64 id, struct hv_interrupt_entry *irq_entry)
{
unsigned long flags;
struct hv_input_unmap_device_interrupt *input;
- struct hv_interrupt_entry *intr_entry;
u64 status;
local_irq_save(flags);
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
memset(input, 0, sizeof(*input));
- intr_entry = &input->interrupt_entry;
input->partition_id = hv_current_partition_id;
input->device_id = id;
- *intr_entry = *old_entry;
+ input->interrupt_entry = *irq_entry;
status = hv_do_hypercall(HVCALL_UNMAP_DEVICE_INTERRUPT, input, NULL);
local_irq_restore(flags);
@@ -115,67 +112,71 @@ static int get_rid_cb(struct pci_dev *pdev, u16 alias, void *data)
return 0;
}
-static union hv_device_id hv_build_pci_dev_id(struct pci_dev *dev)
+static union hv_device_id hv_build_devid_type_pci(struct pci_dev *pdev)
{
- union hv_device_id dev_id;
+ int pos;
+ union hv_device_id hv_devid;
struct rid_data data = {
.bridge = NULL,
- .rid = PCI_DEVID(dev->bus->number, dev->devfn)
+ .rid = PCI_DEVID(pdev->bus->number, pdev->devfn)
};
- pci_for_each_dma_alias(dev, get_rid_cb, &data);
+ pci_for_each_dma_alias(pdev, get_rid_cb, &data);
- dev_id.as_uint64 = 0;
- dev_id.device_type = HV_DEVICE_TYPE_PCI;
- dev_id.pci.segment = pci_domain_nr(dev->bus);
+ hv_devid.as_uint64 = 0;
+ hv_devid.device_type = HV_DEVICE_TYPE_PCI;
+ hv_devid.pci.segment = pci_domain_nr(pdev->bus);
- dev_id.pci.bdf.bus = PCI_BUS_NUM(data.rid);
- dev_id.pci.bdf.device = PCI_SLOT(data.rid);
- dev_id.pci.bdf.function = PCI_FUNC(data.rid);
- dev_id.pci.source_shadow = HV_SOURCE_SHADOW_NONE;
+ hv_devid.pci.bdf.bus = PCI_BUS_NUM(data.rid);
+ hv_devid.pci.bdf.device = PCI_SLOT(data.rid);
+ hv_devid.pci.bdf.function = PCI_FUNC(data.rid);
+ hv_devid.pci.source_shadow = HV_SOURCE_SHADOW_NONE;
- if (data.bridge) {
- int pos;
+ if (data.bridge == NULL)
+ goto out;
- /*
- * Microsoft Hypervisor requires a bus range when the bridge is
- * running in PCI-X mode.
- *
- * To distinguish conventional vs PCI-X bridge, we can check
- * the bridge's PCI-X Secondary Status Register, Secondary Bus
- * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge
- * Specification Revision 1.0 5.2.2.1.3.
- *
- * Value zero means it is in conventional mode, otherwise it is
- * in PCI-X mode.
- */
+ /*
+ * Microsoft Hypervisor requires a bus range when the bridge is
+ * running in PCI-X mode.
+ *
+ * To distinguish conventional vs PCI-X bridge, we can check
+ * the bridge's PCI-X Secondary Status Register, Secondary Bus
+ * Mode and Frequency bits. See PCI Express to PCI/PCI-X Bridge
+ * Specification Revision 1.0 5.2.2.1.3.
+ *
+ * Value zero means it is in conventional mode, otherwise it is
+ * in PCI-X mode.
+ */
- pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX);
- if (pos) {
- u16 status;
+ pos = pci_find_capability(data.bridge, PCI_CAP_ID_PCIX);
+ if (pos) {
+ u16 status;
- pci_read_config_word(data.bridge, pos +
- PCI_X_BRIDGE_SSTATUS, &status);
+ pci_read_config_word(data.bridge, pos + PCI_X_BRIDGE_SSTATUS,
+ &status);
- if (status & PCI_X_SSTATUS_FREQ) {
- /* Non-zero, PCI-X mode */
- u8 sec_bus, sub_bus;
+ if (status & PCI_X_SSTATUS_FREQ) {
+ /* Non-zero, PCI-X mode */
+ u8 sec_bus, sub_bus;
- dev_id.pci.source_shadow = HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE;
+ hv_devid.pci.source_shadow =
+ HV_SOURCE_SHADOW_BRIDGE_BUS_RANGE;
- pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS, &sec_bus);
- dev_id.pci.shadow_bus_range.secondary_bus = sec_bus;
- pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS, &sub_bus);
- dev_id.pci.shadow_bus_range.subordinate_bus = sub_bus;
- }
+ pci_read_config_byte(data.bridge, PCI_SECONDARY_BUS,
+ &sec_bus);
+ hv_devid.pci.shadow_bus_range.secondary_bus = sec_bus;
+ pci_read_config_byte(data.bridge, PCI_SUBORDINATE_BUS,
+ &sub_bus);
+ hv_devid.pci.shadow_bus_range.subordinate_bus = sub_bus;
}
}
- return dev_id;
+out:
+ return hv_devid;
}
-/**
- * hv_map_msi_interrupt() - "Map" the MSI IRQ in the hypervisor.
+/*
+ * hv_map_msi_interrupt() - Map the MSI IRQ in the hypervisor.
* @data: Describes the IRQ
* @out_entry: Hypervisor (MSI) interrupt entry (can be NULL)
*
@@ -188,22 +189,23 @@ int hv_map_msi_interrupt(struct irq_data *data,
{
struct irq_cfg *cfg = irqd_cfg(data);
struct hv_interrupt_entry dummy;
- union hv_device_id device_id;
+ union hv_device_id hv_devid;
struct msi_desc *msidesc;
- struct pci_dev *dev;
+ struct pci_dev *pdev;
int cpu;
msidesc = irq_data_get_msi_desc(data);
- dev = msi_desc_to_pci_dev(msidesc);
- device_id = hv_build_pci_dev_id(dev);
+ pdev = msi_desc_to_pci_dev(msidesc);
+ hv_devid = hv_build_devid_type_pci(pdev);
cpu = cpumask_first(irq_data_get_effective_affinity_mask(data));
- return hv_map_interrupt(device_id, false, cpu, cfg->vector,
+ return hv_map_interrupt(hv_devid, false, cpu, cfg->vector,
out_entry ? out_entry : &dummy);
}
EXPORT_SYMBOL_GPL(hv_map_msi_interrupt);
-static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi_msg *msg)
+static void entry_to_msi_msg(struct hv_interrupt_entry *entry,
+ struct msi_msg *msg)
{
/* High address is always 0 */
msg->address_hi = 0;
@@ -211,17 +213,19 @@ static inline void entry_to_msi_msg(struct hv_interrupt_entry *entry, struct msi
msg->data = entry->msi_entry.data.as_uint32;
}
-static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry);
+static int hv_unmap_msi_interrupt(struct pci_dev *pdev,
+ struct hv_interrupt_entry *irq_entry);
+
static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct hv_interrupt_entry *stored_entry;
struct irq_cfg *cfg = irqd_cfg(data);
struct msi_desc *msidesc;
- struct pci_dev *dev;
+ struct pci_dev *pdev;
int ret;
msidesc = irq_data_get_msi_desc(data);
- dev = msi_desc_to_pci_dev(msidesc);
+ pdev = msi_desc_to_pci_dev(msidesc);
if (!cfg) {
pr_debug("%s: cfg is NULL", __func__);
@@ -240,7 +244,7 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
stored_entry = data->chip_data;
data->chip_data = NULL;
- ret = hv_unmap_msi_interrupt(dev, stored_entry);
+ ret = hv_unmap_msi_interrupt(pdev, stored_entry);
kfree(stored_entry);
@@ -249,10 +253,8 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
}
stored_entry = kzalloc_obj(*stored_entry, GFP_ATOMIC);
- if (!stored_entry) {
- pr_debug("%s: failed to allocate chip data\n", __func__);
+ if (!stored_entry)
return;
- }
ret = hv_map_msi_interrupt(data, stored_entry);
if (ret) {
@@ -262,18 +264,21 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
data->chip_data = stored_entry;
entry_to_msi_msg(data->chip_data, msg);
-
- return;
}
-static int hv_unmap_msi_interrupt(struct pci_dev *dev, struct hv_interrupt_entry *old_entry)
+static int hv_unmap_msi_interrupt(struct pci_dev *pdev,
+ struct hv_interrupt_entry *irq_entry)
{
- return hv_unmap_interrupt(hv_build_pci_dev_id(dev).as_uint64, old_entry);
+ union hv_device_id hv_devid;
+
+ hv_devid = hv_build_devid_type_pci(pdev);
+ return hv_unmap_interrupt(hv_devid.as_uint64, irq_entry);
}
-static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd)
+/* NB: during map, hv_interrupt_entry is saved via data->chip_data */
+static void hv_teardown_msi_irq(struct pci_dev *pdev, struct irq_data *irqd)
{
- struct hv_interrupt_entry old_entry;
+ struct hv_interrupt_entry irq_entry;
struct msi_msg msg;
if (!irqd->chip_data) {
@@ -281,13 +286,13 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd)
return;
}
- old_entry = *(struct hv_interrupt_entry *)irqd->chip_data;
- entry_to_msi_msg(&old_entry, &msg);
+ irq_entry = *(struct hv_interrupt_entry *)irqd->chip_data;
+ entry_to_msi_msg(&irq_entry, &msg);
kfree(irqd->chip_data);
irqd->chip_data = NULL;
- (void)hv_unmap_msi_interrupt(dev, &old_entry);
+ (void)hv_unmap_msi_interrupt(pdev, &irq_entry);
}
/*
@@ -302,7 +307,8 @@ static struct irq_chip hv_pci_msi_controller = {
};
static bool hv_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
- struct irq_domain *real_parent, struct msi_domain_info *info)
+ struct irq_domain *real_parent,
+ struct msi_domain_info *info)
{
struct irq_chip *chip = info->chip;
@@ -317,7 +323,8 @@ static bool hv_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
}
#define HV_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | MSI_FLAG_PCI_MSIX)
-#define HV_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS)
+#define HV_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS)
static struct msi_parent_ops hv_msi_parent_ops = {
.supported_flags = HV_MSI_FLAGS_SUPPORTED,
@@ -329,14 +336,14 @@ static struct msi_parent_ops hv_msi_parent_ops = {
.init_dev_msi_info = hv_init_dev_msi_info,
};
-static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs,
- void *arg)
+/* Allocate nr_irqs IRQs for the given irq domain */
+static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
{
/*
- * TODO: The allocation bits of hv_irq_compose_msi_msg(), i.e. everything except
- * entry_to_msi_msg() should be in here.
+ * TODO: The allocation bits of hv_irq_compose_msi_msg(), i.e.
+ * everything except entry_to_msi_msg() should be in here.
*/
-
int ret;
ret = irq_domain_alloc_irqs_parent(d, virq, nr_irqs, arg);
@@ -344,13 +351,15 @@ static int hv_msi_domain_alloc(struct irq_domain *d, unsigned int virq, unsigned
return ret;
for (int i = 0; i < nr_irqs; ++i) {
- irq_domain_set_info(d, virq + i, 0, &hv_pci_msi_controller, NULL,
- handle_edge_irq, NULL, "edge");
+ irq_domain_set_info(d, virq + i, 0, &hv_pci_msi_controller,
+ NULL, handle_edge_irq, NULL, "edge");
}
+
return 0;
}
-static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs)
+static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs)
{
for (int i = 0; i < nr_irqs; ++i) {
struct irq_data *irqd = irq_domain_get_irq_data(d, virq);
@@ -362,6 +371,7 @@ static void hv_msi_domain_free(struct irq_domain *d, unsigned int virq, unsigned
hv_teardown_msi_irq(to_pci_dev(desc->dev), irqd);
}
+
irq_domain_free_irqs_top(d, virq, nr_irqs);
}
@@ -394,25 +404,25 @@ struct irq_domain * __init hv_create_pci_msi_domain(void)
int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry)
{
- union hv_device_id device_id;
+ union hv_device_id hv_devid;
- device_id.as_uint64 = 0;
- device_id.device_type = HV_DEVICE_TYPE_IOAPIC;
- device_id.ioapic.ioapic_id = (u8)ioapic_id;
+ hv_devid.as_uint64 = 0;
+ hv_devid.device_type = HV_DEVICE_TYPE_IOAPIC;
+ hv_devid.ioapic.ioapic_id = (u8)ioapic_id;
- return hv_unmap_interrupt(device_id.as_uint64, entry);
+ return hv_unmap_interrupt(hv_devid.as_uint64, entry);
}
EXPORT_SYMBOL_GPL(hv_unmap_ioapic_interrupt);
int hv_map_ioapic_interrupt(int ioapic_id, bool level, int cpu, int vector,
struct hv_interrupt_entry *entry)
{
- union hv_device_id device_id;
+ union hv_device_id hv_devid;
- device_id.as_uint64 = 0;
- device_id.device_type = HV_DEVICE_TYPE_IOAPIC;
- device_id.ioapic.ioapic_id = (u8)ioapic_id;
+ hv_devid.as_uint64 = 0;
+ hv_devid.device_type = HV_DEVICE_TYPE_IOAPIC;
+ hv_devid.ioapic.ioapic_id = (u8)ioapic_id;
- return hv_map_interrupt(device_id, level, cpu, vector, entry);
+ return hv_map_interrupt(hv_devid, level, cpu, vector, entry);
}
EXPORT_SYMBOL_GPL(hv_map_ioapic_interrupt);
--
2.51.2.vfs.0.1
^ permalink raw reply related
* [PATCH V1 00/13] PCI passthru on Hyper-V (Part I)
From: Mukesh R @ 2026-04-22 2:32 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
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, and any VMM needing to use it must use the VFIO subsystem.
This supports both full device passthru and SR-IOV based VFs.
At a high level, the hypervisor supports traditional mapped 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 whereby the iommu subsystem simply uses the guest HW page table
(ept/npt/..). This series adds support for both, and both are made to
work with the VFIO subsystem.
While this Part I focuses on memory mappings, upcoming Part II
will focus on irq bypass along with some minor irq remapping
updates.
Based on: cd9f2e7d6e5b (origin/hyperv-next)
Testing:
o Most testing done on hyperv-next:e733a9e28180 using Cloud Hypervisor (51).
o Limited testing on : cd9f2e7d6e5b
o Tested with impending Part II irq patches.
o All tests involved PF passthru of devices using MSIx.
o Following combinations were tested:
- L1VH(1): test 1: Mellanox ConnectX-6 Lx passthru
test 2: NVIDIA Tesla Tesla T4 GPU.
test 3: Both of above simultaneous passthru
- Baremetal dom0/root: All of above.
(1) L1VH: this is a semi privileged VM that runs on Windows root on
Hyper-V, and allows users to create more child VMs.
Pending: This to establish a baseline for further enhancements.
o arm64 : some delta to make this work on arm64 (in progress).
o device sleep/wakeup.
o More stress testing
o CH reports it could not unbind vfio group upon guest shutdown. Need
to reboot for now.
o Qemu support (in progress).
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 (13):
iommu/hyperv: rename hyperv-iommu.c to hyperv-irq.c
x86/hyperv: cosmetic changes in irqdomain.c for readability
x86/hyperv: add insufficient memory support in irqdomain.c
mshv: Provide a way to get partition id if running in a VMM process
mshv: Declarations and definitions for VFIO-MSHV bridge device
mshv: Implement mshv bridge device for VFIO
mshv: Add ioctl support for MSHV-VFIO bridge device
PCI: hv: rename hv_compose_msi_msg to hv_vmbus_compose_msi_msg
mshv: Import data structs around device passthru from hyperv headers
PCI: hv: Build device id for a VMBus device, export PCI devid function
x86/hyperv: Implement hyperv virtual iommu
mshv: Populate mmio mappings for PCI passthru
mshv: pin all ram mem regions if partition has device passthru
MAINTAINERS | 3 +-
arch/x86/hyperv/irqdomain.c | 229 +++--
arch/x86/include/asm/mshyperv.h | 4 +
arch/x86/kernel/pci-dma.c | 2 +
drivers/hv/Makefile | 3 +-
drivers/hv/mshv_root.h | 26 +
drivers/hv/mshv_root_main.c | 256 ++++-
drivers/hv/mshv_vfio.c | 211 ++++
drivers/iommu/Kconfig | 5 +-
drivers/iommu/Makefile | 3 +-
drivers/iommu/hyperv-iommu-root.c | 899 ++++++++++++++++++
.../iommu/{hyperv-iommu.c => hyperv-irq.c} | 2 +-
drivers/iommu/irq_remapping.c | 2 +-
drivers/pci/controller/pci-hyperv.c | 120 ++-
include/asm-generic/mshyperv.h | 34 +
include/hyperv/hvgdk_mini.h | 11 +
include/hyperv/hvhdk_mini.h | 112 +++
include/linux/hyperv.h | 6 +
include/uapi/linux/mshv.h | 31 +
19 files changed, 1790 insertions(+), 169 deletions(-)
create mode 100644 drivers/hv/mshv_vfio.c
create mode 100644 drivers/iommu/hyperv-iommu-root.c
rename drivers/iommu/{hyperv-iommu.c => hyperv-irq.c} (99%)
--
2.51.2.vfs.0.1
^ permalink raw reply
* RE: [PATCH v2 0/7] mshv: Refactor memory region management and map pages at creation
From: Michael Kelley @ 2026-04-22 1:42 UTC (permalink / raw)
To: Stanislav Kinsburskii, kys@microsoft.com, haiyangz@microsoft.com,
wei.liu@kernel.org, decui@microsoft.com, longli@microsoft.com
Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com> Sent: Tuesday, April 21, 2026 7:35 AM
>
> This series refactors the mshv memory region subsystem in preparation
> for mapping populated pages into the hypervisor at movable region
> creation time, rather than relying solely on demand faulting.
>
> The primary motivation is to ensure that when userspace passes a
> pre-populated mapping for a movable memory region, those pages are
> immediately visible to the hypervisor. Previously, all movable regions
> were created with HV_MAP_GPA_NO_ACCESS on every page regardless of
> whether the backing pages were already present, deferring all mapping
> to the fault handler. This added unnecessary fault overhead and
> complicated the initial setup of child partitions with pre-populated
> memory.
[snip]
>
> v2:
> - Rebased on top of latest mainline, simplified the check for valid PFNs,
> added other minor cleanups and improvements.
I'm confused about "simplified the check for valid PFNs".
I see one place in mshv_region_process_pfns() where a PFN
from the mreg_pfns[] array is checked against
MSHV_INVALID_PFN instead of doing pfn_valid(). But there
are 11 other places in the patch set where pfn_valid() is still
used, including in mshv_region_process_pfns().
Michael
>
> ---
>
> Stanislav Kinsburskii (7):
> mshv: Convert from page pointers to PFNs
> mshv: Add support to address range holes remapping
> mshv: Support regions with different VMAs
> mshv: Move pinned region setup to mshv_regions.c
> mshv: Map populated pages on movable region creation
> mshv: Extract MMIO region mapping into separate function
> mshv: Add tracepoint for map GPA hypercall
>
>
> drivers/hv/mshv_regions.c | 589 +++++++++++++++++++++++++++++-----------
> drivers/hv/mshv_root.h | 29 +-
> drivers/hv/mshv_root_hv_call.c | 53 ++--
> drivers/hv/mshv_root_main.c | 99 +------
> drivers/hv/mshv_trace.h | 36 ++
> 5 files changed, 508 insertions(+), 298 deletions(-)
>
^ permalink raw reply
* [PATCH] mshv: Fix interrupt state corruption in hv_do_map_pfns error path
From: Stanislav Kinsburskii @ 2026-04-22 0:15 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
Restore interrupt state before breaking out of the loop on error.
The irq_flags are saved before entering the loop, but the early exit
path on error fails to restore them. This leaves interrupts in an
inconsistent state and can lead to lockdep warnings or other
interrupt-related issues.
Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root_hv_call.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 7ed623668c8ec..6381f949d9d91 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -237,8 +237,10 @@ static int hv_do_map_pfns(u64 partition_id, u64 gfn, u64 pfns_count,
} else {
pfnlist[i] = mmio_spa + done + i;
}
- if (ret)
+ if (ret) {
+ local_irq_restore(irq_flags);
break;
+ }
status = hv_do_rep_hypercall(HVCALL_MAP_GPA_PAGES, rep_count, 0,
input_page, NULL);
^ permalink raw reply related
* Re: [PATCH --to=kys@microsoft.com,haiyangz@microsoft.com,wei.liu@kernel.org,decui@microsoft.com,longli@microsoft.com] mshv: Fix interrupt state corruption in hv_do_map_pfns error path
From: Stanislav Kinsburskii @ 2026-04-22 0:14 UTC (permalink / raw)
To: linux-hyperv, linux-kernel
In-Reply-To: <177681100155.270589.16151793616470732178.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
On Tue, Apr 21, 2026 at 10:36:41PM +0000, Stanislav Kinsburskii wrote:
> Restore interrupt state before breaking out of the loop on error.
>
Please disregard
> The irq_flags are saved before entering the loop, but the early exit
> path on error fails to restore them. This leaves interrupts in an
> inconsistent state and can lead to lockdep warnings or other
> interrupt-related issues.
>
> Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_hv_call.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 7ed623668c8e..6381f949d9d9 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -237,8 +237,10 @@ static int hv_do_map_pfns(u64 partition_id, u64 gfn, u64 pfns_count,
> } else {
> pfnlist[i] = mmio_spa + done + i;
> }
> - if (ret)
> + if (ret) {
> + local_irq_restore(irq_flags);
> break;
> + }
>
> status = hv_do_rep_hypercall(HVCALL_MAP_GPA_PAGES, rep_count, 0,
> input_page, NULL);
>
>
^ permalink raw reply
* [PATCH] mshv: Fix large page unmap count in error path
From: Stanislav Kinsburskii @ 2026-04-21 22:44 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
When hv_do_map_pfns() fails after partially mapping large pages, the
unmap count passed to hv_call_unmap_pfns() is incorrect. The 'done'
variable tracks the number of large pages mapped, but the unmap
function expects the count in 4KB page units.
This causes incomplete cleanup on error, potentially leaving stale
mappings in the partition. Shift the count by large_shift to convert
from large page count to 4KB page count before calling the unmap
function.
Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root_hv_call.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 6381f949d9d9..905ea32e2c75 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -268,8 +268,10 @@ static int hv_do_map_pfns(u64 partition_id, u64 gfn, u64 pfns_count,
if (ret && done) {
u32 unmap_flags = 0;
- if (flags & HV_MAP_GPA_LARGE_PAGE)
+ if (flags & HV_MAP_GPA_LARGE_PAGE) {
unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
+ done <<= large_shift;
+ }
hv_call_unmap_pfns(partition_id, gfn, done, unmap_flags);
}
^ permalink raw reply related
* [PATCH --to=kys@microsoft.com,haiyangz@microsoft.com,wei.liu@kernel.org,decui@microsoft.com,longli@microsoft.com] mshv: Fix interrupt state corruption in hv_do_map_pfns error path
From: Stanislav Kinsburskii @ 2026-04-21 22:36 UTC (permalink / raw)
Cc: linux-hyperv, linux-kernel
Restore interrupt state before breaking out of the loop on error.
The irq_flags are saved before entering the loop, but the early exit
path on error fails to restore them. This leaves interrupts in an
inconsistent state and can lead to lockdep warnings or other
interrupt-related issues.
Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs")
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root_hv_call.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 7ed623668c8e..6381f949d9d9 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -237,8 +237,10 @@ static int hv_do_map_pfns(u64 partition_id, u64 gfn, u64 pfns_count,
} else {
pfnlist[i] = mmio_spa + done + i;
}
- if (ret)
+ if (ret) {
+ local_irq_restore(irq_flags);
break;
+ }
status = hv_do_rep_hypercall(HVCALL_MAP_GPA_PAGES, rep_count, 0,
input_page, NULL);
^ permalink raw reply related
* Re: [PATCH 18/23] cpu/hotplug: Add a new cpuhp_offline_cb() API
From: Thomas Gleixner @ 2026-04-21 18:43 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný,
Jonathan Corbet, Shuah Khan, Catalin Marinas, Will Deacon,
K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
Guenter Roeck, Frederic Weisbecker, Paul E. McKenney,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Anna-Maria Behnsen, Ingo Molnar,
Chen Ridong, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: cgroups, linux-doc, linux-kernel, linux-arm-kernel, linux-hyperv,
linux-hwmon, rcu, netdev, linux-kselftest, Costa Shulyupin,
Qiliang Yuan
In-Reply-To: <4a0ede3e-6e87-414f-a3a3-dd15c32f25ef@redhat.com>
On Tue, Apr 21 2026 at 13:29, Waiman Long wrote:
> On 4/21/26 12:17 PM, Thomas Gleixner wrote:
> Thanks for the great suggestions. I will certainly look into that.
>
> We actually have a cpu_active_mask that will be cleared early in
> sched_cpu_deactivate(). In the CPUHP_AP_SCHED_WAIT_EMPTY state, the CPU
> will still have online bit set but the active bit will be cleared. Or we
> could add another cpumask that can be used to indicate CPUs that have
> reached CPUHP_AP_SCHED_WAIT_EMPTY or below if necessary.
Right. Active mask is immediately cleared when a CPU goes down so that
the scheduler does not enqueue new tasks on it. But you can't use it for
interrupts because on CPU up the mask must be up to date when
irq_affinity_online_cpu() is invoked. The tick has the same constraints.
So for interrupts this should be handled in CPUHP_AP_IRQ_AFFINITY_ONLINE
both in the existing up and the new down callback. That can be a
interrupt core local CPU mask which is updated on the callbacks with the
sparse_irq_lock held.
Same for the tick handover magic.
Thanks,
tglx
^ permalink raw reply
* RE: [PATCH net v3] hv_sock: Report EOF instead of -EIO for FIN
From: Dexuan Cui @ 2026-04-21 17:57 UTC (permalink / raw)
To: Dexuan Cui, KY Srinivasan, Haiyang Zhang, wei.liu@kernel.org,
Long Li, sgarzare@redhat.com, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
horms@kernel.org, niuxuewei.nxw@antgroup.com,
linux-hyperv@vger.kernel.org, virtualization@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: stable@vger.kernel.org, Ben Hillis, Mitchell Levy
In-Reply-To: <20260421025950.1099495-1-decui@microsoft.com>
> From: Dexuan Cui
> Sent: Monday, April 20, 2026 8:00 PM
Please ignore the email, as I just posted an incremental patch here:
https://lore.kernel.org/linux-hyperv/20260421174931.1152238-1-decui@microsoft.com/T/#u
See the link for more context:
https://lore.kernel.org/linux-hyperv/177672238581.1802062.15838493180057695674.git-patchwork-notify@kernel.org/T/#t
^ permalink raw reply
* RE: [EXTERNAL] Re: [PATCH net v2] hv_sock: Report EOF instead of -EIO for FIN
From: Dexuan Cui @ 2026-04-21 17:54 UTC (permalink / raw)
To: Jakub Kicinski, Stefano Garzarella
Cc: patchwork-bot+netdevbpf@kernel.org, KY Srinivasan, Haiyang Zhang,
wei.liu@kernel.org, Long Li, davem@davemloft.net,
edumazet@google.com, pabeni@redhat.com, horms@kernel.org,
niuxuewei.nxw@antgroup.com, linux-hyperv@vger.kernel.org,
virtualization@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org, Ben Hillis,
levymitchell0@gmail.com
In-Reply-To: <20260421071839.30217a60@kernel.org>
> From: Jakub Kicinski <kuba@kernel.org>
> Sent: Tuesday, April 21, 2026 7:19 AM
> ...
> > Anyway, let's wait for Jakub's or other net maintainers' suggestions.
>
> Yes, you have to post an incremental fix
Thanks for the quick replies! I posted an incremental fix:
https://lore.kernel.org/linux-hyperv/20260421174931.1152238-1-decui@microsoft.com/T/#u
^ permalink raw reply
* [PATCH net] hv_sock: Return -EIO for malformed/short packets
From: Dexuan Cui @ 2026-04-21 17:49 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, sgarzare, davem, edumazet,
kuba, pabeni, horms, niuxuewei.nxw, linux-hyperv, virtualization,
netdev, linux-kernel
Cc: stable
Commit f63152958994 fixes a regression, however it fails to report an
error for malformed/short packets -- normally we should never see such
packets, but let's report an error for them just in case.
Fixes: f63152958994 ("hv_sock: Report EOF instead of -EIO for FIN")
Cc: stable@vger.kernel.org
Signed-off-by: Dexuan Cui <decui@microsoft.com>
---
Commit f63152958994 is currently only in net.git's master branch.
net/vmw_vsock/hyperv_transport.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
index 76e78c83fdbc..8faaa14bccda 100644
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -704,18 +704,27 @@ static s64 hvs_stream_has_data(struct vsock_sock *vsk)
if (hvs->recv_desc) {
/* Here hvs->recv_data_len is 0, so hvs->recv_desc must
* be NULL unless it points to the 0-byte-payload FIN
- * packet: see hvs_update_recv_data().
+ * packet or a malformed/short packet: see
+ * hvs_update_recv_data().
*
- * Here all the payload has been dequeued, but
- * hvs_channel_readable_payload() still returns 1,
- * because the VMBus ringbuffer's read_index is not
- * updated for the FIN packet: hvs_stream_dequeue() ->
- * hv_pkt_iter_next() updates the cached priv_read_index
- * but has no opportunity to update the read_index in
- * hv_pkt_iter_close() as hvs_stream_has_data() returns
- * 0 for the FIN packet, so it won't get dequeued.
+ * If hvs->recv_desc points to the FIN packet, here all
+ * the payload has been dequeued and the peer_shutdown
+ * flag is set, but hvs_channel_readable_payload() still
+ * returns 1, because the VMBus ringbuffer's read_index
+ * is not updated for the FIN packet:
+ * hvs_stream_dequeue() -> hv_pkt_iter_next() updates
+ * the cached priv_read_index but has no opportunity to
+ * update the read_index in hv_pkt_iter_close() as
+ * hvs_stream_has_data() returns 0 for the FIN packet,
+ * so it won't get dequeued.
+ *
+ * In case hvs->recv_desc points to a malformed/short
+ * packet, return -EIO.
*/
- return 0;
+ if (hvs->vsk->peer_shutdown & SEND_SHUTDOWN)
+ return 0;
+ else
+ return -EIO;
}
hvs->recv_desc = hv_pkt_iter_first(hvs->chan);
--
2.49.0
^ permalink raw reply related
* Re: [PATCH 18/23] cpu/hotplug: Add a new cpuhp_offline_cb() API
From: Waiman Long @ 2026-04-21 17:29 UTC (permalink / raw)
To: Thomas Gleixner, Tejun Heo, Johannes Weiner, Michal Koutný,
Jonathan Corbet, Shuah Khan, Catalin Marinas, Will Deacon,
K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
Guenter Roeck, Frederic Weisbecker, Paul E. McKenney,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Anna-Maria Behnsen, Ingo Molnar,
Chen Ridong, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: cgroups, linux-doc, linux-kernel, linux-arm-kernel, linux-hyperv,
linux-hwmon, rcu, netdev, linux-kselftest, Costa Shulyupin,
Qiliang Yuan
In-Reply-To: <87o6jcb84w.ffs@tglx>
On 4/21/26 12:17 PM, Thomas Gleixner wrote:
> On Mon, Apr 20 2026 at 23:03, Waiman Long wrote:
>> Add a new cpuhp_offline_cb() API that allows us to offline a set of
>> CPUs one-by-one, run the given callback function and then bring those
>> CPUs back online again while inhibiting any concurrent CPU hotplug
>> operations from happening.
> Please provide a properly structured change log which explains the
> context, the problem and the solution in separate paragraphs and this
> order. This is not new. It's documented...
>
>> This new API can be used to enable runtime adjustment of nohz_full and
>> isolcpus boot command line options. A new cpuhp_offline_cb_mode flag
>> is also added to signal that the system is in this offline callback
>> transient state so that some hotplug operations can be optimized out
>> if we choose to.
> We chose nothing.
>
>> +#include <linux/cpumask_types.h>
> What for? This header only needs a 'struct cpumask' forward declaration
> so that the compiler can handle the pointer argument, no?
>
>> +typedef int (*cpuhp_cb_t)(void *arg);
> You couldn't come up with a more generic name for this, right?
>
>> struct device;
>>
>> extern int lockdep_is_cpus_held(void);
>> @@ -29,6 +31,8 @@ void clear_tasks_mm_cpumask(int cpu);
>> int remove_cpu(unsigned int cpu);
>> int cpu_device_down(struct device *dev);
>> void smp_shutdown_nonboot_cpus(unsigned int primary_cpu);
>> +int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg);
> Ditto.
>
>> +extern bool cpuhp_offline_cb_mode;
> Groan. The only users are in the cpusets code which invokes this muck
> and should therefore know what's going on, no?
>
>> #else /* CONFIG_HOTPLUG_CPU */
>>
>> @@ -43,6 +47,11 @@ static inline void cpu_hotplug_disable(void) { }
>> static inline void cpu_hotplug_enable(void) { }
>> static inline int remove_cpu(unsigned int cpu) { return -EPERM; }
>> static inline void smp_shutdown_nonboot_cpus(unsigned int primary_cpu) { }
>> +static inline int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg)
>> +{
>> + return -EPERM;
> -EPERM?
>
>> +/**
>> + * cpuhp_offline_cb - offline CPUs, invoke callback function & online CPUs afterward
>> + * @mask: A mask of CPUs to be taken offline and then online
>> + * @func: A callback function to be invoked while the given CPUs are offline
>> + * @arg: Argument to be passed back to the callback function
>> + *
>> + * Return: 0 if successful, an error code otherwise
>> + */
>> +int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg)
>> +{
>> + int off_cpu, on_cpu, ret, ret2 = 0;
>> +
>> + if (WARN_ON_ONCE(cpumask_empty(mask) ||
>> + !cpumask_subset(mask, cpu_online_mask)))
>> + return -EINVAL;
> No line break required. You have 100 characters.
>
> But what's worse is that the access to cpu_online_mask is not protected
> against a concurrent CPU hotplug operation.
>
>> +
>> + pr_debug("%s: begin (CPU list = %*pbl)\n", __func__, cpumask_pr_args(mask));
> Tracing?
>
>> + lock_device_hotplug();
>> + cpuhp_offline_cb_mode = true;
>> + /*
>> + * If all offline operations succeed, off_cpu should become nr_cpu_ids.
>> + */
>> + for_each_cpu(off_cpu, mask) {
>> + ret = device_offline(get_cpu_device(off_cpu));
>> + if (unlikely(ret))
>> + break;
>> + }
>> + if (!ret)
>> + ret = func(arg);
>> +
>> + /* Bring previously offline CPUs back online */
>> + for_each_cpu(on_cpu, mask) {
>> + int retries = 0;
>> +
>> + if (on_cpu == off_cpu)
>> + break;
>> +
>> +retry:
>> + ret2 = device_online(get_cpu_device(on_cpu));
>> +
>> + /*
>> + * With the unlikely event that CPU hotplug is disabled while
>> + * this operation is in progress, we will need to wait a bit
>> + * for hotplug to hopefully be re-enabled again. If not, print
>> + * a warning and return the error.
>> + *
>> + * cpu_hotplug_disabled is supposed to be accessed while
>> + * holding the cpu_add_remove_lock mutex. So we need to
>> + * use the data_race() macro to access it here.
>> + */
>> + while ((ret2 == -EBUSY) && data_race(cpu_hotplug_disabled) &&
>> + (++retries <= 5)) {
>> + msleep(20);
>> + if (!data_race(cpu_hotplug_disabled))
>> + goto retry;
>> + }
>> + if (ret2) {
>> + pr_warn("%s: Failed to bring CPU %d back online!\n",
>> + __func__, on_cpu);
> Provide a proper text and not this silly __func__ thing.
>
>> + break;
>> + }
>> + }
> TBH. This is unreviewable gunk and the whole 'unlikely event that CPU
> hotplug is disabled' is just a lazy hack.
>
> All of this can be avoided including this made up callback function.
>
> It's not rocket science to provide:
>
> 1) A function which serializes against any other CPU hotplug
> related action.
>
> 2) A function which brings the CPUs in a given CPU mask down
>
> 3) A function which brings the CPUs in a given CPU mask up
>
> 4) A function which undoes #1
>
> Yeah I know, it's more work and not convoluted enough. But see below.
>
> That brings me to that other hack namely cpuhp_offline_cb_mode, which
> you self described as such in patch 21/23:
>
>> + /*
>> + * Hack: In cpuhp_offline_cb_mode, pretend all partitions are empty
>> + * to prevent unnecessary partition invalidation.
>> + */
>> + if (cpuhp_offline_cb_mode)
>> + return false;
>> +
> We are not merging hacks. End of story. But you knew that already, no?
>
> Let's take a step back and see what you really need to achieve:
>
> 1) Update tick_nohz_full_mask
> 2) Update the managed interrupt mask
> 3) Update CPU sets
>
> Independent of the direction of this update you need to ensure that the
> affected functionality keeps working correctly.
>
> You achieve that by bulk offlining the affected CPUs, invoking a magic
> callback and then bulk onlining the affected CPUs again, which requires
> that ill defined cpuhp_offline_cb_mode hackery and probably some more
> hacks all over the place.
>
> You can achieve the same by doing CPU by CPU operations in the right
> order without this mode hack, when you establish proper limitations for
> this:
>
> At no point in time it's allowed to empty a CPU set or a affected CPU
> mask, except when you completely undo the isolation of CPUs.
>
> That can be computed upfront w/o changing anything at all. Once the
> validity is established, the update can proceed. Or you can leave it
> to user space which can keep the pieces if it gets it wrong.
>
> That's a reasonable limitation as there is absolutely zero justification
> to support something like:
>
> housekeeping_cpus = [CPU 0], isolated_cpus = [CPU 1]
> ---> housekeeping_cpus = [CPU 1], isolated_cpus = [CPU 0]
>
> just because we can with enough horrible hacks.
>
> If you get that out of the way, then a CPU by CPU update becomes the
> obvious and simplest solution. The ordering constraints can be computed
> in user space upfront and there is no reason to do any of this in the
> kernel itself except for an eventual validation step. It might be a tad
> slower, but this is all but a hotpath operation.
>
> Just for the record. I suggested exactly this more than a year ago and
> it's still the right thing to do.
>
> And of course neither your cover letter nor any of the patches give a
> proper rationale why you think that your bulk hackery is better. For the
> very simple reason that there is no rationale at all.
>
> This bulk muck is doomed when your ultimate goal is to avoid the stop
> machine dance. With a per CPU update it is actually doable without more
> ill defined hacks all over the place.
>
> 1) Bring down the CPU to CPUHP_AP_SCHED_WAIT_EMPTY, which is the last
> state before stop machine is invoked.
>
> At that point:
>
> - no user space thread is running on the CPU anymore
>
> - everything related to this CPU has been shut down or moved
> elsewhere
>
> - interrupt managed device queues are quiesced if the CPU was
> the last online one in the queue affinity mask. If not the
> interrupt might still be affine to the CPU, but there is at
> least one other CPU available in the mask.
>
> 2) Update the tick NOHZ handover
>
> This can be done without going into stop machine by providing a
> hotplug callback right between CPUHP_AP_SMPBOOT_THREADS and
> CPUHP_AP_IRQ_AFFINITY_ONLINE.
>
> That's trivial enough to achieve and can work independently of
> NOHZ full.
>
> 3) Rework the affinity management, so that interrupt affinities can
> be reassigned in the CPUHP_AP_IRQ_AFFINITY_ONLINE state.
>
> That needs a lot of thoughts, but there is no real reason why it
> can't work.
>
> 4) Flip the housekeeping CPU masks in sched_cpu_wait_empty() after
> balance_hotplug_wait().
>
> 5) Bring the CPU online again.
>
> For #2 and #3 to work you need a separate CPU mask which avoids touching
> CPU online mask. For #3 this needs some more work to avoid reassigning the
> interrupts once sparse_irq_lock is dropped, but the bulk is achieved
> with the separate CPU mask.
>
> No?
Thanks for the great suggestions. I will certainly look into that.
We actually have a cpu_active_mask that will be cleared early in
sched_cpu_deactivate(). In the CPUHP_AP_SCHED_WAIT_EMPTY state, the CPU
will still have online bit set but the active bit will be cleared. Or we
could add another cpumask that can be used to indicate CPUs that have
reached CPUHP_AP_SCHED_WAIT_EMPTY or below if necessary.
Cheers,
Longman
^ permalink raw reply
* Re: [PATCH net v4 0/5] net: mana: Fix probe/remove error path bugs
From: Simon Horman @ 2026-04-21 16:49 UTC (permalink / raw)
To: Erni Sri Satya Vennela
Cc: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ssengar, dipayanroy, gargaditya,
shirazsaleem, kees, kotaranov, leon, shacharr, stephen,
linux-hyperv, netdev, linux-kernel
In-Reply-To: <20260420124741.1056179-1-ernis@linux.microsoft.com>
On Mon, Apr 20, 2026 at 05:47:34AM -0700, Erni Sri Satya Vennela wrote:
> Fix five bugs in mana_probe()/mana_remove() error handling that can
> cause warnings on uninitialized work structs, NULL pointer dereferences,
> masked errors, and resource leaks when early probe steps fail.
>
> Patches 1-2 move work struct initialization (link_change_work and
> gf_stats_work) to before any error path that could trigger
> mana_remove(), preventing WARN_ON in __flush_work() or debug object
> warnings when sync cancellation runs on uninitialized work structs.
>
> Patch 3 guards mana_remove() against double invocation. If PM resume
> fails, mana_probe() calls mana_remove() which sets gdma_context and
> driver_data to NULL. A failed resume does not unbind the driver, so
> when the device is eventually unbound, mana_remove() is called again
> and dereferences NULL, causing a kernel panic. An early return on
> NULL gdma_context or driver_data makes the second call harmless.
>
> Patch 4 prevents add_adev() from overwriting a port probe error,
> which could leave the driver in a broken state with NULL ports while
> reporting success.
>
> Patch 5 changes 'goto out' to 'break' in mana_remove()'s port loop
> so that mana_destroy_eq() is always reached, preventing EQ leaks when
> a NULL port is encountered.
> ---
> Changes in v4:
> * Correct Fixes tag from ca9c54d2d6a5 to 635096a86edb
> * Correct Fixes tag from ced82fce77e9 to a69839d4327d
Thanks for the updates.
For the series:
Reviewed-by: Simon Horman <horms@kernel.org>
I see that Sashiko provided feedback on patch 4/5.
However, as it notes, the issue it flags is addressed in patch 5/5.
No further action required AFAICS.
^ permalink raw reply
* Re: [PATCH 18/23] cpu/hotplug: Add a new cpuhp_offline_cb() API
From: Thomas Gleixner @ 2026-04-21 16:17 UTC (permalink / raw)
To: Waiman Long, Tejun Heo, Johannes Weiner, Michal Koutný,
Jonathan Corbet, Shuah Khan, Catalin Marinas, Will Deacon,
K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
Guenter Roeck, Frederic Weisbecker, Paul E. McKenney,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Anna-Maria Behnsen, Ingo Molnar,
Chen Ridong, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: cgroups, linux-doc, linux-kernel, linux-arm-kernel, linux-hyperv,
linux-hwmon, rcu, netdev, linux-kselftest, Costa Shulyupin,
Qiliang Yuan, Waiman Long
In-Reply-To: <20260421030351.281436-19-longman@redhat.com>
On Mon, Apr 20 2026 at 23:03, Waiman Long wrote:
> Add a new cpuhp_offline_cb() API that allows us to offline a set of
> CPUs one-by-one, run the given callback function and then bring those
> CPUs back online again while inhibiting any concurrent CPU hotplug
> operations from happening.
Please provide a properly structured change log which explains the
context, the problem and the solution in separate paragraphs and this
order. This is not new. It's documented...
> This new API can be used to enable runtime adjustment of nohz_full and
> isolcpus boot command line options. A new cpuhp_offline_cb_mode flag
> is also added to signal that the system is in this offline callback
> transient state so that some hotplug operations can be optimized out
> if we choose to.
We chose nothing.
> +#include <linux/cpumask_types.h>
What for? This header only needs a 'struct cpumask' forward declaration
so that the compiler can handle the pointer argument, no?
> +typedef int (*cpuhp_cb_t)(void *arg);
You couldn't come up with a more generic name for this, right?
> struct device;
>
> extern int lockdep_is_cpus_held(void);
> @@ -29,6 +31,8 @@ void clear_tasks_mm_cpumask(int cpu);
> int remove_cpu(unsigned int cpu);
> int cpu_device_down(struct device *dev);
> void smp_shutdown_nonboot_cpus(unsigned int primary_cpu);
> +int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg);
Ditto.
> +extern bool cpuhp_offline_cb_mode;
Groan. The only users are in the cpusets code which invokes this muck
and should therefore know what's going on, no?
> #else /* CONFIG_HOTPLUG_CPU */
>
> @@ -43,6 +47,11 @@ static inline void cpu_hotplug_disable(void) { }
> static inline void cpu_hotplug_enable(void) { }
> static inline int remove_cpu(unsigned int cpu) { return -EPERM; }
> static inline void smp_shutdown_nonboot_cpus(unsigned int primary_cpu) { }
> +static inline int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg)
> +{
> + return -EPERM;
-EPERM?
> +/**
> + * cpuhp_offline_cb - offline CPUs, invoke callback function & online CPUs afterward
> + * @mask: A mask of CPUs to be taken offline and then online
> + * @func: A callback function to be invoked while the given CPUs are offline
> + * @arg: Argument to be passed back to the callback function
> + *
> + * Return: 0 if successful, an error code otherwise
> + */
> +int cpuhp_offline_cb(struct cpumask *mask, cpuhp_cb_t func, void *arg)
> +{
> + int off_cpu, on_cpu, ret, ret2 = 0;
> +
> + if (WARN_ON_ONCE(cpumask_empty(mask) ||
> + !cpumask_subset(mask, cpu_online_mask)))
> + return -EINVAL;
No line break required. You have 100 characters.
But what's worse is that the access to cpu_online_mask is not protected
against a concurrent CPU hotplug operation.
> +
> + pr_debug("%s: begin (CPU list = %*pbl)\n", __func__, cpumask_pr_args(mask));
Tracing?
> + lock_device_hotplug();
> + cpuhp_offline_cb_mode = true;
> + /*
> + * If all offline operations succeed, off_cpu should become nr_cpu_ids.
> + */
> + for_each_cpu(off_cpu, mask) {
> + ret = device_offline(get_cpu_device(off_cpu));
> + if (unlikely(ret))
> + break;
> + }
> + if (!ret)
> + ret = func(arg);
> +
> + /* Bring previously offline CPUs back online */
> + for_each_cpu(on_cpu, mask) {
> + int retries = 0;
> +
> + if (on_cpu == off_cpu)
> + break;
> +
> +retry:
> + ret2 = device_online(get_cpu_device(on_cpu));
> +
> + /*
> + * With the unlikely event that CPU hotplug is disabled while
> + * this operation is in progress, we will need to wait a bit
> + * for hotplug to hopefully be re-enabled again. If not, print
> + * a warning and return the error.
> + *
> + * cpu_hotplug_disabled is supposed to be accessed while
> + * holding the cpu_add_remove_lock mutex. So we need to
> + * use the data_race() macro to access it here.
> + */
> + while ((ret2 == -EBUSY) && data_race(cpu_hotplug_disabled) &&
> + (++retries <= 5)) {
> + msleep(20);
> + if (!data_race(cpu_hotplug_disabled))
> + goto retry;
> + }
> + if (ret2) {
> + pr_warn("%s: Failed to bring CPU %d back online!\n",
> + __func__, on_cpu);
Provide a proper text and not this silly __func__ thing.
> + break;
> + }
> + }
TBH. This is unreviewable gunk and the whole 'unlikely event that CPU
hotplug is disabled' is just a lazy hack.
All of this can be avoided including this made up callback function.
It's not rocket science to provide:
1) A function which serializes against any other CPU hotplug
related action.
2) A function which brings the CPUs in a given CPU mask down
3) A function which brings the CPUs in a given CPU mask up
4) A function which undoes #1
Yeah I know, it's more work and not convoluted enough. But see below.
That brings me to that other hack namely cpuhp_offline_cb_mode, which
you self described as such in patch 21/23:
> + /*
> + * Hack: In cpuhp_offline_cb_mode, pretend all partitions are empty
> + * to prevent unnecessary partition invalidation.
> + */
> + if (cpuhp_offline_cb_mode)
> + return false;
> +
We are not merging hacks. End of story. But you knew that already, no?
Let's take a step back and see what you really need to achieve:
1) Update tick_nohz_full_mask
2) Update the managed interrupt mask
3) Update CPU sets
Independent of the direction of this update you need to ensure that the
affected functionality keeps working correctly.
You achieve that by bulk offlining the affected CPUs, invoking a magic
callback and then bulk onlining the affected CPUs again, which requires
that ill defined cpuhp_offline_cb_mode hackery and probably some more
hacks all over the place.
You can achieve the same by doing CPU by CPU operations in the right
order without this mode hack, when you establish proper limitations for
this:
At no point in time it's allowed to empty a CPU set or a affected CPU
mask, except when you completely undo the isolation of CPUs.
That can be computed upfront w/o changing anything at all. Once the
validity is established, the update can proceed. Or you can leave it
to user space which can keep the pieces if it gets it wrong.
That's a reasonable limitation as there is absolutely zero justification
to support something like:
housekeeping_cpus = [CPU 0], isolated_cpus = [CPU 1]
---> housekeeping_cpus = [CPU 1], isolated_cpus = [CPU 0]
just because we can with enough horrible hacks.
If you get that out of the way, then a CPU by CPU update becomes the
obvious and simplest solution. The ordering constraints can be computed
in user space upfront and there is no reason to do any of this in the
kernel itself except for an eventual validation step. It might be a tad
slower, but this is all but a hotpath operation.
Just for the record. I suggested exactly this more than a year ago and
it's still the right thing to do.
And of course neither your cover letter nor any of the patches give a
proper rationale why you think that your bulk hackery is better. For the
very simple reason that there is no rationale at all.
This bulk muck is doomed when your ultimate goal is to avoid the stop
machine dance. With a per CPU update it is actually doable without more
ill defined hacks all over the place.
1) Bring down the CPU to CPUHP_AP_SCHED_WAIT_EMPTY, which is the last
state before stop machine is invoked.
At that point:
- no user space thread is running on the CPU anymore
- everything related to this CPU has been shut down or moved
elsewhere
- interrupt managed device queues are quiesced if the CPU was
the last online one in the queue affinity mask. If not the
interrupt might still be affine to the CPU, but there is at
least one other CPU available in the mask.
2) Update the tick NOHZ handover
This can be done without going into stop machine by providing a
hotplug callback right between CPUHP_AP_SMPBOOT_THREADS and
CPUHP_AP_IRQ_AFFINITY_ONLINE.
That's trivial enough to achieve and can work independently of
NOHZ full.
3) Rework the affinity management, so that interrupt affinities can
be reassigned in the CPUHP_AP_IRQ_AFFINITY_ONLINE state.
That needs a lot of thoughts, but there is no real reason why it
can't work.
4) Flip the housekeeping CPU masks in sched_cpu_wait_empty() after
balance_hotplug_wait().
5) Bring the CPU online again.
For #2 and #3 to work you need a separate CPU mask which avoids touching
CPU online mask. For #3 this needs some more work to avoid reassigning the
interrupts once sparse_irq_lock is dropped, but the bulk is achieved
with the separate CPU mask.
No?
Thanks,
tglx
^ permalink raw reply
* [PATCH v2 7/7] mshv: Add tracepoint for map GPA hypercall
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Add tracing for GPA mapping hypercalls to aid in debugging memory
management issues in child partitions. The tracepoint captures both
successful and failed mapping attempts, including the number of pages
successfully mapped before any failure occurred.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root_hv_call.c | 3 +++
drivers/hv/mshv_trace.h | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index a95f2cfc5da5..7ed623668c8e 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -260,6 +260,9 @@ static int hv_do_map_pfns(u64 partition_id, u64 gfn, u64 pfns_count,
done += completed;
}
+ trace_mshv_map_pfns(partition_id, gfn, pfns_count, page_count,
+ flags, mmio_spa, done, ret);
+
if (ret && done) {
u32 unmap_flags = 0;
diff --git a/drivers/hv/mshv_trace.h b/drivers/hv/mshv_trace.h
index 6b8fa477fa3b..efd2b5d4ab73 100644
--- a/drivers/hv/mshv_trace.h
+++ b/drivers/hv/mshv_trace.h
@@ -538,6 +538,42 @@ TRACE_EVENT(mshv_handle_gpa_intercept,
)
);
+TRACE_EVENT(mshv_map_pfns,
+ TP_PROTO(u64 partition_id, u64 gfn, u64 pfn_count, u64 page_count, u32 flags,
+ u64 mmio_spa, int done, int ret),
+ TP_ARGS(partition_id, gfn, pfn_count, page_count, flags, mmio_spa, done, ret),
+ TP_STRUCT__entry(
+ __field(u64, partition_id)
+ __field(u64, gfn)
+ __field(u64, pfn_count)
+ __field(u64, page_count)
+ __field(u32, flags)
+ __field(u64, mmio_spa)
+ __field(int, done)
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ __entry->partition_id = partition_id;
+ __entry->gfn = gfn;
+ __entry->page_count = page_count;
+ __entry->pfn_count = pfn_count;
+ __entry->flags = flags;
+ __entry->mmio_spa = mmio_spa;
+ __entry->done = done;
+ __entry->ret = ret;
+ ),
+ TP_printk("partition_id=%llu gfn=0x%llx pfn_count=%llu page_count=%llu flags=0x%x mmio_spa=0x%llx done=%d ret=%d",
+ __entry->partition_id,
+ __entry->gfn,
+ __entry->pfn_count,
+ __entry->page_count,
+ __entry->flags,
+ __entry->mmio_spa,
+ __entry->done,
+ __entry->ret
+ )
+);
+
#endif /* _MSHV_TRACE_H_ */
/* This part must be outside protection */
^ permalink raw reply related
* [PATCH v2 6/7] mshv: Extract MMIO region mapping into separate function
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Extract the MMIO region mapping logic from mshv_map_user_memory() into
a dedicated mshv_map_mmio_region() function. This improves code
organization and consistency with the existing mshv_map_pinned_region()
and mshv_map_movable_region() functions.
The new function encapsulates the hv_call_map_mmio_pfns() call,
making the switch statement in mshv_map_user_memory() more concise
and maintaining a uniform pattern for all region types.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_regions.c | 9 +++++++++
drivers/hv/mshv_root.h | 2 ++
drivers/hv/mshv_root_main.c | 5 +----
3 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index c408d61a6ca6..f54f28ef13c5 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -832,3 +832,12 @@ int mshv_map_movable_region(struct mshv_mem_region *region)
return mshv_region_collect_and_map(region, 0, region->nr_pfns,
false);
}
+
+int mshv_map_mmio_region(struct mshv_mem_region *region,
+ unsigned long mmio_pfn)
+{
+ struct mshv_partition *partition = region->partition;
+
+ return hv_call_map_mmio_pfns(partition->pt_id, region->start_gfn,
+ mmio_pfn, region->nr_pfns);
+}
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index ce718056590f..aa22a08e5df2 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -374,5 +374,7 @@ void mshv_region_movable_fini(struct mshv_mem_region *region);
bool mshv_region_movable_init(struct mshv_mem_region *region);
int mshv_map_pinned_region(struct mshv_mem_region *region);
int mshv_map_movable_region(struct mshv_mem_region *region);
+int mshv_map_mmio_region(struct mshv_mem_region *region,
+ unsigned long mmio_pfn);
#endif /* _MSHV_ROOT_H_ */
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 881edc5d6e2b..0111366fc2a9 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1301,10 +1301,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
ret = mshv_map_movable_region(region);
break;
case MSHV_REGION_TYPE_MMIO:
- ret = hv_call_map_mmio_pfns(partition->pt_id,
- region->start_gfn,
- mmio_pfn,
- region->nr_pfns);
+ ret = mshv_map_mmio_region(region, mmio_pfn);
break;
}
^ permalink raw reply related
* [PATCH v2 5/7] mshv: Map populated pages on movable region creation
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Map any populated pages into the hypervisor upfront when creating a
movable region, rather than waiting for faults. Previously, movable
regions were created with all pages marked as HV_MAP_GPA_NO_ACCESS
regardless of whether the userspace mapping contained populated pages.
This guarantees that if the caller passes a populated mapping, those
present pages will be mapped into the hypervisor immediately during
region creation instead of being faulted in later.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_regions.c | 65 ++++++++++++++++++++++++++++++++-----------
drivers/hv/mshv_root.h | 1 +
drivers/hv/mshv_root_main.c | 10 +------
3 files changed, 50 insertions(+), 26 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 4fbcf1ff6fa6..c408d61a6ca6 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -519,7 +519,8 @@ int mshv_region_get(struct mshv_mem_region *region)
static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
unsigned long start,
unsigned long end,
- unsigned long *pfns)
+ unsigned long *pfns,
+ bool do_fault)
{
struct hmm_range range = {
.notifier = ®ion->mreg_mni,
@@ -541,9 +542,12 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
range.hmm_pfns = pfns;
range.start = start;
range.end = min(vma->vm_end, end);
- range.default_flags = HMM_PFN_REQ_FAULT;
- if (vma->vm_flags & VM_WRITE)
- range.default_flags |= HMM_PFN_REQ_WRITE;
+ range.default_flags = 0;
+ if (do_fault) {
+ range.default_flags = HMM_PFN_REQ_FAULT;
+ if (vma->vm_flags & VM_WRITE)
+ range.default_flags |= HMM_PFN_REQ_WRITE;
+ }
ret = hmm_range_fault(&range);
if (ret)
@@ -568,26 +572,40 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
}
/**
- * mshv_region_range_fault - Handle memory range faults for a given region.
- * @region: Pointer to the memory region structure.
- * @pfn_offset: Offset of the page within the region.
- * @pfn_count: Number of pages to handle.
+ * mshv_region_collect_and_map - Collect PFNs for a user range and map them
+ * @region : memory region being processed
+ * @pfn_offset: PFNs offset within the region
+ * @pfn_count : number of PFNs to process
+ * @do_fault : if true, fault in missing pages;
+ * if false, collect only present pages
*
- * This function resolves memory faults for a specified range of pages
- * within a memory region. It uses HMM (Heterogeneous Memory Management)
- * to fault in the required pages and updates the region's page array.
+ * Collects PFNs for the specified portion of @region from the
+ * corresponding userspace VMAs and maps them into the hypervisor. The
+ * behavior depends on @do_fault:
*
- * Return: 0 on success, negative error code on failure.
+ * - true: Fault in missing pages from userspace, ensuring all pages in the
+ * range are present. Used for on-demand page population.
+ * - false: Collect PFNs only for pages already present in userspace,
+ * leaving missing pages as invalid PFN markers.
+ * Used for initial region setup.
+ *
+ * Collected PFNs are stored in region->mreg_pfns[] with HMM bookkeeping
+ * flags cleared, then the range is mapped into the hypervisor. Present
+ * PFNs get mapped with region access permissions; missing PFNs (invalid
+ * entries) get mapped with no-access permissions.
+ *
+ * Return: 0 on success, negative errno on failure.
*/
-static int mshv_region_range_fault(struct mshv_mem_region *region,
- u64 pfn_offset, u64 pfn_count)
+static int mshv_region_collect_and_map(struct mshv_mem_region *region,
+ u64 pfn_offset, u64 pfn_count,
+ bool do_fault)
{
unsigned long start, end;
unsigned long *pfns;
int ret;
u64 i;
- pfns = kmalloc_array(pfn_count, sizeof(*pfns), GFP_KERNEL);
+ pfns = vmalloc_array(pfn_count, sizeof(unsigned long));
if (!pfns)
return -ENOMEM;
@@ -596,7 +614,7 @@ static int mshv_region_range_fault(struct mshv_mem_region *region,
do {
ret = mshv_region_hmm_fault_and_lock(region, start, end,
- pfns);
+ pfns, do_fault);
} while (ret == -EBUSY);
if (ret)
@@ -614,10 +632,17 @@ static int mshv_region_range_fault(struct mshv_mem_region *region,
mutex_unlock(®ion->mreg_mutex);
out:
- kfree(pfns);
+ vfree(pfns);
return ret;
}
+static int mshv_region_range_fault(struct mshv_mem_region *region,
+ u64 pfn_offset, u64 pfn_count)
+{
+ return mshv_region_collect_and_map(region, pfn_offset, pfn_count,
+ true);
+}
+
bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn)
{
u64 pfn_offset, pfn_count;
@@ -801,3 +826,9 @@ int mshv_map_pinned_region(struct mshv_mem_region *region)
err_out:
return ret;
}
+
+int mshv_map_movable_region(struct mshv_mem_region *region)
+{
+ return mshv_region_collect_and_map(region, 0, region->nr_pfns,
+ false);
+}
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index a0bc08a23953..ce718056590f 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -373,5 +373,6 @@ bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn);
void mshv_region_movable_fini(struct mshv_mem_region *region);
bool mshv_region_movable_init(struct mshv_mem_region *region);
int mshv_map_pinned_region(struct mshv_mem_region *region);
+int mshv_map_movable_region(struct mshv_mem_region *region);
#endif /* _MSHV_ROOT_H_ */
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 2e58cb0608d9..881edc5d6e2b 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1298,15 +1298,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
ret = mshv_map_pinned_region(region);
break;
case MSHV_REGION_TYPE_MEM_MOVABLE:
- /*
- * For movable memory regions, remap with no access to let
- * the hypervisor track dirty pages, enabling pre-copy live
- * migration.
- */
- ret = hv_call_map_ram_pfns(partition->pt_id,
- region->start_gfn,
- region->nr_pfns,
- HV_MAP_GPA_NO_ACCESS, NULL);
+ ret = mshv_map_movable_region(region);
break;
case MSHV_REGION_TYPE_MMIO:
ret = hv_call_map_mmio_pfns(partition->pt_id,
^ permalink raw reply related
* [PATCH v2 4/7] mshv: Move pinned region setup to mshv_regions.c
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Move mshv_prepare_pinned_region() from mshv_root_main.c to
mshv_regions.c and rename it to mshv_map_pinned_region(). This
co-locates the pinned region logic with the rest of the memory region
operations.
Make mshv_region_pin(), mshv_region_map(), mshv_region_share(),
mshv_region_unshare(), and mshv_region_invalidate() static, as they are
no longer called outside of mshv_regions.c.
Also fix a bug in the error handling where a mshv_region_map() failure
on a non-encrypted partition would be silently ignored, returning
success instead of propagating the error code.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_regions.c | 79 ++++++++++++++++++++++++++++++++++++++++---
drivers/hv/mshv_root.h | 6 +--
drivers/hv/mshv_root_main.c | 70 +-------------------------------------
3 files changed, 76 insertions(+), 79 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index e4edbd4ced00..4fbcf1ff6fa6 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -287,7 +287,7 @@ static int mshv_region_chunk_share(struct mshv_mem_region *region,
flags, true);
}
-int mshv_region_share(struct mshv_mem_region *region)
+static int mshv_region_share(struct mshv_mem_region *region)
{
u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_SHARED;
@@ -313,7 +313,7 @@ static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
flags, false);
}
-int mshv_region_unshare(struct mshv_mem_region *region)
+static int mshv_region_unshare(struct mshv_mem_region *region)
{
u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_EXCLUSIVE;
@@ -353,7 +353,7 @@ static int mshv_region_remap_pfns(struct mshv_mem_region *region,
mshv_region_chunk_remap);
}
-int mshv_region_map(struct mshv_mem_region *region)
+static int mshv_region_map(struct mshv_mem_region *region)
{
u32 map_flags = region->hv_map_flags;
@@ -377,12 +377,12 @@ static void mshv_region_invalidate_pfns(struct mshv_mem_region *region,
}
}
-void mshv_region_invalidate(struct mshv_mem_region *region)
+static void mshv_region_invalidate(struct mshv_mem_region *region)
{
mshv_region_invalidate_pfns(region, 0, region->nr_pfns);
}
-int mshv_region_pin(struct mshv_mem_region *region)
+static int mshv_region_pin(struct mshv_mem_region *region)
{
u64 done_count, nr_pfns, i;
unsigned long *pfns;
@@ -732,3 +732,72 @@ bool mshv_region_movable_init(struct mshv_mem_region *region)
return true;
}
+
+/**
+ * mshv_map_pinned_region - Pin and map memory regions
+ * @region: Pointer to the memory region structure
+ *
+ * This function processes memory regions that are explicitly marked as pinned.
+ * Pinned regions are preallocated, mapped upfront, and do not rely on fault-based
+ * population. The function ensures the region is properly populated, handles
+ * encryption requirements for SNP partitions if applicable, maps the region,
+ * and performs necessary sharing or eviction operations based on the mapping
+ * result.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int mshv_map_pinned_region(struct mshv_mem_region *region)
+{
+ struct mshv_partition *partition = region->partition;
+ int ret;
+
+ ret = mshv_region_pin(region);
+ if (ret) {
+ pt_err(partition, "Failed to pin memory region: %d\n",
+ ret);
+ goto err_out;
+ }
+
+ /*
+ * For an SNP partition it is a requirement that for every memory region
+ * that we are going to map for this partition we should make sure that
+ * host access to that region is released. This is ensured by doing an
+ * additional hypercall which will update the SLAT to release host
+ * access to guest memory regions.
+ */
+ if (mshv_partition_encrypted(partition)) {
+ ret = mshv_region_unshare(region);
+ if (ret) {
+ pt_err(partition,
+ "Failed to unshare memory region (guest_pfn: %llu): %d\n",
+ region->start_gfn, ret);
+ goto invalidate_region;
+ }
+ }
+
+ ret = mshv_region_map(region);
+ if (!ret)
+ return 0;
+
+ if (mshv_partition_encrypted(partition)) {
+ int shrc;
+
+ shrc = mshv_region_share(region);
+ if (!shrc)
+ goto invalidate_region;
+
+ pt_err(partition,
+ "Failed to share memory region (guest_pfn: %llu): %d\n",
+ region->start_gfn, shrc);
+ /*
+ * Don't unpin if marking shared failed because pages are no
+ * longer mapped in the host, ie root, anymore.
+ */
+ goto err_out;
+ }
+
+invalidate_region:
+ mshv_region_invalidate(region);
+err_out:
+ return ret;
+}
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 6652189953fa..a0bc08a23953 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -367,15 +367,11 @@ extern u8 * __percpu *hv_synic_eventring_tail;
struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
u64 uaddr, u32 flags);
-int mshv_region_share(struct mshv_mem_region *region);
-int mshv_region_unshare(struct mshv_mem_region *region);
-int mshv_region_map(struct mshv_mem_region *region);
-void mshv_region_invalidate(struct mshv_mem_region *region);
-int mshv_region_pin(struct mshv_mem_region *region);
void mshv_region_put(struct mshv_mem_region *region);
int mshv_region_get(struct mshv_mem_region *region);
bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn);
void mshv_region_movable_fini(struct mshv_mem_region *region);
bool mshv_region_movable_init(struct mshv_mem_region *region);
+int mshv_map_pinned_region(struct mshv_mem_region *region);
#endif /* _MSHV_ROOT_H_ */
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 5976afcdc946..2e58cb0608d9 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1253,74 +1253,6 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
return 0;
}
-/**
- * mshv_prepare_pinned_region - Pin and map memory regions
- * @region: Pointer to the memory region structure
- *
- * This function processes memory regions that are explicitly marked as pinned.
- * Pinned regions are preallocated, mapped upfront, and do not rely on fault-based
- * population. The function ensures the region is properly populated, handles
- * encryption requirements for SNP partitions if applicable, maps the region,
- * and performs necessary sharing or eviction operations based on the mapping
- * result.
- *
- * Return: 0 on success, negative error code on failure.
- */
-static int mshv_prepare_pinned_region(struct mshv_mem_region *region)
-{
- struct mshv_partition *partition = region->partition;
- int ret;
-
- ret = mshv_region_pin(region);
- if (ret) {
- pt_err(partition, "Failed to pin memory region: %d\n",
- ret);
- goto err_out;
- }
-
- /*
- * For an SNP partition it is a requirement that for every memory region
- * that we are going to map for this partition we should make sure that
- * host access to that region is released. This is ensured by doing an
- * additional hypercall which will update the SLAT to release host
- * access to guest memory regions.
- */
- if (mshv_partition_encrypted(partition)) {
- ret = mshv_region_unshare(region);
- if (ret) {
- pt_err(partition,
- "Failed to unshare memory region (guest_pfn: %llu): %d\n",
- region->start_gfn, ret);
- goto invalidate_region;
- }
- }
-
- ret = mshv_region_map(region);
- if (ret && mshv_partition_encrypted(partition)) {
- int shrc;
-
- shrc = mshv_region_share(region);
- if (!shrc)
- goto invalidate_region;
-
- pt_err(partition,
- "Failed to share memory region (guest_pfn: %llu): %d\n",
- region->start_gfn, shrc);
- /*
- * Don't unpin if marking shared failed because pages are no
- * longer mapped in the host, ie root, anymore.
- */
- goto err_out;
- }
-
- return 0;
-
-invalidate_region:
- mshv_region_invalidate(region);
-err_out:
- return ret;
-}
-
/*
* This maps two things: guest RAM and for pci passthru mmio space.
*
@@ -1363,7 +1295,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
switch (region->mreg_type) {
case MSHV_REGION_TYPE_MEM_PINNED:
- ret = mshv_prepare_pinned_region(region);
+ ret = mshv_map_pinned_region(region);
break;
case MSHV_REGION_TYPE_MEM_MOVABLE:
/*
^ permalink raw reply related
* [PATCH v2 3/7] mshv: Support regions with different VMAs
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Allow HMM fault handling across memory regions that span multiple VMAs
with different protection flags. The previous implementation assumed a
single VMA per region, which would fail when guest memory crosses VMA
boundaries.
Iterate through VMAs within the range and handle each separately with
appropriate protection flags, enabling more flexible memory region
configurations for partitions.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_regions.c | 77 ++++++++++++++++++++++++++++++++-------------
1 file changed, 55 insertions(+), 22 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 48f035ee0bc1..e4edbd4ced00 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -492,37 +492,73 @@ int mshv_region_get(struct mshv_mem_region *region)
}
/**
- * mshv_region_hmm_fault_and_lock - Handle HMM faults and lock the memory region
+ * mshv_region_hmm_fault_and_lock - Handle HMM faults across VMAs and lock
+ * the memory region
* @region: Pointer to the memory region structure
- * @range: Pointer to the HMM range structure
+ * @start : Starting virtual address of the range to fault
+ * @end : Ending virtual address of the range to fault (exclusive)
+ * @pfns : Output array for page frame numbers with HMM flags
*
* This function performs the following steps:
* 1. Reads the notifier sequence for the HMM range.
* 2. Acquires a read lock on the memory map.
- * 3. Handles HMM faults for the specified range.
- * 4. Releases the read lock on the memory map.
- * 5. If successful, locks the memory region mutex.
- * 6. Verifies if the notifier sequence has changed during the operation.
- * If it has, releases the mutex and returns -EBUSY to match with
- * hmm_range_fault() return code for repeating.
+ * 3. Iterates through VMAs in the specified range, handling each
+ * separately with appropriate protection flags (HMM_PFN_REQ_WRITE set
+ * based on VMA flags).
+ * 4. Handles HMM faults for each VMA segment.
+ * 5. Releases the read lock on the memory map.
+ * 6. If successful, locks the memory region mutex.
+ * 7. Verifies if the notifier sequence has changed during the operation.
+ * If it has, releases the mutex and returns -EBUSY to signal retry.
+ *
+ * The function expects the range [start, end) is backed by valid VMAs.
+ * Returns -EFAULT if any address in the range is not covered by a VMA.
*
* Return: 0 on success, a negative error code otherwise.
*/
static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
- struct hmm_range *range)
+ unsigned long start,
+ unsigned long end,
+ unsigned long *pfns)
{
+ struct hmm_range range = {
+ .notifier = ®ion->mreg_mni,
+ };
+ struct mm_struct *mm = region->mreg_mni.mm;
int ret;
- range->notifier_seq = mmu_interval_read_begin(range->notifier);
- mmap_read_lock(region->mreg_mni.mm);
- ret = hmm_range_fault(range);
- mmap_read_unlock(region->mreg_mni.mm);
+ range.notifier_seq = mmu_interval_read_begin(range.notifier);
+ mmap_read_lock(mm);
+ while (start < end) {
+ struct vm_area_struct *vma;
+
+ vma = vma_lookup(mm, start);
+ if (!vma) {
+ ret = -EFAULT;
+ break;
+ }
+
+ range.hmm_pfns = pfns;
+ range.start = start;
+ range.end = min(vma->vm_end, end);
+ range.default_flags = HMM_PFN_REQ_FAULT;
+ if (vma->vm_flags & VM_WRITE)
+ range.default_flags |= HMM_PFN_REQ_WRITE;
+
+ ret = hmm_range_fault(&range);
+ if (ret)
+ break;
+
+ start = range.end;
+ pfns += (range.end - range.start) >> PAGE_SHIFT;
+ }
+ mmap_read_unlock(mm);
if (ret)
return ret;
mutex_lock(®ion->mreg_mutex);
- if (mmu_interval_read_retry(range->notifier, range->notifier_seq)) {
+ if (mmu_interval_read_retry(range.notifier, range.notifier_seq)) {
mutex_unlock(®ion->mreg_mutex);
cond_resched();
return -EBUSY;
@@ -546,10 +582,7 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
static int mshv_region_range_fault(struct mshv_mem_region *region,
u64 pfn_offset, u64 pfn_count)
{
- struct hmm_range range = {
- .notifier = ®ion->mreg_mni,
- .default_flags = HMM_PFN_REQ_FAULT | HMM_PFN_REQ_WRITE,
- };
+ unsigned long start, end;
unsigned long *pfns;
int ret;
u64 i;
@@ -558,12 +591,12 @@ static int mshv_region_range_fault(struct mshv_mem_region *region,
if (!pfns)
return -ENOMEM;
- range.hmm_pfns = pfns;
- range.start = region->start_uaddr + pfn_offset * HV_HYP_PAGE_SIZE;
- range.end = range.start + pfn_count * HV_HYP_PAGE_SIZE;
+ start = region->start_uaddr + pfn_offset * PAGE_SIZE;
+ end = start + pfn_count * PAGE_SIZE;
do {
- ret = mshv_region_hmm_fault_and_lock(region, &range);
+ ret = mshv_region_hmm_fault_and_lock(region, start, end,
+ pfns);
} while (ret == -EBUSY);
if (ret)
^ permalink raw reply related
* [PATCH v2 2/7] mshv: Add support to address range holes remapping
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Consolidate memory region processing to handle both valid and invalid PFNs
uniformly. This eliminates code duplication across remap, unmap, share, and
unshare operations by using a common range processing interface.
Holes are now remapped with no-access permissions to enable
hypervisor dirty page tracking for precopy live migration.
This refactoring is a precursor to an upcoming change that will map
present pages in movable regions upon region creation, requiring
consistent handling of both mapped and unmapped ranges.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_regions.c | 104 ++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 93 insertions(+), 11 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 1c8cc200e9c1..48f035ee0bc1 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -119,6 +119,57 @@ static long mshv_region_process_pfns(struct mshv_mem_region *region,
return count;
}
+/**
+ * mshv_region_process_hole - Handle a hole (invalid PFNs) in a memory
+ * region
+ * @region : Memory region containing the hole
+ * @flags : Flags to pass to the handler function
+ * @pfn_offset: Starting PFN offset within the region
+ * @pfn_count : Number of PFNs in the hole
+ * @handler : Callback function to invoke for the hole
+ *
+ * Invokes the handler function for a contiguous hole with the specified
+ * parameters.
+ *
+ * Return: Number of PFNs handled, or negative error code.
+ */
+static long mshv_region_process_hole(struct mshv_mem_region *region,
+ u32 flags,
+ u64 pfn_offset, u64 pfn_count,
+ int (*handler)(struct mshv_mem_region *region,
+ u32 flags,
+ u64 pfn_offset,
+ u64 pfn_count,
+ bool huge_page))
+{
+ long ret;
+
+ ret = handler(region, flags, pfn_offset, pfn_count, 0);
+ if (ret)
+ return ret;
+
+ return pfn_count;
+}
+
+static long mshv_region_process_chunk(struct mshv_mem_region *region,
+ u32 flags,
+ u64 pfn_offset, u64 pfn_count,
+ int (*handler)(struct mshv_mem_region *region,
+ u32 flags,
+ u64 pfn_offset,
+ u64 pfn_count,
+ bool huge_page))
+{
+ if (pfn_valid(region->mreg_pfns[pfn_offset]))
+ return mshv_region_process_pfns(region, flags,
+ pfn_offset, pfn_count,
+ handler);
+ else
+ return mshv_region_process_hole(region, flags,
+ pfn_offset, pfn_count,
+ handler);
+}
+
/**
* mshv_region_process_range - Processes a range of PFNs in a region.
* @region : Pointer to the memory region structure.
@@ -146,33 +197,47 @@ static int mshv_region_process_range(struct mshv_mem_region *region,
u64 pfn_count,
bool huge_page))
{
- u64 end;
+ u64 start, end;
long ret;
+ if (!pfn_count)
+ return 0;
+
if (check_add_overflow(pfn_offset, pfn_count, &end))
return -EOVERFLOW;
if (end > region->nr_pfns)
return -EINVAL;
- while (pfn_count) {
- /* Skip non-present pages */
- if (!pfn_valid(region->mreg_pfns[pfn_offset])) {
- pfn_offset++;
- pfn_count--;
+ start = pfn_offset;
+ end = pfn_offset + 1;
+
+ while (end < pfn_offset + pfn_count) {
+ /*
+ * Accumulate contiguous pfns with the same validity
+ * (valid or not).
+ */
+ if (pfn_valid(region->mreg_pfns[start]) ==
+ pfn_valid(region->mreg_pfns[end])) {
+ end++;
continue;
}
- ret = mshv_region_process_pfns(region, flags,
- pfn_offset, pfn_count,
- handler);
+ ret = mshv_region_process_chunk(region, flags,
+ start, end - start,
+ handler);
if (ret < 0)
return ret;
- pfn_offset += ret;
- pfn_count -= ret;
+ start += ret;
}
+ ret = mshv_region_process_chunk(region, flags,
+ start, end - start,
+ handler);
+ if (ret < 0)
+ return ret;
+
return 0;
}
@@ -208,6 +273,9 @@ static int mshv_region_chunk_share(struct mshv_mem_region *region,
u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
+ if (!pfn_valid(region->mreg_pfns[pfn_offset]))
+ return -EINVAL;
+
if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
@@ -233,6 +301,9 @@ static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
+ if (!pfn_valid(region->mreg_pfns[pfn_offset]))
+ return -EINVAL;
+
if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
@@ -256,6 +327,14 @@ static int mshv_region_chunk_remap(struct mshv_mem_region *region,
u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
+ /*
+ * Remap missing pages with no access to let the
+ * hypervisor track dirty pages, enabling precopy live
+ * migration.
+ */
+ if (!pfn_valid(region->mreg_pfns[pfn_offset]))
+ flags = HV_MAP_GPA_NO_ACCESS;
+
if (huge_page)
flags |= HV_MAP_GPA_LARGE_PAGE;
@@ -357,6 +436,9 @@ static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
+ if (!pfn_valid(region->mreg_pfns[pfn_offset]))
+ return 0;
+
if (huge_page)
flags |= HV_UNMAP_GPA_LARGE_PAGE;
^ permalink raw reply related
* [PATCH v2 1/7] mshv: Convert from page pointers to PFNs
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <177678175995.13344.10130389779290396174.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
The HMM interface returns PFNs from hmm_range_fault(), and the
hypervisor hypercalls operate on PFNs. Storing page pointers in
between these interfaces requires unnecessary conversions and
temporary allocations.
Store PFNs directly in memory regions to match the natural data flow.
This eliminates the temporary PFN array allocation in the HMM fault
path and reduces page_to_pfn() conversions throughout the driver.
Convert to page structs via pfn_to_page() only when operations like
unpin_user_page() require them.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_regions.c | 301 ++++++++++++++++++++++------------------
drivers/hv/mshv_root.h | 20 +--
drivers/hv/mshv_root_hv_call.c | 50 +++----
drivers/hv/mshv_root_main.c | 30 ++--
4 files changed, 214 insertions(+), 187 deletions(-)
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index fdffd4f002f6..1c8cc200e9c1 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -18,12 +18,13 @@
#include "mshv_root.h"
#define MSHV_MAP_FAULT_IN_PAGES PTRS_PER_PMD
+#define MSHV_INVALID_PFN ULONG_MAX
/**
* mshv_chunk_stride - Compute stride for mapping guest memory
- * @page : The page to check for huge page backing
- * @gfn : Guest frame number for the mapping
- * @page_count: Total number of pages in the mapping
+ * @page : The page to check for huge page backing
+ * @gfn : Guest frame number for the mapping
+ * @pfn_count: Total number of pages in the mapping
*
* Determines the appropriate stride (in pages) for mapping guest memory.
* Uses huge page stride if the backing page is huge and the guest mapping
@@ -32,18 +33,18 @@
* Return: Stride in pages, or -EINVAL if page order is unsupported.
*/
static int mshv_chunk_stride(struct page *page,
- u64 gfn, u64 page_count)
+ u64 gfn, u64 pfn_count)
{
unsigned int page_order;
/*
* Use single page stride by default. For huge page stride, the
* page must be compound and point to the head of the compound
- * page, and both gfn and page_count must be huge-page aligned.
+ * page, and both gfn and pfn_count must be huge-page aligned.
*/
if (!PageCompound(page) || !PageHead(page) ||
!IS_ALIGNED(gfn, PTRS_PER_PMD) ||
- !IS_ALIGNED(page_count, PTRS_PER_PMD))
+ !IS_ALIGNED(pfn_count, PTRS_PER_PMD))
return 1;
page_order = folio_order(page_folio(page));
@@ -57,60 +58,61 @@ static int mshv_chunk_stride(struct page *page,
/**
* mshv_region_process_chunk - Processes a contiguous chunk of memory pages
* in a region.
- * @region : Pointer to the memory region structure.
- * @flags : Flags to pass to the handler.
- * @page_offset: Offset into the region's pages array to start processing.
- * @page_count : Number of pages to process.
- * @handler : Callback function to handle the chunk.
+ * @region : Pointer to the memory region structure.
+ * @flags : Flags to pass to the handler.
+ * @pfn_offset: Offset into the region's PFNs array to start processing.
+ * @pfn_count : Number of PFNs to process.
+ * @handler : Callback function to handle the chunk.
*
- * This function scans the region's pages starting from @page_offset,
- * checking for contiguous present pages of the same size (normal or huge).
- * It invokes @handler for the chunk of contiguous pages found. Returns the
- * number of pages handled, or a negative error code if the first page is
- * not present or the handler fails.
+ * This function scans the region's PFNs starting from @pfn_offset,
+ * checking for contiguous valid PFNs backed by pages of the same size
+ * (normal or huge). It invokes @handler for the chunk of contiguous valid
+ * PFNs found. Returns the number of PFNs handled, or a negative error code
+ * if the first PFN is invalid or the handler fails.
*
- * Note: The @handler callback must be able to handle both normal and huge
- * pages.
+ * Note: The @handler callback must be able to handle valid PFNs backed by
+ * both normal and huge pages.
*
* Return: Number of pages handled, or negative error code.
*/
-static long mshv_region_process_chunk(struct mshv_mem_region *region,
- u32 flags,
- u64 page_offset, u64 page_count,
- int (*handler)(struct mshv_mem_region *region,
- u32 flags,
- u64 page_offset,
- u64 page_count,
- bool huge_page))
+static long mshv_region_process_pfns(struct mshv_mem_region *region,
+ u32 flags,
+ u64 pfn_offset, u64 pfn_count,
+ int (*handler)(struct mshv_mem_region *region,
+ u32 flags,
+ u64 pfn_offset,
+ u64 pfn_count,
+ bool huge_page))
{
- u64 gfn = region->start_gfn + page_offset;
+ u64 gfn = region->start_gfn + pfn_offset;
u64 count;
- struct page *page;
+ unsigned long pfn;
int stride, ret;
- page = region->mreg_pages[page_offset];
- if (!page)
+ pfn = region->mreg_pfns[pfn_offset];
+ if (!pfn_valid(pfn))
return -EINVAL;
- stride = mshv_chunk_stride(page, gfn, page_count);
+ stride = mshv_chunk_stride(pfn_to_page(pfn), gfn, pfn_count);
if (stride < 0)
return stride;
/* Start at stride since the first stride is validated */
- for (count = stride; count < page_count; count += stride) {
- page = region->mreg_pages[page_offset + count];
+ for (count = stride; count < pfn_count ; count += stride) {
+ pfn = region->mreg_pfns[pfn_offset + count];
- /* Break if current page is not present */
- if (!page)
+ /* Break if current pfn is invalid */
+ if (pfn != MSHV_INVALID_PFN)
break;
/* Break if stride size changes */
- if (stride != mshv_chunk_stride(page, gfn + count,
- page_count - count))
+ if (stride != mshv_chunk_stride(pfn_to_page(pfn),
+ gfn + count,
+ pfn_count - count))
break;
}
- ret = handler(region, flags, page_offset, count, stride > 1);
+ ret = handler(region, flags, pfn_offset, count, stride > 1);
if (ret)
return ret;
@@ -118,70 +120,73 @@ static long mshv_region_process_chunk(struct mshv_mem_region *region,
}
/**
- * mshv_region_process_range - Processes a range of memory pages in a
- * region.
- * @region : Pointer to the memory region structure.
- * @flags : Flags to pass to the handler.
- * @page_offset: Offset into the region's pages array to start processing.
- * @page_count : Number of pages to process.
- * @handler : Callback function to handle each chunk of contiguous
- * pages.
+ * mshv_region_process_range - Processes a range of PFNs in a region.
+ * @region : Pointer to the memory region structure.
+ * @flags : Flags to pass to the handler.
+ * @pfn_offset: Offset into the region's PFNs array to start processing.
+ * @pfn_count : Number of PFNs to process.
+ * @handler : Callback function to handle each chunk of contiguous
+ * valid PFNs.
*
- * Iterates over the specified range of pages in @region, skipping
- * non-present pages. For each contiguous chunk of present pages, invokes
- * @handler via mshv_region_process_chunk.
+ * Iterates over the specified range of PFNs in @region, skipping
+ * invalid PFNs. For each contiguous chunk of valid PFNS, invokes
+ * @handler via mshv_region_process_pfns.
*
- * Note: The @handler callback must be able to handle both normal and huge
- * pages.
+ * Note: The @handler callback must be able to handle PFNs backed by both
+ * normal and huge pages.
*
* Returns 0 on success, or a negative error code on failure.
*/
static int mshv_region_process_range(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count,
+ u64 pfn_offset, u64 pfn_count,
int (*handler)(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset,
- u64 page_count,
+ u64 pfn_offset,
+ u64 pfn_count,
bool huge_page))
{
+ u64 end;
long ret;
- if (page_offset + page_count > region->nr_pages)
+ if (check_add_overflow(pfn_offset, pfn_count, &end))
+ return -EOVERFLOW;
+
+ if (end > region->nr_pfns)
return -EINVAL;
- while (page_count) {
+ while (pfn_count) {
/* Skip non-present pages */
- if (!region->mreg_pages[page_offset]) {
- page_offset++;
- page_count--;
+ if (!pfn_valid(region->mreg_pfns[pfn_offset])) {
+ pfn_offset++;
+ pfn_count--;
continue;
}
- ret = mshv_region_process_chunk(region, flags,
- page_offset,
- page_count,
- handler);
+ ret = mshv_region_process_pfns(region, flags,
+ pfn_offset, pfn_count,
+ handler);
if (ret < 0)
return ret;
- page_offset += ret;
- page_count -= ret;
+ pfn_offset += ret;
+ pfn_count -= ret;
}
return 0;
}
-struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
+struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pfns,
u64 uaddr, u32 flags)
{
struct mshv_mem_region *region;
+ u64 i;
- region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages);
+ region = vzalloc(struct_size(region, mreg_pfns, nr_pfns));
if (!region)
return ERR_PTR(-ENOMEM);
- region->nr_pages = nr_pages;
+ region->nr_pfns = nr_pfns;
region->start_gfn = guest_pfn;
region->start_uaddr = uaddr;
region->hv_map_flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_ADJUSTABLE;
@@ -190,6 +195,9 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
if (flags & BIT(MSHV_SET_MEM_BIT_EXECUTABLE))
region->hv_map_flags |= HV_MAP_GPA_EXECUTABLE;
+ for (i = 0; i < nr_pfns; i++)
+ region->mreg_pfns[i] = MSHV_INVALID_PFN;
+
kref_init(®ion->mreg_refcount);
return region;
@@ -197,15 +205,15 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
static int mshv_region_chunk_share(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count,
+ u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
return hv_call_modify_spa_host_access(region->partition->pt_id,
- region->mreg_pages + page_offset,
- page_count,
+ region->mreg_pfns + pfn_offset,
+ pfn_count,
HV_MAP_GPA_READABLE |
HV_MAP_GPA_WRITABLE,
flags, true);
@@ -216,21 +224,21 @@ int mshv_region_share(struct mshv_mem_region *region)
u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_SHARED;
return mshv_region_process_range(region, flags,
- 0, region->nr_pages,
+ 0, region->nr_pfns,
mshv_region_chunk_share);
}
static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count,
+ u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
if (huge_page)
flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
return hv_call_modify_spa_host_access(region->partition->pt_id,
- region->mreg_pages + page_offset,
- page_count, 0,
+ region->mreg_pfns + pfn_offset,
+ pfn_count, 0,
flags, false);
}
@@ -239,30 +247,30 @@ int mshv_region_unshare(struct mshv_mem_region *region)
u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_EXCLUSIVE;
return mshv_region_process_range(region, flags,
- 0, region->nr_pages,
+ 0, region->nr_pfns,
mshv_region_chunk_unshare);
}
static int mshv_region_chunk_remap(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count,
+ u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
if (huge_page)
flags |= HV_MAP_GPA_LARGE_PAGE;
- return hv_call_map_gpa_pages(region->partition->pt_id,
- region->start_gfn + page_offset,
- page_count, flags,
- region->mreg_pages + page_offset);
+ return hv_call_map_ram_pfns(region->partition->pt_id,
+ region->start_gfn + pfn_offset,
+ pfn_count, flags,
+ region->mreg_pfns + pfn_offset);
}
-static int mshv_region_remap_pages(struct mshv_mem_region *region,
- u32 map_flags,
- u64 page_offset, u64 page_count)
+static int mshv_region_remap_pfns(struct mshv_mem_region *region,
+ u32 map_flags,
+ u64 pfn_offset, u64 pfn_count)
{
return mshv_region_process_range(region, map_flags,
- page_offset, page_count,
+ pfn_offset, pfn_count,
mshv_region_chunk_remap);
}
@@ -270,38 +278,50 @@ int mshv_region_map(struct mshv_mem_region *region)
{
u32 map_flags = region->hv_map_flags;
- return mshv_region_remap_pages(region, map_flags,
- 0, region->nr_pages);
+ return mshv_region_remap_pfns(region, map_flags,
+ 0, region->nr_pfns);
}
-static void mshv_region_invalidate_pages(struct mshv_mem_region *region,
- u64 page_offset, u64 page_count)
+static void mshv_region_invalidate_pfns(struct mshv_mem_region *region,
+ u64 pfn_offset, u64 pfn_count)
{
- if (region->mreg_type == MSHV_REGION_TYPE_MEM_PINNED)
- unpin_user_pages(region->mreg_pages + page_offset, page_count);
+ u64 i;
+
+ for (i = pfn_offset; i < pfn_offset + pfn_count; i++) {
+ if (!pfn_valid(region->mreg_pfns[i]))
+ continue;
+
+ if (region->mreg_type == MSHV_REGION_TYPE_MEM_PINNED)
+ unpin_user_page(pfn_to_page(region->mreg_pfns[i]));
- memset(region->mreg_pages + page_offset, 0,
- page_count * sizeof(struct page *));
+ region->mreg_pfns[i] = MSHV_INVALID_PFN;
+ }
}
void mshv_region_invalidate(struct mshv_mem_region *region)
{
- mshv_region_invalidate_pages(region, 0, region->nr_pages);
+ mshv_region_invalidate_pfns(region, 0, region->nr_pfns);
}
int mshv_region_pin(struct mshv_mem_region *region)
{
- u64 done_count, nr_pages;
+ u64 done_count, nr_pfns, i;
+ unsigned long *pfns;
struct page **pages;
__u64 userspace_addr;
int ret;
- for (done_count = 0; done_count < region->nr_pages; done_count += ret) {
- pages = region->mreg_pages + done_count;
+ pages = kmalloc_array(MSHV_PIN_PAGES_BATCH_SIZE,
+ sizeof(struct page *), GFP_KERNEL);
+ if (!pages)
+ return -ENOMEM;
+
+ for (done_count = 0; done_count < region->nr_pfns; done_count += ret) {
+ pfns = region->mreg_pfns + done_count;
userspace_addr = region->start_uaddr +
done_count * HV_HYP_PAGE_SIZE;
- nr_pages = min(region->nr_pages - done_count,
- MSHV_PIN_PAGES_BATCH_SIZE);
+ nr_pfns = min(region->nr_pfns - done_count,
+ MSHV_PIN_PAGES_BATCH_SIZE);
/*
* Pinning assuming 4k pages works for large pages too.
@@ -311,39 +331,44 @@ int mshv_region_pin(struct mshv_mem_region *region)
* with the FOLL_LONGTERM flag does a large temporary
* allocation of contiguous memory.
*/
- ret = pin_user_pages_fast(userspace_addr, nr_pages,
+ ret = pin_user_pages_fast(userspace_addr, nr_pfns,
FOLL_WRITE | FOLL_LONGTERM,
pages);
- if (ret != nr_pages)
+ if (ret != nr_pfns)
goto release_pages;
+
+ for (i = 0; i < ret; i++)
+ pfns[i] = page_to_pfn(pages[i]);
}
+ kfree(pages);
return 0;
release_pages:
if (ret > 0)
done_count += ret;
- mshv_region_invalidate_pages(region, 0, done_count);
+ mshv_region_invalidate_pfns(region, 0, done_count);
+ kfree(pages);
return ret < 0 ? ret : -ENOMEM;
}
static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
u32 flags,
- u64 page_offset, u64 page_count,
+ u64 pfn_offset, u64 pfn_count,
bool huge_page)
{
if (huge_page)
flags |= HV_UNMAP_GPA_LARGE_PAGE;
- return hv_call_unmap_gpa_pages(region->partition->pt_id,
- region->start_gfn + page_offset,
- page_count, flags);
+ return hv_call_unmap_pfns(region->partition->pt_id,
+ region->start_gfn + pfn_offset,
+ pfn_count, flags);
}
static int mshv_region_unmap(struct mshv_mem_region *region)
{
return mshv_region_process_range(region, 0,
- 0, region->nr_pages,
+ 0, region->nr_pfns,
mshv_region_chunk_unmap);
}
@@ -427,8 +452,8 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
/**
* mshv_region_range_fault - Handle memory range faults for a given region.
* @region: Pointer to the memory region structure.
- * @page_offset: Offset of the page within the region.
- * @page_count: Number of pages to handle.
+ * @pfn_offset: Offset of the page within the region.
+ * @pfn_count: Number of pages to handle.
*
* This function resolves memory faults for a specified range of pages
* within a memory region. It uses HMM (Heterogeneous Memory Management)
@@ -437,7 +462,7 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
* Return: 0 on success, negative error code on failure.
*/
static int mshv_region_range_fault(struct mshv_mem_region *region,
- u64 page_offset, u64 page_count)
+ u64 pfn_offset, u64 pfn_count)
{
struct hmm_range range = {
.notifier = ®ion->mreg_mni,
@@ -447,13 +472,13 @@ static int mshv_region_range_fault(struct mshv_mem_region *region,
int ret;
u64 i;
- pfns = kmalloc_array(page_count, sizeof(*pfns), GFP_KERNEL);
+ pfns = kmalloc_array(pfn_count, sizeof(*pfns), GFP_KERNEL);
if (!pfns)
return -ENOMEM;
range.hmm_pfns = pfns;
- range.start = region->start_uaddr + page_offset * HV_HYP_PAGE_SIZE;
- range.end = range.start + page_count * HV_HYP_PAGE_SIZE;
+ range.start = region->start_uaddr + pfn_offset * HV_HYP_PAGE_SIZE;
+ range.end = range.start + pfn_count * HV_HYP_PAGE_SIZE;
do {
ret = mshv_region_hmm_fault_and_lock(region, &range);
@@ -462,11 +487,15 @@ static int mshv_region_range_fault(struct mshv_mem_region *region,
if (ret)
goto out;
- for (i = 0; i < page_count; i++)
- region->mreg_pages[page_offset + i] = hmm_pfn_to_page(pfns[i]);
+ for (i = 0; i < pfn_count; i++) {
+ if (!(pfns[i] & HMM_PFN_VALID))
+ continue;
+ /* Drop HMM_PFN_* flags to ensure PFNs are valid. */
+ region->mreg_pfns[pfn_offset + i] = pfns[i] & ~HMM_PFN_FLAGS;
+ }
- ret = mshv_region_remap_pages(region, region->hv_map_flags,
- page_offset, page_count);
+ ret = mshv_region_remap_pfns(region, region->hv_map_flags,
+ pfn_offset, pfn_count);
mutex_unlock(®ion->mreg_mutex);
out:
@@ -476,24 +505,24 @@ static int mshv_region_range_fault(struct mshv_mem_region *region,
bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn)
{
- u64 page_offset, page_count;
+ u64 pfn_offset, pfn_count;
int ret;
/* Align the page offset to the nearest MSHV_MAP_FAULT_IN_PAGES. */
- page_offset = ALIGN_DOWN(gfn - region->start_gfn,
- MSHV_MAP_FAULT_IN_PAGES);
+ pfn_offset = ALIGN_DOWN(gfn - region->start_gfn,
+ MSHV_MAP_FAULT_IN_PAGES);
/* Map more pages than requested to reduce the number of faults. */
- page_count = min(region->nr_pages - page_offset,
- MSHV_MAP_FAULT_IN_PAGES);
+ pfn_count = min(region->nr_pfns - pfn_offset,
+ MSHV_MAP_FAULT_IN_PAGES);
- ret = mshv_region_range_fault(region, page_offset, page_count);
+ ret = mshv_region_range_fault(region, pfn_offset, pfn_count);
WARN_ONCE(ret,
- "p%llu: GPA intercept failed: region %#llx-%#llx, gfn %#llx, page_offset %llu, page_count %llu\n",
+ "p%llu: GPA intercept failed: region %#llx-%#llx, gfn %#llx, pfn_offset %llu, pfn_count %llu\n",
region->partition->pt_id, region->start_uaddr,
- region->start_uaddr + (region->nr_pages << HV_HYP_PAGE_SHIFT),
- gfn, page_offset, page_count);
+ region->start_uaddr + (region->nr_pfns << HV_HYP_PAGE_SHIFT),
+ gfn, pfn_offset, pfn_count);
return !ret;
}
@@ -523,16 +552,16 @@ static bool mshv_region_interval_invalidate(struct mmu_interval_notifier *mni,
struct mshv_mem_region *region = container_of(mni,
struct mshv_mem_region,
mreg_mni);
- u64 page_offset, page_count;
+ u64 pfn_offset, pfn_count;
unsigned long mstart, mend;
int ret = -EPERM;
mstart = max(range->start, region->start_uaddr);
mend = min(range->end, region->start_uaddr +
- (region->nr_pages << HV_HYP_PAGE_SHIFT));
+ (region->nr_pfns << HV_HYP_PAGE_SHIFT));
- page_offset = HVPFN_DOWN(mstart - region->start_uaddr);
- page_count = HVPFN_DOWN(mend - mstart);
+ pfn_offset = HVPFN_DOWN(mstart - region->start_uaddr);
+ pfn_count = HVPFN_DOWN(mend - mstart);
if (mmu_notifier_range_blockable(range))
mutex_lock(®ion->mreg_mutex);
@@ -541,12 +570,12 @@ static bool mshv_region_interval_invalidate(struct mmu_interval_notifier *mni,
mmu_interval_set_seq(mni, cur_seq);
- ret = mshv_region_remap_pages(region, HV_MAP_GPA_NO_ACCESS,
- page_offset, page_count);
+ ret = mshv_region_remap_pfns(region, HV_MAP_GPA_NO_ACCESS,
+ pfn_offset, pfn_count);
if (ret)
goto out_unlock;
- mshv_region_invalidate_pages(region, page_offset, page_count);
+ mshv_region_invalidate_pfns(region, pfn_offset, pfn_count);
mutex_unlock(®ion->mreg_mutex);
@@ -558,9 +587,9 @@ static bool mshv_region_interval_invalidate(struct mmu_interval_notifier *mni,
WARN_ONCE(ret,
"Failed to invalidate region %#llx-%#llx (range %#lx-%#lx, event: %u, pages %#llx-%#llx, mm: %#llx): %d\n",
region->start_uaddr,
- region->start_uaddr + (region->nr_pages << HV_HYP_PAGE_SHIFT),
+ region->start_uaddr + (region->nr_pfns << HV_HYP_PAGE_SHIFT),
range->start, range->end, range->event,
- page_offset, page_offset + page_count - 1, (u64)range->mm, ret);
+ pfn_offset, pfn_offset + pfn_count - 1, (u64)range->mm, ret);
return false;
}
@@ -579,7 +608,7 @@ bool mshv_region_movable_init(struct mshv_mem_region *region)
ret = mmu_interval_notifier_insert(®ion->mreg_mni, current->mm,
region->start_uaddr,
- region->nr_pages << HV_HYP_PAGE_SHIFT,
+ region->nr_pfns << HV_HYP_PAGE_SHIFT,
&mshv_region_mni_ops);
if (ret)
return false;
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 1f086dcb7aa1..6652189953fa 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -84,15 +84,15 @@ enum mshv_region_type {
struct mshv_mem_region {
struct hlist_node hnode;
struct kref mreg_refcount;
- u64 nr_pages;
+ u64 nr_pfns;
u64 start_gfn;
u64 start_uaddr;
u32 hv_map_flags;
struct mshv_partition *partition;
enum mshv_region_type mreg_type;
struct mmu_interval_notifier mreg_mni;
- struct mutex mreg_mutex; /* protects region pages remapping */
- struct page *mreg_pages[];
+ struct mutex mreg_mutex; /* protects region PFNs remapping */
+ unsigned long mreg_pfns[];
};
struct mshv_irq_ack_notifier {
@@ -281,11 +281,11 @@ int hv_call_create_partition(u64 flags,
int hv_call_initialize_partition(u64 partition_id);
int hv_call_finalize_partition(u64 partition_id);
int hv_call_delete_partition(u64 partition_id);
-int hv_call_map_mmio_pages(u64 partition_id, u64 gfn, u64 mmio_spa, u64 numpgs);
-int hv_call_map_gpa_pages(u64 partition_id, u64 gpa_target, u64 page_count,
- u32 flags, struct page **pages);
-int hv_call_unmap_gpa_pages(u64 partition_id, u64 gpa_target, u64 page_count,
- u32 flags);
+int hv_call_map_mmio_pfns(u64 partition_id, u64 gfn, u64 mmio_spa, u64 numpgs);
+int hv_call_map_ram_pfns(u64 partition_id, u64 gpa_target, u64 pfn_count,
+ u32 flags, unsigned long *pfns);
+int hv_call_unmap_pfns(u64 partition_id, u64 gpa_target, u64 pfn_count,
+ u32 flags);
int hv_call_delete_vp(u64 partition_id, u32 vp_index);
int hv_call_assert_virtual_interrupt(u64 partition_id, u32 vector,
u64 dest_addr,
@@ -328,8 +328,8 @@ int hv_map_stats_page(enum hv_stats_object_type type,
int hv_unmap_stats_page(enum hv_stats_object_type type,
struct hv_stats_page *page_addr,
const union hv_stats_object_identity *identity);
-int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
- u64 page_struct_count, u32 host_access,
+int hv_call_modify_spa_host_access(u64 partition_id, unsigned long *pfns,
+ u64 pfns_count, u32 host_access,
u32 flags, u8 acquire);
int hv_call_get_partition_property_ex(u64 partition_id, u64 property_code, u64 arg,
void *property_value, size_t property_value_sz);
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index cb55d4d4be2e..a95f2cfc5da5 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -188,17 +188,16 @@ int hv_call_delete_partition(u64 partition_id)
return hv_result_to_errno(status);
}
-/* Ask the hypervisor to map guest ram pages or the guest mmio space */
-static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
- u32 flags, struct page **pages, u64 mmio_spa)
+static int hv_do_map_pfns(u64 partition_id, u64 gfn, u64 pfns_count,
+ u32 flags, unsigned long *pfns, u64 mmio_spa)
{
struct hv_input_map_gpa_pages *input_page;
u64 status, *pfnlist;
unsigned long irq_flags, large_shift = 0;
int ret = 0, done = 0;
- u64 page_count = page_struct_count;
+ u64 page_count = pfns_count;
- if (page_count == 0 || (pages && mmio_spa))
+ if (page_count == 0 || (pfns && mmio_spa))
return -EINVAL;
if (flags & HV_MAP_GPA_LARGE_PAGE) {
@@ -227,14 +226,14 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
for (i = 0; i < rep_count; i++)
if (flags & HV_MAP_GPA_NO_ACCESS) {
pfnlist[i] = 0;
- } else if (pages) {
+ } else if (pfns) {
u64 index = (done + i) << large_shift;
- if (index >= page_struct_count) {
+ if (index >= pfns_count) {
ret = -EINVAL;
break;
}
- pfnlist[i] = page_to_pfn(pages[index]);
+ pfnlist[i] = pfns[index];
} else {
pfnlist[i] = mmio_spa + done + i;
}
@@ -266,37 +265,37 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
if (flags & HV_MAP_GPA_LARGE_PAGE)
unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
- hv_call_unmap_gpa_pages(partition_id, gfn, done, unmap_flags);
+ hv_call_unmap_pfns(partition_id, gfn, done, unmap_flags);
}
return ret;
}
/* Ask the hypervisor to map guest ram pages */
-int hv_call_map_gpa_pages(u64 partition_id, u64 gpa_target, u64 page_count,
- u32 flags, struct page **pages)
+int hv_call_map_ram_pfns(u64 partition_id, u64 gfn, u64 pfn_count,
+ u32 flags, unsigned long *pfns)
{
- return hv_do_map_gpa_hcall(partition_id, gpa_target, page_count,
- flags, pages, 0);
+ return hv_do_map_pfns(partition_id, gfn, pfn_count, flags,
+ pfns, 0);
}
-/* Ask the hypervisor to map guest mmio space */
-int hv_call_map_mmio_pages(u64 partition_id, u64 gfn, u64 mmio_spa, u64 numpgs)
+int hv_call_map_mmio_pfns(u64 partition_id, u64 gfn, u64 mmio_spa,
+ u64 pfn_count)
{
int i;
u32 flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE |
HV_MAP_GPA_NOT_CACHED;
- for (i = 0; i < numpgs; i++)
+ for (i = 0; i < pfn_count; i++)
if (page_is_ram(mmio_spa + i))
return -EINVAL;
- return hv_do_map_gpa_hcall(partition_id, gfn, numpgs, flags, NULL,
- mmio_spa);
+ return hv_do_map_pfns(partition_id, gfn, pfn_count, flags,
+ NULL, mmio_spa);
}
-int hv_call_unmap_gpa_pages(u64 partition_id, u64 gfn, u64 page_count_4k,
- u32 flags)
+int hv_call_unmap_pfns(u64 partition_id, u64 gfn, u64 page_count_4k,
+ u32 flags)
{
struct hv_input_unmap_gpa_pages *input_page;
u64 status, page_count = page_count_4k;
@@ -1009,15 +1008,15 @@ int hv_unmap_stats_page(enum hv_stats_object_type type,
return ret;
}
-int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
- u64 page_struct_count, u32 host_access,
+int hv_call_modify_spa_host_access(u64 partition_id, unsigned long *pfns,
+ u64 pfns_count, u32 host_access,
u32 flags, u8 acquire)
{
struct hv_input_modify_sparse_spa_page_host_access *input_page;
u64 status;
int done = 0;
unsigned long irq_flags, large_shift = 0;
- u64 page_count = page_struct_count;
+ u64 page_count = pfns_count;
u16 code = acquire ? HVCALL_ACQUIRE_SPARSE_SPA_PAGE_HOST_ACCESS :
HVCALL_RELEASE_SPARSE_SPA_PAGE_HOST_ACCESS;
@@ -1051,11 +1050,10 @@ int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
for (i = 0; i < rep_count; i++) {
u64 index = (done + i) << large_shift;
- if (index >= page_struct_count)
+ if (index >= pfns_count)
return -EINVAL;
- input_page->spa_page_list[i] =
- page_to_pfn(pages[index]);
+ input_page->spa_page_list[i] = pfns[index];
}
status = hv_do_rep_hypercall(code, rep_count, 0, input_page,
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index bd1359eb58dd..5976afcdc946 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -618,7 +618,7 @@ mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) {
if (gfn >= region->start_gfn &&
- gfn < region->start_gfn + region->nr_pages)
+ gfn < region->start_gfn + region->nr_pfns)
return region;
}
@@ -1220,20 +1220,20 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
bool is_mmio)
{
struct mshv_mem_region *rg;
- u64 nr_pages = HVPFN_DOWN(mem->size);
+ u64 nr_pfns = HVPFN_DOWN(mem->size);
/* Reject overlapping regions */
spin_lock(&partition->pt_mem_regions_lock);
hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) {
- if (mem->guest_pfn + nr_pages <= rg->start_gfn ||
- rg->start_gfn + rg->nr_pages <= mem->guest_pfn)
+ if (mem->guest_pfn + nr_pfns <= rg->start_gfn ||
+ rg->start_gfn + rg->nr_pfns <= mem->guest_pfn)
continue;
spin_unlock(&partition->pt_mem_regions_lock);
return -EEXIST;
}
spin_unlock(&partition->pt_mem_regions_lock);
- rg = mshv_region_create(mem->guest_pfn, nr_pages,
+ rg = mshv_region_create(mem->guest_pfn, nr_pfns,
mem->userspace_addr, mem->flags);
if (IS_ERR(rg))
return PTR_ERR(rg);
@@ -1371,21 +1371,21 @@ mshv_map_user_memory(struct mshv_partition *partition,
* the hypervisor track dirty pages, enabling pre-copy live
* migration.
*/
- ret = hv_call_map_gpa_pages(partition->pt_id,
- region->start_gfn,
- region->nr_pages,
- HV_MAP_GPA_NO_ACCESS, NULL);
+ ret = hv_call_map_ram_pfns(partition->pt_id,
+ region->start_gfn,
+ region->nr_pfns,
+ HV_MAP_GPA_NO_ACCESS, NULL);
break;
case MSHV_REGION_TYPE_MMIO:
- ret = hv_call_map_mmio_pages(partition->pt_id,
- region->start_gfn,
- mmio_pfn,
- region->nr_pages);
+ ret = hv_call_map_mmio_pfns(partition->pt_id,
+ region->start_gfn,
+ mmio_pfn,
+ region->nr_pfns);
break;
}
trace_mshv_map_user_memory(partition->pt_id, region->start_uaddr,
- region->start_gfn, region->nr_pages,
+ region->start_gfn, region->nr_pfns,
region->hv_map_flags, ret);
if (ret)
@@ -1423,7 +1423,7 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
/* Paranoia check */
if (region->start_uaddr != mem->userspace_addr ||
region->start_gfn != mem->guest_pfn ||
- region->nr_pages != HVPFN_DOWN(mem->size)) {
+ region->nr_pfns != HVPFN_DOWN(mem->size)) {
spin_unlock(&partition->pt_mem_regions_lock);
return -EINVAL;
}
^ permalink raw reply related
* [PATCH v2 0/7] mshv: Refactor memory region management and map pages at creation
From: Stanislav Kinsburskii @ 2026-04-21 14:35 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli; +Cc: linux-hyperv, linux-kernel
This series refactors the mshv memory region subsystem in preparation
for mapping populated pages into the hypervisor at movable region
creation time, rather than relying solely on demand faulting.
The primary motivation is to ensure that when userspace passes a
pre-populated mapping for a movable memory region, those pages are
immediately visible to the hypervisor. Previously, all movable regions
were created with HV_MAP_GPA_NO_ACCESS on every page regardless of
whether the backing pages were already present, deferring all mapping
to the fault handler. This added unnecessary fault overhead and
complicated the initial setup of child partitions with pre-populated
memory.
The series takes a bottom-up approach:
- Patches 1-2 lay the groundwork by converting internal data structures
from page pointers to PFNs and teaching the range processing
infrastructure to handle holes (invalid PFNs) uniformly. The PFN
conversion eliminates redundant page_to_pfn()/pfn_to_page() conversions
between the HMM interface (which returns PFNs) and the hypervisor
hypercalls (which consume PFNs). The hole handling enables mapping
regions that contain a mix of present and absent pages, remapping holes
with no-access permissions to preserve hypervisor dirty page tracking
for precopy live migration.
- Patch 3 extends HMM fault handling to support memory regions that span
multiple VMAs with different protection flags, which is required for
flexible guest memory layouts.
- Patch 4 consolidates region setup by moving pinned region preparation
into mshv_regions.c, making five helper functions static, and fixing
a pre-existing bug where mshv_region_map() failures on non-encrypted
partitions were silently ignored.
- Patch 5 is the core functional change: movable regions now collect
already-present PFNs from userspace at creation time and map them
into the hypervisor immediately. A new do_fault parameter controls
whether hmm_range_fault() should fault in missing pages or only
collect those already present.
- Patches 6-7 are cleanups: extracting the MMIO mapping path into its
own function for consistency with the pinned and movable paths, and
adding a tracepoint for GPA mapping hypercalls to aid debugging.
v2:
- Rebased on top of latest mainline, simplified the check for valid PFNs,
added other minor cleanups and improvements.
---
Stanislav Kinsburskii (7):
mshv: Convert from page pointers to PFNs
mshv: Add support to address range holes remapping
mshv: Support regions with different VMAs
mshv: Move pinned region setup to mshv_regions.c
mshv: Map populated pages on movable region creation
mshv: Extract MMIO region mapping into separate function
mshv: Add tracepoint for map GPA hypercall
drivers/hv/mshv_regions.c | 589 +++++++++++++++++++++++++++++-----------
drivers/hv/mshv_root.h | 29 +-
drivers/hv/mshv_root_hv_call.c | 53 ++--
drivers/hv/mshv_root_main.c | 99 +------
drivers/hv/mshv_trace.h | 36 ++
5 files changed, 508 insertions(+), 298 deletions(-)
^ permalink raw reply
* Re: [PATCH 16/23] genirq/cpuhotplug: Use RCU to protect access of HK_TYPE_MANAGED_IRQ cpumask
From: Waiman Long @ 2026-04-21 14:29 UTC (permalink / raw)
To: Thomas Gleixner, Tejun Heo, Johannes Weiner, Michal Koutný,
Jonathan Corbet, Shuah Khan, Catalin Marinas, Will Deacon,
K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui, Long Li,
Guenter Roeck, Frederic Weisbecker, Paul E. McKenney,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, Steven Rostedt, Mathieu Desnoyers,
Lai Jiangshan, Zqiang, Anna-Maria Behnsen, Ingo Molnar,
Chen Ridong, Peter Zijlstra, Juri Lelli, Vincent Guittot,
Dietmar Eggemann, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: cgroups, linux-doc, linux-kernel, linux-arm-kernel, linux-hyperv,
linux-hwmon, rcu, netdev, linux-kselftest, Costa Shulyupin,
Qiliang Yuan
In-Reply-To: <87qzo8bs9m.ffs@tglx>
On 4/21/26 5:02 AM, Thomas Gleixner wrote:
> On Mon, Apr 20 2026 at 23:03, Waiman Long wrote:
>
>> As HK_TYPE_MANAGED_IRQ cpumask is going to be changeable at run time,
>> use RCU to protect access to the cpumask.
>>
>> To enable the new HK_TYPE_MANAGED_IRQ cpumask to take effect, the
>> following steps can be done.
> Can be done?
>
>> 1) Update the HK_TYPE_MANAGED_IRQ cpumask to take out the newly isolated
>> CPUs and add back the de-isolated CPUs.
>> 2) Tear down the affected CPUs to cause irq_migrate_all_off_this_cpu()
>> to be called on the affected CPUs to migrate the irqs to other
>> HK_TYPE_MANAGED_IRQ housekeeping CPUs.
>> 3) Bring up the previously offline CPUs to invoke
>> irq_affinity_online_cpu() to allow the newly de-isolated CPUs to
>> be used for managed irqs.
> Which previously offline CPUs?
This part should go into another patch.
>
>> diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
>> index 2e8072437826..8270c4de260b 100644
>> --- a/kernel/irq/manage.c
>> +++ b/kernel/irq/manage.c
>> @@ -263,6 +263,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, bool
>> housekeeping_enabled(HK_TYPE_MANAGED_IRQ)) {
>> const struct cpumask *hk_mask;
>>
>> + guard(rcu)();
>> hk_mask = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ);
>>
>> cpumask_and(tmp_mask, mask, hk_mask);
> How is this hunk related to $Subject?
The subject is actually about using RCU to protect access to
housekeeping cpumask. There are extra info in the commit log that
should go to another patch.
Cheers,
Longman
>
^ permalink raw reply
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