* [PATCH v2 00/12] x86/msr: Inline rdmsr/wrmsr instructions
From: Juergen Gross @ 2025-09-30 7:03 UTC (permalink / raw)
To: linux-kernel, x86, linux-coco, kvm, linux-hyperv, virtualization,
llvm
Cc: xin, Juergen Gross, Kirill A. Shutemov, Dave Hansen,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, H. Peter Anvin,
Sean Christopherson, Paolo Bonzini, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Vitaly Kuznetsov,
Boris Ostrovsky, xen-devel, Ajay Kaher, Alexey Makhalov,
Broadcom internal kernel review list, Andy Lutomirski,
Peter Zijlstra, Nathan Chancellor, Nick Desaulniers,
Bill Wendling, Justin Stitt
When building a kernel with CONFIG_PARAVIRT_XXL the paravirt
infrastructure will always use functions for reading or writing MSRs,
even when running on bare metal.
Switch to inline RDMSR/WRMSR instructions in this case, reducing the
paravirt overhead.
In order to make this less intrusive, some further reorganization of
the MSR access helpers is done in the first 5 patches.
The next 5 patches are converting the non-paravirt case to use direct
inlining of the MSR access instructions, including the WRMSRNS
instruction and the immediate variants of RDMSR and WRMSR if possible.
Patch 11 removes the PV hooks for MSR accesses and implements the
Xen PV cases via calls depending on X86_FEATURE_XENPV, which results
in runtime patching those calls away for the non-XenPV case.
Patch 12 is a final little cleanup patch.
This series has been tested to work with Xen PV and on bare metal.
This series is inspired by Xin Li, who used a similar approach, but
(in my opinion) with some flaws. Originally I thought it should be
possible to use the paravirt infrastructure, but this turned out to be
rather complicated, especially for the Xen PV case in the *_safe()
variants of the MSR access functions.
Changes since V1:
- Use Xin Li's approach for inlining
- Several new patches
Juergen Gross (9):
coco/tdx: Rename MSR access helpers
x86/sev: replace call of native_wrmsr() with native_wrmsrq()
x86/kvm: Remove the KVM private read_msr() function
x86/msr: minimize usage of native_*() msr access functions
x86/msr: Move MSR trace calls one function level up
x86/msr: Use the alternatives mechanism for WRMSR
x86/msr: Use the alternatives mechanism for RDMSR
x86/paravirt: Don't use pv_ops vector for MSR access functions
x86/msr: Reduce number of low level MSR access helpers
Xin Li (Intel) (3):
x86/cpufeatures: Add a CPU feature bit for MSR immediate form
instructions
x86/opcode: Add immediate form MSR instructions
x86/extable: Add support for immediate form MSR instructions
arch/x86/coco/tdx/tdx.c | 8 +-
arch/x86/hyperv/ivm.c | 2 +-
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/fred.h | 2 +-
arch/x86/include/asm/kvm_host.h | 10 -
arch/x86/include/asm/msr.h | 409 +++++++++++++++++++-------
arch/x86/include/asm/paravirt.h | 67 -----
arch/x86/include/asm/paravirt_types.h | 13 -
arch/x86/include/asm/sev-internal.h | 7 +-
arch/x86/kernel/cpu/scattered.c | 1 +
arch/x86/kernel/kvmclock.c | 2 +-
arch/x86/kernel/paravirt.c | 5 -
arch/x86/kvm/svm/svm.c | 16 +-
arch/x86/kvm/vmx/vmx.c | 4 +-
arch/x86/lib/x86-opcode-map.txt | 5 +-
arch/x86/mm/extable.c | 39 ++-
arch/x86/xen/enlighten_pv.c | 24 +-
arch/x86/xen/pmu.c | 5 +-
tools/arch/x86/lib/x86-opcode-map.txt | 5 +-
19 files changed, 383 insertions(+), 242 deletions(-)
--
2.51.0
^ permalink raw reply
* Re: [PATCH 0/6] scsi: Remove redundant ternary operators
From: Martin K. Petersen @ 2025-09-30 2:36 UTC (permalink / raw)
To: James E.J. Bottomley, Kashyap Desai, Sumit Saxena, Shivasharan S,
Chandrakanth patil, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Ingo Molnar, Thomas Gleixner, Christophe JAILLET,
Khalid Aziz, Bart Van Assche, Easwar Hariharan, Damien Le Moal,
Prateek Singh Rathore, Geoff Levand, Al Viro, Thomas Fourier,
linux-scsi, linux-kernel, megaraidlinux.pdl, linux-hyperv,
Liao Yuanhong
Cc: Martin K . Petersen
In-Reply-To: <20250902132359.83059-1-liaoyuanhong@vivo.com>
On Tue, 02 Sep 2025 21:23:40 +0800, Liao Yuanhong wrote:
> For ternary operators in the form of "a ? true : false" or
> "a ? false : true", if 'a' itself returns a boolean result, the ternary
> operator can be omitted. Remove redundant ternary operators to clean up the
> code.
>
> Liao Yuanhong (6):
> scsi: arcmsr: Remove redundant ternary operators
> scsi: csiostor: Remove redundant ternary operators
> scsi: isci: Remove redundant ternary operators
> scsi: megaraid_sas: Remove redundant ternary operators
> scsi: scsi_transport_fc: Remove redundant ternary operators
> scsi: storvsc: Remove redundant ternary operators
>
> [...]
Applied to 6.18/scsi-queue, thanks!
[6/6] scsi: storvsc: Remove redundant ternary operators
https://git.kernel.org/mkp/scsi/c/15968590f07c
--
Martin K. Petersen
^ permalink raw reply
* Re: [PATCH v4 0/5] mshv: Fixes for stats and vp state page mappings
From: Nuno Das Neves @ 2025-09-29 18:19 UTC (permalink / raw)
To: Stanislav Kinsburskii
Cc: linux-hyperv, linux-kernel, prapal, easwar.hariharan, tiala,
anirudh, paekkaladevi, kys, haiyangz, wei.liu, decui
In-Reply-To: <aNcd60fpoI1b6LUT@skinsburskii.localdomain>
On 9/26/2025 4:12 PM, Stanislav Kinsburskii wrote:
> On Fri, Sep 26, 2025 at 09:23:10AM -0700, Nuno Das Neves wrote:
>> There are some differences in how L1VH partitions must map stats and vp
>> state pages, some of which are due to differences across hypervisor
>> versions. Detect and handle these cases.
>>
>
> I'm not sure that support for older and actully broken versions on
> hypervisor need to be usptreamed, as these versions will go away sooner
> or later and this support will become dead weight.
>
As far as I know, these changes are relevant for shipped versions of the
hypervisor - they are not 'broken' except in some very specific cases
(live migration on L1VH, I think?)
The hypervisor team added a feature bit for these changes so that both old
and new versions of these APIs can be supported.
> I think we should upstrem only the changes needed for the new versiong
> of hypervisors instead and carry legacy support out of tree until it
> becomes obsoleted.
>
Which version do you suggest to be the cutoff?
I'd prefer to support as many versions of the hypervisor as we can, as
long as they are at all relevant. We can remove the support later.
Removing prematurely just creates friction. Inevitably some users will
find themselves running on an older hypervisor and then it just fails
with a cryptic error. This includes myself, since I test L1VH on Azure
which typically has older hypervisor versions.
Nuno
> Thanks,
> Stanislav
>
>
>> Patch 1:
>> Fix for the logic of when to map the vp stats page for the root scheduler.
>>
>> Patch 2-3:
>> Add HVCALL_GET_PARTITION_PROPERTY_EX and use it to query "vmm capabilities" on
>> module init.
>>
>> Patches 4-5:
>> Check a feature bit in vmm capabilities, to take a new code path for mapping
>> stats and vp state pages. In this case, the stats and vp state pages must be
>> allocated by Linux, and a new hypercall HVCALL_MAP_VP_STATS_PAGE2 must be used
>> to map the stats page.
>>
>> ---
>> v4:
>> - Fixed some __packed attributes on unions [Stanislav]
>> - Cleaned up mshv_init_vmm_caps() [Stanislav]
>> - Cleaned up loop in hv_call_map_stats_page2() [Stanislav]
>>
>> v3:
>> https://lore.kernel.org/linux-hyperv/1758066262-15477-1-git-send-email-nunodasneves@linux.microsoft.com/T/#t
>> - Fix bug in patch 4, in mshv_partition_ioctl_create_vp() cleanup path
>> [kernel test robot]
>> - Make hv_unmap_vp_state_page() use struct page to match hv_map_vp_state_page()
>> - Remove SELF == PARENT check which doesn't belong in patch 5 [Easwar]
>>
>> v2:
>> https://lore.kernel.org/linux-hyperv/1757546089-2002-1-git-send-email-nunodasneves@linux.microsoft.com/T/#t
>> - Remove patch falling back to SELF page if PARENT mapping fails [Easwar]
>> (To be included in a future series)
>> - Fix formatting of function definitions [Easwar]
>> - Fix some wording in commit messages [Praveen]
>> - Proceed with driver init even if getting vmm capabilities fails [Anirudh]
>>
>> v1:
>> https://lore.kernel.org/linux-hyperv/1756428230-3599-1-git-send-email-nunodasneves@linux.microsoft.com/T/#t
>>
>> ---
>> Jinank Jain (2):
>> mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
>> mshv: Introduce new hypercall to map stats page for L1VH partitions
>>
>> Nuno Das Neves (1):
>> mshv: Only map vp->vp_stats_pages if on root scheduler
>>
>> Purna Pavan Chandra Aekkaladevi (2):
>> mshv: Add the HVCALL_GET_PARTITION_PROPERTY_EX hypercall
>> mshv: Get the vmm capabilities offered by the hypervisor
>>
>> drivers/hv/mshv_root.h | 24 +++--
>> drivers/hv/mshv_root_hv_call.c | 185 +++++++++++++++++++++++++++++++--
>> drivers/hv/mshv_root_main.c | 127 ++++++++++++----------
>> include/hyperv/hvgdk_mini.h | 2 +
>> include/hyperv/hvhdk.h | 40 +++++++
>> include/hyperv/hvhdk_mini.h | 33 ++++++
>> 6 files changed, 337 insertions(+), 74 deletions(-)
>>
>> --
>> 2.34.1
^ permalink raw reply
* RE: [PATCH v4 5/5] mshv: Introduce new hypercall to map stats page for L1VH partitions
From: Michael Kelley @ 2025-09-29 17:57 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, prapal@linux.microsoft.com,
easwar.hariharan@linux.microsoft.com, tiala@microsoft.com,
anirudh@anirudhrb.com, paekkaladevi@linux.microsoft.com,
skinsburskii@linux.microsoft.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, Jinank Jain
In-Reply-To: <1758903795-18636-6-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, September 26, 2025 9:23 AM
>
> From: Jinank Jain <jinankjain@linux.microsoft.com>
>
> Introduce HVCALL_MAP_STATS_PAGE2 which provides a map location (GPFN)
> to map the stats to. This hypercall is required for L1VH partitions,
> depending on the hypervisor version. This uses the same check as the
> state page map location; mshv_use_overlay_gpfn().
>
> Add mshv_map_vp_state_page() helpers to use this new hypercall or the
> old one depending on availability.
>
> For unmapping, the original HVCALL_UNMAP_STATS_PAGE works for both
> cases.
>
> Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
> ---
> drivers/hv/mshv_root.h | 10 ++--
> drivers/hv/mshv_root_hv_call.c | 93 ++++++++++++++++++++++++++++++++--
> drivers/hv/mshv_root_main.c | 22 ++++----
> include/hyperv/hvgdk_mini.h | 1 +
> include/hyperv/hvhdk_mini.h | 7 +++
> 5 files changed, 113 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index dbe2d1d0b22f..0dfccfbe6123 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -297,11 +297,11 @@ int hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,
> int hv_call_disconnect_port(u64 connection_partition_id,
> union hv_connection_id connection_id);
> int hv_call_notify_port_ring_empty(u32 sint_index);
> -int hv_call_map_stat_page(enum hv_stats_object_type type,
> - const union hv_stats_object_identity *identity,
> - void **addr);
> -int hv_call_unmap_stat_page(enum hv_stats_object_type type,
> - const union hv_stats_object_identity *identity);
> +int hv_map_stats_page(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity,
> + void **addr);
> +int hv_unmap_stats_page(enum hv_stats_object_type type, void *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,
> u32 flags, u8 acquire);
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 98c6278ff151..5a805b3dec0b 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -804,9 +804,51 @@ hv_call_notify_port_ring_empty(u32 sint_index)
> return hv_result_to_errno(status);
> }
>
> -int hv_call_map_stat_page(enum hv_stats_object_type type,
> - const union hv_stats_object_identity *identity,
> - void **addr)
> +static int hv_call_map_stats_page2(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity,
> + u64 map_location)
> +{
> + unsigned long flags;
> + struct hv_input_map_stats_page2 *input;
> + u64 status;
> + int ret;
> +
> + if (!map_location || !mshv_use_overlay_gpfn())
> + return -EINVAL;
> +
> + do {
> + local_irq_save(flags);
> + input = *this_cpu_ptr(hyperv_pcpu_input_arg);
> +
> + memset(input, 0, sizeof(*input));
> + input->type = type;
> + input->identity = *identity;
> + input->map_location = map_location;
> +
> + status = hv_do_hypercall(HVCALL_MAP_STATS_PAGE2, input, NULL);
> +
> + local_irq_restore(flags);
> +
> + ret = hv_result_to_errno(status);
> +
> + if (!ret)
> + break;
This logic is incorrect. If the hypercall returns
HV_STATUS_INSUFFICIENT_MEMORY, then errno -ENOMEM is immediately
returned to the caller without any opportunity to do hv_call_deposit_pages().
> +
> + if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
> + hv_status_debug(status, "\n");
> + break;
> + }
> +
> + ret = hv_call_deposit_pages(NUMA_NO_NODE,
> + hv_current_partition_id, 1);
> + } while (!ret);
> +
> + return ret;
> +}
> +
> +static int hv_call_map_stats_page(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity,
> + void **addr)
> {
> unsigned long flags;
> struct hv_input_map_stats_page *input;
> @@ -845,8 +887,36 @@ int hv_call_map_stat_page(enum hv_stats_object_type type,
> return ret;
> }
>
> -int hv_call_unmap_stat_page(enum hv_stats_object_type type,
> - const union hv_stats_object_identity *identity)
> +int hv_map_stats_page(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity,
> + void **addr)
> +{
> + int ret;
> + struct page *allocated_page = NULL;
> +
> + if (!addr)
> + return -EINVAL;
> +
> + if (mshv_use_overlay_gpfn()) {
> + allocated_page = alloc_page(GFP_KERNEL);
> + if (!allocated_page)
> + return -ENOMEM;
> +
> + ret = hv_call_map_stats_page2(type, identity,
> + page_to_pfn(allocated_page));
This should use page_to_hvpfn() per my comments in Patch 4 of this series.
> + *addr = page_address(allocated_page);
> + } else {
> + ret = hv_call_map_stats_page(type, identity, addr);
> + }
> +
> + if (ret && allocated_page)
> + __free_page(allocated_page);
Might want to do *addr = NULL after freeing the page so that the caller
can't erroneously reference the free page. Again, the current caller doesn't
do that, so current code isn't broken.
> +
> + return ret;
> +}
> +
> +static int hv_call_unmap_stats_page(enum hv_stats_object_type type,
> + const union hv_stats_object_identity *identity)
> {
> unsigned long flags;
> struct hv_input_unmap_stats_page *input;
> @@ -865,6 +935,19 @@ int hv_call_unmap_stat_page(enum hv_stats_object_type type,
> return hv_result_to_errno(status);
> }
>
> +int hv_unmap_stats_page(enum hv_stats_object_type type, void *page_addr,
> + const union hv_stats_object_identity *identity)
> +{
> + int ret;
> +
> + ret = hv_call_unmap_stats_page(type, identity);
> +
> + if (mshv_use_overlay_gpfn() && page_addr)
> + __free_page(virt_to_page(page_addr));
> +
> + return ret;
> +}
> +
> int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
> u64 page_struct_count, u32 host_access,
> u32 flags, u8 acquire)
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 2d0ad17acde6..71a8ab5db3b8 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -841,7 +841,8 @@ mshv_vp_release(struct inode *inode, struct file *filp)
> return 0;
> }
>
> -static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
> +static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index,
> + void *stats_pages[])
> {
> union hv_stats_object_identity identity = {
> .vp.partition_id = partition_id,
> @@ -849,10 +850,10 @@ static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
> };
>
> identity.vp.stats_area_type = HV_STATS_AREA_SELF;
> - hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
> + hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
>
> identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
> - hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
> + hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
> }
>
> static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
> @@ -865,14 +866,14 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
> int err;
>
> identity.vp.stats_area_type = HV_STATS_AREA_SELF;
> - err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
> - &stats_pages[HV_STATS_AREA_SELF]);
> + err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
> + &stats_pages[HV_STATS_AREA_SELF]);
> if (err)
> return err;
>
> identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
> - err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
> - &stats_pages[HV_STATS_AREA_PARENT]);
> + err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
> + &stats_pages[HV_STATS_AREA_PARENT]);
> if (err)
> goto unmap_self;
>
> @@ -880,7 +881,7 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
>
> unmap_self:
> identity.vp.stats_area_type = HV_STATS_AREA_SELF;
> - hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
> + hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
> return err;
> }
>
> @@ -988,7 +989,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> kfree(vp);
> unmap_stats_pages:
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> - mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
> + mshv_vp_stats_unmap(partition->pt_id, args.vp_index, stats_pages);
> unmap_ghcb_page:
> if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
> hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> @@ -1740,7 +1741,8 @@ static void destroy_partition(struct mshv_partition *partition)
> continue;
>
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> - mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
> + mshv_vp_stats_unmap(partition->pt_id, vp->vp_index,
> + (void **)vp->vp_stats_pages);
>
> if (vp->vp_register_page) {
> (void)hv_unmap_vp_state_page(partition->pt_id,
> diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
> index ff4325fb623a..f66565106d21 100644
> --- a/include/hyperv/hvgdk_mini.h
> +++ b/include/hyperv/hvgdk_mini.h
> @@ -493,6 +493,7 @@ union hv_vp_assist_msr_contents { /*
> HV_REGISTER_VP_ASSIST_PAGE */
> #define HVCALL_GET_PARTITION_PROPERTY_EX 0x0101
> #define HVCALL_MMIO_READ 0x0106
> #define HVCALL_MMIO_WRITE 0x0107
> +#define HVCALL_MAP_STATS_PAGE2 0x0131
>
> /* HV_HYPERCALL_INPUT */
> #define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
> diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
> index bf2ce27dfcc5..064bf735cab6 100644
> --- a/include/hyperv/hvhdk_mini.h
> +++ b/include/hyperv/hvhdk_mini.h
> @@ -177,6 +177,13 @@ struct hv_input_map_stats_page {
> union hv_stats_object_identity identity;
> } __packed;
>
> +struct hv_input_map_stats_page2 {
> + u32 type; /* enum hv_stats_object_type */
> + u32 padding;
> + union hv_stats_object_identity identity;
> + u64 map_location;
> +} __packed;
> +
> struct hv_output_map_stats_page {
> u64 map_location;
> } __packed;
> --
> 2.34.1
>
^ permalink raw reply
* RE: [PATCH v4 4/5] mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
From: Michael Kelley @ 2025-09-29 17:56 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, prapal@linux.microsoft.com,
easwar.hariharan@linux.microsoft.com, tiala@microsoft.com,
anirudh@anirudhrb.com, paekkaladevi@linux.microsoft.com,
skinsburskii@linux.microsoft.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, Jinank Jain
In-Reply-To: <1758903795-18636-5-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, September 26, 2025 9:23 AM
>
> From: Jinank Jain <jinankjain@linux.microsoft.com>
>
> Introduce mshv_use_overlay_gpfn() to check if a page needs to be
> allocated and passed to the hypervisor to map VP state pages. This is
> only needed on L1VH, and only on some (newer) versions of the
> hypervisor, hence the need to check vmm_capabilities.
>
> Introduce functions hv_map/unmap_vp_state_page() to handle the
> allocation and freeing.
>
> Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
> Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Reviewed-by: Anirudh Rayabharam <anirudh@anirudhrb.com>
> ---
> drivers/hv/mshv_root.h | 11 ++---
> drivers/hv/mshv_root_hv_call.c | 61 ++++++++++++++++++++++++---
> drivers/hv/mshv_root_main.c | 76 +++++++++++++++++-----------------
> 3 files changed, 98 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index 0cb1e2589fe1..dbe2d1d0b22f 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -279,11 +279,12 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
> /* Choose between pages and bytes */
> struct hv_vp_state_data state_data, u64 page_count,
> struct page **pages, u32 num_bytes, u8 *bytes);
> -int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl,
> - struct page **state_page);
> -int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl);
> +int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl,
> + struct page **state_page);
> +int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + struct page *state_page,
> + union hv_input_vtl input_vtl);
> int hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,
> u64 connection_partition_id, struct hv_port_info *port_info,
> u8 port_vtl, u8 min_connection_vtl, int node);
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 3fd3cce23f69..98c6278ff151 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -526,9 +526,9 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
> return ret;
> }
>
> -int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl,
> - struct page **state_page)
> +static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl,
> + struct page **state_page)
> {
> struct hv_input_map_vp_state_page *input;
> struct hv_output_map_vp_state_page *output;
> @@ -547,7 +547,14 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> input->type = type;
> input->input_vtl = input_vtl;
>
> - status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input, output);
This function must zero the input area before using it. Otherwise,
flags.map_location_provided is uninitialized when *state_page is NULL. It will
have whatever value was left by the previous user of hyperv_pcpu_input_arg,
potentially producing bizarre results. And there's a reserved field that won't be
set to zero.
> + if (*state_page) {
> + input->flags.map_location_provided = 1;
> + input->requested_map_location =
> + page_to_pfn(*state_page);
Technically, this should be page_to_hvpfn() since the PFN value is being sent to
Hyper-V. I know root (and L1VH?) partitions must run with the same page size
as the Hyper-V host, but it's better to not leave code buried here that will blow
up if the "same page size requirement" should ever change.
And after making the hypercall, there's an invocation of pfn_to_page(), which
should account for the same. Unfortunately, there's not an existing hvpfn_to_page()
function.
> + }
> +
> + status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input,
> + output);
>
> if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
> if (hv_result_success(status))
> @@ -565,8 +572,39 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> return ret;
> }
>
> -int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl)
> +static bool mshv_use_overlay_gpfn(void)
> +{
> + return hv_l1vh_partition() &&
> + mshv_root.vmm_caps.vmm_can_provide_overlay_gpfn;
> +}
> +
> +int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl,
> + struct page **state_page)
> +{
> + int ret = 0;
> + struct page *allocated_page = NULL;
> +
> + if (mshv_use_overlay_gpfn()) {
> + allocated_page = alloc_page(GFP_KERNEL);
> + if (!allocated_page)
> + return -ENOMEM;
> + *state_page = allocated_page;
> + } else {
> + *state_page = NULL;
> + }
> +
> + ret = hv_call_map_vp_state_page(partition_id, vp_index, type, input_vtl,
> + state_page);
> +
> + if (ret && allocated_page)
> + __free_page(allocated_page);
For robustness, you might want to set *state_page = NULL here so the
caller doesn't have a reference to the page that has been freed. I didn't
see any cases where the caller incorrectly checks the returned
*state_page value after an error, so the current code isn't broken.
> +
> + return ret;
> +}
> +
> +static int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl)
> {
> unsigned long flags;
> u64 status;
> @@ -590,6 +628,17 @@ int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> return hv_result_to_errno(status);
> }
>
> +int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + struct page *state_page, union hv_input_vtl input_vtl)
> +{
> + int ret = hv_call_unmap_vp_state_page(partition_id, vp_index, type, input_vtl);
> +
> + if (mshv_use_overlay_gpfn() && state_page)
> + __free_page(state_page);
> +
> + return ret;
> +}
> +
> 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_main.c b/drivers/hv/mshv_root_main.c
> index e199770ecdfa..2d0ad17acde6 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -890,7 +890,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition
> *partition,
> {
> struct mshv_create_vp args;
> struct mshv_vp *vp;
> - struct page *intercept_message_page, *register_page, *ghcb_page;
> + struct page *intercept_msg_page, *register_page, *ghcb_page;
> void *stats_pages[2];
> long ret;
>
> @@ -908,28 +908,25 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> if (ret)
> return ret;
>
> - ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> - input_vtl_zero,
> - &intercept_message_page);
> + ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> + input_vtl_zero, &intercept_msg_page);
> if (ret)
> goto destroy_vp;
>
> if (!mshv_partition_encrypted(partition)) {
> - ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_REGISTERS,
> - input_vtl_zero,
> - ®ister_page);
> + ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_REGISTERS,
> + input_vtl_zero, ®ister_page);
> if (ret)
> goto unmap_intercept_message_page;
> }
>
> if (mshv_partition_encrypted(partition) &&
> is_ghcb_mapping_available()) {
> - ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_GHCB,
> - input_vtl_normal,
> - &ghcb_page);
> + ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_GHCB,
> + input_vtl_normal, &ghcb_page);
> if (ret)
> goto unmap_register_page;
> }
> @@ -960,7 +957,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> atomic64_set(&vp->run.vp_signaled_count, 0);
>
> vp->vp_index = args.vp_index;
> - vp->vp_intercept_msg_page = page_to_virt(intercept_message_page);
> + vp->vp_intercept_msg_page = page_to_virt(intercept_msg_page);
> if (!mshv_partition_encrypted(partition))
> vp->vp_register_page = page_to_virt(register_page);
>
> @@ -993,21 +990,19 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
> unmap_ghcb_page:
> - if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available()) {
> - hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_GHCB,
> - input_vtl_normal);
> - }
> + if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
> + hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_GHCB, ghcb_page,
> + input_vtl_normal);
> unmap_register_page:
> - if (!mshv_partition_encrypted(partition)) {
> - hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_REGISTERS,
> - input_vtl_zero);
> - }
> + if (!mshv_partition_encrypted(partition))
> + hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_REGISTERS,
> + register_page, input_vtl_zero);
> unmap_intercept_message_page:
> - hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> - input_vtl_zero);
> + hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> + intercept_msg_page, input_vtl_zero);
> destroy_vp:
> hv_call_delete_vp(partition->pt_id, args.vp_index);
> return ret;
> @@ -1748,24 +1743,27 @@ static void destroy_partition(struct mshv_partition *partition)
> mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
>
> if (vp->vp_register_page) {
> - (void)hv_call_unmap_vp_state_page(partition->pt_id,
> - vp->vp_index,
> - HV_VP_STATE_PAGE_REGISTERS,
> - input_vtl_zero);
> + (void)hv_unmap_vp_state_page(partition->pt_id,
> + vp->vp_index,
> + HV_VP_STATE_PAGE_REGISTERS,
> + virt_to_page(vp->vp_register_page),
> + input_vtl_zero);
> vp->vp_register_page = NULL;
> }
>
> - (void)hv_call_unmap_vp_state_page(partition->pt_id,
> - vp->vp_index,
> - HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> - input_vtl_zero);
> + (void)hv_unmap_vp_state_page(partition->pt_id,
> + vp->vp_index,
> + HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> + virt_to_page(vp->vp_intercept_msg_page),
> + input_vtl_zero);
> vp->vp_intercept_msg_page = NULL;
>
> if (vp->vp_ghcb_page) {
> - (void)hv_call_unmap_vp_state_page(partition->pt_id,
> - vp->vp_index,
> - HV_VP_STATE_PAGE_GHCB,
> - input_vtl_normal);
> + (void)hv_unmap_vp_state_page(partition->pt_id,
> + vp->vp_index,
> + HV_VP_STATE_PAGE_GHCB,
> + virt_to_page(vp->vp_ghcb_page),
> + input_vtl_normal);
> vp->vp_ghcb_page = NULL;
> }
>
> --
> 2.34.1
>
^ permalink raw reply
* RE: [PATCH v4 2/5] mshv: Add the HVCALL_GET_PARTITION_PROPERTY_EX hypercall
From: Michael Kelley @ 2025-09-29 17:55 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, prapal@linux.microsoft.com,
easwar.hariharan@linux.microsoft.com, tiala@microsoft.com,
anirudh@anirudhrb.com, paekkaladevi@linux.microsoft.com,
skinsburskii@linux.microsoft.com
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com
In-Reply-To: <1758903795-18636-3-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, September 26, 2025 9:23 AM
>
> From: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
>
> This hypercall can be used to fetch extended properties of a
> partition. Extended properties are properties with values larger than
> a u64. Some of these also need additional input arguments.
>
> Add helper function for using the hypercall in the mshv_root driver.
>
> Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Anirudh Rayabharam <anirudh@anirudhrb.com>
> Reviewed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
> ---
> drivers/hv/mshv_root.h | 2 ++
> drivers/hv/mshv_root_hv_call.c | 31 ++++++++++++++++++++++++++
> include/hyperv/hvgdk_mini.h | 1 +
> include/hyperv/hvhdk.h | 40 ++++++++++++++++++++++++++++++++++
> include/hyperv/hvhdk_mini.h | 26 ++++++++++++++++++++++
> 5 files changed, 100 insertions(+)
>
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index e3931b0f1269..4aeb03bea6b6 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -303,6 +303,8 @@ int hv_call_unmap_stat_page(enum hv_stats_object_type type,
> int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
> u64 page_struct_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);
>
> extern struct mshv_root mshv_root;
> extern enum hv_scheduler_type hv_scheduler_type;
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index c9c274f29c3c..3fd3cce23f69 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -590,6 +590,37 @@ int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> return hv_result_to_errno(status);
> }
>
> +int hv_call_get_partition_property_ex(u64 partition_id, u64 property_code,
> + u64 arg, void *property_value,
> + size_t property_value_sz)
> +{
> + u64 status;
> + unsigned long flags;
> + struct hv_input_get_partition_property_ex *input;
> + struct hv_output_get_partition_property_ex *output;
> +
> + local_irq_save(flags);
> + input = *this_cpu_ptr(hyperv_pcpu_input_arg);
> + output = *this_cpu_ptr(hyperv_pcpu_output_arg);
> +
> + memset(input, 0, sizeof(*input));
> + input->partition_id = partition_id;
> + input->property_code = property_code;
> + input->arg = arg;
> + status = hv_do_hypercall(HVCALL_GET_PARTITION_PROPERTY_EX, input, output);
> +
> + if (!hv_result_success(status)) {
> + hv_status_debug(status, "\n");
> + local_irq_restore(flags);
Nit: It would be marginally better to do the local_irq_restore() first, and then
output the error message so that interrupts are not disabled any longer than
needed.
> + return hv_result_to_errno(status);
> + }
> + memcpy(property_value, &output->property_value, property_value_sz);
> +
> + local_irq_restore(flags);
> +
> + return 0;
> +}
> +
> int
> hv_call_clear_virtual_interrupt(u64 partition_id)
> {
> diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
> index 1be7f6a02304..ff4325fb623a 100644
> --- a/include/hyperv/hvgdk_mini.h
> +++ b/include/hyperv/hvgdk_mini.h
> @@ -490,6 +490,7 @@ union hv_vp_assist_msr_contents { /*
> HV_REGISTER_VP_ASSIST_PAGE */
> #define HVCALL_GET_VP_STATE 0x00e3
> #define HVCALL_SET_VP_STATE 0x00e4
> #define HVCALL_GET_VP_CPUID_VALUES 0x00f4
> +#define HVCALL_GET_PARTITION_PROPERTY_EX 0x0101
> #define HVCALL_MMIO_READ 0x0106
> #define HVCALL_MMIO_WRITE 0x0107
>
> diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
> index b4067ada02cf..416c0d45b793 100644
> --- a/include/hyperv/hvhdk.h
> +++ b/include/hyperv/hvhdk.h
> @@ -376,6 +376,46 @@ struct hv_input_set_partition_property {
> u64 property_value;
> } __packed;
>
> +union hv_partition_property_arg {
> + u64 as_uint64;
> + struct {
> + union {
> + u32 arg;
> + u32 vp_index;
> + };
> + u16 reserved0;
> + u8 reserved1;
> + u8 object_type;
> + } __packed;
> +};
> +
> +struct hv_input_get_partition_property_ex {
> + u64 partition_id;
> + u32 property_code; /* enum hv_partition_property_code */
> + u32 padding;
> + union {
> + union hv_partition_property_arg arg_data;
> + u64 arg;
This union, and the "u64 arg" member, seems redundant since
union hv_partition_property_arg already has a member "as_uint64".
Maybe this is just being copied from the Windows versions of these
structures, in which case I realize there are constraints on what you
want to change or fix, and you can ignore my comment.
> + };
> +} __packed;
> +
> +/*
> + * NOTE: Should use hv_input_set_partition_property_ex_header to compute this
> + * size, but hv_input_get_partition_property_ex is identical so it suffices
> + */
> +#define HV_PARTITION_PROPERTY_EX_MAX_VAR_SIZE \
> + (HV_HYP_PAGE_SIZE - sizeof(struct hv_input_get_partition_property_ex))
> +
> +union hv_partition_property_ex {
> + u8 buffer[HV_PARTITION_PROPERTY_EX_MAX_VAR_SIZE];
It's unclear what this "buffer" field is trying to do, and particularly its size.
The comment above references hv_input_set_partition_property_ex_header,
which doesn't exist in the Linux code. And why is the max size of the output
buffer reduced by the size of the header of the input to "set partition property"?
> + struct hv_partition_property_vmm_capabilities vmm_capabilities;
> + /* More fields to be filled in when needed */
> +};
> +
> +struct hv_output_get_partition_property_ex {
> + union hv_partition_property_ex property_value;
> +} __packed;
> +
> enum hv_vp_state_page_type {
> HV_VP_STATE_PAGE_REGISTERS = 0,
> HV_VP_STATE_PAGE_INTERCEPT_MESSAGE = 1,
> diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
> index 858f6a3925b3..bf2ce27dfcc5 100644
> --- a/include/hyperv/hvhdk_mini.h
> +++ b/include/hyperv/hvhdk_mini.h
> @@ -96,8 +96,34 @@ enum hv_partition_property_code {
> HV_PARTITION_PROPERTY_XSAVE_STATES = 0x00060007,
> HV_PARTITION_PROPERTY_MAX_XSAVE_DATA_SIZE = 0x00060008,
> HV_PARTITION_PROPERTY_PROCESSOR_CLOCK_FREQUENCY = 0x00060009,
> +
> + /* Extended properties with larger property values */
> + HV_PARTITION_PROPERTY_VMM_CAPABILITIES = 0x00090007,
> };
>
> +#define HV_PARTITION_VMM_CAPABILITIES_BANK_COUNT 1
> +#define HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT 59
> +
> +struct hv_partition_property_vmm_capabilities {
> + u16 bank_count;
> + u16 reserved[3];
> + union {
> + u64 as_uint64[HV_PARTITION_VMM_CAPABILITIES_BANK_COUNT];
> + struct {
> + u64 map_gpa_preserve_adjustable: 1;
> + u64 vmm_can_provide_overlay_gpfn: 1;
> + u64 vp_affinity_property: 1;
> +#if IS_ENABLED(CONFIG_ARM64)
> + u64 vmm_can_provide_gic_overlay_locations: 1;
> +#else
> + u64 reservedbit3: 1;
> +#endif
> + u64 assignable_synthetic_proc_features: 1;
> + u64 reserved0: HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT;
> + } __packed;
> + };
> +} __packed;
> +
> enum hv_snp_status {
> HV_SNP_STATUS_NONE = 0,
> HV_SNP_STATUS_AVAILABLE = 1,
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH v2 4/4] Drivers: hv: Add support for movable memory regions
From: Stanislav Kinsburskii @ 2025-09-29 16:03 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <175916156212.55038.16727147489322393965.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Introduce support for movable memory regions in the Hyper-V root partition
driver, thus improving memory management flexibility and preparing the
driver for advanced use cases such as dynamic memory remapping.
Integrate mmu_interval_notifier for movable regions, implement functions to
handle HMM faults and memory invalidation, and update memory region mapping
logic to support movable regions.
While MMU notifiers are commonly used in virtualization drivers, this
implementation leverages HMM (Heterogeneous Memory Management) for its
tailored functionality. HMM provides a ready-made framework for mirroring,
invalidation, and fault handling, avoiding the need to reimplement these
mechanisms for a single callback. Although MMU notifiers are more generic,
using HMM reduces boilerplate and ensures maintainability by utilizing a
mechanism specifically designed for such use cases.
Signed-off-by: Anirudh Rayabharam <anrayabh@linux.microsoft.com>
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/Kconfig | 1
drivers/hv/mshv_root.h | 8 +
drivers/hv/mshv_root_main.c | 326 ++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 325 insertions(+), 10 deletions(-)
diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
index e24f6299c3760..9d24a8c8c52e3 100644
--- a/drivers/hv/Kconfig
+++ b/drivers/hv/Kconfig
@@ -68,6 +68,7 @@ config MSHV_ROOT
depends on PAGE_SIZE_4KB
select EVENTFD
select VIRT_XFER_TO_GUEST_WORK
+ select HMM_MIRROR
default n
help
Select this option to enable support for booting and running as root
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 97e64d5341b6e..13367c84497c0 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -15,6 +15,7 @@
#include <linux/hashtable.h>
#include <linux/dev_printk.h>
#include <linux/build_bug.h>
+#include <linux/mmu_notifier.h>
#include <uapi/linux/mshv.h>
/*
@@ -81,9 +82,14 @@ struct mshv_mem_region {
struct {
u64 large_pages: 1; /* 2MiB */
u64 range_pinned: 1;
- u64 reserved: 62;
+ u64 is_ram : 1; /* mem region can be ram or mmio */
+ u64 reserved: 61;
} flags;
struct mshv_partition *partition;
+#if defined(CONFIG_MMU_NOTIFIER)
+ struct mmu_interval_notifier mni;
+ struct mutex mutex; /* protects region pages remapping */
+#endif
struct page *pages[];
};
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index e3f55bddd933e..2d9b077d7a209 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -29,11 +29,14 @@
#include <linux/crash_dump.h>
#include <linux/panic_notifier.h>
#include <linux/vmalloc.h>
+#include <linux/hmm.h>
#include "mshv_eventfd.h"
#include "mshv.h"
#include "mshv_root.h"
+#define MSHV_MAP_FAULT_IN_PAGES HPAGE_PMD_NR
+
MODULE_AUTHOR("Microsoft");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Microsoft Hyper-V root partition VMM interface /dev/mshv");
@@ -74,6 +77,11 @@ static int mshv_vp_mmap(struct file *file, struct vm_area_struct *vma);
static vm_fault_t mshv_vp_fault(struct vm_fault *vmf);
static int mshv_init_async_handler(struct mshv_partition *partition);
static void mshv_async_hvcall_handler(void *data, u64 *status);
+static struct mshv_mem_region
+ *mshv_partition_region_by_gfn(struct mshv_partition *pt, u64 gfn);
+static int mshv_region_remap_pages(struct mshv_mem_region *region,
+ u32 map_flags, u64 page_offset,
+ u64 page_count);
static const union hv_input_vtl input_vtl_zero;
static const union hv_input_vtl input_vtl_normal = {
@@ -600,14 +608,197 @@ static long mshv_run_vp_with_root_scheduler(struct mshv_vp *vp)
static_assert(sizeof(struct hv_message) <= MSHV_RUN_VP_BUF_SZ,
"sizeof(struct hv_message) must not exceed MSHV_RUN_VP_BUF_SZ");
+#ifdef CONFIG_X86_64
+
+#if defined(CONFIG_MMU_NOTIFIER)
+/**
+ * mshv_region_hmm_fault_and_lock - Handle HMM faults and lock the memory region
+ * @region: Pointer to the memory region structure
+ * @range: Pointer to the HMM range structure
+ *
+ * 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.
+ *
+ * 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)
+{
+ int ret;
+
+ range->notifier_seq = mmu_interval_read_begin(range->notifier);
+ mmap_read_lock(region->mni.mm);
+ ret = hmm_range_fault(range);
+ mmap_read_unlock(region->mni.mm);
+ if (ret)
+ return ret;
+
+ mutex_lock(®ion->mutex);
+
+ if (mmu_interval_read_retry(range->notifier, range->notifier_seq)) {
+ mutex_unlock(®ion->mutex);
+ cond_resched();
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * 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.
+ *
+ * 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.
+ *
+ * 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)
+{
+ struct hmm_range range = {
+ .notifier = ®ion->mni,
+ .default_flags = HMM_PFN_REQ_FAULT | HMM_PFN_REQ_WRITE,
+ };
+ unsigned long *pfns;
+ int ret;
+ u64 i;
+
+ pfns = kmalloc_array(page_count, sizeof(unsigned long), 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;
+
+ do {
+ ret = mshv_region_hmm_fault_and_lock(region, &range);
+ } while (ret == -EBUSY);
+
+ if (ret)
+ goto out;
+
+ for (i = 0; i < page_count; i++)
+ region->pages[page_offset + i] = hmm_pfn_to_page(pfns[i]);
+
+ if (PageHuge(region->pages[page_offset]))
+ region->flags.large_pages = true;
+
+ ret = mshv_region_remap_pages(region, region->hv_map_flags,
+ page_offset, page_count);
+
+ mutex_unlock(®ion->mutex);
+out:
+ kfree(pfns);
+ return ret;
+}
+#else /* CONFIG_MMU_NOTIFIER */
+static int mshv_region_range_fault(struct mshv_mem_region *region,
+ u64 page_offset, u64 page_count)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_MMU_NOTIFIER */
+
+static bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn)
+{
+ u64 page_offset, page_count;
+ int ret;
+
+ if (WARN_ON_ONCE(region->flags.range_pinned))
+ return false;
+
+ /* 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);
+
+ /* Map more pages than requested to reduce the number of faults. */
+ page_count = min(region->nr_pages - page_offset,
+ MSHV_MAP_FAULT_IN_PAGES);
+
+ ret = mshv_region_range_fault(region, page_offset, page_count);
+
+ WARN_ONCE(ret,
+ "p%llu: GPA intercept failed: region %#llx-%#llx, gfn %#llx, page_offset %llu, page_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);
+
+ return !ret;
+}
+
+/**
+ * mshv_handle_gpa_intercept - Handle GPA (Guest Physical Address) intercepts.
+ * @vp: Pointer to the virtual processor structure.
+ *
+ * This function processes GPA intercepts by identifying the memory region
+ * corresponding to the intercepted GPA, aligning the page offset, and
+ * mapping the required pages. It ensures that the region is valid and
+ * handles faults efficiently by mapping multiple pages at once.
+ *
+ * Return: true if the intercept was handled successfully, false otherwise.
+ */
+static bool mshv_handle_gpa_intercept(struct mshv_vp *vp)
+{
+ struct mshv_partition *p = vp->vp_partition;
+ struct mshv_mem_region *region;
+ struct hv_x64_memory_intercept_message *msg;
+ u64 gfn;
+
+ msg = (struct hv_x64_memory_intercept_message *)
+ vp->vp_intercept_msg_page->u.payload;
+
+ gfn = HVPFN_DOWN(msg->guest_physical_address);
+
+ region = mshv_partition_region_by_gfn(p, gfn);
+ if (!region)
+ return false;
+
+ if (WARN_ON_ONCE(!region->flags.is_ram))
+ return false;
+
+ if (WARN_ON_ONCE(region->flags.range_pinned))
+ return false;
+
+ return mshv_region_handle_gfn_fault(region, gfn);
+}
+
+#else /* CONFIG_X86_64 */
+
+static bool mshv_handle_gpa_intercept(struct mshv_vp *vp) { return false; }
+
+#endif /* CONFIG_X86_64 */
+
+static bool mshv_vp_handle_intercept(struct mshv_vp *vp)
+{
+ switch (vp->vp_intercept_msg_page->header.message_type) {
+ case HVMSG_GPA_INTERCEPT:
+ return mshv_handle_gpa_intercept(vp);
+ }
+ return false;
+}
+
static long mshv_vp_ioctl_run_vp(struct mshv_vp *vp, void __user *ret_msg)
{
long rc;
- if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
- rc = mshv_run_vp_with_root_scheduler(vp);
- else
- rc = mshv_run_vp_with_hyp_scheduler(vp);
+ do {
+ if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
+ rc = mshv_run_vp_with_root_scheduler(vp);
+ else
+ rc = mshv_run_vp_with_hyp_scheduler(vp);
+ } while (rc == 0 && mshv_vp_handle_intercept(vp));
if (rc)
return rc;
@@ -1205,6 +1396,108 @@ mshv_partition_region_by_uaddr(struct mshv_partition *partition, u64 uaddr)
return NULL;
}
+#if defined(CONFIG_MMU_NOTIFIER)
+static void mshv_region_movable_fini(struct mshv_mem_region *region)
+{
+ if (region->flags.range_pinned)
+ return;
+
+ mmu_interval_notifier_remove(®ion->mni);
+}
+
+/**
+ * mshv_region_interval_invalidate - Invalidate a range of memory region
+ * @mni: Pointer to the mmu_interval_notifier structure
+ * @range: Pointer to the mmu_notifier_range structure
+ * @cur_seq: Current sequence number for the interval notifier
+ *
+ * This function invalidates a memory region by remapping its pages with
+ * no access permissions. It locks the region's mutex to ensure thread safety
+ * and updates the sequence number for the interval notifier. If the range
+ * is blockable, it uses a blocking lock; otherwise, it attempts a non-blocking
+ * lock and returns false if unsuccessful.
+ *
+ * Return: true if the region was successfully invalidated, false otherwise.
+ */
+static bool
+mshv_region_interval_invalidate(struct mmu_interval_notifier *mni,
+ const struct mmu_notifier_range *range,
+ unsigned long cur_seq)
+{
+ struct mshv_mem_region *region = container_of(mni,
+ struct mshv_mem_region,
+ mni);
+ u64 page_offset, page_count;
+ unsigned long mstart, mend;
+ int ret;
+
+ if (!mmget_not_zero(mni->mm))
+ return true;
+
+ if (mmu_notifier_range_blockable(range)) {
+ mutex_lock(®ion->mutex);
+ } else if (!mutex_trylock(®ion->mutex)) {
+ mmput(mni->mm);
+ return false;
+ }
+
+ mmu_interval_set_seq(mni, cur_seq);
+
+ mstart = max(range->start, region->start_uaddr);
+ mend = min(range->end, region->start_uaddr +
+ (region->nr_pages << HV_HYP_PAGE_SHIFT));
+
+ page_offset = HVPFN_DOWN(mstart - region->start_uaddr);
+ page_count = HVPFN_DOWN(mend - mstart);
+
+ ret = mshv_region_remap_pages(region, HV_MAP_GPA_NO_ACCESS,
+ page_offset, page_count);
+
+ 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),
+ range->start, range->end, range->event,
+ page_offset, page_offset + page_count - 1, (u64)range->mm, ret);
+
+ mshv_region_invalidate_pages(region, page_offset, page_count);
+
+ mutex_unlock(®ion->mutex);
+ mmput(mni->mm);
+
+ return true;
+}
+
+static const struct mmu_interval_notifier_ops mshv_region_mni_ops = {
+ .invalidate = mshv_region_interval_invalidate,
+};
+
+static bool mshv_region_movable_init(struct mshv_mem_region *region)
+{
+ int ret;
+
+ ret = mmu_interval_notifier_insert(®ion->mni, current->mm,
+ region->start_uaddr,
+ region->nr_pages << HV_HYP_PAGE_SHIFT,
+ &mshv_region_mni_ops);
+ if (ret)
+ return false;
+
+ mutex_init(®ion->mutex);
+
+ return true;
+}
+#else
+static inline void mshv_region_movable_fini(struct mshv_mem_region *region)
+{
+}
+
+static inline bool mshv_region_movable_init(struct mshv_mem_region *region)
+{
+ return false;
+}
+#endif
+
/*
* NB: caller checks and makes sure mem->size is page aligned
* Returns: 0 with regionpp updated on success, or -errno
@@ -1237,9 +1530,14 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
if (mem->flags & BIT(MSHV_SET_MEM_BIT_EXECUTABLE))
region->hv_map_flags |= HV_MAP_GPA_EXECUTABLE;
- /* Note: large_pages flag populated when we pin the pages */
- if (!is_mmio)
- region->flags.range_pinned = true;
+ /* Note: large_pages flag populated when pages are allocated. */
+ if (!is_mmio) {
+ region->flags.is_ram = true;
+
+ if (mshv_partition_encrypted(partition) ||
+ !mshv_region_movable_init(region))
+ region->flags.range_pinned = true;
+ }
region->partition = partition;
@@ -1359,9 +1657,16 @@ mshv_map_user_memory(struct mshv_partition *partition,
if (is_mmio)
ret = hv_call_map_mmio_pages(partition->pt_id, mem.guest_pfn,
mmio_pfn, HVPFN_DOWN(mem.size));
- else
+ else if (region->flags.range_pinned)
ret = mshv_prepare_pinned_region(region);
-
+ else
+ /*
+ * For non-pinned regions, remap with no access to let the
+ * hypervisor track dirty pages, enabling pre-copy live
+ * migration.
+ */
+ ret = mshv_region_remap_pages(region, HV_MAP_GPA_NO_ACCESS,
+ 0, region->nr_pages);
if (ret)
goto errout;
@@ -1384,6 +1689,9 @@ static void mshv_partition_destroy_region(struct mshv_mem_region *region)
hlist_del(®ion->hnode);
+ if (region->flags.is_ram)
+ mshv_region_movable_fini(region);
+
if (mshv_partition_encrypted(partition)) {
ret = mshv_partition_region_share(region);
if (ret) {
^ permalink raw reply related
* [PATCH v2 3/4] Drivers: hv: Batch GPA unmap operations to improve large region performance
From: Stanislav Kinsburskii @ 2025-09-29 16:03 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <175916156212.55038.16727147489322393965.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Reduce overhead when unmapping large memory regions by batching GPA unmap
operations in 2MB-aligned chunks.
Use a dedicated constant for batch size to improve code clarity and
maintainability.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root.h | 2 ++
drivers/hv/mshv_root_hv_call.c | 2 +-
drivers/hv/mshv_root_main.c | 15 ++++++++++++---
3 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index e3931b0f12693..97e64d5341b6e 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -32,6 +32,8 @@ static_assert(HV_HYP_PAGE_SIZE == MSHV_HV_PAGE_SIZE);
#define MSHV_PIN_PAGES_BATCH_SIZE (0x10000000ULL / HV_HYP_PAGE_SIZE)
+#define MSHV_MAX_UNMAP_GPA_PAGES 512
+
struct mshv_vp {
u32 vp_index;
struct mshv_partition *vp_partition;
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index c9c274f29c3c6..0696024ccfe31 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -17,7 +17,7 @@
/* Determined empirically */
#define HV_INIT_PARTITION_DEPOSIT_PAGES 208
#define HV_MAP_GPA_DEPOSIT_PAGES 256
-#define HV_UMAP_GPA_PAGES 512
+#define HV_UMAP_GPA_PAGES MSHV_MAX_UNMAP_GPA_PAGES
#define HV_PAGE_COUNT_2M_ALIGNED(pg_count) (!((pg_count) & (0x200 - 1)))
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index d8143bc8dbcfb..e3f55bddd933e 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1378,6 +1378,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
static void mshv_partition_destroy_region(struct mshv_mem_region *region)
{
struct mshv_partition *partition = region->partition;
+ u64 page_offset;
u32 unmap_flags = 0;
int ret;
@@ -1396,9 +1397,17 @@ static void mshv_partition_destroy_region(struct mshv_mem_region *region)
if (region->flags.large_pages)
unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
- /* ignore unmap failures and continue as process may be exiting */
- hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
- region->nr_pages, unmap_flags);
+ for (page_offset = 0; page_offset < region->nr_pages; page_offset++) {
+ if (!region->pages[page_offset])
+ continue;
+
+ hv_call_unmap_gpa_pages(partition->pt_id,
+ ALIGN(region->start_gfn + page_offset,
+ MSHV_MAX_UNMAP_GPA_PAGES),
+ MSHV_MAX_UNMAP_GPA_PAGES, unmap_flags);
+
+ page_offset += MSHV_MAX_UNMAP_GPA_PAGES - 1;
+ }
mshv_region_invalidate(region);
^ permalink raw reply related
* [PATCH v2 2/4] Drivers: hv: Centralize guest memory region destruction
From: Stanislav Kinsburskii @ 2025-09-29 16:03 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <175916156212.55038.16727147489322393965.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Centralize guest memory region destruction to prevent resource leaks and
inconsistent cleanup across unmap and partition destruction paths.
Unify region removal, encrypted partition access recovery, and region
invalidation to improve maintainability and reliability. Reduce code
duplication and make future updates less error-prone by encapsulating
cleanup logic in a single helper.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root_main.c | 65 ++++++++++++++++++++++---------------------
1 file changed, 34 insertions(+), 31 deletions(-)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 8cb309dce6258..d8143bc8dbcfb 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1375,13 +1375,42 @@ mshv_map_user_memory(struct mshv_partition *partition,
return ret;
}
+static void mshv_partition_destroy_region(struct mshv_mem_region *region)
+{
+ struct mshv_partition *partition = region->partition;
+ u32 unmap_flags = 0;
+ int ret;
+
+ hlist_del(®ion->hnode);
+
+ if (mshv_partition_encrypted(partition)) {
+ ret = mshv_partition_region_share(region);
+ if (ret) {
+ pt_err(partition,
+ "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
+ ret);
+ return;
+ }
+ }
+
+ if (region->flags.large_pages)
+ unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
+
+ /* ignore unmap failures and continue as process may be exiting */
+ hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
+ region->nr_pages, unmap_flags);
+
+ mshv_region_invalidate(region);
+
+ vfree(region);
+}
+
/* Called for unmapping both the guest ram and the mmio space */
static long
mshv_unmap_user_memory(struct mshv_partition *partition,
struct mshv_user_mem_region mem)
{
struct mshv_mem_region *region;
- u32 unmap_flags = 0;
if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
return -EINVAL;
@@ -1396,18 +1425,8 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
region->nr_pages != HVPFN_DOWN(mem.size))
return -EINVAL;
- hlist_del(®ion->hnode);
+ mshv_partition_destroy_region(region);
- if (region->flags.large_pages)
- unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
-
- /* ignore unmap failures and continue as process may be exiting */
- hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
- region->nr_pages, unmap_flags);
-
- mshv_region_invalidate(region);
-
- vfree(region);
return 0;
}
@@ -1743,8 +1762,8 @@ static void destroy_partition(struct mshv_partition *partition)
{
struct mshv_vp *vp;
struct mshv_mem_region *region;
- int i, ret;
struct hlist_node *n;
+ int i;
if (refcount_read(&partition->pt_ref_count)) {
pt_err(partition,
@@ -1804,25 +1823,9 @@ static void destroy_partition(struct mshv_partition *partition)
remove_partition(partition);
- /* Remove regions, regain access to the memory and unpin the pages */
hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
- hnode) {
- hlist_del(®ion->hnode);
-
- if (mshv_partition_encrypted(partition)) {
- ret = mshv_partition_region_share(region);
- if (ret) {
- pt_err(partition,
- "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
- ret);
- return;
- }
- }
-
- mshv_region_invalidate(region);
-
- vfree(region);
- }
+ hnode)
+ mshv_partition_destroy_region(region);
/* Withdraw and free all pages we deposited */
hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
^ permalink raw reply related
* [PATCH v2 1/4] Drivers: hv: Refactor and rename memory region handling functions
From: Stanislav Kinsburskii @ 2025-09-29 16:03 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <175916156212.55038.16727147489322393965.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
Simplify and unify memory region management to improve code clarity and
reliability. Consolidate pinning and invalidation logic, adopt consistent
naming, and remove redundant checks to reduce complexity.
Enhance documentation and update call sites for maintainability.
Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
drivers/hv/mshv_root_main.c | 78 +++++++++++++++++++------------------------
1 file changed, 35 insertions(+), 43 deletions(-)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index a1c8c3bc79bf1..8cb309dce6258 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1120,8 +1120,8 @@ mshv_region_map(struct mshv_mem_region *region)
}
static void
-mshv_region_evict_pages(struct mshv_mem_region *region,
- u64 page_offset, u64 page_count)
+mshv_region_invalidate_pages(struct mshv_mem_region *region,
+ u64 page_offset, u64 page_count)
{
if (region->flags.range_pinned)
unpin_user_pages(region->pages + page_offset, page_count);
@@ -1131,29 +1131,24 @@ mshv_region_evict_pages(struct mshv_mem_region *region,
}
static void
-mshv_region_evict(struct mshv_mem_region *region)
+mshv_region_invalidate(struct mshv_mem_region *region)
{
- mshv_region_evict_pages(region, 0, region->nr_pages);
+ mshv_region_invalidate_pages(region, 0, region->nr_pages);
}
static int
-mshv_region_populate_pages(struct mshv_mem_region *region,
- u64 page_offset, u64 page_count)
+mshv_region_pin(struct mshv_mem_region *region)
{
u64 done_count, nr_pages;
struct page **pages;
__u64 userspace_addr;
int ret;
- if (page_offset + page_count > region->nr_pages)
- return -EINVAL;
-
- for (done_count = 0; done_count < page_count; done_count += ret) {
- pages = region->pages + page_offset + done_count;
+ for (done_count = 0; done_count < region->nr_pages; done_count += ret) {
+ pages = region->pages + done_count;
userspace_addr = region->start_uaddr +
- (page_offset + done_count) *
- HV_HYP_PAGE_SIZE;
- nr_pages = min(page_count - done_count,
+ done_count * HV_HYP_PAGE_SIZE;
+ nr_pages = min(region->nr_pages - done_count,
MSHV_PIN_PAGES_BATCH_SIZE);
/*
@@ -1164,34 +1159,23 @@ mshv_region_populate_pages(struct mshv_mem_region *region,
* with the FOLL_LONGTERM flag does a large temporary
* allocation of contiguous memory.
*/
- if (region->flags.range_pinned)
- ret = pin_user_pages_fast(userspace_addr,
- nr_pages,
- FOLL_WRITE | FOLL_LONGTERM,
- pages);
- else
- ret = -EOPNOTSUPP;
-
+ ret = pin_user_pages_fast(userspace_addr, nr_pages,
+ FOLL_WRITE | FOLL_LONGTERM,
+ pages);
if (ret < 0)
goto release_pages;
}
- if (PageHuge(region->pages[page_offset]))
+ if (PageHuge(region->pages[0]))
region->flags.large_pages = true;
return 0;
release_pages:
- mshv_region_evict_pages(region, page_offset, done_count);
+ mshv_region_invalidate_pages(region, 0, done_count);
return ret;
}
-static int
-mshv_region_populate(struct mshv_mem_region *region)
-{
- return mshv_region_populate_pages(region, 0, region->nr_pages);
-}
-
static struct mshv_mem_region *
mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
{
@@ -1264,17 +1248,25 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
return 0;
}
-/*
- * Map guest ram. if snp, make sure to release that from the host first
- * Side Effects: In case of failure, pages are unpinned when feasible.
+/**
+ * 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_partition_mem_region_map(struct mshv_mem_region *region)
+static int mshv_prepare_pinned_region(struct mshv_mem_region *region)
{
struct mshv_partition *partition = region->partition;
int ret;
- ret = mshv_region_populate(region);
+ ret = mshv_region_pin(region);
if (ret) {
pt_err(partition, "Failed to populate memory region: %d\n",
ret);
@@ -1294,7 +1286,7 @@ mshv_partition_mem_region_map(struct mshv_mem_region *region)
pt_err(partition,
"Failed to unshare memory region (guest_pfn: %llu): %d\n",
region->start_gfn, ret);
- goto evict_region;
+ goto invalidate_region;
}
}
@@ -1304,7 +1296,7 @@ mshv_partition_mem_region_map(struct mshv_mem_region *region)
shrc = mshv_partition_region_share(region);
if (!shrc)
- goto evict_region;
+ goto invalidate_region;
pt_err(partition,
"Failed to share memory region (guest_pfn: %llu): %d\n",
@@ -1318,8 +1310,8 @@ mshv_partition_mem_region_map(struct mshv_mem_region *region)
return 0;
-evict_region:
- mshv_region_evict(region);
+invalidate_region:
+ mshv_region_invalidate(region);
err_out:
return ret;
}
@@ -1368,7 +1360,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
ret = hv_call_map_mmio_pages(partition->pt_id, mem.guest_pfn,
mmio_pfn, HVPFN_DOWN(mem.size));
else
- ret = mshv_partition_mem_region_map(region);
+ ret = mshv_prepare_pinned_region(region);
if (ret)
goto errout;
@@ -1413,7 +1405,7 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
region->nr_pages, unmap_flags);
- mshv_region_evict(region);
+ mshv_region_invalidate(region);
vfree(region);
return 0;
@@ -1827,7 +1819,7 @@ static void destroy_partition(struct mshv_partition *partition)
}
}
- mshv_region_evict(region);
+ mshv_region_invalidate(region);
vfree(region);
}
^ permalink raw reply related
* [PATCH v2 0/4] Introduce movable pages for Hyper-V guests
From: Stanislav Kinsburskii @ 2025-09-29 16:02 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
From the start, the root-partition driver allocates, pins, and maps all
guest memory into the hypervisor at guest creation. This is simple: Linux
cannot move the pages, so the guest’s view in Linux and in Microsoft
Hypervisor never diverges.
However, this approach has major drawbacks:
- NUMA: affinity can’t be changed at runtime, so you can’t migrate guest memory closer to the CPUs running it → performance hit.
- Memory management: unused guest memory can’t be swapped out, compacted, or merged.
- Provisioning time: upfront allocation/pinning slows guest create/destroy.
- Overcommit: no memory overcommit on hosts with pinned-guest memory.
This series adds movable memory pages for Hyper-V child partitions. Guest
pages are no longer allocated upfront; they’re allocated and mapped into
the hypervisor on demand (i.e., when the guest touches a GFN that isn’t yet
backed by a host PFN).
When a page is moved, Linux no longer holds it and it is unmapped from the hypervisor.
As a result, Hyper-V guests behave like regular Linux processes, enabling standard Linux memory features to apply to guests.
Exceptions (still pinned):
1. Encrypted guests (explicit).
2 Guests with passthrough devices (implicitly pinned by the VFIO framework).
v2:
- Split unmap batching into a separate patch.
- Fixed commit messages from v1 review.
- Renamed a few functions for clarity.
---
Stanislav Kinsburskii (4):
Drivers: hv: Refactor and rename memory region handling functions
Drivers: hv: Centralize guest memory region destruction
Drivers: hv: Batch GPA unmap operations to improve large region performance
Drivers: hv: Add support for movable memory regions
drivers/hv/Kconfig | 1
drivers/hv/mshv_root.h | 10 +
drivers/hv/mshv_root_hv_call.c | 2
drivers/hv/mshv_root_main.c | 472 +++++++++++++++++++++++++++++++++-------
4 files changed, 403 insertions(+), 82 deletions(-)
^ permalink raw reply
* Re: [PATCH v4 4/5] mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
From: Tianyu Lan @ 2025-09-29 5:31 UTC (permalink / raw)
To: Nuno Das Neves
Cc: linux-hyperv, linux-kernel, prapal, easwar.hariharan, tiala,
anirudh, paekkaladevi, skinsburskii, kys, haiyangz, wei.liu,
decui, Jinank Jain
In-Reply-To: <1758903795-18636-5-git-send-email-nunodasneves@linux.microsoft.com>
On Sat, Sep 27, 2025 at 12:48 AM Nuno Das Neves
<nunodasneves@linux.microsoft.com> wrote:
>
> From: Jinank Jain <jinankjain@linux.microsoft.com>
>
> Introduce mshv_use_overlay_gpfn() to check if a page needs to be
> allocated and passed to the hypervisor to map VP state pages. This is
> only needed on L1VH, and only on some (newer) versions of the
> hypervisor, hence the need to check vmm_capabilities.
>
> Introduce functions hv_map/unmap_vp_state_page() to handle the
> allocation and freeing.
>
> Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
> Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> Reviewed-by: Anirudh Rayabharam <anirudh@anirudhrb.com>
> ---
Reviewed-by: Tianyu Lan <tiala@microsoft.com>
> drivers/hv/mshv_root.h | 11 ++---
> drivers/hv/mshv_root_hv_call.c | 61 ++++++++++++++++++++++++---
> drivers/hv/mshv_root_main.c | 76 +++++++++++++++++-----------------
> 3 files changed, 98 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index 0cb1e2589fe1..dbe2d1d0b22f 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -279,11 +279,12 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
> /* Choose between pages and bytes */
> struct hv_vp_state_data state_data, u64 page_count,
> struct page **pages, u32 num_bytes, u8 *bytes);
> -int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl,
> - struct page **state_page);
> -int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl);
> +int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl,
> + struct page **state_page);
> +int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + struct page *state_page,
> + union hv_input_vtl input_vtl);
> int hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,
> u64 connection_partition_id, struct hv_port_info *port_info,
> u8 port_vtl, u8 min_connection_vtl, int node);
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index 3fd3cce23f69..98c6278ff151 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -526,9 +526,9 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
> return ret;
> }
>
> -int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl,
> - struct page **state_page)
> +static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl,
> + struct page **state_page)
> {
> struct hv_input_map_vp_state_page *input;
> struct hv_output_map_vp_state_page *output;
> @@ -547,7 +547,14 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> input->type = type;
> input->input_vtl = input_vtl;
>
> - status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input, output);
> + if (*state_page) {
> + input->flags.map_location_provided = 1;
> + input->requested_map_location =
> + page_to_pfn(*state_page);
> + }
> +
> + status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input,
> + output);
>
> if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
> if (hv_result_success(status))
> @@ -565,8 +572,39 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> return ret;
> }
>
> -int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> - union hv_input_vtl input_vtl)
> +static bool mshv_use_overlay_gpfn(void)
> +{
> + return hv_l1vh_partition() &&
> + mshv_root.vmm_caps.vmm_can_provide_overlay_gpfn;
> +}
> +
> +int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl,
> + struct page **state_page)
> +{
> + int ret = 0;
> + struct page *allocated_page = NULL;
> +
> + if (mshv_use_overlay_gpfn()) {
> + allocated_page = alloc_page(GFP_KERNEL);
> + if (!allocated_page)
> + return -ENOMEM;
> + *state_page = allocated_page;
> + } else {
> + *state_page = NULL;
> + }
> +
> + ret = hv_call_map_vp_state_page(partition_id, vp_index, type, input_vtl,
> + state_page);
> +
> + if (ret && allocated_page)
> + __free_page(allocated_page);
> +
> + return ret;
> +}
> +
> +static int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + union hv_input_vtl input_vtl)
> {
> unsigned long flags;
> u64 status;
> @@ -590,6 +628,17 @@ int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> return hv_result_to_errno(status);
> }
>
> +int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> + struct page *state_page, union hv_input_vtl input_vtl)
> +{
> + int ret = hv_call_unmap_vp_state_page(partition_id, vp_index, type, input_vtl);
> +
> + if (mshv_use_overlay_gpfn() && state_page)
> + __free_page(state_page);
> +
> + return ret;
> +}
> +
> 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_main.c b/drivers/hv/mshv_root_main.c
> index e199770ecdfa..2d0ad17acde6 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -890,7 +890,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> {
> struct mshv_create_vp args;
> struct mshv_vp *vp;
> - struct page *intercept_message_page, *register_page, *ghcb_page;
> + struct page *intercept_msg_page, *register_page, *ghcb_page;
> void *stats_pages[2];
> long ret;
>
> @@ -908,28 +908,25 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> if (ret)
> return ret;
>
> - ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> - input_vtl_zero,
> - &intercept_message_page);
> + ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> + input_vtl_zero, &intercept_msg_page);
> if (ret)
> goto destroy_vp;
>
> if (!mshv_partition_encrypted(partition)) {
> - ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_REGISTERS,
> - input_vtl_zero,
> - ®ister_page);
> + ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_REGISTERS,
> + input_vtl_zero, ®ister_page);
> if (ret)
> goto unmap_intercept_message_page;
> }
>
> if (mshv_partition_encrypted(partition) &&
> is_ghcb_mapping_available()) {
> - ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_GHCB,
> - input_vtl_normal,
> - &ghcb_page);
> + ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_GHCB,
> + input_vtl_normal, &ghcb_page);
> if (ret)
> goto unmap_register_page;
> }
> @@ -960,7 +957,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> atomic64_set(&vp->run.vp_signaled_count, 0);
>
> vp->vp_index = args.vp_index;
> - vp->vp_intercept_msg_page = page_to_virt(intercept_message_page);
> + vp->vp_intercept_msg_page = page_to_virt(intercept_msg_page);
> if (!mshv_partition_encrypted(partition))
> vp->vp_register_page = page_to_virt(register_page);
>
> @@ -993,21 +990,19 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
> if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
> mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
> unmap_ghcb_page:
> - if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available()) {
> - hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_GHCB,
> - input_vtl_normal);
> - }
> + if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
> + hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_GHCB, ghcb_page,
> + input_vtl_normal);
> unmap_register_page:
> - if (!mshv_partition_encrypted(partition)) {
> - hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_REGISTERS,
> - input_vtl_zero);
> - }
> + if (!mshv_partition_encrypted(partition))
> + hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_REGISTERS,
> + register_page, input_vtl_zero);
> unmap_intercept_message_page:
> - hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
> - HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> - input_vtl_zero);
> + hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
> + HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> + intercept_msg_page, input_vtl_zero);
> destroy_vp:
> hv_call_delete_vp(partition->pt_id, args.vp_index);
> return ret;
> @@ -1748,24 +1743,27 @@ static void destroy_partition(struct mshv_partition *partition)
> mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
>
> if (vp->vp_register_page) {
> - (void)hv_call_unmap_vp_state_page(partition->pt_id,
> - vp->vp_index,
> - HV_VP_STATE_PAGE_REGISTERS,
> - input_vtl_zero);
> + (void)hv_unmap_vp_state_page(partition->pt_id,
> + vp->vp_index,
> + HV_VP_STATE_PAGE_REGISTERS,
> + virt_to_page(vp->vp_register_page),
> + input_vtl_zero);
> vp->vp_register_page = NULL;
> }
>
> - (void)hv_call_unmap_vp_state_page(partition->pt_id,
> - vp->vp_index,
> - HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> - input_vtl_zero);
> + (void)hv_unmap_vp_state_page(partition->pt_id,
> + vp->vp_index,
> + HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
> + virt_to_page(vp->vp_intercept_msg_page),
> + input_vtl_zero);
> vp->vp_intercept_msg_page = NULL;
>
> if (vp->vp_ghcb_page) {
> - (void)hv_call_unmap_vp_state_page(partition->pt_id,
> - vp->vp_index,
> - HV_VP_STATE_PAGE_GHCB,
> - input_vtl_normal);
> + (void)hv_unmap_vp_state_page(partition->pt_id,
> + vp->vp_index,
> + HV_VP_STATE_PAGE_GHCB,
> + virt_to_page(vp->vp_ghcb_page),
> + input_vtl_normal);
> vp->vp_ghcb_page = NULL;
> }
>
> --
> 2.34.1
>
>
--
Thanks
Tianyu Lan
^ permalink raw reply
* Re: [PATCH v4 2/5] mshv: Add the HVCALL_GET_PARTITION_PROPERTY_EX hypercall
From: Tianyu Lan @ 2025-09-29 5:31 UTC (permalink / raw)
To: Nuno Das Neves
Cc: linux-hyperv, linux-kernel, prapal, easwar.hariharan, tiala,
anirudh, paekkaladevi, skinsburskii, kys, haiyangz, wei.liu,
decui
In-Reply-To: <1758903795-18636-3-git-send-email-nunodasneves@linux.microsoft.com>
On Sat, Sep 27, 2025 at 12:23 AM Nuno Das Neves
<nunodasneves@linux.microsoft.com> wrote:
>
> From: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
>
> This hypercall can be used to fetch extended properties of a
> partition. Extended properties are properties with values larger than
> a u64. Some of these also need additional input arguments.
>
> Add helper function for using the hypercall in the mshv_root driver.
>
> Signed-off-by: Purna Pavan Chandra Aekkaladevi <paekkaladevi@linux.microsoft.com>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> Reviewed-by: Anirudh Rayabharam <anirudh@anirudhrb.com>
> Reviewed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
> Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
> ---
Reviewed-by: Tianyu Lan <tiala@microsoft.com>
> drivers/hv/mshv_root.h | 2 ++
> drivers/hv/mshv_root_hv_call.c | 31 ++++++++++++++++++++++++++
> include/hyperv/hvgdk_mini.h | 1 +
> include/hyperv/hvhdk.h | 40 ++++++++++++++++++++++++++++++++++
> include/hyperv/hvhdk_mini.h | 26 ++++++++++++++++++++++
> 5 files changed, 100 insertions(+)
>
> diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
> index e3931b0f1269..4aeb03bea6b6 100644
> --- a/drivers/hv/mshv_root.h
> +++ b/drivers/hv/mshv_root.h
> @@ -303,6 +303,8 @@ int hv_call_unmap_stat_page(enum hv_stats_object_type type,
> int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
> u64 page_struct_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);
>
> extern struct mshv_root mshv_root;
> extern enum hv_scheduler_type hv_scheduler_type;
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index c9c274f29c3c..3fd3cce23f69 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -590,6 +590,37 @@ int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
> return hv_result_to_errno(status);
> }
>
> +int hv_call_get_partition_property_ex(u64 partition_id, u64 property_code,
> + u64 arg, void *property_value,
> + size_t property_value_sz)
> +{
> + u64 status;
> + unsigned long flags;
> + struct hv_input_get_partition_property_ex *input;
> + struct hv_output_get_partition_property_ex *output;
> +
> + local_irq_save(flags);
> + input = *this_cpu_ptr(hyperv_pcpu_input_arg);
> + output = *this_cpu_ptr(hyperv_pcpu_output_arg);
> +
> + memset(input, 0, sizeof(*input));
> + input->partition_id = partition_id;
> + input->property_code = property_code;
> + input->arg = arg;
> + status = hv_do_hypercall(HVCALL_GET_PARTITION_PROPERTY_EX, input, output);
> +
> + if (!hv_result_success(status)) {
> + hv_status_debug(status, "\n");
> + local_irq_restore(flags);
> + return hv_result_to_errno(status);
> + }
> + memcpy(property_value, &output->property_value, property_value_sz);
> +
> + local_irq_restore(flags);
> +
> + return 0;
> +}
> +
> int
> hv_call_clear_virtual_interrupt(u64 partition_id)
> {
> diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
> index 1be7f6a02304..ff4325fb623a 100644
> --- a/include/hyperv/hvgdk_mini.h
> +++ b/include/hyperv/hvgdk_mini.h
> @@ -490,6 +490,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
> #define HVCALL_GET_VP_STATE 0x00e3
> #define HVCALL_SET_VP_STATE 0x00e4
> #define HVCALL_GET_VP_CPUID_VALUES 0x00f4
> +#define HVCALL_GET_PARTITION_PROPERTY_EX 0x0101
> #define HVCALL_MMIO_READ 0x0106
> #define HVCALL_MMIO_WRITE 0x0107
>
> diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
> index b4067ada02cf..416c0d45b793 100644
> --- a/include/hyperv/hvhdk.h
> +++ b/include/hyperv/hvhdk.h
> @@ -376,6 +376,46 @@ struct hv_input_set_partition_property {
> u64 property_value;
> } __packed;
>
> +union hv_partition_property_arg {
> + u64 as_uint64;
> + struct {
> + union {
> + u32 arg;
> + u32 vp_index;
> + };
> + u16 reserved0;
> + u8 reserved1;
> + u8 object_type;
> + } __packed;
> +};
> +
> +struct hv_input_get_partition_property_ex {
> + u64 partition_id;
> + u32 property_code; /* enum hv_partition_property_code */
> + u32 padding;
> + union {
> + union hv_partition_property_arg arg_data;
> + u64 arg;
> + };
> +} __packed;
> +
> +/*
> + * NOTE: Should use hv_input_set_partition_property_ex_header to compute this
> + * size, but hv_input_get_partition_property_ex is identical so it suffices
> + */
> +#define HV_PARTITION_PROPERTY_EX_MAX_VAR_SIZE \
> + (HV_HYP_PAGE_SIZE - sizeof(struct hv_input_get_partition_property_ex))
> +
> +union hv_partition_property_ex {
> + u8 buffer[HV_PARTITION_PROPERTY_EX_MAX_VAR_SIZE];
> + struct hv_partition_property_vmm_capabilities vmm_capabilities;
> + /* More fields to be filled in when needed */
> +};
> +
> +struct hv_output_get_partition_property_ex {
> + union hv_partition_property_ex property_value;
> +} __packed;
> +
> enum hv_vp_state_page_type {
> HV_VP_STATE_PAGE_REGISTERS = 0,
> HV_VP_STATE_PAGE_INTERCEPT_MESSAGE = 1,
> diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
> index 858f6a3925b3..bf2ce27dfcc5 100644
> --- a/include/hyperv/hvhdk_mini.h
> +++ b/include/hyperv/hvhdk_mini.h
> @@ -96,8 +96,34 @@ enum hv_partition_property_code {
> HV_PARTITION_PROPERTY_XSAVE_STATES = 0x00060007,
> HV_PARTITION_PROPERTY_MAX_XSAVE_DATA_SIZE = 0x00060008,
> HV_PARTITION_PROPERTY_PROCESSOR_CLOCK_FREQUENCY = 0x00060009,
> +
> + /* Extended properties with larger property values */
> + HV_PARTITION_PROPERTY_VMM_CAPABILITIES = 0x00090007,
> };
>
> +#define HV_PARTITION_VMM_CAPABILITIES_BANK_COUNT 1
> +#define HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT 59
> +
> +struct hv_partition_property_vmm_capabilities {
> + u16 bank_count;
> + u16 reserved[3];
> + union {
> + u64 as_uint64[HV_PARTITION_VMM_CAPABILITIES_BANK_COUNT];
> + struct {
> + u64 map_gpa_preserve_adjustable: 1;
> + u64 vmm_can_provide_overlay_gpfn: 1;
> + u64 vp_affinity_property: 1;
> +#if IS_ENABLED(CONFIG_ARM64)
> + u64 vmm_can_provide_gic_overlay_locations: 1;
> +#else
> + u64 reservedbit3: 1;
> +#endif
> + u64 assignable_synthetic_proc_features: 1;
> + u64 reserved0: HV_PARTITION_VMM_CAPABILITIES_RESERVED_BITFIELD_COUNT;
> + } __packed;
> + };
> +} __packed;
> +
> enum hv_snp_status {
> HV_SNP_STATUS_NONE = 0,
> HV_SNP_STATUS_AVAILABLE = 1,
> --
> 2.34.1
>
>
--
Thanks
Tianyu Lan
^ permalink raw reply
* Re: [PATCH 0/3] Introduce movable pages for Hyper-V guests
From: Mukesh R @ 2025-09-27 2:02 UTC (permalink / raw)
To: Stanislav Kinsburskii, kys, haiyangz, wei.liu, decui
Cc: linux-hyperv, linux-kernel
In-Reply-To: <175874669044.157998.15064894246017794777.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
On 9/24/25 14:30, Stanislav Kinsburskii wrote:
>>From the start, the root-partition driver allocates, pins, and maps all
> guest memory into the hypervisor at guest creation. This is simple: Linux
> cannot move the pages, so the guest?s view in Linux and in Microsoft
> Hypervisor never diverges.
>
> However, this approach has major drawbacks:
> - NUMA: affinity can?t be changed at runtime, so you can?t migrate guest memory closer to the CPUs running it ? performance hit.
> - Memory management: unused guest memory can?t be swapped out, compacted, or merged.
> - Provisioning time: upfront allocation/pinning slows guest create/destroy.
> - Overcommit: no memory overcommit on hosts with pinned-guest memory.
>
> This series adds movable memory pages for Hyper-V child partitions. Guest
> pages are no longer allocated upfront; they?re allocated and mapped into
> the hypervisor on demand (i.e., when the guest touches a GFN that isn?t yet
> backed by a host PFN).
> When a page is moved, Linux no longer holds it and it is unmapped from the hypervisor.
> As a result, Hyper-V guests behave like regular Linux processes, enabling standard Linux memory features to apply to guests.
>
> Exceptions (still pinned):
> 1. Encrypted guests (explicit).
> 2 Guests with passthrough devices (implicitly pinned by the VFIO framework).
As I had commented internally, I am not fully comfortable about the
approach here, specially around use of HMM, and the correctness of
locking for shared memory regions, but my knowledge is from 4.15 and
maybe outdated, and don't have time right now. So I won't object to it
if other hard core mmu developers think there are no issues.
However, we won't be using this for minkernel, so would like a driver
boot option to disable it upon boot that we can just set in minkernel
init path. This option can also be used to disable it if problems are
observed on the field. Minkernel design is still being worked on, so I
cannot provide much details on it yet.
Thanks,
-Mukesh
> ---
>
> Stanislav Kinsburskii (3):
> Drivers: hv: Rename a few memory region related functions for clarity
> Drivers: hv: Centralize guest memory region destruction in helper
> Drivers: hv: Add support for movable memory regions
>
>
> drivers/hv/Kconfig | 1
> drivers/hv/mshv_root.h | 8 +
> drivers/hv/mshv_root_main.c | 448 +++++++++++++++++++++++++++++++++++++------
> 3 files changed, 397 insertions(+), 60 deletions(-)
>
^ permalink raw reply
* Re: [PATCH v4 0/5] mshv: Fixes for stats and vp state page mappings
From: Stanislav Kinsburskii @ 2025-09-26 23:12 UTC (permalink / raw)
To: Nuno Das Neves
Cc: linux-hyperv, linux-kernel, prapal, easwar.hariharan, tiala,
anirudh, paekkaladevi, kys, haiyangz, wei.liu, decui
In-Reply-To: <1758903795-18636-1-git-send-email-nunodasneves@linux.microsoft.com>
On Fri, Sep 26, 2025 at 09:23:10AM -0700, Nuno Das Neves wrote:
> There are some differences in how L1VH partitions must map stats and vp
> state pages, some of which are due to differences across hypervisor
> versions. Detect and handle these cases.
>
I'm not sure that support for older and actully broken versions on
hypervisor need to be usptreamed, as these versions will go away sooner
or later and this support will become dead weight.
I think we should upstrem only the changes needed for the new versiong
of hypervisors instead and carry legacy support out of tree until it
becomes obsoleted.
Thanks,
Stanislav
> Patch 1:
> Fix for the logic of when to map the vp stats page for the root scheduler.
>
> Patch 2-3:
> Add HVCALL_GET_PARTITION_PROPERTY_EX and use it to query "vmm capabilities" on
> module init.
>
> Patches 4-5:
> Check a feature bit in vmm capabilities, to take a new code path for mapping
> stats and vp state pages. In this case, the stats and vp state pages must be
> allocated by Linux, and a new hypercall HVCALL_MAP_VP_STATS_PAGE2 must be used
> to map the stats page.
>
> ---
> v4:
> - Fixed some __packed attributes on unions [Stanislav]
> - Cleaned up mshv_init_vmm_caps() [Stanislav]
> - Cleaned up loop in hv_call_map_stats_page2() [Stanislav]
>
> v3:
> https://lore.kernel.org/linux-hyperv/1758066262-15477-1-git-send-email-nunodasneves@linux.microsoft.com/T/#t
> - Fix bug in patch 4, in mshv_partition_ioctl_create_vp() cleanup path
> [kernel test robot]
> - Make hv_unmap_vp_state_page() use struct page to match hv_map_vp_state_page()
> - Remove SELF == PARENT check which doesn't belong in patch 5 [Easwar]
>
> v2:
> https://lore.kernel.org/linux-hyperv/1757546089-2002-1-git-send-email-nunodasneves@linux.microsoft.com/T/#t
> - Remove patch falling back to SELF page if PARENT mapping fails [Easwar]
> (To be included in a future series)
> - Fix formatting of function definitions [Easwar]
> - Fix some wording in commit messages [Praveen]
> - Proceed with driver init even if getting vmm capabilities fails [Anirudh]
>
> v1:
> https://lore.kernel.org/linux-hyperv/1756428230-3599-1-git-send-email-nunodasneves@linux.microsoft.com/T/#t
>
> ---
> Jinank Jain (2):
> mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
> mshv: Introduce new hypercall to map stats page for L1VH partitions
>
> Nuno Das Neves (1):
> mshv: Only map vp->vp_stats_pages if on root scheduler
>
> Purna Pavan Chandra Aekkaladevi (2):
> mshv: Add the HVCALL_GET_PARTITION_PROPERTY_EX hypercall
> mshv: Get the vmm capabilities offered by the hypervisor
>
> drivers/hv/mshv_root.h | 24 +++--
> drivers/hv/mshv_root_hv_call.c | 185 +++++++++++++++++++++++++++++++--
> drivers/hv/mshv_root_main.c | 127 ++++++++++++----------
> include/hyperv/hvgdk_mini.h | 2 +
> include/hyperv/hvhdk.h | 40 +++++++
> include/hyperv/hvhdk_mini.h | 33 ++++++
> 6 files changed, 337 insertions(+), 74 deletions(-)
>
> --
> 2.34.1
^ permalink raw reply
* Re: [PATCH 2/3] Drivers: hv: Centralize guest memory region destruction in helper
From: Stanislav Kinsburskii @ 2025-09-26 22:10 UTC (permalink / raw)
To: Nuno Das Neves; +Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <66893d19-654e-4ef7-9a9c-c33a5549dbea@linux.microsoft.com>
On Fri, Sep 26, 2025 at 11:15:54AM -0700, Nuno Das Neves wrote:
> On 9/24/2025 2:31 PM, Stanislav Kinsburskii wrote:
<snip>
> > + /*
> > + * Unmap only the mapped pages to optimize performance,
> > + * especially for large memory regions.
> > + */
> > + for (page_offset = 0; page_offset < region->nr_pages; page_offset += page_count) {
> > + page_count = 1;
> > + if (!region->pages[page_offset])
> > + continue;
> I mentioned it above, but can this even happen in the current code (i.e. without
> moveable pages)?
>
No.
> Also, has the impact of this change been measured? I understand the logic behind
> the change - there could be large unmapped sequences within the region so we might
> be able to skip a lot of reps of the unmap hypercall, but the region could also be
> very fragmented and this method might cause *more* reps in that case, right?
>
I see your point. Indeed, we should optimize the number of pages to
unmap by the maximum number allowed for the hypercall.
I'll make this change, thanks.
> Either way, this change belongs in a separate patch.
Fair enough.
Thanks,
Stanislav
> > +
> > + for (; page_count < region->nr_pages - page_offset; page_count++) {
> > + if (!region->pages[page_offset + page_count])
> > + break;
> > + }
> > +
> > + /* ignore unmap failures and continue as process may be exiting */
> > + hv_call_unmap_gpa_pages(partition->pt_id,
> > + region->start_gfn + page_offset,
> > + page_count, unmap_flags);
> > + }
> > +
> > + mshv_region_evict(region);
> > +
> > + vfree(region);
> > +}
> > +
> > /* Called for unmapping both the guest ram and the mmio space */
> > static long
> > mshv_unmap_user_memory(struct mshv_partition *partition,
> > struct mshv_user_mem_region mem)
> > {
> > struct mshv_mem_region *region;
> > - u32 unmap_flags = 0;
> >
> > if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
> > return -EINVAL;
> > @@ -1407,18 +1453,7 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
> > region->nr_pages != HVPFN_DOWN(mem.size))
> > return -EINVAL;
> >
> > - hlist_del(®ion->hnode);
> > -
> > - if (region->flags.large_pages)
> > - unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
> > -
> > - /* ignore unmap failures and continue as process may be exiting */
> > - hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
> > - region->nr_pages, unmap_flags);
> > -
> > - mshv_region_evict(region);
> > -
> > - vfree(region);
> > + mshv_partition_destroy_region(region);
> > return 0;
> > }
> >
> > @@ -1754,8 +1789,8 @@ static void destroy_partition(struct mshv_partition *partition)
> > {
> > struct mshv_vp *vp;
> > struct mshv_mem_region *region;
> > - int i, ret;
> > struct hlist_node *n;
> > + int i;
> >
> > if (refcount_read(&partition->pt_ref_count)) {
> > pt_err(partition,
> > @@ -1815,25 +1850,9 @@ static void destroy_partition(struct mshv_partition *partition)
> >
> > remove_partition(partition);
> >
> > - /* Remove regions, regain access to the memory and unpin the pages */
> > hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
> > - hnode) {
> > - hlist_del(®ion->hnode);
> > -
> > - if (mshv_partition_encrypted(partition)) {
> > - ret = mshv_partition_region_share(region);
> > - if (ret) {
> > - pt_err(partition,
> > - "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
> > - ret);
> > - return;
> > - }
> > - }
> > -
> > - mshv_region_evict(region);
> > -
> > - vfree(region);
> > - }
> > + hnode)
> > + mshv_partition_destroy_region(region);
> >
> > /* Withdraw and free all pages we deposited */
> > hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
> >
> >
>
^ permalink raw reply
* Re: [PATCH 1/3] Drivers: hv: Rename a few memory region related functions for clarity
From: Stanislav Kinsburskii @ 2025-09-26 21:58 UTC (permalink / raw)
To: Nuno Das Neves; +Cc: kys, haiyangz, wei.liu, decui, linux-hyperv, linux-kernel
In-Reply-To: <6165c48c-a71e-4aa0-99d3-2ff8158ddd4a@linux.microsoft.com>
On Fri, Sep 26, 2025 at 10:14:25AM -0700, Nuno Das Neves wrote:
> On 9/24/2025 2:31 PM, Stanislav Kinsburskii wrote:
> > A cleanup and precursor patch.
> >
> This line doesn't add much, I think you can remove it.
>
It actually means something important: it explains why a change is being
made and that other changes to follow will make more sense out of this
one.
> >
> > static int
> > -mshv_region_populate(struct mshv_mem_region *region)
> > +mshv_region_pin(struct mshv_mem_region *region)
> > {
> > - return mshv_region_populate_pages(region, 0, region->nr_pages);
> > + return mshv_region_pin_pages(region, 0, region->nr_pages);
> > }
> Do we ever partially pin a region? Maybe we don't need a function called
> mshv_region_pin_pages() and we just have mshv_region_pin() instead.
>
We don't and we likley won't until we support virtio-iommu.
I'll can remove mshv_region_pin_pages.
> >
> > static struct mshv_mem_region *
> > @@ -1264,17 +1259,25 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
> > return 0;
> > }
> >
> > -/*
> > - * Map guest ram. if snp, make sure to release that from the host first
> > - * Side Effects: In case of failure, pages are unpinned when feasible.
> > +/**
> > + * mshv_handle_pinned_region - Handle pinned 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_partition_mem_region_map(struct mshv_mem_region *region)
> > +static int mshv_handle_pinned_region(struct mshv_mem_region *region)
>
> Why the verb "handle"? It doesn't provide any information on what the function does,
> when it might be called etc. Maybe mshv_init_pinned_region() ?
>
I see what you mean. Indeed, "handle" isn't goot, but "init" is quite
overloaded either. I think "mshv_prepare_pinned_region" suit better
here.
Is it okay with you?
Thanks,
Stanilav
^ permalink raw reply
* Re: [PATCH 2/3] Drivers: hv: Centralize guest memory region destruction in helper
From: Nuno Das Neves @ 2025-09-26 18:15 UTC (permalink / raw)
To: Stanislav Kinsburskii, kys, haiyangz, wei.liu, decui
Cc: linux-hyperv, linux-kernel
In-Reply-To: <175874947750.157998.7004962396456082421.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
On 9/24/2025 2:31 PM, Stanislav Kinsburskii wrote:
> This is a precursor and cleanup patch.
>
This line can be removed, IMO, it doesn't add much.
> - Introduce mshv_partition_destroy_region() to encapsulate memory region
> cleanup, including:
I think these points should just be short paragraphs instead.
> - Removing the region from the partition's list
> - Regaining access for encrypted partitions
> - Unmapping only mapped pages for efficiency
The optimization here seems like new functionality that should be in a
separate patch. Also, can there be unmapped pages in a region before
patch #3? If not it should be introduced with/after that patch.
> - Evicting and freeing the region
>
> - Update mshv_unmap_user_memory() to call mshv_partition_destroy_region()
> instead of duplicating cleanup logic.
>
> - Update destroy_partition() to use mshv_partition_destroy_region() for
> all regions, removing the previous inlined cleanup loop.
>
> These changes eliminate code duplication, ensure consistent cleanup, and
> improve maintainability for both unmap and partition destruction paths.
This sentence maybe should go first in the description, because it summarizes
the reasoning for the changes nicely.
Also, make sure to describe your changes in imperative mood, e.g. Instead of
"These changes eliminate code duplication..." just "Eliminate code duplication..."
https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
>
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_main.c | 83 ++++++++++++++++++++++++++-----------------
> 1 file changed, 51 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index 5ed6bce334417..c0f6023e459c2 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -1386,13 +1386,59 @@ mshv_map_user_memory(struct mshv_partition *partition,
> return ret;
> }
>
> +static void mshv_partition_destroy_region(struct mshv_mem_region *region)
> +{
> + struct mshv_partition *partition = region->partition;
> + u64 page_offset, page_count;
> + u32 unmap_flags = 0;
> + int ret;
> +
> + hlist_del(®ion->hnode);
> +
> + if (mshv_partition_encrypted(partition)) {
> + ret = mshv_partition_region_share(region);
> + if (ret) {
> + pt_err(partition,
> + "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
> + ret);
> + return;
> + }
> + }
> +
> + if (region->flags.large_pages)
> + unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
> +
> + /*
> + * Unmap only the mapped pages to optimize performance,
> + * especially for large memory regions.
> + */
> + for (page_offset = 0; page_offset < region->nr_pages; page_offset += page_count) {
> + page_count = 1;
> + if (!region->pages[page_offset])
> + continue;
I mentioned it above, but can this even happen in the current code (i.e. without
moveable pages)?
Also, has the impact of this change been measured? I understand the logic behind
the change - there could be large unmapped sequences within the region so we might
be able to skip a lot of reps of the unmap hypercall, but the region could also be
very fragmented and this method might cause *more* reps in that case, right?
Either way, this change belongs in a separate patch.
> +
> + for (; page_count < region->nr_pages - page_offset; page_count++) {
> + if (!region->pages[page_offset + page_count])
> + break;
> + }
> +
> + /* ignore unmap failures and continue as process may be exiting */
> + hv_call_unmap_gpa_pages(partition->pt_id,
> + region->start_gfn + page_offset,
> + page_count, unmap_flags);
> + }
> +
> + mshv_region_evict(region);
> +
> + vfree(region);
> +}
> +
> /* Called for unmapping both the guest ram and the mmio space */
> static long
> mshv_unmap_user_memory(struct mshv_partition *partition,
> struct mshv_user_mem_region mem)
> {
> struct mshv_mem_region *region;
> - u32 unmap_flags = 0;
>
> if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
> return -EINVAL;
> @@ -1407,18 +1453,7 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
> region->nr_pages != HVPFN_DOWN(mem.size))
> return -EINVAL;
>
> - hlist_del(®ion->hnode);
> -
> - if (region->flags.large_pages)
> - unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
> -
> - /* ignore unmap failures and continue as process may be exiting */
> - hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
> - region->nr_pages, unmap_flags);
> -
> - mshv_region_evict(region);
> -
> - vfree(region);
> + mshv_partition_destroy_region(region);
> return 0;
> }
>
> @@ -1754,8 +1789,8 @@ static void destroy_partition(struct mshv_partition *partition)
> {
> struct mshv_vp *vp;
> struct mshv_mem_region *region;
> - int i, ret;
> struct hlist_node *n;
> + int i;
>
> if (refcount_read(&partition->pt_ref_count)) {
> pt_err(partition,
> @@ -1815,25 +1850,9 @@ static void destroy_partition(struct mshv_partition *partition)
>
> remove_partition(partition);
>
> - /* Remove regions, regain access to the memory and unpin the pages */
> hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
> - hnode) {
> - hlist_del(®ion->hnode);
> -
> - if (mshv_partition_encrypted(partition)) {
> - ret = mshv_partition_region_share(region);
> - if (ret) {
> - pt_err(partition,
> - "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
> - ret);
> - return;
> - }
> - }
> -
> - mshv_region_evict(region);
> -
> - vfree(region);
> - }
> + hnode)
> + mshv_partition_destroy_region(region);
>
> /* Withdraw and free all pages we deposited */
> hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
>
>
^ permalink raw reply
* Re: [PATCH 1/3] Drivers: hv: Rename a few memory region related functions for clarity
From: Nuno Das Neves @ 2025-09-26 17:14 UTC (permalink / raw)
To: Stanislav Kinsburskii, kys, haiyangz, wei.liu, decui
Cc: linux-hyperv, linux-kernel
In-Reply-To: <175874946244.157998.2185691597101633735.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>
On 9/24/2025 2:31 PM, Stanislav Kinsburskii wrote:
> A cleanup and precursor patch.
>
This line doesn't add much, I think you can remove it.
> Rename "mshv_partition_mem_region_map" to "mshv_handle_pinned_region",
> "mshv_region_populate" to "mshv_pin_region" and
> "mshv_region_populate_pages" to "mshv_region_pin_pages"
> to better reflect its purpose of handling pinned memory regions.
>
> Update the "mshv_handle_pinned_region" function's documentation to provide
> detailed information about its behavior and return values.
>
> Also drop the check for range as pinned, as this function is static and
> all the memory regions are pinned anyway.
>
> Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
> ---
> drivers/hv/mshv_root_main.c | 41 ++++++++++++++++++++++-------------------
> 1 file changed, 22 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index a1c8c3bc79bf1..5ed6bce334417 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -1137,8 +1137,8 @@ mshv_region_evict(struct mshv_mem_region *region)
> }
>
> static int
> -mshv_region_populate_pages(struct mshv_mem_region *region,
> - u64 page_offset, u64 page_count)
> +mshv_region_pin_pages(struct mshv_mem_region *region,
> + u64 page_offset, u64 page_count)
> {
> u64 done_count, nr_pages;
> struct page **pages;
> @@ -1164,14 +1164,9 @@ mshv_region_populate_pages(struct mshv_mem_region *region,
> * with the FOLL_LONGTERM flag does a large temporary
> * allocation of contiguous memory.
> */
> - if (region->flags.range_pinned)
> - ret = pin_user_pages_fast(userspace_addr,
> - nr_pages,
> - FOLL_WRITE | FOLL_LONGTERM,
> - pages);
> - else
> - ret = -EOPNOTSUPP;
> -
> + ret = pin_user_pages_fast(userspace_addr, nr_pages,
> + FOLL_WRITE | FOLL_LONGTERM,
> + pages);
> if (ret < 0)
> goto release_pages;
> }
> @@ -1187,9 +1182,9 @@ mshv_region_populate_pages(struct mshv_mem_region *region,
> }
>
> static int
> -mshv_region_populate(struct mshv_mem_region *region)
> +mshv_region_pin(struct mshv_mem_region *region)
> {
> - return mshv_region_populate_pages(region, 0, region->nr_pages);
> + return mshv_region_pin_pages(region, 0, region->nr_pages);
> }
Do we ever partially pin a region? Maybe we don't need a function called
mshv_region_pin_pages() and we just have mshv_region_pin() instead.
>
> static struct mshv_mem_region *
> @@ -1264,17 +1259,25 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
> return 0;
> }
>
> -/*
> - * Map guest ram. if snp, make sure to release that from the host first
> - * Side Effects: In case of failure, pages are unpinned when feasible.
> +/**
> + * mshv_handle_pinned_region - Handle pinned 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_partition_mem_region_map(struct mshv_mem_region *region)
> +static int mshv_handle_pinned_region(struct mshv_mem_region *region)
Why the verb "handle"? It doesn't provide any information on what the function does,
when it might be called etc. Maybe mshv_init_pinned_region() ?
> {
> struct mshv_partition *partition = region->partition;
> int ret;
>
> - ret = mshv_region_populate(region);
> + ret = mshv_region_pin(region);
> if (ret) {
> pt_err(partition, "Failed to populate memory region: %d\n",
> ret);
> @@ -1368,7 +1371,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
> ret = hv_call_map_mmio_pages(partition->pt_id, mem.guest_pfn,
> mmio_pfn, HVPFN_DOWN(mem.size));
> else
> - ret = mshv_partition_mem_region_map(region);
> + ret = mshv_handle_pinned_region(region);
>
> if (ret)
> goto errout;
>
>
^ permalink raw reply
* Re: [PATCH net-next v6 0/9] vsock: add namespace support to vhost-vsock
From: Stefano Garzarella @ 2025-09-26 16:56 UTC (permalink / raw)
To: Bobby Eshleman
Cc: Shuah Khan, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin,
Jason Wang, Xuan Zhuo, Eugenio Pérez, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
Broadcom internal kernel review list, virtualization, netdev,
linux-kselftest, linux-kernel, kvm, linux-hyperv, berrange,
Bobby Eshleman
In-Reply-To: <wcts7brlugr337mcdfbrz5vkhvjikcaql3pdzgke5ahuuut37v@mgcqyo2umu7w>
On Fri, 26 Sept 2025 at 15:53, Stefano Garzarella <sgarzare@redhat.com> wrote:
>
> Hi Bobby,
>
> On Tue, Sep 16, 2025 at 04:43:44PM -0700, Bobby Eshleman wrote:
> >This series adds namespace support to vhost-vsock and loopback. It does
> >not add namespaces to any of the other guest transports (virtio-vsock,
> >hyperv, or vmci).
>
> Thanks for this new series and the patience!
> I've been a bit messed up after KVM Forum between personal stuff and
> other things. I'm starting to review and test today, so between this
> afternoon and Monday I hope to send you all my comments.
Okay, I reviewed most of them (I'll do the selftest patches on Monday)
and I think we are near :-)
Just a general suggestion, please spend more time on commit description.
All of them should explain better the reasoning behind. This it will
simplify the review, but also future debug.
Thanks and have a nice weekend!
Stefano
^ permalink raw reply
* Re: [PATCH net-next v6 6/9] vhost/vsock: add netns support
From: Stefano Garzarella @ 2025-09-26 16:52 UTC (permalink / raw)
To: Bobby Eshleman
Cc: Shuah Khan, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin,
Jason Wang, Xuan Zhuo, Eugenio Pérez, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
Broadcom internal kernel review list, virtualization, netdev,
linux-kselftest, linux-kernel, kvm, linux-hyperv, berrange,
Bobby Eshleman
In-Reply-To: <20250916-vsock-vmtest-v6-6-064d2eb0c89d@meta.com>
On Tue, Sep 16, 2025 at 04:43:50PM -0700, Bobby Eshleman wrote:
>From: Bobby Eshleman <bobbyeshleman@meta.com>
>
>Add the ability to isolate vsock flows using namespaces.
>
>The VM, via the vhost_vsock struct, inherits its namespace from the
>process that opens the vhost-vsock device. vhost_vsock lookup functions
>are modified to take into account the mode (e.g., if CIDs are matching
>but modes don't align, then return NULL).
>
>vhost_vsock now acquires a reference to the namespace.
>
>Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
>
>---
>Changes in v5:
>- respect pid namespaces when assigning namespace to vhost_vsock
>---
> drivers/vhost/vsock.c | 74 +++++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 66 insertions(+), 8 deletions(-)
>
>diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
>index 34adf0cf9124..1aabe9f85503 100644
>--- a/drivers/vhost/vsock.c
>+++ b/drivers/vhost/vsock.c
>@@ -46,6 +46,11 @@ static DEFINE_READ_MOSTLY_HASHTABLE(vhost_vsock_hash, 8);
> struct vhost_vsock {
> struct vhost_dev dev;
> struct vhost_virtqueue vqs[2];
>+ struct net *net;
>+ netns_tracker ns_tracker;
>+
>+ /* The ns mode at the time vhost_vsock was created */
>+ enum vsock_net_mode orig_net_mode;
>
> /* Link to global vhost_vsock_hash, writes use vhost_vsock_mutex */
> struct hlist_node hash;
>@@ -64,10 +69,40 @@ static u32 vhost_transport_get_local_cid(void)
> return VHOST_VSOCK_DEFAULT_HOST_CID;
> }
>
>+/* Return true if the namespace net can access the vhost_vsock vsock.
>+ * Otherwise, return false.
>+ *
>+ * If the netns is the same, it doesn't matter if it is local or global. The
>+ * vsock sockets within a namespace can always communicate.
>+ *
>+ * If the netns is different, then we need to check if the current namespace
>+ * mode is global and if the namespace mode at the time of the vhost_vsock
>+ * being created is global. If so, then we allow it. By checking the namespace
>+ * mode at the time of the vhost_vsock's creation we allow the flow to continue
>+ * working even if the namespace mode changes to "local" in the middle of a
>+ * socket's lifetime. If we used the current namespace mode instead, then any
>+ * socket that was alive prior to the mode change would suddenly fail.
>+ */
>+static bool vhost_vsock_net_check_mode(struct net *net,
>+ struct vhost_vsock *vsock,
>+ bool check_global)
>+{
>+ if (net_eq(net, vsock->net))
>+ return true;
>+
>+ return check_global &&
>+ (vsock_net_mode(net) == VSOCK_NET_MODE_GLOBAL &&
>+ vsock->orig_net_mode == VSOCK_NET_MODE_GLOBAL);
>+}
>+
> /* Callers that dereference the return value must hold vhost_vsock_mutex or the
> * RCU read lock.
>+ *
>+ * If check_global is true, evaluate the vhost_vsock namespace and namespace
>+ * net argument as matching if they are both in global mode.
> */
>-static struct vhost_vsock *vhost_vsock_get(u32 guest_cid)
>+static struct vhost_vsock *vhost_vsock_get(u32 guest_cid, struct net *net,
>+ bool check_global)
> {
> struct vhost_vsock *vsock;
>
>@@ -78,9 +113,9 @@ static struct vhost_vsock *vhost_vsock_get(u32 guest_cid)
> if (other_cid == 0)
> continue;
>
>- if (other_cid == guest_cid)
>+ if (other_cid == guest_cid &&
>+ vhost_vsock_net_check_mode(net, vsock, check_global))
> return vsock;
>-
> }
>
> return NULL;
>@@ -272,13 +307,14 @@ static int
> vhost_transport_send_pkt(struct sk_buff *skb)
> {
> struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb);
>+ struct net *net = virtio_vsock_skb_net(skb);
> struct vhost_vsock *vsock;
> int len = skb->len;
>
> rcu_read_lock();
>
> /* Find the vhost_vsock according to guest context id */
>- vsock = vhost_vsock_get(le64_to_cpu(hdr->dst_cid));
>+ vsock = vhost_vsock_get(le64_to_cpu(hdr->dst_cid), net, true);
> if (!vsock) {
> rcu_read_unlock();
> kfree_skb(skb);
>@@ -305,7 +341,7 @@ vhost_transport_cancel_pkt(struct vsock_sock *vsk)
> rcu_read_lock();
>
> /* Find the vhost_vsock according to guest context id */
>- vsock = vhost_vsock_get(vsk->remote_addr.svm_cid);
>+ vsock = vhost_vsock_get(vsk->remote_addr.svm_cid, sock_net(sk_vsock(vsk)), true);
> if (!vsock)
> goto out;
>
>@@ -462,11 +498,12 @@ static struct virtio_transport vhost_transport = {
>
> static bool vhost_transport_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid)
> {
>+ struct net *net = sock_net(sk_vsock(vsk));
> struct vhost_vsock *vsock;
> bool seqpacket_allow = false;
>
> rcu_read_lock();
>- vsock = vhost_vsock_get(remote_cid);
>+ vsock = vhost_vsock_get(remote_cid, net, true);
>
> if (vsock)
> seqpacket_allow = vsock->seqpacket_allow;
>@@ -526,6 +563,8 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work)
> continue;
> }
>
>+ virtio_vsock_skb_set_net(skb, vsock->net);
>+ virtio_vsock_skb_set_orig_net_mode(skb, vsock->orig_net_mode);
In virtio_transport_common.c we do this in the alloc_skb function, can
we do the same also here?
And maybe also in the virtio_transport.c (i.e. in virtio_vsock_rx_fill()
or adding a wrapper around virtio_vsock_alloc_linear_skb()).
> total_len += sizeof(*hdr) + skb->len;
>
> /* Deliver to monitoring devices all received packets */
>@@ -652,10 +691,14 @@ static void vhost_vsock_free(struct vhost_vsock *vsock)
>
> static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
> {
>+
> struct vhost_virtqueue **vqs;
> struct vhost_vsock *vsock;
>+ struct net *net;
> int ret;
>
>+ net = current->nsproxy->net_ns;
>+
> /* This struct is large and allocation could fail, fall back to vmalloc
> * if there is no other way.
> */
>@@ -669,6 +712,12 @@ static int vhost_vsock_dev_open(struct inode *inode, struct file *file)
> goto out;
> }
>
>+ vsock->net = get_net_track(net, &vsock->ns_tracker, GFP_KERNEL);
>+
>+ /* Cache the mode of the namespace so that if that netns mode changes,
>+ * the vhost_vsock will continue to function as expected. */
>+ vsock->orig_net_mode = vsock_net_mode(net);
>+
> vsock->guest_cid = 0; /* no CID assigned yet */
> vsock->seqpacket_allow = false;
>
>@@ -707,8 +756,16 @@ static void vhost_vsock_reset_orphans(struct sock *sk)
> * executing.
> */
>
>+ /* DELETE ME:
mmm, to be deleted, right? :-)
>+ *
>+ * for each connected socket:
>+ * vhost_vsock = vsock_sk(sk)
>+ *
>+ * find the peer
>+ */
>+
> /* If the peer is still valid, no need to reset connection */
>- if (vhost_vsock_get(vsk->remote_addr.svm_cid))
>+ if (vhost_vsock_get(vsk->remote_addr.svm_cid, sock_net(sk), false))
Can we add a comment here to explain why `check_global` is false?
Thanks,
Stefano
> return;
>
> /* If the close timeout is pending, let it expire. This avoids races
>@@ -753,6 +810,7 @@ static int vhost_vsock_dev_release(struct inode *inode, struct file *file)
> virtio_vsock_skb_queue_purge(&vsock->send_pkt_queue);
>
> vhost_dev_cleanup(&vsock->dev);
>+ put_net_track(vsock->net, &vsock->ns_tracker);
> kfree(vsock->dev.vqs);
> vhost_vsock_free(vsock);
> return 0;
>@@ -779,7 +837,7 @@ static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u64 guest_cid)
>
> /* Refuse if CID is already in use */
> mutex_lock(&vhost_vsock_mutex);
>- other = vhost_vsock_get(guest_cid);
>+ other = vhost_vsock_get(guest_cid, vsock->net, true);
> if (other && other != vsock) {
> mutex_unlock(&vhost_vsock_mutex);
> return -EADDRINUSE;
>
>--
>2.47.3
>
^ permalink raw reply
* Re: [PATCH net-next v6 5/9] vsock/virtio: add netns to virtio transport common
From: Stefano Garzarella @ 2025-09-26 16:43 UTC (permalink / raw)
To: Bobby Eshleman
Cc: Shuah Khan, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin,
Jason Wang, Xuan Zhuo, Eugenio Pérez, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
Broadcom internal kernel review list, virtualization, netdev,
linux-kselftest, linux-kernel, kvm, linux-hyperv, berrange,
Bobby Eshleman
In-Reply-To: <20250916-vsock-vmtest-v6-5-064d2eb0c89d@meta.com>
On Tue, Sep 16, 2025 at 04:43:49PM -0700, Bobby Eshleman wrote:
>From: Bobby Eshleman <bobbyeshleman@meta.com>
>
>Add support to the virtio-vsock common code for passing around net
>namespace pointers (tx and rx). The series still requires vhost/virtio
>transport support to be added by future patches.
>
>Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
>---
> include/linux/virtio_vsock.h | 1 +
> net/vmw_vsock/virtio_transport_common.c | 18 ++++++++++++++++--
> 2 files changed, 17 insertions(+), 2 deletions(-)
>
>diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h
>index ea955892488a..165157580cb8 100644
>--- a/include/linux/virtio_vsock.h
>+++ b/include/linux/virtio_vsock.h
>@@ -196,6 +196,7 @@ struct virtio_vsock_pkt_info {
> u32 remote_cid, remote_port;
> struct vsock_sock *vsk;
> struct msghdr *msg;
>+ struct net *net;
> u32 pkt_len;
> u16 type;
> u16 op;
>diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
>index 1a9129e33d51..8a08a5103e7c 100644
>--- a/net/vmw_vsock/virtio_transport_common.c
>+++ b/net/vmw_vsock/virtio_transport_common.c
>@@ -316,6 +316,11 @@ static struct sk_buff *virtio_transport_alloc_skb(struct virtio_vsock_pkt_info *
> info->flags,
> zcopy);
>
>+ virtio_vsock_skb_set_net(skb, info->net);
>+
>+ if (vsk)
>+ virtio_vsock_skb_set_orig_net_mode(skb, vsk->orig_net_mode);
If `vsk` is NULL we don't set it, is it right?
I'd add a comment to explain that, because it's not clear.
Thanks,
Stefano
>+
> return skb;
> out:
> kfree_skb(skb);
>@@ -527,6 +532,7 @@ static int virtio_transport_send_credit_update(struct vsock_sock *vsk)
> struct virtio_vsock_pkt_info info = {
> .op = VIRTIO_VSOCK_OP_CREDIT_UPDATE,
> .vsk = vsk,
>+ .net = sock_net(sk_vsock(vsk)),
> };
>
> return virtio_transport_send_pkt_info(vsk, &info);
>@@ -1067,6 +1073,7 @@ int virtio_transport_connect(struct vsock_sock *vsk)
> struct virtio_vsock_pkt_info info = {
> .op = VIRTIO_VSOCK_OP_REQUEST,
> .vsk = vsk,
>+ .net = sock_net(sk_vsock(vsk)),
> };
>
> return virtio_transport_send_pkt_info(vsk, &info);
>@@ -1082,6 +1089,7 @@ int virtio_transport_shutdown(struct vsock_sock *vsk, int mode)
> (mode & SEND_SHUTDOWN ?
> VIRTIO_VSOCK_SHUTDOWN_SEND : 0),
> .vsk = vsk,
>+ .net = sock_net(sk_vsock(vsk)),
> };
>
> return virtio_transport_send_pkt_info(vsk, &info);
>@@ -1108,6 +1116,7 @@ virtio_transport_stream_enqueue(struct vsock_sock *vsk,
> .msg = msg,
> .pkt_len = len,
> .vsk = vsk,
>+ .net = sock_net(sk_vsock(vsk)),
> };
>
> return virtio_transport_send_pkt_info(vsk, &info);
>@@ -1145,6 +1154,7 @@ static int virtio_transport_reset(struct vsock_sock *vsk,
> .op = VIRTIO_VSOCK_OP_RST,
> .reply = !!skb,
> .vsk = vsk,
>+ .net = sock_net(sk_vsock(vsk)),
> };
>
> /* Send RST only if the original pkt is not a RST pkt */
>@@ -1165,6 +1175,7 @@ static int virtio_transport_reset_no_sock(const struct virtio_transport *t,
> .op = VIRTIO_VSOCK_OP_RST,
> .type = le16_to_cpu(hdr->type),
> .reply = true,
>+ .net = virtio_vsock_skb_net(skb),
> };
> struct sk_buff *reply;
>
>@@ -1465,6 +1476,7 @@ virtio_transport_send_response(struct vsock_sock *vsk,
> .remote_port = le32_to_cpu(hdr->src_port),
> .reply = true,
> .vsk = vsk,
>+ .net = sock_net(sk_vsock(vsk)),
> };
>
> return virtio_transport_send_pkt_info(vsk, &info);
>@@ -1578,7 +1590,9 @@ static bool virtio_transport_valid_type(u16 type)
> void virtio_transport_recv_pkt(struct virtio_transport *t,
> struct sk_buff *skb)
> {
>+ enum vsock_net_mode orig_net_mode = virtio_vsock_skb_orig_net_mode(skb);
> struct virtio_vsock_hdr *hdr = virtio_vsock_hdr(skb);
>+ struct net *net = virtio_vsock_skb_net(skb);
> struct sockaddr_vm src, dst;
> struct vsock_sock *vsk;
> struct sock *sk;
>@@ -1606,9 +1620,9 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
> /* The socket must be in connected or bound table
> * otherwise send reset back
> */
>- sk = vsock_find_connected_socket(&src, &dst, vsock_global_dummy_net());
>+ sk = vsock_find_connected_socket(&src, &dst, net, orig_net_mode);
> if (!sk) {
>- sk = vsock_find_bound_socket(&dst, vsock_global_dummy_net());
>+ sk = vsock_find_bound_socket(&dst, net, orig_net_mode);
> if (!sk) {
> (void)virtio_transport_reset_no_sock(t, skb);
> goto free_pkt;
>
>--
>2.47.3
>
^ permalink raw reply
* Re: [PATCH net-next v6 3/9] vsock: add netns to vsock core
From: Stefano Garzarella @ 2025-09-26 16:39 UTC (permalink / raw)
To: Bobby Eshleman
Cc: Shuah Khan, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin,
Jason Wang, Xuan Zhuo, Eugenio Pérez, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
Broadcom internal kernel review list, virtualization, netdev,
linux-kselftest, linux-kernel, kvm, linux-hyperv, berrange,
Bobby Eshleman
In-Reply-To: <20250916-vsock-vmtest-v6-3-064d2eb0c89d@meta.com>
On Tue, Sep 16, 2025 at 04:43:47PM -0700, Bobby Eshleman wrote:
>From: Bobby Eshleman <bobbyeshleman@meta.com>
>
>Add netns to logic to vsock core. Additionally, modify transport hook
>prototypes to be used by later transport-specific patches (e.g.,
>*_seqpacket_allow()).
>
>Namespaces are supported primarily by changing socket lookup functions
>(e.g., vsock_find_connected_socket()) to take into account the socket
>namespace and the namespace mode before considering a candidate socket a
>"match".
>
>Introduce a dummy namespace struct, __vsock_global_dummy_net, to be
>used by transports that do not support namespacing. This dummy always
>has mode "global" to preserve previous CID behavior.
>
>This patch also introduces the sysctl /proc/sys/net/vsock/ns_mode that
>accepts the "global" or "local" mode strings.
>
>The transports (besides vhost) are modified to use the global dummy.
Why not `vhost`?
>
>Add netns functionality (initialization, passing to transports, procfs,
>etc...) to the af_vsock socket layer. Later patches that add netns
>support to transports depend on this patch.
>
>Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
>
>---
>Changes in v6:
>- unregister sysctl ops in vsock_exit()
>- af_vsock: clarify description of CID behavior
>- af_vsock: fix buf vs buffer naming, and length checking
>- af_vsock: fix length checking w/ correct ctl_table->maxlen
>
>Changes in v5:
>- vsock_global_net() -> vsock_global_dummy_net()
>- update comments for new uAPI
>- use /proc/sys/net/vsock/ns_mode instead of /proc/net/vsock_ns_mode
>- add prototype changes so patch remains compilable
>---
> drivers/vhost/vsock.c | 4 +-
> include/net/af_vsock.h | 15 ++-
> net/vmw_vsock/af_vsock.c | 219 ++++++++++++++++++++++++++++++--
> net/vmw_vsock/hyperv_transport.c | 2 +-
> net/vmw_vsock/virtio_transport.c | 6 +-
> net/vmw_vsock/virtio_transport_common.c | 4 +-
> net/vmw_vsock/vmci_transport.c | 6 +-
> net/vmw_vsock/vsock_loopback.c | 4 +-
> 8 files changed, 234 insertions(+), 26 deletions(-)
>
>diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
>index ae01457ea2cd..34adf0cf9124 100644
>--- a/drivers/vhost/vsock.c
>+++ b/drivers/vhost/vsock.c
>@@ -404,7 +404,7 @@ static bool vhost_transport_msgzerocopy_allow(void)
> return true;
> }
>
>-static bool vhost_transport_seqpacket_allow(u32 remote_cid);
>+static bool vhost_transport_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid);
>
> static struct virtio_transport vhost_transport = {
> .transport = {
>@@ -460,7 +460,7 @@ static struct virtio_transport vhost_transport = {
> .send_pkt = vhost_transport_send_pkt,
> };
>
>-static bool vhost_transport_seqpacket_allow(u32 remote_cid)
>+static bool vhost_transport_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid)
> {
> struct vhost_vsock *vsock;
> bool seqpacket_allow = false;
>diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h
>index 2857e97699de..628e35ae9d00 100644
>--- a/include/net/af_vsock.h
>+++ b/include/net/af_vsock.h
>@@ -145,7 +145,7 @@ struct vsock_transport {
> int flags);
> int (*seqpacket_enqueue)(struct vsock_sock *vsk, struct msghdr *msg,
> size_t len);
>- bool (*seqpacket_allow)(u32 remote_cid);
>+ bool (*seqpacket_allow)(struct vsock_sock *vsk, u32 remote_cid);
Why we need this change?
Not sure if we should do in a separate patch.
> u32 (*seqpacket_has_data)(struct vsock_sock *vsk);
>
> /* Notification. */
>@@ -215,9 +215,12 @@ void vsock_enqueue_accept(struct sock *listener, struct sock *connected);
> void vsock_insert_connected(struct vsock_sock *vsk);
> void vsock_remove_bound(struct vsock_sock *vsk);
> void vsock_remove_connected(struct vsock_sock *vsk);
>-struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr);
>+struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr, struct net *net,
>+ enum vsock_net_mode orig_net_mode);
> struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
>- struct sockaddr_vm *dst);
>+ struct sockaddr_vm *dst,
>+ struct net *net,
>+ enum vsock_net_mode orig_net_mode);
> void vsock_remove_sock(struct vsock_sock *vsk);
> void vsock_for_each_connected_socket(struct vsock_transport *transport,
> void (*fn)(struct sock *sk));
>@@ -259,6 +262,12 @@ static inline bool vsock_msgzerocopy_allow(const struct vsock_transport *t)
> return t->msgzerocopy_allow && t->msgzerocopy_allow();
> }
>
I'd add a comment here to explain when it should be used.
>+extern struct net __vsock_global_dummy_net;
>+static inline struct net *vsock_global_dummy_net(void)
>+{
>+ return &__vsock_global_dummy_net;
>+}
>+
> static inline enum vsock_net_mode vsock_net_mode(struct net *net)
> {
> enum vsock_net_mode ret;
>diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
>index 0538948d5fd9..c78aba9cd20e 100644
>--- a/net/vmw_vsock/af_vsock.c
>+++ b/net/vmw_vsock/af_vsock.c
>@@ -83,6 +83,35 @@
> * TCP_ESTABLISHED - connected
> * TCP_CLOSING - disconnecting
> * TCP_LISTEN - listening
>+ *
>+ * - Namespaces in vsock support two different modes configured
>+ * through /proc/sys/net/vsock/ns_mode. The modes are "local" and "global".
>+ * Each mode defines how the namespace interacts with CIDs.
>+ * /proc/sys/net/vsock/ns_mode is write-once, so that it may be configured
>+ * and locked down by a namespace manager. The default is "global". The mode
>+ * is set per-namespace.
>+ *
>+ * The modes affect the allocation and accessibility of CIDs as follows:
>+
>+ * - global - access and allocation are all system-wide
>+ * - all CID allocation from global namespaces draw from the same
>+ * system-wide pool
>+ * - if one global namespace has already allocated some CID, another
>+ * global namespace will not be able to allocate the same CID
>+ * - global mode AF_VSOCK sockets can reach any VM or socket in any global
>+ * namespace, they are not contained to only their own namespace
>+ * - AF_VSOCK sockets in a global mode namespace cannot reach VMs or
>+ * sockets in any local mode namespace
>+ * - local - access and allocation are contained within the namespace
>+ * - CID allocation draws only from a private pool local only to the
>+ * namespace, and does not affect the CIDs available for allocation in any
>+ * other namespace (global or local)
>+ * - VMs in a local namespace do not collide with CIDs in any other local
>+ * namespace or any global namespace. For example, if a VM in a local mode
>+ * namespace is given CID 10, then CID 10 is still available for
>+ * allocation in any other namespace, but not in the same namespace
>+ * - AF_VSOCK sockets in a local mode namespace can connect only to VMs or
>+ * other sockets within their own namespace.
> */
>
> #include <linux/compat.h>
>@@ -100,6 +129,7 @@
> #include <linux/module.h>
> #include <linux/mutex.h>
> #include <linux/net.h>
>+#include <linux/proc_fs.h>
> #include <linux/poll.h>
> #include <linux/random.h>
> #include <linux/skbuff.h>
>@@ -111,9 +141,14 @@
> #include <linux/workqueue.h>
> #include <net/sock.h>
> #include <net/af_vsock.h>
>+#include <net/netns/vsock.h>
> #include <uapi/linux/vm_sockets.h>
> #include <uapi/asm-generic/ioctls.h>
>
>+#define VSOCK_NET_MODE_STR_GLOBAL "global"
>+#define VSOCK_NET_MODE_STR_LOCAL "local"
>+#define VSOCK_NET_MODE_STR_MAX 8
Why 8 ?
>+
> static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr);
> static void vsock_sk_destruct(struct sock *sk);
> static int vsock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
>@@ -149,6 +184,9 @@ static const struct vsock_transport *transport_dgram;
> static const struct vsock_transport *transport_local;
> static DEFINE_MUTEX(vsock_register_mutex);
>
>+struct net __vsock_global_dummy_net;
>+EXPORT_SYMBOL_GPL(__vsock_global_dummy_net);
>+
> /**** UTILS ****/
>
> /* Each bound VSocket is stored in the bind hash table and each connected
>@@ -235,17 +273,21 @@ static void __vsock_remove_connected(struct vsock_sock *vsk)
> sock_put(&vsk->sk);
> }
>
>-static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr)
>+static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr,
>+ struct net *net,
>+ enum vsock_net_mode orig_net_mode)
> {
> struct vsock_sock *vsk;
>
> list_for_each_entry(vsk, vsock_bound_sockets(addr), bound_table) {
>- if (vsock_addr_equals_addr(addr, &vsk->local_addr))
>+ if (vsock_addr_equals_addr(addr, &vsk->local_addr) &&
>+ vsock_net_check_mode(vsk, net, orig_net_mode))
> return sk_vsock(vsk);
>
> if (addr->svm_port == vsk->local_addr.svm_port &&
> (vsk->local_addr.svm_cid == VMADDR_CID_ANY ||
>- addr->svm_cid == VMADDR_CID_ANY))
>+ addr->svm_cid == VMADDR_CID_ANY) &&
>+ vsock_net_check_mode(vsk, net, orig_net_mode))
> return sk_vsock(vsk);
> }
>
>@@ -253,14 +295,17 @@ static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr)
> }
>
> static struct sock *__vsock_find_connected_socket(struct sockaddr_vm *src,
>- struct sockaddr_vm *dst)
>+ struct sockaddr_vm *dst,
>+ struct net *net,
>+ enum vsock_net_mode orig_net_mode)
> {
> struct vsock_sock *vsk;
>
> list_for_each_entry(vsk, vsock_connected_sockets(src, dst),
> connected_table) {
> if (vsock_addr_equals_addr(src, &vsk->remote_addr) &&
>- dst->svm_port == vsk->local_addr.svm_port) {
>+ dst->svm_port == vsk->local_addr.svm_port &&
>+ vsock_net_check_mode(vsk, net, orig_net_mode)) {
> return sk_vsock(vsk);
> }
> }
>@@ -304,12 +349,13 @@ void vsock_remove_connected(struct vsock_sock *vsk)
> }
> EXPORT_SYMBOL_GPL(vsock_remove_connected);
>
>-struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr)
>+struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr, struct net *net,
>+ enum vsock_net_mode orig_net_mode)
> {
> struct sock *sk;
>
> spin_lock_bh(&vsock_table_lock);
>- sk = __vsock_find_bound_socket(addr);
>+ sk = __vsock_find_bound_socket(addr, net, orig_net_mode);
> if (sk)
> sock_hold(sk);
>
>@@ -320,12 +366,14 @@ struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr)
> EXPORT_SYMBOL_GPL(vsock_find_bound_socket);
>
> struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
>- struct sockaddr_vm *dst)
>+ struct sockaddr_vm *dst,
>+ struct net *net,
>+ enum vsock_net_mode orig_net_mode)
> {
> struct sock *sk;
>
> spin_lock_bh(&vsock_table_lock);
>- sk = __vsock_find_connected_socket(src, dst);
>+ sk = __vsock_find_connected_socket(src, dst, net, orig_net_mode);
> if (sk)
> sock_hold(sk);
>
>@@ -528,7 +576,7 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk)
>
> if (sk->sk_type == SOCK_SEQPACKET) {
> if (!new_transport->seqpacket_allow ||
>- !new_transport->seqpacket_allow(remote_cid)) {
>+ !new_transport->seqpacket_allow(vsk, remote_cid)) {
> module_put(new_transport->module);
> return -ESOCKTNOSUPPORT;
> }
>@@ -676,6 +724,7 @@ static void vsock_pending_work(struct work_struct *work)
> static int __vsock_bind_connectible(struct vsock_sock *vsk,
> struct sockaddr_vm *addr)
> {
>+ struct net *net = sock_net(sk_vsock(vsk));
> static u32 port;
> struct sockaddr_vm new_addr;
>
>@@ -695,7 +744,8 @@ static int __vsock_bind_connectible(struct vsock_sock *vsk,
>
> new_addr.svm_port = port++;
>
>- if (!__vsock_find_bound_socket(&new_addr)) {
>+ if (!__vsock_find_bound_socket(&new_addr, net,
>+ vsk->orig_net_mode)) {
> found = true;
> break;
> }
>@@ -712,7 +762,8 @@ static int __vsock_bind_connectible(struct vsock_sock *vsk,
> return -EACCES;
> }
>
>- if (__vsock_find_bound_socket(&new_addr))
>+ if (__vsock_find_bound_socket(&new_addr, net,
>+ vsk->orig_net_mode))
> return -EADDRINUSE;
> }
>
>@@ -2552,6 +2603,7 @@ static int vsock_create(struct net *net, struct socket *sock,
> return -ENOMEM;
>
> vsk = vsock_sk(sk);
>+ vsk->orig_net_mode = vsock_net_mode(net);
>
> if (sock->type == SOCK_DGRAM) {
> ret = vsock_assign_transport(vsk, NULL);
>@@ -2636,6 +2688,139 @@ static struct miscdevice vsock_device = {
> .fops = &vsock_device_ops,
> };
>
>+static int vsock_net_mode_string(const struct ctl_table *table, int write,
>+ void *buffer, size_t *lenp, loff_t *ppos)
>+{
>+ char data[VSOCK_NET_MODE_STR_MAX] = {0};
>+ enum vsock_net_mode mode;
>+ struct ctl_table tmp;
>+ struct net *net;
>+ int ret;
>+
>+ if (!table->data || !table->maxlen || !*lenp) {
>+ *lenp = 0;
>+ return 0;
>+ }
>+
>+ net = current->nsproxy->net_ns;
>+ tmp = *table;
>+ tmp.data = data;
>+
>+ if (!write) {
>+ const char *p;
>+
>+ mode = vsock_net_mode(net);
>+
Can we use a switch here?
>+ if (mode == VSOCK_NET_MODE_GLOBAL) {
>+ p = VSOCK_NET_MODE_STR_GLOBAL;
>+ } else if (mode == VSOCK_NET_MODE_LOCAL) {
>+ p = VSOCK_NET_MODE_STR_LOCAL;
>+ } else {
>+ WARN_ONCE(true, "netns has invalid vsock mode");
>+ *lenp = 0;
>+ return 0;
>+ }
>+
>+ strscpy(data, p, sizeof(data));
>+ tmp.maxlen = strlen(p);
>+ }
>+
>+ ret = proc_dostring(&tmp, write, buffer, lenp, ppos);
>+ if (ret)
>+ return ret;
>+
>+ if (write) {
>+ if (*lenp >= sizeof(data))
>+ return -EINVAL;
>+
>+ if (!strncmp(data, VSOCK_NET_MODE_STR_GLOBAL, sizeof(data)))
>+ mode = VSOCK_NET_MODE_GLOBAL;
>+ else if (!strncmp(data, VSOCK_NET_MODE_STR_LOCAL, sizeof(data)))
>+ mode = VSOCK_NET_MODE_LOCAL;
>+ else
>+ return -EINVAL;
>+
>+ if (!vsock_net_write_mode(net, mode))
>+ return -EPERM;
>+ }
>+
>+ return 0;
>+}
>+
>+static struct ctl_table vsock_table[] = {
>+ {
>+ .procname = "ns_mode",
>+ .data = &init_net.vsock.mode,
>+ .maxlen = VSOCK_NET_MODE_STR_MAX,
>+ .mode = 0644,
>+ .proc_handler = vsock_net_mode_string
>+ },
>+};
>+
>+static int __net_init vsock_sysctl_register(struct net *net)
>+{
>+ struct ctl_table *table;
>+
>+ if (net_eq(net, &init_net)) {
>+ table = vsock_table;
>+ } else {
>+ table = kmemdup(vsock_table, sizeof(vsock_table), GFP_KERNEL);
>+ if (!table)
>+ goto err_alloc;
>+
>+ table[0].data = &net->vsock.mode;
>+ }
>+
>+ net->vsock.vsock_hdr = register_net_sysctl_sz(net, "net/vsock", table,
>+ ARRAY_SIZE(vsock_table));
>+ if (!net->vsock.vsock_hdr)
>+ goto err_reg;
>+
>+ return 0;
>+
>+err_reg:
>+ if (!net_eq(net, &init_net))
>+ kfree(table);
>+err_alloc:
>+ return -ENOMEM;
>+}
>+
>+static void vsock_sysctl_unregister(struct net *net)
>+{
>+ const struct ctl_table *table;
>+
>+ table = net->vsock.vsock_hdr->ctl_table_arg;
>+ unregister_net_sysctl_table(net->vsock.vsock_hdr);
>+ if (!net_eq(net, &init_net))
>+ kfree(table);
>+}
>+
>+static void vsock_net_init(struct net *net)
>+{
>+ spin_lock_init(&net->vsock.lock);
>+ net->vsock.mode = VSOCK_NET_MODE_GLOBAL;
>+}
>+
>+static __net_init int vsock_sysctl_init_net(struct net *net)
>+{
>+ vsock_net_init(net);
>+
>+ if (vsock_sysctl_register(net))
>+ return -ENOMEM;
>+
>+ return 0;
>+}
>+
>+static __net_exit void vsock_sysctl_exit_net(struct net *net)
>+{
>+ vsock_sysctl_unregister(net);
>+}
>+
>+static struct pernet_operations vsock_sysctl_ops __net_initdata = {
>+ .init = vsock_sysctl_init_net,
>+ .exit = vsock_sysctl_exit_net,
>+};
>+
> static int __init vsock_init(void)
> {
> int err = 0;
>@@ -2663,10 +2848,19 @@ static int __init vsock_init(void)
> goto err_unregister_proto;
> }
>
>+ if (register_pernet_subsys(&vsock_sysctl_ops)) {
>+ err = -ENOMEM;
>+ goto err_unregister_sock;
>+ }
>+
>+ vsock_net_init(&init_net);
>+ vsock_net_init(vsock_global_dummy_net());
> vsock_bpf_build_proto();
>
> return 0;
>
>+err_unregister_sock:
>+ sock_unregister(AF_VSOCK);
> err_unregister_proto:
> proto_unregister(&vsock_proto);
> err_deregister_misc:
>@@ -2680,6 +2874,7 @@ static void __exit vsock_exit(void)
> misc_deregister(&vsock_device);
> sock_unregister(AF_VSOCK);
> proto_unregister(&vsock_proto);
>+ unregister_pernet_subsys(&vsock_sysctl_ops);
> }
>
> const struct vsock_transport *vsock_core_get_transport(struct vsock_sock *vsk)
>diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
>index 432fcbbd14d4..79bc55eeecb3 100644
>--- a/net/vmw_vsock/hyperv_transport.c
>+++ b/net/vmw_vsock/hyperv_transport.c
>@@ -313,7 +313,7 @@ static void hvs_open_connection(struct vmbus_channel *chan)
> return;
>
> hvs_addr_init(&addr, conn_from_host ? if_type : if_instance);
>- sk = vsock_find_bound_socket(&addr);
>+ sk = vsock_find_bound_socket(&addr, vsock_global_dummy_net());
Instead of using `vsock_global_dummy_net()` in each transport, can we
just provide a new vsock_find_bound_socket() to be used with a `net`
param (e.g. vsock_find_bound_socket_net)?
The "old" `vsock_find_bound_socket()` can simply call
vsock_find_bound_socket_net() with the dummy one, so we don't need to
change anything in the transports that don't care about netns and we
don't need to expose it to transports.
> if (!sk)
> return;
>
>diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
>index b6569b0ca2bb..4626ba0428ef 100644
>--- a/net/vmw_vsock/virtio_transport.c
>+++ b/net/vmw_vsock/virtio_transport.c
>@@ -536,7 +536,7 @@ static bool virtio_transport_msgzerocopy_allow(void)
> return true;
> }
>
>-static bool virtio_transport_seqpacket_allow(u32 remote_cid);
>+static bool virtio_transport_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid);
>
> static struct virtio_transport virtio_transport = {
> .transport = {
>@@ -593,7 +593,7 @@ static struct virtio_transport virtio_transport = {
> .can_msgzerocopy = virtio_transport_can_msgzerocopy,
> };
>
>-static bool virtio_transport_seqpacket_allow(u32 remote_cid)
>+static bool virtio_transport_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid)
> {
> struct virtio_vsock *vsock;
> bool seqpacket_allow;
>@@ -659,6 +659,8 @@ static void virtio_transport_rx_work(struct work_struct *work)
> if (payload_len)
> virtio_vsock_skb_put(skb, payload_len);
>
>+ virtio_vsock_skb_set_net(skb, vsock_global_dummy_net());
>+ virtio_vsock_skb_set_orig_net_mode(skb, VSOCK_NET_MODE_GLOBAL);
> virtio_transport_deliver_tap_pkt(skb);
> virtio_transport_recv_pkt(&virtio_transport, skb);
> }
>diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
>index dcc8a1d5851e..1a9129e33d51 100644
>--- a/net/vmw_vsock/virtio_transport_common.c
>+++ b/net/vmw_vsock/virtio_transport_common.c
>@@ -1606,9 +1606,9 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
> /* The socket must be in connected or bound table
> * otherwise send reset back
> */
>- sk = vsock_find_connected_socket(&src, &dst);
>+ sk = vsock_find_connected_socket(&src, &dst, vsock_global_dummy_net());
> if (!sk) {
>- sk = vsock_find_bound_socket(&dst);
>+ sk = vsock_find_bound_socket(&dst, vsock_global_dummy_net());
> if (!sk) {
> (void)virtio_transport_reset_no_sock(t, skb);
> goto free_pkt;
>diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
>index 7eccd6708d66..aa0cd2efe561 100644
>--- a/net/vmw_vsock/vmci_transport.c
>+++ b/net/vmw_vsock/vmci_transport.c
>@@ -703,9 +703,11 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
> vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
> vsock_addr_init(&dst, pkt->dg.dst.context, pkt->dst_port);
>
>- sk = vsock_find_connected_socket(&src, &dst);
>+ sk = vsock_find_connected_socket(&src, &dst, vsock_global_dummy_net(),
>+ VSOCK_NET_MODE_GLOBAL);
> if (!sk) {
>- sk = vsock_find_bound_socket(&dst);
>+ sk = vsock_find_bound_socket(&dst, vsock_global_dummy_net(),
>+ VSOCK_NET_MODE_GLOBAL);
As I mentioned, I'd like to avoid all of these changes and provide a new
function to take care of netns where the "old" ones hide the dummy
stuff.
Just to be clear, I'd like to avoid changes in transports that don't
support netns if it's possible.
If it's a mess, I can reconsider it :-)
Thanks,
Stefano
> if (!sk) {
> /* We could not find a socket for this specified
> * address. If this packet is a RST, we just drop it.
>diff --git a/net/vmw_vsock/vsock_loopback.c b/net/vmw_vsock/vsock_loopback.c
>index 6e78927a598e..1b2fab73e0d0 100644
>--- a/net/vmw_vsock/vsock_loopback.c
>+++ b/net/vmw_vsock/vsock_loopback.c
>@@ -46,7 +46,7 @@ static int vsock_loopback_cancel_pkt(struct vsock_sock *vsk)
> return 0;
> }
>
>-static bool vsock_loopback_seqpacket_allow(u32 remote_cid);
>+static bool vsock_loopback_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid);
> static bool vsock_loopback_msgzerocopy_allow(void)
> {
> return true;
>@@ -106,7 +106,7 @@ static struct virtio_transport loopback_transport = {
> .send_pkt = vsock_loopback_send_pkt,
> };
>
>-static bool vsock_loopback_seqpacket_allow(u32 remote_cid)
>+static bool vsock_loopback_seqpacket_allow(struct vsock_sock *vsk, u32 remote_cid)
> {
> return true;
> }
>
>--
>2.47.3
>
^ permalink raw reply
* [PATCH v4 4/5] mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
From: Nuno Das Neves @ 2025-09-26 16:23 UTC (permalink / raw)
To: linux-hyperv, linux-kernel, prapal, easwar.hariharan, tiala,
anirudh, paekkaladevi, skinsburskii
Cc: kys, haiyangz, wei.liu, decui, Jinank Jain, Nuno Das Neves
In-Reply-To: <1758903795-18636-1-git-send-email-nunodasneves@linux.microsoft.com>
From: Jinank Jain <jinankjain@linux.microsoft.com>
Introduce mshv_use_overlay_gpfn() to check if a page needs to be
allocated and passed to the hypervisor to map VP state pages. This is
only needed on L1VH, and only on some (newer) versions of the
hypervisor, hence the need to check vmm_capabilities.
Introduce functions hv_map/unmap_vp_state_page() to handle the
allocation and freeing.
Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: Praveen K Paladugu <prapal@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
Reviewed-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Reviewed-by: Anirudh Rayabharam <anirudh@anirudhrb.com>
---
drivers/hv/mshv_root.h | 11 ++---
drivers/hv/mshv_root_hv_call.c | 61 ++++++++++++++++++++++++---
drivers/hv/mshv_root_main.c | 76 +++++++++++++++++-----------------
3 files changed, 98 insertions(+), 50 deletions(-)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 0cb1e2589fe1..dbe2d1d0b22f 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -279,11 +279,12 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
/* Choose between pages and bytes */
struct hv_vp_state_data state_data, u64 page_count,
struct page **pages, u32 num_bytes, u8 *bytes);
-int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl,
- struct page **state_page);
-int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl);
+int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl,
+ struct page **state_page);
+int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ struct page *state_page,
+ union hv_input_vtl input_vtl);
int hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,
u64 connection_partition_id, struct hv_port_info *port_info,
u8 port_vtl, u8 min_connection_vtl, int node);
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 3fd3cce23f69..98c6278ff151 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -526,9 +526,9 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
return ret;
}
-int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl,
- struct page **state_page)
+static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl,
+ struct page **state_page)
{
struct hv_input_map_vp_state_page *input;
struct hv_output_map_vp_state_page *output;
@@ -547,7 +547,14 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
input->type = type;
input->input_vtl = input_vtl;
- status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input, output);
+ if (*state_page) {
+ input->flags.map_location_provided = 1;
+ input->requested_map_location =
+ page_to_pfn(*state_page);
+ }
+
+ status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input,
+ output);
if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
if (hv_result_success(status))
@@ -565,8 +572,39 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
return ret;
}
-int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl)
+static bool mshv_use_overlay_gpfn(void)
+{
+ return hv_l1vh_partition() &&
+ mshv_root.vmm_caps.vmm_can_provide_overlay_gpfn;
+}
+
+int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl,
+ struct page **state_page)
+{
+ int ret = 0;
+ struct page *allocated_page = NULL;
+
+ if (mshv_use_overlay_gpfn()) {
+ allocated_page = alloc_page(GFP_KERNEL);
+ if (!allocated_page)
+ return -ENOMEM;
+ *state_page = allocated_page;
+ } else {
+ *state_page = NULL;
+ }
+
+ ret = hv_call_map_vp_state_page(partition_id, vp_index, type, input_vtl,
+ state_page);
+
+ if (ret && allocated_page)
+ __free_page(allocated_page);
+
+ return ret;
+}
+
+static int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl)
{
unsigned long flags;
u64 status;
@@ -590,6 +628,17 @@ int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
return hv_result_to_errno(status);
}
+int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ struct page *state_page, union hv_input_vtl input_vtl)
+{
+ int ret = hv_call_unmap_vp_state_page(partition_id, vp_index, type, input_vtl);
+
+ if (mshv_use_overlay_gpfn() && state_page)
+ __free_page(state_page);
+
+ return ret;
+}
+
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_main.c b/drivers/hv/mshv_root_main.c
index e199770ecdfa..2d0ad17acde6 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -890,7 +890,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
{
struct mshv_create_vp args;
struct mshv_vp *vp;
- struct page *intercept_message_page, *register_page, *ghcb_page;
+ struct page *intercept_msg_page, *register_page, *ghcb_page;
void *stats_pages[2];
long ret;
@@ -908,28 +908,25 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
if (ret)
return ret;
- ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
- input_vtl_zero,
- &intercept_message_page);
+ ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
+ input_vtl_zero, &intercept_msg_page);
if (ret)
goto destroy_vp;
if (!mshv_partition_encrypted(partition)) {
- ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_REGISTERS,
- input_vtl_zero,
- ®ister_page);
+ ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_REGISTERS,
+ input_vtl_zero, ®ister_page);
if (ret)
goto unmap_intercept_message_page;
}
if (mshv_partition_encrypted(partition) &&
is_ghcb_mapping_available()) {
- ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_GHCB,
- input_vtl_normal,
- &ghcb_page);
+ ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_GHCB,
+ input_vtl_normal, &ghcb_page);
if (ret)
goto unmap_register_page;
}
@@ -960,7 +957,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
atomic64_set(&vp->run.vp_signaled_count, 0);
vp->vp_index = args.vp_index;
- vp->vp_intercept_msg_page = page_to_virt(intercept_message_page);
+ vp->vp_intercept_msg_page = page_to_virt(intercept_msg_page);
if (!mshv_partition_encrypted(partition))
vp->vp_register_page = page_to_virt(register_page);
@@ -993,21 +990,19 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
unmap_ghcb_page:
- if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available()) {
- hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_GHCB,
- input_vtl_normal);
- }
+ if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
+ hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_GHCB, ghcb_page,
+ input_vtl_normal);
unmap_register_page:
- if (!mshv_partition_encrypted(partition)) {
- hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_REGISTERS,
- input_vtl_zero);
- }
+ if (!mshv_partition_encrypted(partition))
+ hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_REGISTERS,
+ register_page, input_vtl_zero);
unmap_intercept_message_page:
- hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
- input_vtl_zero);
+ hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
+ intercept_msg_page, input_vtl_zero);
destroy_vp:
hv_call_delete_vp(partition->pt_id, args.vp_index);
return ret;
@@ -1748,24 +1743,27 @@ static void destroy_partition(struct mshv_partition *partition)
mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
if (vp->vp_register_page) {
- (void)hv_call_unmap_vp_state_page(partition->pt_id,
- vp->vp_index,
- HV_VP_STATE_PAGE_REGISTERS,
- input_vtl_zero);
+ (void)hv_unmap_vp_state_page(partition->pt_id,
+ vp->vp_index,
+ HV_VP_STATE_PAGE_REGISTERS,
+ virt_to_page(vp->vp_register_page),
+ input_vtl_zero);
vp->vp_register_page = NULL;
}
- (void)hv_call_unmap_vp_state_page(partition->pt_id,
- vp->vp_index,
- HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
- input_vtl_zero);
+ (void)hv_unmap_vp_state_page(partition->pt_id,
+ vp->vp_index,
+ HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
+ virt_to_page(vp->vp_intercept_msg_page),
+ input_vtl_zero);
vp->vp_intercept_msg_page = NULL;
if (vp->vp_ghcb_page) {
- (void)hv_call_unmap_vp_state_page(partition->pt_id,
- vp->vp_index,
- HV_VP_STATE_PAGE_GHCB,
- input_vtl_normal);
+ (void)hv_unmap_vp_state_page(partition->pt_id,
+ vp->vp_index,
+ HV_VP_STATE_PAGE_GHCB,
+ virt_to_page(vp->vp_ghcb_page),
+ input_vtl_normal);
vp->vp_ghcb_page = NULL;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v4 5/5] mshv: Introduce new hypercall to map stats page for L1VH partitions
From: Nuno Das Neves @ 2025-09-26 16:23 UTC (permalink / raw)
To: linux-hyperv, linux-kernel, prapal, easwar.hariharan, tiala,
anirudh, paekkaladevi, skinsburskii
Cc: kys, haiyangz, wei.liu, decui, Jinank Jain, Nuno Das Neves
In-Reply-To: <1758903795-18636-1-git-send-email-nunodasneves@linux.microsoft.com>
From: Jinank Jain <jinankjain@linux.microsoft.com>
Introduce HVCALL_MAP_STATS_PAGE2 which provides a map location (GPFN)
to map the stats to. This hypercall is required for L1VH partitions,
depending on the hypervisor version. This uses the same check as the
state page map location; mshv_use_overlay_gpfn().
Add mshv_map_vp_state_page() helpers to use this new hypercall or the
old one depending on availability.
For unmapping, the original HVCALL_UNMAP_STATS_PAGE works for both
cases.
Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: Easwar Hariharan <easwar.hariharan@linux.microsoft.com>
---
drivers/hv/mshv_root.h | 10 ++--
drivers/hv/mshv_root_hv_call.c | 93 ++++++++++++++++++++++++++++++++--
drivers/hv/mshv_root_main.c | 22 ++++----
include/hyperv/hvgdk_mini.h | 1 +
include/hyperv/hvhdk_mini.h | 7 +++
5 files changed, 113 insertions(+), 20 deletions(-)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index dbe2d1d0b22f..0dfccfbe6123 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -297,11 +297,11 @@ int hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,
int hv_call_disconnect_port(u64 connection_partition_id,
union hv_connection_id connection_id);
int hv_call_notify_port_ring_empty(u32 sint_index);
-int hv_call_map_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity,
- void **addr);
-int hv_call_unmap_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity);
+int hv_map_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ void **addr);
+int hv_unmap_stats_page(enum hv_stats_object_type type, void *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,
u32 flags, u8 acquire);
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 98c6278ff151..5a805b3dec0b 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -804,9 +804,51 @@ hv_call_notify_port_ring_empty(u32 sint_index)
return hv_result_to_errno(status);
}
-int hv_call_map_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity,
- void **addr)
+static int hv_call_map_stats_page2(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ u64 map_location)
+{
+ unsigned long flags;
+ struct hv_input_map_stats_page2 *input;
+ u64 status;
+ int ret;
+
+ if (!map_location || !mshv_use_overlay_gpfn())
+ return -EINVAL;
+
+ do {
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+
+ memset(input, 0, sizeof(*input));
+ input->type = type;
+ input->identity = *identity;
+ input->map_location = map_location;
+
+ status = hv_do_hypercall(HVCALL_MAP_STATS_PAGE2, input, NULL);
+
+ local_irq_restore(flags);
+
+ ret = hv_result_to_errno(status);
+
+ if (!ret)
+ break;
+
+ if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
+ hv_status_debug(status, "\n");
+ break;
+ }
+
+ ret = hv_call_deposit_pages(NUMA_NO_NODE,
+ hv_current_partition_id, 1);
+ } while (!ret);
+
+ return ret;
+}
+
+static int hv_call_map_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ void **addr)
{
unsigned long flags;
struct hv_input_map_stats_page *input;
@@ -845,8 +887,36 @@ int hv_call_map_stat_page(enum hv_stats_object_type type,
return ret;
}
-int hv_call_unmap_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity)
+int hv_map_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ void **addr)
+{
+ int ret;
+ struct page *allocated_page = NULL;
+
+ if (!addr)
+ return -EINVAL;
+
+ if (mshv_use_overlay_gpfn()) {
+ allocated_page = alloc_page(GFP_KERNEL);
+ if (!allocated_page)
+ return -ENOMEM;
+
+ ret = hv_call_map_stats_page2(type, identity,
+ page_to_pfn(allocated_page));
+ *addr = page_address(allocated_page);
+ } else {
+ ret = hv_call_map_stats_page(type, identity, addr);
+ }
+
+ if (ret && allocated_page)
+ __free_page(allocated_page);
+
+ return ret;
+}
+
+static int hv_call_unmap_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity)
{
unsigned long flags;
struct hv_input_unmap_stats_page *input;
@@ -865,6 +935,19 @@ int hv_call_unmap_stat_page(enum hv_stats_object_type type,
return hv_result_to_errno(status);
}
+int hv_unmap_stats_page(enum hv_stats_object_type type, void *page_addr,
+ const union hv_stats_object_identity *identity)
+{
+ int ret;
+
+ ret = hv_call_unmap_stats_page(type, identity);
+
+ if (mshv_use_overlay_gpfn() && page_addr)
+ __free_page(virt_to_page(page_addr));
+
+ return ret;
+}
+
int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
u64 page_struct_count, u32 host_access,
u32 flags, u8 acquire)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 2d0ad17acde6..71a8ab5db3b8 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -841,7 +841,8 @@ mshv_vp_release(struct inode *inode, struct file *filp)
return 0;
}
-static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
+static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index,
+ void *stats_pages[])
{
union hv_stats_object_identity identity = {
.vp.partition_id = partition_id,
@@ -849,10 +850,10 @@ static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
};
identity.vp.stats_area_type = HV_STATS_AREA_SELF;
- hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+ hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
- hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+ hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
}
static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
@@ -865,14 +866,14 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
int err;
identity.vp.stats_area_type = HV_STATS_AREA_SELF;
- err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
- &stats_pages[HV_STATS_AREA_SELF]);
+ err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
+ &stats_pages[HV_STATS_AREA_SELF]);
if (err)
return err;
identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
- err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
- &stats_pages[HV_STATS_AREA_PARENT]);
+ err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
+ &stats_pages[HV_STATS_AREA_PARENT]);
if (err)
goto unmap_self;
@@ -880,7 +881,7 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
unmap_self:
identity.vp.stats_area_type = HV_STATS_AREA_SELF;
- hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+ hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
return err;
}
@@ -988,7 +989,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
kfree(vp);
unmap_stats_pages:
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
- mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
+ mshv_vp_stats_unmap(partition->pt_id, args.vp_index, stats_pages);
unmap_ghcb_page:
if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
@@ -1740,7 +1741,8 @@ static void destroy_partition(struct mshv_partition *partition)
continue;
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
- mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
+ mshv_vp_stats_unmap(partition->pt_id, vp->vp_index,
+ (void **)vp->vp_stats_pages);
if (vp->vp_register_page) {
(void)hv_unmap_vp_state_page(partition->pt_id,
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index ff4325fb623a..f66565106d21 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -493,6 +493,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
#define HVCALL_GET_PARTITION_PROPERTY_EX 0x0101
#define HVCALL_MMIO_READ 0x0106
#define HVCALL_MMIO_WRITE 0x0107
+#define HVCALL_MAP_STATS_PAGE2 0x0131
/* HV_HYPERCALL_INPUT */
#define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
index bf2ce27dfcc5..064bf735cab6 100644
--- a/include/hyperv/hvhdk_mini.h
+++ b/include/hyperv/hvhdk_mini.h
@@ -177,6 +177,13 @@ struct hv_input_map_stats_page {
union hv_stats_object_identity identity;
} __packed;
+struct hv_input_map_stats_page2 {
+ u32 type; /* enum hv_stats_object_type */
+ u32 padding;
+ union hv_stats_object_identity identity;
+ u64 map_location;
+} __packed;
+
struct hv_output_map_stats_page {
u64 map_location;
} __packed;
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox