* [PATCH] mshv: Do pre-commit before mapping a guest VM GPA
@ 2026-11-07 2:48 Mukesh R
2026-11-07 3:11 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Mukesh R @ 2026-11-07 2:48 UTC (permalink / raw)
To: linux-hyperv, linux-kernel; +Cc: haiyangz, wei.liu, decui, mhkelley58
Pre commit reduces TLB flush overheads in the hypervisor. On systems
with large number of CPUs, there is signficant advantage to doing
this. Pre-commit allows the hypervisor to build all data structs needed
in anticipation of the map gpa hypercall, thus reducing or eliminating
deposit page returns from the map gpa hypercall, and as a result reducing
TLB flushes. In some performance benchmarks, it made the maps of large
ranges almost 30% faster.
Signed-off-by: Mukesh R <mrathor@linux.microsoft.com>
---
drivers/hv/mshv_root_hv_call.c | 52 ++++++++++++++++++++++++++++++++++
include/asm-generic/mshyperv.h | 3 ++
include/hyperv/hvgdk_mini.h | 1 +
include/hyperv/hvhdk.h | 7 +++++
4 files changed, 63 insertions(+)
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index bf0f90515dad..5c44ba8f72b0 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -181,6 +181,54 @@ int hv_call_delete_partition(u64 partition_id)
return hv_result_to_errno(status);
}
+/*
+ * Pre commit to reduce TLB flush overheads in the hypervisor. On systems with
+ * large CPUs, there is signficant advantage to doing this. Pre-commit allows
+ * the hypervisor to build all data structs needed in anticipation of the map
+ * gpa hypercall, thus reducing or eliminating deposit page returns from the
+ * map gpa hypercall, and as a result reducing TLB flushes.
+ */
+static int hv_pre_commit_gpa(u64 pt_id, u64 gfn, u64 num_pages)
+{
+ ulong irq_flags;
+ struct hv_input_precommit_gpa_pages *input_page;
+ int rc = 0;
+ u64 status, done = 0;
+
+ while (done < num_pages) {
+ u16 rep_count = min(num_pages - done, HV_REP_COUNT_MAX);
+
+ local_irq_save(irq_flags);
+ input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
+ memset(input_page, 0, sizeof(*input_page));
+
+ input_page->partition_id = pt_id;
+ input_page->target_gpa_base = gfn;
+
+ status = hv_do_rep_hypercall(HVCALL_PRECOMMIT_GPA_PAGES,
+ rep_count, 0, input_page, NULL);
+ local_irq_restore(irq_flags);
+
+ done += hv_repcomp(status);
+ gfn += hv_repcomp(status);
+
+ if (hv_result_needs_memory(status)) {
+ rc = hv_deposit_memory(pt_id, status);
+ if (rc)
+ break;
+
+ } else if (!hv_result_success(status)) {
+ pr_err("%s: failed to pre commit for gfn %#llx done %llu/%llu, status=%#llx (%s)\n",
+ __func__, gfn, done, num_pages, status,
+ hv_result_to_string(hv_result(status)));
+ rc = hv_result_to_errno(status);
+ break;
+ }
+ }
+
+ return rc;
+}
+
/* Ask the hypervisor to map guest ram pages or the guest mmio space */
static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
u32 flags, struct page **pages, u64 mmio_spa)
@@ -205,6 +253,10 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
page_count >>= large_shift;
}
+ ret = hv_pre_commit_gpa(partition_id, gfn, page_struct_count);
+ if (ret)
+ return ret;
+
while (done < page_count) {
ulong i, completed, remain = page_count - done;
int rep_count = min(remain, HV_MAP_GPA_BATCH_SIZE);
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index 4c9f3353074a..a3d497d99b0b 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -28,6 +28,9 @@
#define VTPM_BASE_ADDRESS 0xfed40000
+#define HV_REP_COUNT_MAX \
+ (HV_HYPERCALL_REP_COMP_MASK >> HV_HYPERCALL_REP_COMP_OFFSET)
+
enum hv_partition_type {
HV_PARTITION_TYPE_GUEST,
HV_PARTITION_TYPE_ROOT,
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index 966945f0504f..16c41ee5079a 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -489,6 +489,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
#define HVCALL_CREATE_DEVICE_DOMAIN 0x00b1
#define HVCALL_ATTACH_DEVICE_DOMAIN 0x00b2
+#define HVCALL_PRECOMMIT_GPA_PAGES 0x00be
#define HVCALL_SIGNAL_EVENT_DIRECT 0x00c0
#define HVCALL_POST_MESSAGE_DIRECT 0x00c1
#define HVCALL_DISPATCH_VP 0x00c2
diff --git a/include/hyperv/hvhdk.h b/include/hyperv/hvhdk.h
index 0c89c62c9706..3944e51d0a2d 100644
--- a/include/hyperv/hvhdk.h
+++ b/include/hyperv/hvhdk.h
@@ -953,4 +953,11 @@ struct hv_input_modify_sparse_spa_page_host_access {
#define HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE 0x4
#define HV_MODIFY_SPA_PAGE_HOST_ACCESS_HUGE_PAGE 0x8
+struct hv_input_precommit_gpa_pages { /* HV_INPUT_PRECOMMIT_GPA_PAGES */
+ u64 partition_id;
+ u32 flags;
+ u32 reserved;
+ u64 target_gpa_base;
+} __packed;
+
#endif /* _HV_HVHDK_H */
--
2.51.2.vfs.0.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] mshv: Do pre-commit before mapping a guest VM GPA
2026-11-07 2:48 [PATCH] mshv: Do pre-commit before mapping a guest VM GPA Mukesh R
@ 2026-11-07 3:11 ` sashiko-bot
2026-11-10 0:08 ` Mukesh R
0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-11-07 3:11 UTC (permalink / raw)
To: Mukesh R, linux-hyperv
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [High] mshv: GFP_KERNEL allocation in hv_pre_commit_gpa() during MMU
invalidation
--
commit 326d99247cfb889e00e7b24f8217bcbc574b15e8
Author: Mukesh R <mrathor@linux.microsoft.com>
mshv: Do pre-commit before mapping a guest VM GPA
This commit adds an HVCALL_PRECOMMIT_GPA_PAGES step before mapping guest
GPAs in hv_do_map_gpa_hcall() to pre-allocate hypervisor data structures
and reduce TLB flushes.
> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
> index e761addda50e1..595d044fb9264 100644
> --- a/drivers/hv/mshv_root_hv_call.c
> +++ b/drivers/hv/mshv_root_hv_call.c
> @@ -181,6 +181,54 @@ int hv_call_delete_partition(u64 partition_id)
> return hv_result_to_errno(status);
> }
>
> +/*
> + * Pre commit to reduce TLB flush overheads in the hypervisor. On systems with
> + * large CPUs, there is signficant advantage to doing this. Pre-commit allows
> + * the hypervisor to build all data structs needed in anticipation of the map
> + * gpa hypercall, thus reducing or eliminating deposit page returns from the
> + * map gpa hypercall, and as a result reducing TLB flushes.
> + */
> +static int hv_pre_commit_gpa(u64 pt_id, u64 gfn, u64 num_pages)
> +{
> + ulong irq_flags;
> + struct hv_input_precommit_gpa_pages *input_page;
> + int rc = 0;
> + u64 status, done = 0;
> +
> + while (done < num_pages) {
> + u16 rep_count = min(num_pages - done, HV_REP_COUNT_MAX);
> +
> + local_irq_save(irq_flags);
> + input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
> + memset(input_page, 0, sizeof(*input_page));
> +
> + input_page->partition_id = pt_id;
> + input_page->target_gpa_base = gfn;
> +
> + status = hv_do_rep_hypercall(HVCALL_PRECOMMIT_GPA_PAGES,
> + rep_count, 0, input_page, NULL);
> + local_irq_restore(irq_flags);
> +
> + done += hv_repcomp(status);
> + gfn += hv_repcomp(status);
> +
> + if (hv_result_needs_memory(status)) {
> + rc = hv_deposit_memory(pt_id, status);
> + if (rc)
> + break;
> +
> + } else if (!hv_result_success(status)) {
> + pr_err("%s: failed to pre commit for gfn %#llx done %llu/%llu, status=%#llx (%s)\n",
> + __func__, gfn, done, num_pages, status,
> + hv_result_to_string(hv_result(status)));
> + rc = hv_result_to_errno(status);
> + break;
> + }
> + }
> +
> + return rc;
> +}
> +
> /* Ask the hypervisor to map guest ram pages or the guest mmio space */
> static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
> u32 flags, struct page **pages, u64 mmio_spa)
> @@ -205,6 +253,10 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
> page_count >>= large_shift;
> }
>
> + ret = hv_pre_commit_gpa(partition_id, gfn, page_struct_count);
> + if (ret)
> + return ret;
[Severity: High]
Can calling hv_pre_commit_gpa() unconditionally here sleep and enter
direct reclaim from an MMU notifier invalidation callback?
mshv_region_interval_invalidate() calls mshv_region_remap_pages() with
HV_MAP_GPA_NO_ACCESS while holding region->mreg_mutex inside
mmu_notifier_invalidate_range_start(), including when
!mmu_notifier_range_blockable(range):
drivers/hv/mshv_regions.c:mshv_region_interval_invalidate() {
...
if (mmu_notifier_range_blockable(range))
mutex_lock(®ion->mreg_mutex);
else if (!mutex_trylock(®ion->mreg_mutex))
goto out_fail;
mmu_interval_set_seq(mni, cur_seq);
ret = mshv_region_remap_pages(region, HV_MAP_GPA_NO_ACCESS,
page_offset, page_count);
...
}
That reaches hv_do_map_gpa_hcall(), which now calls hv_pre_commit_gpa()
with page_struct_count (4K pages, with input_page->flags = 0 even when the
region was mapped with HV_MAP_GPA_LARGE_PAGE). If the hypervisor asks for
more memory (for example to shatter a 2M SLAT entry into 4K entries),
hv_deposit_memory() -> hv_call_deposit_pages() allocates pages with
GFP_KERNEL. This sleeps in non-blockable MMU notifier context, triggers a
lockdep splat on __mmu_notifier_invalidate_range_start_map in
fs_reclaim_acquire(), and can deadlock if direct reclaim re-enters
mshv_region_interval_invalidate() on region->mreg_mutex.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107024842.3929415-1-mrathor@linux.microsoft.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] mshv: Do pre-commit before mapping a guest VM GPA
2026-11-07 3:11 ` sashiko-bot
@ 2026-11-10 0:08 ` Mukesh R
0 siblings, 0 replies; 3+ messages in thread
From: Mukesh R @ 2026-11-10 0:08 UTC (permalink / raw)
To: sashiko-reviews, linux-hyperv
On 11/6/26 19:11, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential
> issue to consider:
> - [High] mshv: GFP_KERNEL allocation in hv_pre_commit_gpa() during MMU
> invalidation
> --
>
> commit 326d99247cfb889e00e7b24f8217bcbc574b15e8
> Author: Mukesh R <mrathor@linux.microsoft.com>
>
> mshv: Do pre-commit before mapping a guest VM GPA
>
> This commit adds an HVCALL_PRECOMMIT_GPA_PAGES step before mapping guest
> GPAs in hv_do_map_gpa_hcall() to pre-allocate hypervisor data structures
> and reduce TLB flushes.
>
>> diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
>> index e761addda50e1..595d044fb9264 100644
>> --- a/drivers/hv/mshv_root_hv_call.c
>> +++ b/drivers/hv/mshv_root_hv_call.c
>> @@ -181,6 +181,54 @@ int hv_call_delete_partition(u64 partition_id)
>> return hv_result_to_errno(status);
>> }
>>
>> +/*
>> + * Pre commit to reduce TLB flush overheads in the hypervisor. On systems with
>> + * large CPUs, there is signficant advantage to doing this. Pre-commit allows
>> + * the hypervisor to build all data structs needed in anticipation of the map
>> + * gpa hypercall, thus reducing or eliminating deposit page returns from the
>> + * map gpa hypercall, and as a result reducing TLB flushes.
>> + */
>> +static int hv_pre_commit_gpa(u64 pt_id, u64 gfn, u64 num_pages)
>> +{
>> + ulong irq_flags;
>> + struct hv_input_precommit_gpa_pages *input_page;
>> + int rc = 0;
>> + u64 status, done = 0;
>> +
>> + while (done < num_pages) {
>> + u16 rep_count = min(num_pages - done, HV_REP_COUNT_MAX);
>> +
>> + local_irq_save(irq_flags);
>> + input_page = *this_cpu_ptr(hyperv_pcpu_input_arg);
>> + memset(input_page, 0, sizeof(*input_page));
>> +
>> + input_page->partition_id = pt_id;
>> + input_page->target_gpa_base = gfn;
>> +
>> + status = hv_do_rep_hypercall(HVCALL_PRECOMMIT_GPA_PAGES,
>> + rep_count, 0, input_page, NULL);
>> + local_irq_restore(irq_flags);
>> +
>> + done += hv_repcomp(status);
>> + gfn += hv_repcomp(status);
>> +
>> + if (hv_result_needs_memory(status)) {
>> + rc = hv_deposit_memory(pt_id, status);
>> + if (rc)
>> + break;
>> +
>> + } else if (!hv_result_success(status)) {
>> + pr_err("%s: failed to pre commit for gfn %#llx done %llu/%llu, status=%#llx (%s)\n",
>> + __func__, gfn, done, num_pages, status,
>> + hv_result_to_string(hv_result(status)));
>> + rc = hv_result_to_errno(status);
>> + break;
>> + }
>> + }
>> +
>> + return rc;
>> +}
>> +
>> /* Ask the hypervisor to map guest ram pages or the guest mmio space */
>> static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
>> u32 flags, struct page **pages, u64 mmio_spa)
>> @@ -205,6 +253,10 @@ static int hv_do_map_gpa_hcall(u64 partition_id, u64 gfn, u64 page_struct_count,
>> page_count >>= large_shift;
>> }
>>
>> + ret = hv_pre_commit_gpa(partition_id, gfn, page_struct_count);
>> + if (ret)
>> + return ret;
>
> [Severity: High]
> Can calling hv_pre_commit_gpa() unconditionally here sleep and enter
> direct reclaim from an MMU notifier invalidation callback?
>
> mshv_region_interval_invalidate() calls mshv_region_remap_pages() with
> HV_MAP_GPA_NO_ACCESS while holding region->mreg_mutex inside
> mmu_notifier_invalidate_range_start(), including when
> !mmu_notifier_range_blockable(range):
>
> drivers/hv/mshv_regions.c:mshv_region_interval_invalidate() {
> ...
> if (mmu_notifier_range_blockable(range))
> mutex_lock(®ion->mreg_mutex);
> else if (!mutex_trylock(®ion->mreg_mutex))
> goto out_fail;
>
> mmu_interval_set_seq(mni, cur_seq);
>
> ret = mshv_region_remap_pages(region, HV_MAP_GPA_NO_ACCESS,
> page_offset, page_count);
> ...
> }
>
> That reaches hv_do_map_gpa_hcall(), which now calls hv_pre_commit_gpa()
> with page_struct_count (4K pages, with input_page->flags = 0 even when the
> region was mapped with HV_MAP_GPA_LARGE_PAGE). If the hypervisor asks for
> more memory (for example to shatter a 2M SLAT entry into 4K entries),
> hv_deposit_memory() -> hv_call_deposit_pages() allocates pages with
> GFP_KERNEL. This sleeps in non-blockable MMU notifier context, triggers a
> lockdep splat on __mmu_notifier_invalidate_range_start_map in
> fs_reclaim_acquire(), and can deadlock if direct reclaim re-enters
> mshv_region_interval_invalidate() on region->mreg_mutex.
>
All pre-commit does is pre-deposit pages, so map hypercall right below
doesn't keep coming back with insufficient memory. So there is no
regression here... As for the reclaim notifier issue, it needs to
be addressed for movable pages, or rather, movable pages needs to
be re-assessed to resolve such issues.
Thanks,
-Mukesh
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-10 0:08 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-07 2:48 [PATCH] mshv: Do pre-commit before mapping a guest VM GPA Mukesh R
2026-11-07 3:11 ` sashiko-bot
2026-11-10 0:08 ` Mukesh R
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox