mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/3] RISC-V: KVM: Fix hugepage mapping handling during dirty logging
@ 2026-03-04  9:21 wang.yechao255
  2026-03-04  9:26 ` [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages " wang.yechao255
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: wang.yechao255 @ 2026-03-04  9:21 UTC (permalink / raw)
  To: anup, atish.patra, pjw, palmer, aou, alex
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, wang.yechao255

From: Wang Yechao <wang.yechao255@zte.com.cn>

This series fixes an issue where dirty logging fails to work correctly
with huge pages.

v1 -> v2:
  - fix the lost write protection on huge pages
  - split huge pages before handling the write protection fault.

  - Link to v1:
  https://lore.kernel.org/linux-riscv/20260226172245358qVZavIykLL2QC0KoqTO-I@zte.com.cn/T/#u

Wang Yechao (3):
  RISC-V: KVM: Fix lost write protection on huge pages during dirty
    logging
  RISC-V: KVM: Allow splitting huge pages to arbitrary level
  RISC-V: KVM: Split huge pages during fault handling for dirty logging

 arch/riscv/include/asm/kvm_gstage.h |  4 ++
 arch/riscv/kvm/gstage.c             | 85 +++++++++++++++++++++++++++--
 2 files changed, 85 insertions(+), 4 deletions(-)

-- 
2.47.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages  during dirty logging
  2026-03-04  9:21 [PATCH v2 0/3] RISC-V: KVM: Fix hugepage mapping handling during dirty logging wang.yechao255
@ 2026-03-04  9:26 ` wang.yechao255
  2026-03-06  3:19   ` Nutty.Liu
  2026-03-04  9:28 ` [PATCH v2 2/3] RISC-V: KVM: Allow splitting huge pages to arbitrary level wang.yechao255
  2026-03-04  9:30 ` [PATCH v2 3/3] RISC-V: KVM: Split huge pages during fault handling for dirty logging wang.yechao255
  2 siblings, 1 reply; 5+ messages in thread
From: wang.yechao255 @ 2026-03-04  9:26 UTC (permalink / raw)
  To: anup, atish.patra, pjw, palmer, aou, alex
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, wang.yechao255

From: Wang Yechao <wang.yechao255@zte.com.cn>

When enabling dirty log in small chunks (e.g., QEMU default chunk
size of 256K), the chunk size is always smaller than the page size
of huge pages (1G or 2M) used in the gstage page tables. This caused
the write protection to be incorrectly skipped for huge PTEs because
the condition `(end - addr) >= page_size` was not satisfied.

Remove the size check in `kvm_riscv_gstage_wp_range()` to ensure huge
PTEs are always write-protected regardless of the chunk size. Additionally,
explicitly align the address down to the page size before invoking
`kvm_riscv_gstage_op_pte()` to guarantee that the address passed to the
operation function is page-aligned.

This fixes the issue where dirty pages might not be tracked correctly
when using huge pages.

Signed-off-by: Wang Yechao <wang.yechao255@zte.com.cn>
---
 arch/riscv/kvm/gstage.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index b67d60d722c2..d2001d508046 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -304,10 +304,9 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
 		if (!found_leaf)
 			goto next;

-		if (!(addr & (page_size - 1)) && ((end - addr) >= page_size))
-			kvm_riscv_gstage_op_pte(gstage, addr, ptep,
-						ptep_level, GSTAGE_OP_WP);
-
+		addr = ALIGN_DOWN(addr, page_size);
+		kvm_riscv_gstage_op_pte(gstage, addr, ptep,
+					ptep_level, GSTAGE_OP_WP);
 next:
 		addr += page_size;
 	}
-- 
2.47.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 2/3] RISC-V: KVM: Allow splitting huge pages to arbitrary   level
  2026-03-04  9:21 [PATCH v2 0/3] RISC-V: KVM: Fix hugepage mapping handling during dirty logging wang.yechao255
  2026-03-04  9:26 ` [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages " wang.yechao255
@ 2026-03-04  9:28 ` wang.yechao255
  2026-03-04  9:30 ` [PATCH v2 3/3] RISC-V: KVM: Split huge pages during fault handling for dirty logging wang.yechao255
  2 siblings, 0 replies; 5+ messages in thread
From: wang.yechao255 @ 2026-03-04  9:28 UTC (permalink / raw)
  To: anup, atish.patra, pjw, palmer, aou, alex
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, wang.yechao255

From: Wang Yechao <wang.yechao255@zte.com.cn>

This patch introduces the function kvm_riscv_gstage_split_huge().
It splits the huge page covering a given guest physical address down
to a specified target level (e.g., from 1G to 2M or 4K). The caller
provides a memory cache for allocating any intermediate page tables
and may request a TLB flush after the split.

This functionality will be used by subsequent patches to split huge
pages before handling the write-protection fault, or for other operations
that require page-level granularity.

Signed-off-by: Wang Yechao <wang.yechao255@zte.com.cn>
---
 arch/riscv/include/asm/kvm_gstage.h |  4 ++
 arch/riscv/kvm/gstage.c             | 69 +++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
index 595e2183173e..373748c6745e 100644
--- a/arch/riscv/include/asm/kvm_gstage.h
+++ b/arch/riscv/include/asm/kvm_gstage.h
@@ -53,6 +53,10 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
 			      bool page_rdonly, bool page_exec,
 			      struct kvm_gstage_mapping *out_map);

+int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,
+                                struct kvm_mmu_memory_cache *pcache,
+                                gpa_t addr, u32 target_level, bool flush);
+
 enum kvm_riscv_gstage_op {
 	GSTAGE_OP_NOP = 0,	/* Nothing */
 	GSTAGE_OP_CLEAR,	/* Clear/Unmap */
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index d2001d508046..92331e9b0bb8 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -209,6 +209,75 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
 	return kvm_riscv_gstage_set_pte(gstage, pcache, out_map);
 }

+static inline unsigned long make_child_pte(unsigned long huge_pte, int index,
+					   unsigned long child_page_size)
+{
+	unsigned long child_pte = huge_pte;
+	unsigned long child_pfn_offset;
+
+	/*
+	 * The child_pte already has the base address of the huge page being
+	 * split. So we just have to OR in the offset to the page at the next
+	 * lower level for the given index.
+	 */
+	child_pfn_offset = index * (child_page_size / PAGE_SIZE);
+	child_pte |= pte_val(pfn_pte(child_pfn_offset, __pgprot(0)));
+
+	return child_pte;
+}
+
+int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,
+				struct kvm_mmu_memory_cache *pcache,
+				gpa_t addr, u32 target_level, bool flush)
+{
+	u32 current_level = kvm_riscv_gstage_pgd_levels - 1;
+	pte_t *next_ptep = (pte_t *)gstage->pgd;
+	pte_t *ptep;
+	unsigned long huge_pte, child_pte;
+	unsigned long child_page_size;
+	int i, ret;
+
+	while(current_level > target_level) {
+		ptep = (pte_t *)&next_ptep[gstage_pte_index(addr, current_level)];
+
+		if (!pte_val(ptep_get(ptep)))
+			break;
+
+		if (!gstage_pte_leaf(ptep)) {
+			next_ptep = (pte_t *)gstage_pte_page_vaddr(ptep_get(ptep));
+			current_level--;
+			continue;
+		}
+
+		huge_pte = pte_val(ptep_get(ptep));
+
+		ret = gstage_level_to_page_size(current_level - 1, &child_page_size);
+		if (ret)
+			return ret;
+
+		if (!pcache)
+			return -ENOMEM;
+		next_ptep = kvm_mmu_memory_cache_alloc(pcache);
+		if (!next_ptep)
+			return -ENOMEM;
+
+		set_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)),
+				__pgprot(_PAGE_TABLE)));
+
+		for (i = 0; i < PTRS_PER_PTE; i++) {
+			child_pte = make_child_pte(huge_pte, i, child_page_size);
+			set_pte((pte_t *)&next_ptep[i], __pte(child_pte));
+		}
+
+		if (flush)
+			gstage_tlb_flush(gstage, current_level, addr);
+
+		current_level--;
+	}
+
+	return 0;
+}
+
 void kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,
 			     pte_t *ptep, u32 ptep_level, enum kvm_riscv_gstage_op op)
 {
-- 
2.47.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 3/3] RISC-V: KVM: Split huge pages during fault handling for   dirty logging
  2026-03-04  9:21 [PATCH v2 0/3] RISC-V: KVM: Fix hugepage mapping handling during dirty logging wang.yechao255
  2026-03-04  9:26 ` [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages " wang.yechao255
  2026-03-04  9:28 ` [PATCH v2 2/3] RISC-V: KVM: Allow splitting huge pages to arbitrary level wang.yechao255
@ 2026-03-04  9:30 ` wang.yechao255
  2 siblings, 0 replies; 5+ messages in thread
From: wang.yechao255 @ 2026-03-04  9:30 UTC (permalink / raw)
  To: anup, atish.patra, pjw, palmer, aou, alex
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, wang.yechao255

From: Wang Yechao <wang.yechao255@zte.com.cn>

During dirty logging, all huge pages are write-protected. When the guest
writes to a write-protected huge page, a page fault is triggered. Before
recovering the write permission, the huge page must be split into smaller
pages (e.g., 4K). After splitting, the normal mapping process proceeds,
allowing write permission to be restored at the smaller page granularity.

This ensures that dirty logging works correctly with huge pages.

Signed-off-by: Wang Yechao <wang.yechao255@zte.com.cn>
---
 arch/riscv/kvm/gstage.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 92331e9b0bb8..27ea6bb81553 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -171,6 +171,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
 {
 	pgprot_t prot;
 	int ret;
+	pte_t *ptep;
+	u32 ptep_level;
+	bool found_leaf;

 	out_map->addr = gpa;
 	out_map->level = 0;
@@ -179,6 +182,12 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
 	if (ret)
 		return ret;

+	found_leaf = kvm_riscv_gstage_get_leaf(gstage, gpa, &ptep, &ptep_level);
+	if (found_leaf && ptep_level > out_map->level) {
+		kvm_riscv_gstage_split_huge(gstage, pcache, gpa,
+					    out_map->level, true);
+	}
+
 	/*
 	 * A RISC-V implementation can choose to either:
 	 * 1) Update 'A' and 'D' PTE bits in hardware
-- 
2.47.3

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages during dirty logging
  2026-03-04  9:26 ` [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages " wang.yechao255
@ 2026-03-06  3:19   ` Nutty.Liu
  0 siblings, 0 replies; 5+ messages in thread
From: Nutty.Liu @ 2026-03-06  3:19 UTC (permalink / raw)
  To: wang.yechao255, anup, atish.patra, pjw, palmer, aou, alex
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel


On 3/4/2026 5:26 PM, wang.yechao255@zte.com.cn wrote:
> From: Wang Yechao <wang.yechao255@zte.com.cn>
>
> When enabling dirty log in small chunks (e.g., QEMU default chunk
> size of 256K), the chunk size is always smaller than the page size
> of huge pages (1G or 2M) used in the gstage page tables. This caused
> the write protection to be incorrectly skipped for huge PTEs because
> the condition `(end - addr) >= page_size` was not satisfied.
>
> Remove the size check in `kvm_riscv_gstage_wp_range()` to ensure huge
> PTEs are always write-protected regardless of the chunk size. Additionally,
> explicitly align the address down to the page size before invoking
> `kvm_riscv_gstage_op_pte()` to guarantee that the address passed to the
> operation function is page-aligned.
>
> This fixes the issue where dirty pages might not be tracked correctly
> when using huge pages.
Would it be better to add a description of which specific commit is 
fixed  like  "Fixes: <id>..." ?

Otherwise,
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>

Thanks,
Nutty
> Signed-off-by: Wang Yechao <wang.yechao255@zte.com.cn>
> ---
>   arch/riscv/kvm/gstage.c | 7 +++----
>   1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
> index b67d60d722c2..d2001d508046 100644
> --- a/arch/riscv/kvm/gstage.c
> +++ b/arch/riscv/kvm/gstage.c
> @@ -304,10 +304,9 @@ void kvm_riscv_gstage_wp_range(struct kvm_gstage *gstage, gpa_t start, gpa_t end
>   		if (!found_leaf)
>   			goto next;
>
> -		if (!(addr & (page_size - 1)) && ((end - addr) >= page_size))
> -			kvm_riscv_gstage_op_pte(gstage, addr, ptep,
> -						ptep_level, GSTAGE_OP_WP);
> -
> +		addr = ALIGN_DOWN(addr, page_size);
> +		kvm_riscv_gstage_op_pte(gstage, addr, ptep,
> +					ptep_level, GSTAGE_OP_WP);
>   next:
>   		addr += page_size;
>   	}

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-03-06  3:19 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-03-04  9:21 [PATCH v2 0/3] RISC-V: KVM: Fix hugepage mapping handling during dirty logging wang.yechao255
2026-03-04  9:26 ` [PATCH v2 1/3] RISC-V: KVM: Fix lost write protection on huge pages " wang.yechao255
2026-03-06  3:19   ` Nutty.Liu
2026-03-04  9:28 ` [PATCH v2 2/3] RISC-V: KVM: Allow splitting huge pages to arbitrary level wang.yechao255
2026-03-04  9:30 ` [PATCH v2 3/3] RISC-V: KVM: Split huge pages during fault handling for dirty logging wang.yechao255

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®