AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/01 16:23 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "VIRTUALIZATION",
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_riscv_gstage_split_huge",
    "kvm_arch_mmu_enable_log_dirty_pt_masked",
    "kvm_arch_commit_memory_region"
  ],
  "Reasoning": "The patch implements eager page splitting for KVM on RISC-V, modifying the KVM MMU logic. This is a functional change that should be fuzzed to ensure memory management and dirty logging work correctly without introducing memory corruption or crashes.",
  "WorthFuzzing": true
}

1/1 2026/08/01 16:23 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit ed9c905da6bba62c17fc437a73965d32d2440ce2\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sat Aug 1 16:23:18 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt\nindex b5493a7f8f228..d78eda7ee23a5 100644\n--- a/Documentation/admin-guide/kernel-parameters.txt\n+++ b/Documentation/admin-guide/kernel-parameters.txt\n@@ -3047,7 +3047,7 @@ Kernel parameters\n \t\t\tDefault is 0 (don't ignore, but inject #GP)\n \n \tkvm.eager_page_split=\n-\t\t\t[KVM,X86] Controls whether or not KVM will try to\n+\t\t\t[KVM,X86,RISCV] Controls whether or not KVM will try to\n \t\t\tproactively split all huge pages during dirty logging.\n \t\t\tEager page splitting reduces interruptions to vCPU\n \t\t\texecution by eliminating the write-protection faults\n@@ -3067,7 +3067,10 @@ Kernel parameters\n \t\t\tthe KVM_CLEAR_DIRTY ioctl, and only for the pages being\n \t\t\tcleared.\n \n-\t\t\tEager page splitting is only supported when kvm.tdp_mmu=Y.\n+\t\t\tOn x86, eager page splitting is only supported when\n+\t\t\tkvm.tdp_mmu=Y.\n+\n+\t\t\tOn RISCV, eager page splitting is supported by default.\n \n \t\t\tDefault is Y (on).\n \ndiff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h\nindex 21e2019df0cf5..f726279780177 100644\n--- a/arch/riscv/include/asm/kvm_gstage.h\n+++ b/arch/riscv/include/asm/kvm_gstage.h\n@@ -64,9 +64,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n \t\t\t      bool page_rdonly, bool page_exec,\n \t\t\t      struct kvm_gstage_mapping *out_map);\n \n-int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n-\t\t\t\tstruct kvm_mmu_memory_cache *pcache,\n-\t\t\t\tgpa_t addr, u32 target_level, bool flush);\n+bool kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n+\t\t\t\t struct kvm_mmu_memory_cache *pcache,\n+\t\t\t\t gpa_t addr, u32 target_level, bool flush);\n \n enum kvm_riscv_gstage_op {\n \tGSTAGE_OP_NOP = 0,\t/* Nothing */\ndiff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h\nindex e2d5808169e44..71e455d166905 100644\n--- a/arch/riscv/include/asm/kvm_host.h\n+++ b/arch/riscv/include/asm/kvm_host.h\n@@ -86,6 +86,7 @@ struct kvm_arch {\n \tpgd_t *pgd;\n \tphys_addr_t pgd_phys;\n \tunsigned long pgd_levels;\n+\tstruct kvm_mmu_memory_cache pgd_split_page_cache;\n \n \t/* Guest Timer */\n \tstruct kvm_guest_timer timer;\ndiff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c\nindex b0474fcf065aa..01679aef53289 100644\n--- a/arch/riscv/kvm/gstage.c\n+++ b/arch/riscv/kvm/gstage.c\n@@ -307,19 +307,20 @@ static inline unsigned long make_child_pte(unsigned long huge_pte, int index,\n \treturn child_pte;\n }\n \n-int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n-\t\t\t\tstruct kvm_mmu_memory_cache *pcache,\n-\t\t\t\tgpa_t addr, u32 target_level, bool flush)\n+bool kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n+\t\t\t\t struct kvm_mmu_memory_cache *pcache,\n+\t\t\t\t gpa_t addr, u32 target_level, bool flush)\n {\n \tu32 current_level = gstage-\u003epgd_levels - 1;\n \tpte_t *next_ptep = (pte_t *)gstage-\u003epgd;\n \tunsigned long huge_pte, child_pte;\n \tunsigned long child_page_size;\n+\tbool need_flush = false;\n \tpte_t *ptep;\n \tint i, ret;\n \n \tif (!pcache)\n-\t\treturn -ENOMEM;\n+\t\treturn false;\n \n \twhile(current_level \u003e target_level) {\n \t\tptep = (pte_t *)\u0026next_ptep[gstage_pte_index(gstage, addr, current_level)];\n@@ -337,27 +338,35 @@ int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\n \n \t\tret = gstage_level_to_page_size(gstage, current_level - 1, \u0026child_page_size);\n \t\tif (ret)\n-\t\t\treturn ret;\n+\t\t\treturn need_flush;\n \n \t\tnext_ptep = kvm_mmu_memory_cache_alloc(pcache);\n \t\tif (!next_ptep)\n-\t\t\treturn -ENOMEM;\n+\t\t\treturn need_flush;\n \n \t\tfor (i = 0; i \u003c PTRS_PER_PTE; i++) {\n \t\t\tchild_pte = make_child_pte(huge_pte, i, child_page_size);\n \t\t\tset_pte((pte_t *)\u0026next_ptep[i], __pte(child_pte));\n \t\t}\n \n+\t\t/*\n+\t\t * Ensure the writes to the child PTEs are visible before\n+\t\t * linking the new page table to the parent PTE.\n+\t\t */\n+\t\tsmp_wmb();\n+\n \t\tset_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)),\n \t\t\t\t__pgprot(_PAGE_TABLE)));\n \n \t\tif (flush)\n \t\t\tgstage_tlb_flush(gstage, current_level, addr);\n+\t\telse\n+\t\t\tneed_flush = true;\n \n \t\tcurrent_level--;\n \t}\n \n-\treturn 0;\n+\treturn need_flush;\n }\n \n bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,\ndiff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c\nindex 8a0aa5e0e216e..4314753fc47a8 100644\n--- a/arch/riscv/kvm/mmu.c\n+++ b/arch/riscv/kvm/mmu.c\n@@ -16,6 +16,9 @@\n #include \u003casm/kvm_mmu.h\u003e\n #include \u003casm/kvm_nacl.h\u003e\n \n+static bool __read_mostly eager_page_split = true;\n+module_param(eager_page_split, bool, 0644);\n+\n static void mmu_wp_memory_region(struct kvm *kvm, int slot)\n {\n \tstruct kvm_memslots *slots = kvm_memslots(kvm);\n@@ -98,6 +101,62 @@ void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size)\n \t\t\t\t\t    size \u003e\u003e PAGE_SHIFT);\n }\n \n+static bool need_topup_split_caches_or_resched(struct kvm *kvm, int count)\n+{\n+\tstruct kvm_mmu_memory_cache *cache;\n+\n+\tif (need_resched() || rwlock_needbreak(\u0026kvm-\u003emmu_lock))\n+\t\treturn true;\n+\n+\tcache = \u0026kvm-\u003earch.pgd_split_page_cache;\n+\treturn kvm_mmu_memory_cache_nr_free_objects(cache) \u003c count;\n+}\n+\n+static bool mmu_split_huge_pages(struct kvm_gstage *gstage,\n+\t\t\t\t phys_addr_t start, phys_addr_t end)\n+{\n+\tstruct kvm *kvm = gstage-\u003ekvm;\n+\tstruct kvm_mmu_memory_cache *pcache = \u0026kvm-\u003earch.pgd_split_page_cache;\n+\tphys_addr_t addr = ALIGN_DOWN(start, PMD_SIZE);\n+\tphys_addr_t last_flush_gfn = addr \u003e\u003e PAGE_SHIFT;\n+\tint count = gstage-\u003epgd_levels;\n+\tbool flush = false;\n+\tint ret;\n+\n+\tlockdep_assert_held_write(\u0026kvm-\u003emmu_lock);\n+\n+\twhile (addr \u003c end) {\n+\t\tif (need_topup_split_caches_or_resched(kvm, count)) {\n+\t\t\tif (flush) {\n+\t\t\t\tkvm_flush_remote_tlbs_range(kvm, last_flush_gfn,\n+\t\t\t\t\t  (addr \u003e\u003e PAGE_SHIFT) - last_flush_gfn);\n+\t\t\t\tlast_flush_gfn = addr \u003e\u003e PAGE_SHIFT;\n+\t\t\t\tflush = false;\n+\t\t\t}\n+\n+\t\t\twrite_unlock(\u0026kvm-\u003emmu_lock);\n+\t\t\tcond_resched();\n+\n+\t\t\tret = kvm_mmu_topup_memory_cache(pcache, count);\n+\t\t\tif (ret) {\n+\t\t\t\tkvm_err(\"Failed to toup split page cache\\n\");\n+\t\t\t\twrite_lock(\u0026kvm-\u003emmu_lock);\n+\t\t\t\treturn flush;\n+\t\t\t}\n+\t\t\twrite_lock(\u0026kvm-\u003emmu_lock);\n+\t\t}\n+\n+\t\tif (!kvm-\u003earch.pgd)\n+\t\t\treturn flush;\n+\n+\t\tflush |= kvm_riscv_gstage_split_huge(gstage, pcache, addr, 0, false);\n+\n+\t\taddr += PMD_SIZE;\n+\t}\n+\n+\treturn flush;\n+}\n+\n void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,\n \t\t\t\t\t     struct kvm_memory_slot *slot,\n \t\t\t\t\t     gfn_t gfn_offset,\n@@ -107,14 +166,20 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,\n \tphys_addr_t start = (base_gfn +  __ffs(mask)) \u003c\u003c PAGE_SHIFT;\n \tphys_addr_t end = (base_gfn + __fls(mask) + 1) \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n-\tbool flush;\n \n \tkvm_riscv_gstage_init(\u0026gstage, kvm);\n \n-\tflush = kvm_riscv_gstage_wp_range(\u0026gstage, start, end);\n-\tif (flush)\n-\t\tkvm_flush_remote_tlbs_range(kvm, start \u003e\u003e PAGE_SHIFT,\n-\t\t\t\t\t    (end - start) \u003e\u003e PAGE_SHIFT);\n+\tkvm_riscv_gstage_wp_range(\u0026gstage, start, end);\n+\n+\tif (kvm_dirty_log_manual_protect_and_init_set(kvm)) {\n+\t\tif (READ_ONCE(eager_page_split))\n+\t\t\tmmu_split_huge_pages(\u0026gstage, start, end);\n+\t}\n+\n+\t/*\n+\t * remote TLB flush is not needed here since callers of\n+\t * kvm_arch_mmu_enable_log_dirty_pt_masked() already do it.\n+\t */\n }\n \n void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)\n@@ -152,6 +217,25 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,\n \t\t\t\t\t    size \u003e\u003e PAGE_SHIFT);\n }\n \n+static void mmu_split_memory_region(struct kvm *kvm, int slot)\n+{\n+\tstruct kvm_memslots *slots = kvm_memslots(kvm);\n+\tstruct kvm_memory_slot *memslot = id_to_memslot(slots, slot);\n+\tphys_addr_t start = memslot-\u003ebase_gfn \u003c\u003c PAGE_SHIFT;\n+\tphys_addr_t end = (memslot-\u003ebase_gfn + memslot-\u003enpages) \u003c\u003c PAGE_SHIFT;\n+\tstruct kvm_gstage gstage;\n+\tbool flush;\n+\n+\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\n+\twrite_lock(\u0026kvm-\u003emmu_lock);\n+\tflush = mmu_split_huge_pages(\u0026gstage, start, end);\n+\twrite_unlock(\u0026kvm-\u003emmu_lock);\n+\n+\tif (flush)\n+\t\tkvm_flush_remote_tlbs_memslot(kvm, memslot);\n+}\n+\n void kvm_arch_commit_memory_region(struct kvm *kvm,\n \t\t\t\tstruct kvm_memory_slot *old,\n \t\t\t\tconst struct kvm_memory_slot *new,\n@@ -165,6 +249,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,\n \t\tif (kvm_dirty_log_manual_protect_and_init_set(kvm))\n \t\t\treturn;\n \t\tmmu_wp_memory_region(kvm, new-\u003eid);\n+\n+\t\tif (READ_ONCE(eager_page_split))\n+\t\t\tmmu_split_memory_region(kvm, new-\u003eid);\n \t}\n }\n \n@@ -677,6 +764,7 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)\n \tkvm-\u003earch.pgd = page_to_virt(pgd_page);\n \tkvm-\u003earch.pgd_phys = page_to_phys(pgd_page);\n \tkvm-\u003earch.pgd_levels = kvm_riscv_gstage_max_pgd_levels;\n+\tkvm-\u003earch.pgd_split_page_cache.gfp_zero = __GFP_ZERO;\n \n \treturn 0;\n }\n@@ -704,6 +792,8 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm)\n \n \tif (pgd)\n \t\tfree_pages((unsigned long)pgd, get_order(kvm_riscv_gstage_pgd_size));\n+\n+\tkvm_mmu_free_memory_cache(\u0026kvm-\u003earch.pgd_split_page_cache);\n }\n \n void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu)\n"
}

2/1 2026/08/01 16:23 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "VIRTUALIZATION",
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_riscv_gstage_split_huge",
    "kvm_arch_mmu_enable_log_dirty_pt_masked",
    "kvm_arch_commit_memory_region"
  ],
  "Reasoning": "The patch implements eager page splitting for KVM on RISC-V, modifying the KVM MMU logic. This is a functional change that should be fuzzed to ensure memory management and dirty logging work correctly without introducing memory corruption or crashes.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit ed9c905da6bba62c17fc437a73965d32d2440ce2
Author: syz-cluster <triage@syzkaller.com>
Date:   Sat Aug 1 16:23:18 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index b5493a7f8f228..d78eda7ee23a5 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3047,7 +3047,7 @@ Kernel parameters
 			Default is 0 (don't ignore, but inject #GP)
 
 	kvm.eager_page_split=
-			[KVM,X86] Controls whether or not KVM will try to
+			[KVM,X86,RISCV] Controls whether or not KVM will try to
 			proactively split all huge pages during dirty logging.
 			Eager page splitting reduces interruptions to vCPU
 			execution by eliminating the write-protection faults
@@ -3067,7 +3067,10 @@ Kernel parameters
 			the KVM_CLEAR_DIRTY ioctl, and only for the pages being
 			cleared.
 
-			Eager page splitting is only supported when kvm.tdp_mmu=Y.
+			On x86, eager page splitting is only supported when
+			kvm.tdp_mmu=Y.
+
+			On RISCV, eager page splitting is supported by default.
 
 			Default is Y (on).
 
diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
index 21e2019df0cf5..f726279780177 100644
--- a/arch/riscv/include/asm/kvm_gstage.h
+++ b/arch/riscv/include/asm/kvm_gstage.h
@@ -64,9 +64,9 @@ 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);
+bool 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 */
diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index e2d5808169e44..71e455d166905 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -86,6 +86,7 @@ struct kvm_arch {
 	pgd_t *pgd;
 	phys_addr_t pgd_phys;
 	unsigned long pgd_levels;
+	struct kvm_mmu_memory_cache pgd_split_page_cache;
 
 	/* Guest Timer */
 	struct kvm_guest_timer timer;
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index b0474fcf065aa..01679aef53289 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -307,19 +307,20 @@ static inline unsigned long make_child_pte(unsigned long huge_pte, int index,
 	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)
+bool 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 = gstage->pgd_levels - 1;
 	pte_t *next_ptep = (pte_t *)gstage->pgd;
 	unsigned long huge_pte, child_pte;
 	unsigned long child_page_size;
+	bool need_flush = false;
 	pte_t *ptep;
 	int i, ret;
 
 	if (!pcache)
-		return -ENOMEM;
+		return false;
 
 	while(current_level > target_level) {
 		ptep = (pte_t *)&next_ptep[gstage_pte_index(gstage, addr, current_level)];
@@ -337,27 +338,35 @@ int kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,
 
 		ret = gstage_level_to_page_size(gstage, current_level - 1, &child_page_size);
 		if (ret)
-			return ret;
+			return need_flush;
 
 		next_ptep = kvm_mmu_memory_cache_alloc(pcache);
 		if (!next_ptep)
-			return -ENOMEM;
+			return need_flush;
 
 		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));
 		}
 
+		/*
+		 * Ensure the writes to the child PTEs are visible before
+		 * linking the new page table to the parent PTE.
+		 */
+		smp_wmb();
+
 		set_pte(ptep, pfn_pte(PFN_DOWN(__pa(next_ptep)),
 				__pgprot(_PAGE_TABLE)));
 
 		if (flush)
 			gstage_tlb_flush(gstage, current_level, addr);
+		else
+			need_flush = true;
 
 		current_level--;
 	}
 
-	return 0;
+	return need_flush;
 }
 
 bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 8a0aa5e0e216e..4314753fc47a8 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -16,6 +16,9 @@
 #include <asm/kvm_mmu.h>
 #include <asm/kvm_nacl.h>
 
+static bool __read_mostly eager_page_split = true;
+module_param(eager_page_split, bool, 0644);
+
 static void mmu_wp_memory_region(struct kvm *kvm, int slot)
 {
 	struct kvm_memslots *slots = kvm_memslots(kvm);
@@ -98,6 +101,62 @@ void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size)
 					    size >> PAGE_SHIFT);
 }
 
+static bool need_topup_split_caches_or_resched(struct kvm *kvm, int count)
+{
+	struct kvm_mmu_memory_cache *cache;
+
+	if (need_resched() || rwlock_needbreak(&kvm->mmu_lock))
+		return true;
+
+	cache = &kvm->arch.pgd_split_page_cache;
+	return kvm_mmu_memory_cache_nr_free_objects(cache) < count;
+}
+
+static bool mmu_split_huge_pages(struct kvm_gstage *gstage,
+				 phys_addr_t start, phys_addr_t end)
+{
+	struct kvm *kvm = gstage->kvm;
+	struct kvm_mmu_memory_cache *pcache = &kvm->arch.pgd_split_page_cache;
+	phys_addr_t addr = ALIGN_DOWN(start, PMD_SIZE);
+	phys_addr_t last_flush_gfn = addr >> PAGE_SHIFT;
+	int count = gstage->pgd_levels;
+	bool flush = false;
+	int ret;
+
+	lockdep_assert_held_write(&kvm->mmu_lock);
+
+	while (addr < end) {
+		if (need_topup_split_caches_or_resched(kvm, count)) {
+			if (flush) {
+				kvm_flush_remote_tlbs_range(kvm, last_flush_gfn,
+					  (addr >> PAGE_SHIFT) - last_flush_gfn);
+				last_flush_gfn = addr >> PAGE_SHIFT;
+				flush = false;
+			}
+
+			write_unlock(&kvm->mmu_lock);
+			cond_resched();
+
+			ret = kvm_mmu_topup_memory_cache(pcache, count);
+			if (ret) {
+				kvm_err("Failed to toup split page cache\n");
+				write_lock(&kvm->mmu_lock);
+				return flush;
+			}
+			write_lock(&kvm->mmu_lock);
+		}
+
+		if (!kvm->arch.pgd)
+			return flush;
+
+		flush |= kvm_riscv_gstage_split_huge(gstage, pcache, addr, 0, false);
+
+		addr += PMD_SIZE;
+	}
+
+	return flush;
+}
+
 void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
 					     struct kvm_memory_slot *slot,
 					     gfn_t gfn_offset,
@@ -107,14 +166,20 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
 	phys_addr_t start = (base_gfn +  __ffs(mask)) << PAGE_SHIFT;
 	phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT;
 	struct kvm_gstage gstage;
-	bool flush;
 
 	kvm_riscv_gstage_init(&gstage, kvm);
 
-	flush = kvm_riscv_gstage_wp_range(&gstage, start, end);
-	if (flush)
-		kvm_flush_remote_tlbs_range(kvm, start >> PAGE_SHIFT,
-					    (end - start) >> PAGE_SHIFT);
+	kvm_riscv_gstage_wp_range(&gstage, start, end);
+
+	if (kvm_dirty_log_manual_protect_and_init_set(kvm)) {
+		if (READ_ONCE(eager_page_split))
+			mmu_split_huge_pages(&gstage, start, end);
+	}
+
+	/*
+	 * remote TLB flush is not needed here since callers of
+	 * kvm_arch_mmu_enable_log_dirty_pt_masked() already do it.
+	 */
 }
 
 void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
@@ -152,6 +217,25 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
 					    size >> PAGE_SHIFT);
 }
 
+static void mmu_split_memory_region(struct kvm *kvm, int slot)
+{
+	struct kvm_memslots *slots = kvm_memslots(kvm);
+	struct kvm_memory_slot *memslot = id_to_memslot(slots, slot);
+	phys_addr_t start = memslot->base_gfn << PAGE_SHIFT;
+	phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT;
+	struct kvm_gstage gstage;
+	bool flush;
+
+	kvm_riscv_gstage_init(&gstage, kvm);
+
+	write_lock(&kvm->mmu_lock);
+	flush = mmu_split_huge_pages(&gstage, start, end);
+	write_unlock(&kvm->mmu_lock);
+
+	if (flush)
+		kvm_flush_remote_tlbs_memslot(kvm, memslot);
+}
+
 void kvm_arch_commit_memory_region(struct kvm *kvm,
 				struct kvm_memory_slot *old,
 				const struct kvm_memory_slot *new,
@@ -165,6 +249,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
 		if (kvm_dirty_log_manual_protect_and_init_set(kvm))
 			return;
 		mmu_wp_memory_region(kvm, new->id);
+
+		if (READ_ONCE(eager_page_split))
+			mmu_split_memory_region(kvm, new->id);
 	}
 }
 
@@ -677,6 +764,7 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
 	kvm->arch.pgd = page_to_virt(pgd_page);
 	kvm->arch.pgd_phys = page_to_phys(pgd_page);
 	kvm->arch.pgd_levels = kvm_riscv_gstage_max_pgd_levels;
+	kvm->arch.pgd_split_page_cache.gfp_zero = __GFP_ZERO;
 
 	return 0;
 }
@@ -704,6 +792,8 @@ void kvm_riscv_mmu_free_pgd(struct kvm *kvm)
 
 	if (pgd)
 		free_pages((unsigned long)pgd, get_order(kvm_riscv_gstage_pgd_size));
+
+	kvm_mmu_free_memory_cache(&kvm->arch.pgd_split_page_cache);
 }
 
 void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu)

3/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5747
							output: 40
							thoughts: 306
						

Thoughts:
**Analyzing the Eager Page Splitting Patch for RISC-V KVM**

Okay, so there's a new patch out there, implementing eager page splitting for KVM on RISC-V. My initial thought is to assess its fuzzing potential. It's touching `arch/riscv/kvm/gstage.c` and `arch/riscv/kvm/mmu.c`, which immediately flags it as core kernel logic within the RISC-V KVM implementation. *That's promising for fuzzing - a definite "yes."*

Now, let's drill down and identify the key functions that are being altered. I see the addition of `kvm_riscv_gstage_split_huge` and `mmu_split_huge_pages`, alongside the introduction of `kvm.eager_page_split` as a RISC-V specific parameter. Function `kvm_riscv_gstage_split_huge` return type is changed to bool. Modifications are made to `kvm_arch_mmu_enable_log_dirty_pt_masked` and `kvm_arch_commit_memory_region`. Changes are also made to the PGD allocation and deallocation functions: `kvm_riscv_mmu_alloc_pgd` and `kvm_riscv_mmu_free_pgd`. This is clearly a functional change affecting the MMU's behavior. I need to take a close look at both `arch/riscv/kvm/mmu.c` and `arch/riscv/kvm/gstage.c` to fully understand the impact.



4/2 2026/08/01 16:23 tool grepper
0m Args:
{
  "Expression": "kvm_riscv_gstage_split_huge",
  "PathPrefix": "arch/riscv/kvm/"
}

Results:
{
  "Output": "arch/riscv/kvm/gstage.c=207=int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n--\narch/riscv/kvm/gstage.c-278-\t\tif (ptep_level \u003e out_map-\u003elevel) {\narch/riscv/kvm/gstage.c:279:\t\t\tkvm_riscv_gstage_split_huge(gstage, pcache, gpa,\narch/riscv/kvm/gstage.c-280-\t\t\t\t\t\t    out_map-\u003elevel, true);\n--\narch/riscv/kvm/gstage.c=293=static inline unsigned long make_child_pte(unsigned long huge_pte, int index,\n--\narch/riscv/kvm/gstage.c-309-\narch/riscv/kvm/gstage.c:310:bool kvm_riscv_gstage_split_huge(struct kvm_gstage *gstage,\narch/riscv/kvm/gstage.c-311-\t\t\t\t struct kvm_mmu_memory_cache *pcache,\n--\narch/riscv/kvm/mmu.c=115=static bool mmu_split_huge_pages(struct kvm_gstage *gstage,\n--\narch/riscv/kvm/mmu.c-151-\narch/riscv/kvm/mmu.c:152:\t\tflush |= kvm_riscv_gstage_split_huge(gstage, pcache, addr, 0, false);\narch/riscv/kvm/mmu.c-153-\n"
}

5/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6865
							output: 33
							thoughts: 125
						

Thoughts:
**Initial Assessment of RISC-V KVM Functions**

Okay, so I'm taking a look at these functions in the RISC-V KVM code and making some initial observations. I'm seeing that `kvm_riscv_gstage_split_huge`, `kvm_arch_mmu_enable_log_dirty_pt_masked`, `kvm_arch_commit_memory_region`, `kvm_riscv_mmu_alloc_pgd`, and `kvm_riscv_mmu_free_pgd` are all exported – that is, they're non-static. This means they're likely designed for use outside of their immediate compilation unit, which tells me they're key interfaces or helper functions that other parts of the KVM implementation, or potentially even other modules, would need access to. This is important context.

Next step: I should verify this by checking the Kconfig options related to KVM support on RISC-V. This will give me a better understanding of how these functions fit into the larger KVM configuration and what dependencies might be at play. I'm keen to understand how they’re enabled/disabled based on the build configuration.



6/2 2026/08/01 16:23 tool grepper
0m Args:
{
  "Expression": "config KVM",
  "PathPrefix": "arch/riscv/kvm/Kconfig"
}

Results:
{
  "Output": "arch/riscv/kvm/Kconfig=18=if VIRTUALIZATION\narch/riscv/kvm/Kconfig-19-\narch/riscv/kvm/Kconfig:20:config KVM\narch/riscv/kvm/Kconfig-21-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n"
}

7/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7377
							output: 32
							thoughts: 15
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Analyzing the Configuration Dependency: KVM**

