git: 5b1f1fd62435 - main - hyperv: Fix single page invalidation path
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 09 Oct 2026 22:13:46 UTC
The branch main has been updated by aokblast:
URL: https://cgit.FreeBSD.org/src/commit/?id=5b1f1fd624352840cd9666447ff9cdebeaf910d4
commit 5b1f1fd624352840cd9666447ff9cdebeaf910d4
Author: ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2026-10-05 19:58:06 +0000
Commit: ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-10-09 22:13:31 +0000
hyperv: Fix single page invalidation path
A single page invalidation sets addr2 == 0. In the original code, it
falsely flush the whole address in non global pmap. However, the
kernel pmap are all PG_G, which means a single page flush will always be
staled and thus become invalid. As a result, we set parameter based on
their op in a new helper function instead of relying on args. This
affects only on AMD platform as Intel has their PTI implementation.
PR: 291577
Tested by: franco@opnsense.org
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D60380
---
sys/dev/hyperv/vmbus/hyperv_mmu.c | 82 ++++++++++++++++++++-------------------
1 file changed, 43 insertions(+), 39 deletions(-)
diff --git a/sys/dev/hyperv/vmbus/hyperv_mmu.c b/sys/dev/hyperv/vmbus/hyperv_mmu.c
index 8e982974161c..0a75a5b070b6 100644
--- a/sys/dev/hyperv/vmbus/hyperv_mmu.c
+++ b/sys/dev/hyperv/vmbus/hyperv_mmu.c
@@ -83,6 +83,39 @@ static inline int fill_gva_list(uint64_t gva_list[],
return gva_n;
}
+static void
+hv_tlb_flush_params(pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2,
+ enum invl_op_codes op, uint64_t *addr_space, uint64_t *flags,
+ vm_offset_t *start, vm_offset_t *end)
+{
+ switch (op) {
+ case INVL_OP_TLB:
+ case INVL_OP_TLB_PCID:
+ case INVL_OP_TLB_INVPCID_PTI:
+ case INVL_OP_TLB_INVPCID:
+ *start = *end = 0;
+ break;
+ case INVL_OP_PG:
+ case INVL_OP_PG_INVPCID:
+ case INVL_OP_PG_PCID:
+ *start = addr1;
+ *end = addr1 + PAGE_SIZE;
+ break;
+ default:
+ *start = addr1;
+ *end = addr2;
+ break;
+ }
+ if (pmap == kernel_pmap || *end == 0) {
+ *addr_space = 0;
+ *flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
+ } else {
+ *addr_space = pmap->pm_cr3 & ~CR3_PCID_MASK;
+ *flags = 0;
+ }
+ if (*end == 0 && pmap != kernel_pmap)
+ *flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
+}
inline int hv_cpumask_to_vpset(struct hv_vpset *vpset,
const cpuset_t *cpus, struct vmbus_softc * sc)
@@ -135,7 +168,7 @@ hv_vm_tlb_flush(pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2,
int cpu, vcpu;
int max_gvas, gva_n;
uint64_t status = 0;
- uint64_t cr3;
+ vm_offset_t start, end;
/*
* Hyper-V doesn't handle the invalidating cache. Let system handle it.
@@ -178,18 +211,9 @@ hv_vm_tlb_flush(pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2,
("hv_tlb_flush: interrupts disabled"));
critical_enter();
flush->processor_mask = 0;
- cr3 = pmap->pm_cr3;
-
- if (op == INVL_OP_TLB || op == INVL_OP_TLB_INVPCID ||
- op == INVL_OP_TLB_INVPCID_PTI || op == INVL_OP_TLB_PCID) {
- flush->address_space = 0;
- flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
- } else {
- flush->address_space = cr3;
- flush->address_space &= ~CR3_PCID_MASK;
- flush->flags = 0;
- }
+ hv_tlb_flush_params(pmap, addr1, addr2, op, &flush->address_space,
+ &flush->flags, &start, &end);
if(CPU_CMP(&mask, &all_cpus) == 0) {
flush->flags |= HV_FLUSH_ALL_PROCESSORS;
} else {
@@ -207,19 +231,14 @@ hv_vm_tlb_flush(pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2,
goto native;
}
max_gvas = (PAGE_SIZE - sizeof(*flush)) / sizeof(flush->gva_list[0]);
- if (addr2 == 0) {
- flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
- status = hypercall_do_md(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE,
- (uint64_t)flush, (uint64_t)NULL);
- } else if ((addr2 && (addr2 -addr1)/HV_TLB_FLUSH_UNIT) > max_gvas) {
+ if (end == 0 || (end - start) / HV_TLB_FLUSH_UNIT > max_gvas) {
status = hypercall_do_md(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE,
(uint64_t)flush, (uint64_t)NULL);
} else {
- gva_n = fill_gva_list(flush->gva_list, addr1, addr2);
+ gva_n = fill_gva_list(flush->gva_list, start, end);
status = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST,
gva_n, 0, (uint64_t)flush, (uint64_t)NULL);
-
}
if(status)
goto native;
@@ -256,22 +275,13 @@ hv_flush_tlb_others_ex(pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2,
return EINVAL;
flush = *VMBUS_PCPU_PTR(sc, cpu_mem, curcpu);
uint64_t status = 0;
- uint64_t cr3;
+ vm_offset_t start, end;
if (!(hyperv_recommends & HYPERV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
return EINVAL;
- cr3 = pmap->pm_cr3;
- if (op == INVL_OP_TLB) {
- flush->address_space = 0;
- flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
- } else {
-
- flush->address_space = cr3;
- flush->address_space &= ~CR3_PCID_MASK;
- flush->flags = 0;
- }
-
+ hv_tlb_flush_params(pmap, addr1, addr2, op, &flush->address_space,
+ &flush->flags, &start, &end);
flush->hv_vp_set.valid_bank_mask = 0;
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
@@ -287,19 +297,13 @@ hv_flush_tlb_others_ex(pmap_t pmap, vm_offset_t addr1, vm_offset_t addr2,
sizeof(flush->hv_vp_set.bank_contents[0])) /
sizeof(flush->hv_vp_set.bank_contents[0]);
- if (addr2 == 0) {
- flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
- status = hv_do_rep_hypercall(
- HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX,
- 0, nr_bank, (uint64_t)flush, (uint64_t)NULL);
- } else if (addr2 &&
- ((addr2 - addr1)/HV_TLB_FLUSH_UNIT) > max_gvas) {
+ if (end == 0 || ((end - start) / HV_TLB_FLUSH_UNIT) > max_gvas) {
status = hv_do_rep_hypercall(
HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX,
0, nr_bank, (uint64_t)flush, (uint64_t)NULL);
} else {
gva_n = fill_gva_list(&flush->hv_vp_set.bank_contents[nr_bank],
- addr1, addr2);
+ start, end);
status = hv_do_rep_hypercall(
HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX,
gva_n, nr_bank, (uint64_t)flush, (uint64_t)NULL);