Alright, let's break this down. My immediate focus is on `KVM` – that's the key configuration element we're dealing with. The question that immediately surfaces is: what does this depend on? What are its prerequisites? Is it self-contained, or are there underlying libraries, hardware features, or kernel modules that *must* be present and correctly configured for `KVM` to function as expected? Is there a known minimum or compatible version to consider, such as kernel version or QEMU version? Understanding those dependencies will be critical for any troubleshooting or modification efforts. I need to quickly determine the scope of this particular configuration.



8/2 2026/08/01 16:23 tool grepper
0m Args:
{
  "Expression": "depends on",
  "PathPrefix": "arch/riscv/kvm/Kconfig"
}

Results:
{
  "Output": "arch/riscv/kvm/Kconfig=20=config KVM\narch/riscv/kvm/Kconfig-21-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\narch/riscv/kvm/Kconfig:22:\tdepends on RISCV_SBI \u0026\u0026 MMU\narch/riscv/kvm/Kconfig-23-\tselect HAVE_KVM_IRQCHIP\n"
}

9/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7697
							output: 49
							thoughts: 20
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:

**KVM Function Identification Process**

Right, so it's looking like `KVM` is the crucial piece here. Now I need to nail down the *exact* function names I'll be working with. I can't afford any ambiguity or reliance on fuzzy recall. My next step is to meticulously identify each necessary function, ensuring I'm referencing the precise interface I'm targeting. No room for guesswork, just precise function names. Let's dig in and get the right syntax.



10/2 2026/08/01 16:23 tool grepper
0m Args:
{
  "Expression": "kvm_arch_mmu_enable_log_dirty_pt_masked",
  "PathPrefix": "arch/riscv/kvm/mmu.c"
}

Results:
{
  "Output": "arch/riscv/kvm/mmu.c=115=static bool mmu_split_huge_pages(struct kvm_gstage *gstage,\n--\narch/riscv/kvm/mmu.c-159-\narch/riscv/kvm/mmu.c:160:void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,\narch/riscv/kvm/mmu.c-161-\t\t\t\t\t     struct kvm_memory_slot *slot,\n--\narch/riscv/kvm/mmu.c-180-\t * remote TLB flush is not needed here since callers of\narch/riscv/kvm/mmu.c:181:\t * kvm_arch_mmu_enable_log_dirty_pt_masked() already do it.\narch/riscv/kvm/mmu.c-182-\t */\n"
}

11/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8134
							output: 42
							thoughts: 13
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Analyzing `kvm_arch_commit_memory_region`**

Alright, let's dissect this. When I see `kvm_arch_commit_memory_region`, my mind immediately goes to the architectural specifics of memory management within the KVM hypervisor. My first thought is: this is a critical function. It's the point where changes to the guest's memory map are *actually* made permanent. We're talking about the final validation and application of memory region changes, potentially triggering TLB invalidations, page table updates, and all the associated hardware synchronization.

I'm thinking about the implications for performance. How efficiently is this implemented? Are there any potential bottlenecks here? Is the design optimized for minimal latency, especially during live migration or VM creation? The context around it matters - what’s the calling path? Is it called frequently? Infrequently? What are the common scenarios that trigger it?

I’m also wondering about the locking mechanisms involved. What kind of spinlocks, mutexes, or RCU structures are used to protect shared data structures? Are they effective? Is there any potential for contention, deadlocks, or livelocks? How does the code handle the interaction between different architectures (x86, ARM, etc.) in this function? The architecture-specific logic within this function is often where subtle, hard-to-debug issues arise.

Security is another immediate concern. What checks are in place to prevent memory corruption or privilege escalation? Are there robust bounds checks and validation of the region parameters? This is a high-privilege function, so vulnerabilities here could be catastrophic. I'm looking for any signs of potential TOCTOU (Time-of-Check-to-Time-of-Use) vulnerabilities or other security issues in the memory region commit. What type of isolation guarantees are being made for the guest?

Finally, I’m mentally charting the interaction of this function with other KVM subsystems. How does it interact with the MMU emulation, the virtualized interrupt controller, and the device emulation layers? It has to integrate with all of them to operate properly. It is the core of memory-related operations. It is likely called from many subsystems.



12/2 2026/08/01 16:23 tool grepper
0m Args:
{
  "Expression": "kvm_arch_commit_memory_region",
  "PathPrefix": "arch/riscv/kvm/mmu.c"
}

Results:
{
  "Output": "arch/riscv/kvm/mmu.c=220=static void mmu_split_memory_region(struct kvm *kvm, int slot)\n--\narch/riscv/kvm/mmu.c-238-\narch/riscv/kvm/mmu.c:239:void kvm_arch_commit_memory_region(struct kvm *kvm,\narch/riscv/kvm/mmu.c-240-\t\t\t\tstruct kvm_memory_slot *old,\n"
}

13/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8810
							output: 32
							thoughts: 180
						

Thoughts:
**My Assessment of the KVM RISC-V Page Splitting Patch**

Alright, let's break this down. My initial assessment is that this patch is introducing eager page splitting within KVM on RISC-V, which is a significant functional change to the KVM MMU logic. Based on the symbols listed, the key areas affected are `kvm_riscv_gstage_split_huge`, `kvm_arch_mmu_enable_log_dirty_pt_masked`, and `kvm_arch_commit_memory_region`.

My primary concern, as always with MMU changes, is memory safety. Specifically, I need to ensure that this eager splitting doesn't introduce any memory corruption issues or, even worse, cause kernel crashes, particularly within the contexts of dirty logging. Fuzzing is the obvious next step, absolutely critical, to thoroughly test the new memory management and dirty logging behaviors. I'll need to develop fuzzing harnesses tailored to these specific functions.

Now, let's take a quick look at the configuration dependencies. `KVM` is, of course, a given. I'll confirm that `RISCV_SBI` and `MMU` are present, which are pretty much standard on any RISC-V KVM setup. I'll also double-check that `VIRTUALIZATION` is correctly defined as a prerequisite for `KVM`, especially within `arch/riscv/kvm/Kconfig` to ensure no build issues arise. My goal is to ensure that the configuration landscape is correct and complete for proper testing.



14/2 2026/08/01 16:23 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config VIRTUALIZATION",
  "PathPrefix": "arch/riscv/Kconfig"
}

Results:
{
  "error": "no matches"
}

15/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9369
							output: 36
							thoughts: 18
						

Thoughts:
Here's my take on the thought process:

**Kconfig Review - RISC-V KVM**

Alright, time to revisit `arch/riscv/kvm/Kconfig`. I've been over it before, but given the recent discussions and developments in RISC-V KVM, it's prudent to give it another, focused look. I need to ensure that the configuration options are still sound, properly placed, and reflecting the current state of the architecture support. Are there any new dependencies I missed? Have any existing options become obsolete or require refinement? I'm particularly interested in how recent patches have impacted the existing options and if there are any new opportunities to improve the user experience with new options. A thorough check is essential to maintain the integrity and usability of our configuration system for the RISC-V KVM implementation.



16/2 2026/08/01 16:23 tool grepper
0m Args:
{
  "Expression": "menuconfig VIRTUALIZATION",
  "PathPrefix": "arch/riscv/kvm/Kconfig"
}

Results:
{
  "Output": "arch/riscv/kvm/Kconfig=6=source \"virt/kvm/Kconfig\"\narch/riscv/kvm/Kconfig-7-\narch/riscv/kvm/Kconfig:8:menuconfig VIRTUALIZATION\narch/riscv/kvm/Kconfig-9-\tbool \"Virtualization\"\n"
}

17/2 2026/08/01 16:23 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9688
							output: 139
							thoughts: 85
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental processing:

**Virtualization Configuration Considerations**

Alright, the core requirement is clear: we absolutely need `VIRTUALIZATION`. Now, the next question is about implementation. Specifically, I need to determine if `KVM` alone will suffice, or if there are additional virtualization components necessary. My understanding is that `KVM` is often synonymous with a virtualization capability, but I need to confirm. Let me clarify - `KVM` is nested *within* `VIRTUALIZATION`, not the other way around. This means that simply enabling `KVM` is not automatically enabling `VIRTUALIZATION`. Therefore, to meet the initial requirement, we *definitely* need `VIRTUALIZATION`. Now, let's nail down these results. We're getting somewhere.



18/2 2026/08/01 16:23 tool set-results
0m Args:
{
  "EnableConfigs": [
    "VIRTUALIZATION",
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_riscv_gstage_split_huge",
    "kvm_arch_mmu_enable_log_dirty_pt_masked",
    "kvm_arch_commit_memory_region"
  ],
  "Reasoning": "The patch implements eager page splitting for KVM on RISC-V, modifying the KVM MMU logic. This is a functional change that should be fuzzed to ensure memory management and dirty logging work correctly without introducing memory corruption or crashes.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "VIRTUALIZATION",
    "KVM"
  ],
  "FocusSymbols": [
    "kvm_riscv_gstage_split_huge",
    "kvm_arch_mmu_enable_log_dirty_pt_masked",
    "kvm_arch_commit_memory_region"
  ],
  "Reasoning": "The patch implements eager page splitting for KVM on RISC-V, modifying the KVM MMU logic. This is a functional change that should be fuzzed to ensure memory management and dirty logging work correctly without introducing memory corruption or crashes.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)