AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/07 02:03 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "TEST_HMM",
    "MSHV_ROOT"
  ],
  "FocusSymbols": [
    "hmm_range_fault_unlocked",
    "hmm_range_fault",
    "hmm_do_fault",
    "hmm_record_fault",
    "hmm_range_fault_locked",
    "dmirror_read_unlocked",
    "dmirror_range_fault_unlocked",
    "dmirror_fault_unlocked",
    "mshv_region_hmm_fault_and_lock"
  ],
  "Reasoning": "The patch introduces a new API `hmm_range_fault_unlocked()` in the HMM subsystem to allow dropping the mmap lock during page faults, which is useful for regions managed by userfaultfd. It refactors the core `hmm_range_fault()` logic to support this and updates the `test_hmm` module and `mshv_regions` driver to utilize the new functionality. This is a functional change to memory management and driver code that should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/07 02:03 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit f4b7593f6fe86cb6c540e644a9b1d1dcdf880e70\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jul 7 02:03:25 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/mm/hmm.rst b/Documentation/mm/hmm.rst\nindex 7d61b7a8b65b79..844343712abdda 100644\n--- a/Documentation/mm/hmm.rst\n+++ b/Documentation/mm/hmm.rst\n@@ -208,6 +208,65 @@ invalidate() callback. That lock must be held before calling\n mmu_interval_read_retry() to avoid any race with a concurrent CPU page table\n update.\n \n+Dropping the mmap lock during page faults\n+=========================================\n+\n+Some VMAs have fault handlers that need to release the mmap lock while\n+servicing a fault (for example, regions managed by ``userfaultfd``).\n+``hmm_range_fault()`` cannot be used on such mappings because it must hold the\n+mmap lock for the duration of the call. Drivers that need to support them\n+should call::\n+\n+  int hmm_range_fault_unlocked(struct hmm_range *range);\n+\n+The caller must not hold ``mmap_read_lock`` before the call.\n+``hmm_range_fault_unlocked()`` takes the mmap read lock internally and allows\n+``handle_mm_fault()`` to drop it during fault handling. If the mmap lock is\n+dropped, the function returns ``-EBUSY``. The caller must then restart the\n+walk from ``range-\u003estart`` with a fresh notifier sequence. ``-EINTR`` is\n+returned if a fatal signal is pending during retry handling.\n+\n+A typical caller looks like this::\n+\n+ int driver_populate_range_unlocked(...)\n+ {\n+      struct hmm_range range;\n+      ...\n+\n+      range.notifier = \u0026interval_sub;\n+      range.start = ...;\n+      range.end = ...;\n+      range.hmm_pfns = ...;\n+\n+      if (!mmget_not_zero(interval_sub.mm))\n+          return -EFAULT;\n+\n+ again:\n+      range.notifier_seq = mmu_interval_read_begin(\u0026interval_sub);\n+      ret = hmm_range_fault_unlocked(\u0026range);\n+      if (ret) {\n+          if (ret == -EBUSY)\n+              goto again;\n+          goto out_put;\n+      }\n+\n+      take_lock(driver-\u003eupdate);\n+      if (mmu_interval_read_retry(\u0026interval_sub, range.notifier_seq)) {\n+          release_lock(driver-\u003eupdate);\n+          goto again;\n+      }\n+\n+      /* Use pfns array content to update device page table,\n+       * under the update lock */\n+\n+      release_lock(driver-\u003eupdate);\n+      ret = 0;\n+\n+ out_put:\n+      mmput(interval_sub.mm);\n+      return ret;\n+ }\n+\n Leverage default_flags and pfn_flags_mask\n =========================================\n \ndiff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c\nindex 6d65e5b4215295..04676f06c5c742 100644\n--- a/drivers/hv/mshv_regions.c\n+++ b/drivers/hv/mshv_regions.c\n@@ -388,13 +388,11 @@ int mshv_region_get(struct mshv_mem_region *region)\n  *\n  * This function performs the following steps:\n  * 1. Reads the notifier sequence for the HMM range.\n- * 2. Acquires a read lock on the memory map.\n- * 3. Handles HMM faults for the specified range.\n- * 4. Releases the read lock on the memory map.\n- * 5. If successful, locks the memory region mutex.\n- * 6. Verifies if the notifier sequence has changed during the operation.\n+ * 2. Handles HMM faults for the specified range.\n+ * 3. If successful, locks the memory region mutex.\n+ * 4. Verifies if the notifier sequence has changed during the operation.\n  *    If it has, releases the mutex and returns -EBUSY to match with\n- *    hmm_range_fault() return code for repeating.\n+ *    hmm_range_fault_unlocked() so the caller retries the range fault.\n  *\n  * Return: 0 on success, a negative error code otherwise.\n  */\n@@ -404,9 +402,7 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,\n \tint ret;\n \n \trange-\u003enotifier_seq = mmu_interval_read_begin(range-\u003enotifier);\n-\tmmap_read_lock(region-\u003emreg_mni.mm);\n-\tret = hmm_range_fault(range);\n-\tmmap_read_unlock(region-\u003emreg_mni.mm);\n+\tret = hmm_range_fault_unlocked(range);\n \tif (ret)\n \t\treturn ret;\n \ndiff --git a/include/linux/hmm.h b/include/linux/hmm.h\nindex db75ffc949a7ac..1b584e5a29001e 100644\n--- a/include/linux/hmm.h\n+++ b/include/linux/hmm.h\n@@ -123,6 +123,7 @@ struct hmm_range {\n  * Please see Documentation/mm/hmm.rst for how to use the range API.\n  */\n int hmm_range_fault(struct hmm_range *range);\n+int hmm_range_fault_unlocked(struct hmm_range *range);\n \n /*\n  * HMM_RANGE_DEFAULT_TIMEOUT - default timeout (ms) when waiting for a range\ndiff --git a/lib/test_hmm.c b/lib/test_hmm.c\nindex 45c0cb9922184a..dcc3a23bb983dc 100644\n--- a/lib/test_hmm.c\n+++ b/lib/test_hmm.c\n@@ -389,6 +389,76 @@ static int dmirror_range_fault(struct dmirror *dmirror,\n \treturn ret;\n }\n \n+static int dmirror_range_fault_unlocked(struct dmirror *dmirror,\n+\t\t\t\t\tstruct hmm_range *range)\n+{\n+\tunsigned long timeout =\n+\t\tjiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);\n+\tint ret;\n+\n+\twhile (true) {\n+\t\tif (time_after(jiffies, timeout)) {\n+\t\t\tret = -EBUSY;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\trange-\u003enotifier_seq = mmu_interval_read_begin(range-\u003enotifier);\n+\t\tret = hmm_range_fault_unlocked(range);\n+\t\tif (ret) {\n+\t\t\tif (ret == -EBUSY)\n+\t\t\t\tcontinue;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tmutex_lock(\u0026dmirror-\u003emutex);\n+\t\tif (mmu_interval_read_retry(range-\u003enotifier,\n+\t\t\t\t\t    range-\u003enotifier_seq)) {\n+\t\t\tmutex_unlock(\u0026dmirror-\u003emutex);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\n+\tret = dmirror_do_fault(dmirror, range);\n+\n+\tmutex_unlock(\u0026dmirror-\u003emutex);\n+out:\n+\treturn ret;\n+}\n+\n+static int dmirror_fault_unlocked(struct dmirror *dmirror,\n+\t\t\t\t  unsigned long start,\n+\t\t\t\t  unsigned long end, bool write)\n+{\n+\tstruct mm_struct *mm = dmirror-\u003enotifier.mm;\n+\tunsigned long addr;\n+\tunsigned long pfns[32];\n+\tstruct hmm_range range = {\n+\t\t.notifier = \u0026dmirror-\u003enotifier,\n+\t\t.hmm_pfns = pfns,\n+\t\t.pfn_flags_mask = 0,\n+\t\t.default_flags =\n+\t\t\tHMM_PFN_REQ_FAULT | (write ? HMM_PFN_REQ_WRITE : 0),\n+\t\t.dev_private_owner = dmirror-\u003emdevice,\n+\t};\n+\tint ret = 0;\n+\n+\tif (!mmget_not_zero(mm))\n+\t\treturn -EFAULT;\n+\n+\tfor (addr = start; addr \u003c end; addr = range.end) {\n+\t\trange.start = addr;\n+\t\trange.end = min(addr + (ARRAY_SIZE(pfns) \u003c\u003c PAGE_SHIFT), end);\n+\n+\t\tret = dmirror_range_fault_unlocked(dmirror, \u0026range);\n+\t\tif (ret)\n+\t\t\tbreak;\n+\t}\n+\n+\tmmput(mm);\n+\treturn ret;\n+}\n+\n static int dmirror_fault(struct dmirror *dmirror, unsigned long start,\n \t\t\t unsigned long end, bool write)\n {\n@@ -488,6 +558,47 @@ static int dmirror_read(struct dmirror *dmirror, struct hmm_dmirror_cmd *cmd)\n \treturn ret;\n }\n \n+static int dmirror_read_unlocked(struct dmirror *dmirror,\n+\t\t\t\t struct hmm_dmirror_cmd *cmd)\n+{\n+\tstruct dmirror_bounce bounce;\n+\tunsigned long start, end;\n+\tunsigned long size = cmd-\u003enpages \u003c\u003c PAGE_SHIFT;\n+\tint ret;\n+\n+\tstart = cmd-\u003eaddr;\n+\tend = start + size;\n+\tif (end \u003c start)\n+\t\treturn -EINVAL;\n+\n+\tret = dmirror_bounce_init(\u0026bounce, start, size);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\twhile (1) {\n+\t\tmutex_lock(\u0026dmirror-\u003emutex);\n+\t\tret = dmirror_do_read(dmirror, start, end, \u0026bounce);\n+\t\tmutex_unlock(\u0026dmirror-\u003emutex);\n+\t\tif (ret != -ENOENT)\n+\t\t\tbreak;\n+\n+\t\tstart = cmd-\u003eaddr + (bounce.cpages \u003c\u003c PAGE_SHIFT);\n+\t\tret = dmirror_fault_unlocked(dmirror, start, end, false);\n+\t\tif (ret)\n+\t\t\tbreak;\n+\t\tcmd-\u003efaults++;\n+\t}\n+\n+\tif (ret == 0) {\n+\t\tif (copy_to_user(u64_to_user_ptr(cmd-\u003eptr), bounce.ptr,\n+\t\t\t\t bounce.size))\n+\t\t\tret = -EFAULT;\n+\t}\n+\tcmd-\u003ecpages = bounce.cpages;\n+\tdmirror_bounce_fini(\u0026bounce);\n+\treturn ret;\n+}\n+\n static int dmirror_do_write(struct dmirror *dmirror, unsigned long start,\n \t\t\t    unsigned long end, struct dmirror_bounce *bounce)\n {\n@@ -1572,6 +1683,9 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,\n \t\tdmirror-\u003eflags = cmd.npages;\n \t\tret = 0;\n \t\tbreak;\n+\tcase HMM_DMIRROR_READ_UNLOCKED:\n+\t\tret = dmirror_read_unlocked(dmirror, \u0026cmd);\n+\t\tbreak;\n \n \tdefault:\n \t\treturn -EINVAL;\ndiff --git a/lib/test_hmm_uapi.h b/lib/test_hmm_uapi.h\nindex f94c6d45733828..ea9b0ec404fb55 100644\n--- a/lib/test_hmm_uapi.h\n+++ b/lib/test_hmm_uapi.h\n@@ -38,6 +38,7 @@ struct hmm_dmirror_cmd {\n #define HMM_DMIRROR_CHECK_EXCLUSIVE\t_IOWR('H', 0x06, struct hmm_dmirror_cmd)\n #define HMM_DMIRROR_RELEASE\t\t_IOWR('H', 0x07, struct hmm_dmirror_cmd)\n #define HMM_DMIRROR_FLAGS\t\t_IOWR('H', 0x08, struct hmm_dmirror_cmd)\n+#define HMM_DMIRROR_READ_UNLOCKED\t_IOWR('H', 0x09, struct hmm_dmirror_cmd)\n \n #define HMM_DMIRROR_FLAG_FAIL_ALLOC\t(1ULL \u003c\u003c 0)\n \ndiff --git a/mm/hmm.c b/mm/hmm.c\nindex 4f3f627d2b4772..316116994209c8 100644\n--- a/mm/hmm.c\n+++ b/mm/hmm.c\n@@ -32,9 +32,27 @@\n \n struct hmm_vma_walk {\n \tstruct hmm_range\t*range;\n+\tint\t\t\t*locked;\n \tunsigned long\t\tlast;\n+\tunsigned long\t\tend;\n+\tunsigned int\t\trequired_fault;\n };\n \n+/*\n+ * Internal sentinel returned by walk callbacks when they need a page fault.\n+ * The callback stores end/required_fault in hmm_vma_walk; the outer loop\n+ * consumes the sentinel and never propagates it to the caller.\n+ */\n+#define HMM_FAULT_PENDING\t-EAGAIN\n+\n+/*\n+ * Internal sentinel returned by hmm_do_fault() when handle_mm_fault()\n+ * completes a page fault with the mmap lock dropped. hmm_do_fault() sets\n+ * *locked = 0; the outer loop consumes the sentinel and never propagates it\n+ * to the caller.\n+ */\n+#define HMM_FAULT_UNLOCKED\t-ENOLCK\n+\n enum {\n \tHMM_NEED_FAULT = 1 \u003c\u003c 0,\n \tHMM_NEED_WRITE_FAULT = 1 \u003c\u003c 1,\n@@ -60,37 +78,25 @@ static int hmm_pfns_fill(unsigned long addr, unsigned long end,\n }\n \n /*\n- * hmm_vma_fault() - fault in a range lacking valid pmd or pte(s)\n- * @addr: range virtual start address (inclusive)\n- * @end: range virtual end address (exclusive)\n- * @required_fault: HMM_NEED_* flags\n- * @walk: mm_walk structure\n- * Return: -EBUSY after page fault, or page fault error\n+ * hmm_record_fault() - record a range that needs to be faulted in\n  *\n- * This function will be called whenever pmd_none() or pte_none() returns true,\n- * or whenever there is no page directory covering the virtual address range.\n+ * Called by the walk callbacks when they discover that part of the range\n+ * needs a page fault.  The callback records what to fault and returns\n+ * HMM_FAULT_PENDING; the outer loop in hmm_range_fault_locked() drops\n+ * back out of walk_page_range() and invokes handle_mm_fault() from a context\n+ * where no page-table or hugetlb_vma_lock is held.\n  */\n-static int hmm_vma_fault(unsigned long addr, unsigned long end,\n-\t\t\t unsigned int required_fault, struct mm_walk *walk)\n+static int hmm_record_fault(unsigned long addr, unsigned long end,\n+\t\t\t    unsigned int required_fault,\n+\t\t\t    struct mm_walk *walk)\n {\n \tstruct hmm_vma_walk *hmm_vma_walk = walk-\u003eprivate;\n-\tstruct vm_area_struct *vma = walk-\u003evma;\n-\tunsigned int fault_flags = FAULT_FLAG_REMOTE;\n \n \tWARN_ON_ONCE(!required_fault);\n \thmm_vma_walk-\u003elast = addr;\n-\n-\tif (required_fault \u0026 HMM_NEED_WRITE_FAULT) {\n-\t\tif (!(vma-\u003evm_flags \u0026 VM_WRITE))\n-\t\t\treturn -EPERM;\n-\t\tfault_flags |= FAULT_FLAG_WRITE;\n-\t}\n-\n-\tfor (; addr \u003c end; addr += PAGE_SIZE)\n-\t\tif (handle_mm_fault(vma, addr, fault_flags, NULL) \u0026\n-\t\t    VM_FAULT_ERROR)\n-\t\t\treturn -EFAULT;\n-\treturn -EBUSY;\n+\thmm_vma_walk-\u003eend = end;\n+\thmm_vma_walk-\u003erequired_fault = required_fault;\n+\treturn HMM_FAULT_PENDING;\n }\n \n static unsigned int hmm_pte_need_fault(const struct hmm_vma_walk *hmm_vma_walk,\n@@ -174,7 +180,7 @@ static int hmm_vma_walk_hole(unsigned long addr, unsigned long end,\n \t\treturn hmm_pfns_fill(addr, end, range, HMM_PFN_ERROR);\n \t}\n \tif (required_fault)\n-\t\treturn hmm_vma_fault(addr, end, required_fault, walk);\n+\t\treturn hmm_record_fault(addr, end, required_fault, walk);\n \treturn hmm_pfns_fill(addr, end, range, 0);\n }\n \n@@ -209,7 +215,7 @@ static int hmm_vma_handle_pmd(struct mm_walk *walk, unsigned long addr,\n \trequired_fault =\n \t\thmm_range_need_fault(hmm_vma_walk, hmm_pfns, npages, cpu_flags);\n \tif (required_fault)\n-\t\treturn hmm_vma_fault(addr, end, required_fault, walk);\n+\t\treturn hmm_record_fault(addr, end, required_fault, walk);\n \n \tpfn = pmd_pfn(pmd) + ((addr \u0026 ~PMD_MASK) \u003e\u003e PAGE_SHIFT);\n \tfor (i = 0; addr \u003c end; addr += PAGE_SIZE, i++, pfn++) {\n@@ -328,7 +334,7 @@ static int hmm_vma_handle_pte(struct mm_walk *walk, unsigned long addr,\n fault:\n \tpte_unmap(ptep);\n \t/* Fault any virtual address we were asked to fault */\n-\treturn hmm_vma_fault(addr, end, required_fault, walk);\n+\treturn hmm_record_fault(addr, end, required_fault, walk);\n }\n \n #ifdef CONFIG_ARCH_SUPPORTS_PMD_SOFTLEAF\n@@ -371,7 +377,7 @@ static int hmm_vma_handle_absent_pmd(struct mm_walk *walk, unsigned long start,\n \t\t\t\t\t      npages, 0);\n \tif (required_fault) {\n \t\tif (softleaf_is_device_private(entry))\n-\t\t\treturn hmm_vma_fault(addr, end, required_fault, walk);\n+\t\t\treturn hmm_record_fault(addr, end, required_fault, walk);\n \t\telse\n \t\t\treturn -EFAULT;\n \t}\n@@ -517,7 +523,7 @@ static int hmm_vma_walk_pud(pud_t *pudp, unsigned long start, unsigned long end,\n \t\t\t\t\t\t      npages, cpu_flags);\n \t\tif (required_fault) {\n \t\t\tspin_unlock(ptl);\n-\t\t\treturn hmm_vma_fault(addr, end, required_fault, walk);\n+\t\t\treturn hmm_record_fault(addr, end, required_fault, walk);\n \t\t}\n \n \t\tpfn = pud_pfn(pud) + ((addr \u0026 ~PUD_MASK) \u003e\u003e PAGE_SHIFT);\n@@ -564,21 +570,8 @@ static int hmm_vma_walk_hugetlb_entry(pte_t *pte, unsigned long hmask,\n \trequired_fault =\n \t\thmm_pte_need_fault(hmm_vma_walk, pfn_req_flags, cpu_flags);\n \tif (required_fault) {\n-\t\tint ret;\n-\n \t\tspin_unlock(ptl);\n-\t\thugetlb_vma_unlock_read(vma);\n-\t\t/*\n-\t\t * Avoid deadlock: drop the vma lock before calling\n-\t\t * hmm_vma_fault(), which will itself potentially take and\n-\t\t * drop the vma lock. This is also correct from a\n-\t\t * protection point of view, because there is no further\n-\t\t * use here of either pte or ptl after dropping the vma\n-\t\t * lock.\n-\t\t */\n-\t\tret = hmm_vma_fault(addr, end, required_fault, walk);\n-\t\thugetlb_vma_lock_read(vma);\n-\t\treturn ret;\n+\t\treturn hmm_record_fault(addr, end, required_fault, walk);\n \t}\n \n \tpfn = pte_pfn(entry) + ((start \u0026 ~hmask) \u003e\u003e PAGE_SHIFT);\n@@ -637,29 +630,66 @@ static const struct mm_walk_ops hmm_walk_ops = {\n \t.walk_lock\t= PGWALK_RDLOCK,\n };\n \n-/**\n- * hmm_range_fault - try to fault some address in a virtual address range\n- * @range:\targument structure\n- *\n- * Returns 0 on success or one of the following error codes:\n+/*\n+ * hmm_do_fault - fault in a range recorded by a walk callback\n  *\n- * -EINVAL:\tInvalid arguments or mm or virtual address is in an invalid vma\n- *\t\t(e.g., device file vma).\n- * -ENOMEM:\tOut of memory.\n- * -EPERM:\tInvalid permission (e.g., asking for write and range is read\n- *\t\tonly).\n- * -EBUSY:\tThe range has been invalidated and the caller needs to wait for\n- *\t\tthe invalidation to finish.\n- * -EFAULT:     A page was requested to be valid and could not be made valid\n- *              ie it has no backing VMA or it is illegal to access\n+ * Called from the outer loop in hmm_range_fault_locked() after a callback\n+ * returned HMM_FAULT_PENDING.  At this point we hold only mmap_lock;\n+ * the page-table spinlock and any hugetlb_vma_lock acquired by the walk\n+ * framework have already been released by the unwind.\n  *\n- * This is similar to get_user_pages(), except that it can read the page tables\n- * without mutating them (ie causing faults).\n+ * Returns -EBUSY on success (all pages faulted, caller should re-walk).\n+ * Returns a negative errno on failure.\n  */\n-int hmm_range_fault(struct hmm_range *range)\n+static int hmm_do_fault(struct mm_struct *mm,\n+\t\t\tstruct hmm_vma_walk *hmm_vma_walk)\n+{\n+\tunsigned long addr = hmm_vma_walk-\u003elast;\n+\tunsigned long end = hmm_vma_walk-\u003eend;\n+\tunsigned int required_fault = hmm_vma_walk-\u003erequired_fault;\n+\tunsigned int fault_flags = FAULT_FLAG_REMOTE;\n+\tstruct vm_area_struct *vma;\n+\n+\tif (hmm_vma_walk-\u003elocked)\n+\t\tfault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;\n+\n+\tvma = vma_lookup(mm, addr);\n+\tif (!vma)\n+\t\treturn -EFAULT;\n+\n+\tif (required_fault \u0026 HMM_NEED_WRITE_FAULT) {\n+\t\tif (!(vma-\u003evm_flags \u0026 VM_WRITE))\n+\t\t\treturn -EPERM;\n+\t\tfault_flags |= FAULT_FLAG_WRITE;\n+\t}\n+\n+\tfor (; addr \u003c end; addr += PAGE_SIZE) {\n+\t\tvm_fault_t ret;\n+\n+\t\tret = handle_mm_fault(vma, addr, fault_flags, NULL);\n+\n+\t\tif (ret \u0026 (VM_FAULT_COMPLETED | VM_FAULT_RETRY)) {\n+\t\t\t*hmm_vma_walk-\u003elocked = 0;\n+\t\t\treturn HMM_FAULT_UNLOCKED;\n+\t\t}\n+\n+\t\tif (ret \u0026 VM_FAULT_ERROR) {\n+\t\t\tint err = vm_fault_to_errno(ret, 0);\n+\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t\tBUG();\n+\t\t}\n+\t}\n+\n+\treturn -EBUSY;\n+}\n+\n+static int hmm_range_fault_locked(struct hmm_range *range, int *locked)\n {\n \tstruct hmm_vma_walk hmm_vma_walk = {\n \t\t.range = range,\n+\t\t.locked = locked,\n \t\t.last = range-\u003estart,\n \t};\n \tstruct mm_struct *mm = range-\u003enotifier-\u003emm;\n@@ -674,6 +704,22 @@ int hmm_range_fault(struct hmm_range *range)\n \t\t\treturn -EBUSY;\n \t\tret = walk_page_range(mm, hmm_vma_walk.last, range-\u003eend,\n \t\t\t\t      \u0026hmm_walk_ops, \u0026hmm_vma_walk);\n+\t\t/*\n+\t\t * When HMM_FAULT_PENDING is returned a walk callback\n+\t\t * recorded a range that needs handle_mm_fault();\n+\t\t * hmm_do_fault() runs the fault outside walk_page_range()\n+\t\t * (so no page-table or hugetlb_vma_lock is held) and\n+\t\t * returns -EBUSY so the loop re-walks and picks up the\n+\t\t * now-present entries.\n+\t\t */\n+\t\tif (ret == HMM_FAULT_PENDING) {\n+\t\t\tret = hmm_do_fault(mm, \u0026hmm_vma_walk);\n+\t\t\tif (ret == HMM_FAULT_UNLOCKED) {\n+\t\t\t\tif (fatal_signal_pending(current))\n+\t\t\t\t\treturn -EINTR;\n+\t\t\t\treturn -EBUSY;\n+\t\t\t}\n+\t\t}\n \t\t/*\n \t\t * When -EBUSY is returned the loop restarts with\n \t\t * hmm_vma_walk.last set to an address that has not been stored\n@@ -683,8 +729,65 @@ int hmm_range_fault(struct hmm_range *range)\n \t} while (ret == -EBUSY);\n \treturn ret;\n }\n+\n+/**\n+ * hmm_range_fault - try to fault some address in a virtual address range\n+ * @range:\targument structure\n+ *\n+ * Returns 0 on success or one of the following error codes:\n+ *\n+ * -EINVAL:\tInvalid arguments or mm or virtual address is in an invalid vma\n+ *\t\t(e.g., device file vma).\n+ * -ENOMEM:\tOut of memory.\n+ * -EPERM:\tInvalid permission (e.g., asking for write and range is read\n+ *\t\tonly).\n+ * -EBUSY:\tThe range has been invalidated and the caller needs to wait for\n+ *\t\tthe invalidation to finish.\n+ * -EFAULT:     A page was requested to be valid and could not be made valid\n+ *              ie it has no backing VMA or it is illegal to access\n+ *\n+ * This is similar to get_user_pages(), except that it can read the page tables\n+ * without mutating them (ie causing faults).\n+ *\n+ * The mmap lock must be held by the caller and will remain held on return.\n+ * For a variant that allows the mmap lock to be dropped during faults (e.g.,\n+ * for userfaultfd support), see hmm_range_fault_unlocked().\n+ */\n+int hmm_range_fault(struct hmm_range *range)\n+{\n+\treturn hmm_range_fault_locked(range, NULL);\n+}\n EXPORT_SYMBOL(hmm_range_fault);\n \n+/**\n+ * hmm_range_fault_unlocked - fault in a range, possibly dropping the mmap lock\n+ * @range:\targument structure\n+ *\n+ * This is similar to hmm_range_fault(), except the caller must not hold the\n+ * mmap lock. The function takes the mmap read lock internally and allows\n+ * handle_mm_fault() to drop it during faults. If the lock is dropped, the\n+ * function returns -EBUSY and the caller must restart the walk with a fresh\n+ * mmu_interval_read_begin() sequence.\n+ *\n+ * Returns 0 on success or one of the error codes documented for\n+ * hmm_range_fault(). -EINTR is returned if a fatal signal is pending during\n+ * retry handling.\n+ */\n+int hmm_range_fault_unlocked(struct hmm_range *range)\n+{\n+\tstruct mm_struct *mm = range-\u003enotifier-\u003emm;\n+\tint locked = 1;\n+\tint ret;\n+\n+\tmmap_read_lock(mm);\n+\tret = hmm_range_fault_locked(range, \u0026locked);\n+\tif (locked)\n+\t\tmmap_read_unlock(mm);\n+\n+\treturn ret;\n+}\n+EXPORT_SYMBOL(hmm_range_fault_unlocked);\n+\n /**\n  * hmm_dma_map_alloc - Allocate HMM map structure\n  * @dev: device to allocate structure for\ndiff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c\nindex 2f2b9879d10051..71765c4d80f3e4 100644\n--- a/tools/testing/selftests/mm/hmm-tests.c\n+++ b/tools/testing/selftests/mm/hmm-tests.c\n@@ -29,6 +29,10 @@\n #include \u003csys/mman.h\u003e\n #include \u003csys/ioctl.h\u003e\n #include \u003csys/time.h\u003e\n+#include \u003csys/syscall.h\u003e\n+#include \u003csys/eventfd.h\u003e\n+#include \u003clinux/userfaultfd.h\u003e\n+#include \u003cpoll.h\u003e\n \n /*\n  * This is a private UAPI to the kernel test module so it isn't exported\n@@ -2949,4 +2953,149 @@ TEST_F_TIMEOUT(hmm, benchmark_thp_migration, 120)\n \t\t\t\t\t\u0026thp_results, \u0026regular_results);\n \t}\n }\n+/*\n+ * Test that HMM can fault in pages backed by userfaultfd using the\n+ * hmm_range_fault_unlocked() path. This exercises the lock-drop retry\n+ * logic in the HMM framework.\n+ */\n+struct uffd_thread_args {\n+\tint uffd;\n+\tint stop_fd;\n+\tvoid *page_buffer;\n+\tunsigned long page_size;\n+};\n+\n+static void *uffd_handler_thread(void *arg)\n+{\n+\tstruct uffd_thread_args *args = arg;\n+\tstruct uffd_msg msg;\n+\tstruct uffdio_copy copy;\n+\tstruct pollfd pollfd[2];\n+\tint ret;\n+\n+\tpollfd[0].fd = args-\u003euffd;\n+\tpollfd[0].events = POLLIN;\n+\tpollfd[1].fd = args-\u003estop_fd;\n+\tpollfd[1].events = POLLIN;\n+\n+\twhile (1) {\n+\t\tret = poll(pollfd, 2, -1);\n+\t\tif (ret \u003c= 0)\n+\t\t\tbreak;\n+\t\tif (pollfd[1].revents)\n+\t\t\tbreak;\n+\t\tif (!(pollfd[0].revents \u0026 POLLIN))\n+\t\t\tbreak;\n+\n+\t\tret = read(args-\u003euffd, \u0026msg, sizeof(msg));\n+\t\tif (ret != sizeof(msg))\n+\t\t\tbreak;\n+\n+\t\tif (msg.event != UFFD_EVENT_PAGEFAULT)\n+\t\t\tbreak;\n+\n+\t\t/* Fill the page with a known pattern */\n+\t\tmemset(args-\u003epage_buffer, 0xAB, args-\u003epage_size);\n+\n+\t\tcopy.dst = msg.arg.pagefault.address \u0026 ~(args-\u003epage_size - 1);\n+\t\tcopy.src = (unsigned long)args-\u003epage_buffer;\n+\t\tcopy.len = args-\u003epage_size;\n+\t\tcopy.mode = 0;\n+\t\tcopy.copy = 0;\n+\n+\t\tret = ioctl(args-\u003euffd, UFFDIO_COPY, \u0026copy);\n+\t\tif (ret \u003c 0)\n+\t\t\tbreak;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+TEST_F(hmm, userfaultfd_read)\n+{\n+\tstruct hmm_buffer *buffer;\n+\tstruct uffd_thread_args uffd_args;\n+\tunsigned long npages;\n+\tunsigned long size;\n+\tunsigned long i;\n+\tunsigned char *ptr;\n+\tpthread_t thread;\n+\tint uffd;\n+\tint stop_fd;\n+\tint ret;\n+\tstruct uffdio_api api;\n+\tstruct uffdio_register reg;\n+\tuint64_t stop = 1;\n+\tssize_t nwrite;\n+\n+\tnpages = 4;\n+\tsize = npages \u003c\u003c self-\u003epage_shift;\n+\n+\t/* Create userfaultfd */\n+\tuffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);\n+\tif (uffd \u003c 0)\n+\t\tSKIP(return, \"userfaultfd not available\");\n+\n+\tapi.api = UFFD_API;\n+\tapi.features = 0;\n+\tret = ioctl(uffd, UFFDIO_API, \u0026api);\n+\tASSERT_EQ(ret, 0);\n+\n+\tbuffer = malloc(sizeof(*buffer));\n+\tASSERT_NE(buffer, NULL);\n+\n+\tbuffer-\u003efd = -1;\n+\tbuffer-\u003esize = size;\n+\tbuffer-\u003emirror = malloc(size);\n+\tASSERT_NE(buffer-\u003emirror, NULL);\n+\n+\t/* Create anonymous mapping */\n+\tbuffer-\u003eptr = mmap(NULL, size,\n+\t\t\t   PROT_READ | PROT_WRITE,\n+\t\t\t   MAP_PRIVATE | MAP_ANONYMOUS,\n+\t\t\t   -1, 0);\n+\tASSERT_NE(buffer-\u003eptr, MAP_FAILED);\n+\n+\t/* Register the region with userfaultfd */\n+\treg.range.start = (unsigned long)buffer-\u003eptr;\n+\treg.range.len = size;\n+\treg.mode = UFFDIO_REGISTER_MODE_MISSING;\n+\tret = ioctl(uffd, UFFDIO_REGISTER, \u0026reg);\n+\tASSERT_EQ(ret, 0);\n+\n+\t/* Set up the handler thread */\n+\tuffd_args.uffd = uffd;\n+\tstop_fd = eventfd(0, EFD_CLOEXEC);\n+\tASSERT_GE(stop_fd, 0);\n+\tuffd_args.stop_fd = stop_fd;\n+\tuffd_args.page_buffer = malloc(self-\u003epage_size);\n+\tASSERT_NE(uffd_args.page_buffer, NULL);\n+\tuffd_args.page_size = self-\u003epage_size;\n+\n+\tret = pthread_create(\u0026thread, NULL, uffd_handler_thread, \u0026uffd_args);\n+\tASSERT_EQ(ret, 0);\n+\n+\t/*\n+\t * Use the unlocked read path which allows the mmap lock to be\n+\t * dropped during the fault, enabling userfaultfd resolution.\n+\t */\n+\tret = hmm_dmirror_cmd(self-\u003efd, HMM_DMIRROR_READ_UNLOCKED,\n+\t\t\t      buffer, npages);\n+\tASSERT_EQ(ret, 0);\n+\tASSERT_EQ(buffer-\u003ecpages, npages);\n+\n+\t/* Verify the device read the data filled by the uffd handler */\n+\tptr = buffer-\u003emirror;\n+\tfor (i = 0; i \u003c size; ++i)\n+\t\tASSERT_EQ(ptr[i], (unsigned char)0xAB);\n+\n+\tnwrite = write(stop_fd, \u0026stop, sizeof(stop));\n+\tASSERT_EQ(nwrite, sizeof(stop));\n+\tpthread_join(thread, NULL);\n+\tclose(stop_fd);\n+\tfree(uffd_args.page_buffer);\n+\tclose(uffd);\n+\thmm_buffer_free(buffer);\n+}\n+\n TEST_HARNESS_MAIN\n"
}

2/1 2026/07/07 02:03 agent patch-evaluator
1m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "TEST_HMM",
    "MSHV_ROOT"
  ],
  "FocusSymbols": [
    "hmm_range_fault_unlocked",
    "hmm_range_fault",
    "hmm_do_fault",
    "hmm_record_fault",
    "hmm_range_fault_locked",
    "dmirror_read_unlocked",
    "dmirror_range_fault_unlocked",
    "dmirror_fault_unlocked",
    "mshv_region_hmm_fault_and_lock"
  ],
  "Reasoning": "The patch introduces a new API `hmm_range_fault_unlocked()` in the HMM subsystem to allow dropping the mmap lock during page faults, which is useful for regions managed by userfaultfd. It refactors the core `hmm_range_fault()` logic to support this and updates the `test_hmm` module and `mshv_regions` driver to utilize the new functionality. This is a functional change to memory management and driver code that should be fuzzed.",
  "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).

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.
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.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. 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 f4b7593f6fe86cb6c540e644a9b1d1dcdf880e70
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jul 7 02:03:25 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/mm/hmm.rst b/Documentation/mm/hmm.rst
index 7d61b7a8b65b79..844343712abdda 100644
--- a/Documentation/mm/hmm.rst
+++ b/Documentation/mm/hmm.rst
@@ -208,6 +208,65 @@ invalidate() callback. That lock must be held before calling
 mmu_interval_read_retry() to avoid any race with a concurrent CPU page table
 update.
 
+Dropping the mmap lock during page faults
+=========================================
+
+Some VMAs have fault handlers that need to release the mmap lock while
+servicing a fault (for example, regions managed by ``userfaultfd``).
+``hmm_range_fault()`` cannot be used on such mappings because it must hold the
+mmap lock for the duration of the call. Drivers that need to support them
+should call::
+
+  int hmm_range_fault_unlocked(struct hmm_range *range);
+
+The caller must not hold ``mmap_read_lock`` before the call.
+``hmm_range_fault_unlocked()`` takes the mmap read lock internally and allows
+``handle_mm_fault()`` to drop it during fault handling. If the mmap lock is
+dropped, the function returns ``-EBUSY``. The caller must then restart the
+walk from ``range->start`` with a fresh notifier sequence. ``-EINTR`` is
+returned if a fatal signal is pending during retry handling.
+
+A typical caller looks like this::
+
+ int driver_populate_range_unlocked(...)
+ {
+      struct hmm_range range;
+      ...
+
+      range.notifier = &interval_sub;
+      range.start = ...;
+      range.end = ...;
+      range.hmm_pfns = ...;
+
+      if (!mmget_not_zero(interval_sub.mm))
+          return -EFAULT;
+
+ again:
+      range.notifier_seq = mmu_interval_read_begin(&interval_sub);
+      ret = hmm_range_fault_unlocked(&range);
+      if (ret) {
+          if (ret == -EBUSY)
+              goto again;
+          goto out_put;
+      }
+
+      take_lock(driver->update);
+      if (mmu_interval_read_retry(&interval_sub, range.notifier_seq)) {
+          release_lock(driver->update);
+          goto again;
+      }
+
+      /* Use pfns array content to update device page table,
+       * under the update lock */
+
+      release_lock(driver->update);
+      ret = 0;
+
+ out_put:
+      mmput(interval_sub.mm);
+      return ret;
+ }
+
 Leverage default_flags and pfn_flags_mask
 =========================================
 
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 6d65e5b4215295..04676f06c5c742 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -388,13 +388,11 @@ int mshv_region_get(struct mshv_mem_region *region)
  *
  * 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.
+ * 2. Handles HMM faults for the specified range.
+ * 3. If successful, locks the memory region mutex.
+ * 4. 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.
+ *    hmm_range_fault_unlocked() so the caller retries the range fault.
  *
  * Return: 0 on success, a negative error code otherwise.
  */
@@ -404,9 +402,7 @@ static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,
 	int ret;
 
 	range->notifier_seq = mmu_interval_read_begin(range->notifier);
-	mmap_read_lock(region->mreg_mni.mm);
-	ret = hmm_range_fault(range);
-	mmap_read_unlock(region->mreg_mni.mm);
+	ret = hmm_range_fault_unlocked(range);
 	if (ret)
 		return ret;
 
diff --git a/include/linux/hmm.h b/include/linux/hmm.h
index db75ffc949a7ac..1b584e5a29001e 100644
--- a/include/linux/hmm.h
+++ b/include/linux/hmm.h
@@ -123,6 +123,7 @@ struct hmm_range {
  * Please see Documentation/mm/hmm.rst for how to use the range API.
  */
 int hmm_range_fault(struct hmm_range *range);
+int hmm_range_fault_unlocked(struct hmm_range *range);
 
 /*
  * HMM_RANGE_DEFAULT_TIMEOUT - default timeout (ms) when waiting for a range
diff --git a/lib/test_hmm.c b/lib/test_hmm.c
index 45c0cb9922184a..dcc3a23bb983dc 100644
--- a/lib/test_hmm.c
+++ b/lib/test_hmm.c
@@ -389,6 +389,76 @@ static int dmirror_range_fault(struct dmirror *dmirror,
 	return ret;
 }
 
+static int dmirror_range_fault_unlocked(struct dmirror *dmirror,
+					struct hmm_range *range)
+{
+	unsigned long timeout =
+		jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
+	int ret;
+
+	while (true) {
+		if (time_after(jiffies, timeout)) {
+			ret = -EBUSY;
+			goto out;
+		}
+
+		range->notifier_seq = mmu_interval_read_begin(range->notifier);
+		ret = hmm_range_fault_unlocked(range);
+		if (ret) {
+			if (ret == -EBUSY)
+				continue;
+			goto out;
+		}
+
+		mutex_lock(&dmirror->mutex);
+		if (mmu_interval_read_retry(range->notifier,
+					    range->notifier_seq)) {
+			mutex_unlock(&dmirror->mutex);
+			continue;
+		}
+		break;
+	}
+
+	ret = dmirror_do_fault(dmirror, range);
+
+	mutex_unlock(&dmirror->mutex);
+out:
+	return ret;
+}
+
+static int dmirror_fault_unlocked(struct dmirror *dmirror,
+				  unsigned long start,
+				  unsigned long end, bool write)
+{
+	struct mm_struct *mm = dmirror->notifier.mm;
+	unsigned long addr;
+	unsigned long pfns[32];
+	struct hmm_range range = {
+		.notifier = &dmirror->notifier,
+		.hmm_pfns = pfns,
+		.pfn_flags_mask = 0,
+		.default_flags =
+			HMM_PFN_REQ_FAULT | (write ? HMM_PFN_REQ_WRITE : 0),
+		.dev_private_owner = dmirror->mdevice,
+	};
+	int ret = 0;
+
+	if (!mmget_not_zero(mm))
+		return -EFAULT;
+
+	for (addr = start; addr < end; addr = range.end) {
+		range.start = addr;
+		range.end = min(addr + (ARRAY_SIZE(pfns) << PAGE_SHIFT), end);
+
+		ret = dmirror_range_fault_unlocked(dmirror, &range);
+		if (ret)
+			break;
+	}
+
+	mmput(mm);
+	return ret;
+}
+
 static int dmirror_fault(struct dmirror *dmirror, unsigned long start,
 			 unsigned long end, bool write)
 {
@@ -488,6 +558,47 @@ static int dmirror_read(struct dmirror *dmirror, struct hmm_dmirror_cmd *cmd)
 	return ret;
 }
 
+static int dmirror_read_unlocked(struct dmirror *dmirror,
+				 struct hmm_dmirror_cmd *cmd)
+{
+	struct dmirror_bounce bounce;
+	unsigned long start, end;
+	unsigned long size = cmd->npages << PAGE_SHIFT;
+	int ret;
+
+	start = cmd->addr;
+	end = start + size;
+	if (end < start)
+		return -EINVAL;
+
+	ret = dmirror_bounce_init(&bounce, start, size);
+	if (ret)
+		return ret;
+
+	while (1) {
+		mutex_lock(&dmirror->mutex);
+		ret = dmirror_do_read(dmirror, start, end, &bounce);
+		mutex_unlock(&dmirror->mutex);
+		if (ret != -ENOENT)
+			break;
+
+		start = cmd->addr + (bounce.cpages << PAGE_SHIFT);
+		ret = dmirror_fault_unlocked(dmirror, start, end, false);
+		if (ret)
+			break;
+		cmd->faults++;
+	}
+
+	if (ret == 0) {
+		if (copy_to_user(u64_to_user_ptr(cmd->ptr), bounce.ptr,
+				 bounce.size))
+			ret = -EFAULT;
+	}
+	cmd->cpages = bounce.cpages;
+	dmirror_bounce_fini(&bounce);
+	return ret;
+}
+
 static int dmirror_do_write(struct dmirror *dmirror, unsigned long start,
 			    unsigned long end, struct dmirror_bounce *bounce)
 {
@@ -1572,6 +1683,9 @@ static long dmirror_fops_unlocked_ioctl(struct file *filp,
 		dmirror->flags = cmd.npages;
 		ret = 0;
 		break;
+	case HMM_DMIRROR_READ_UNLOCKED:
+		ret = dmirror_read_unlocked(dmirror, &cmd);
+		break;
 
 	default:
 		return -EINVAL;
diff --git a/lib/test_hmm_uapi.h b/lib/test_hmm_uapi.h
index f94c6d45733828..ea9b0ec404fb55 100644
--- a/lib/test_hmm_uapi.h
+++ b/lib/test_hmm_uapi.h
@@ -38,6 +38,7 @@ struct hmm_dmirror_cmd {
 #define HMM_DMIRROR_CHECK_EXCLUSIVE	_IOWR('H', 0x06, struct hmm_dmirror_cmd)
 #define HMM_DMIRROR_RELEASE		_IOWR('H', 0x07, struct hmm_dmirror_cmd)
 #define HMM_DMIRROR_FLAGS		_IOWR('H', 0x08, struct hmm_dmirror_cmd)
+#define HMM_DMIRROR_READ_UNLOCKED	_IOWR('H', 0x09, struct hmm_dmirror_cmd)
 
 #define HMM_DMIRROR_FLAG_FAIL_ALLOC	(1ULL << 0)
 
diff --git a/mm/hmm.c b/mm/hmm.c
index 4f3f627d2b4772..316116994209c8 100644
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -32,9 +32,27 @@
 
 struct hmm_vma_walk {
 	struct hmm_range	*range;
+	int			*locked;
 	unsigned long		last;
+	unsigned long		end;
+	unsigned int		required_fault;
 };
 
+/*
+ * Internal sentinel returned by walk callbacks when they need a page fault.
+ * The callback stores end/required_fault in hmm_vma_walk; the outer loop
+ * consumes the sentinel and never propagates it to the caller.
+ */
+#define HMM_FAULT_PENDING	-EAGAIN
+
+/*
+ * Internal sentinel returned by hmm_do_fault() when handle_mm_fault()
+ * completes a page fault with the mmap lock dropped. hmm_do_fault() sets
+ * *locked = 0; the outer loop consumes the sentinel and never propagates it
+ * to the caller.
+ */
+#define HMM_FAULT_UNLOCKED	-ENOLCK
+
 enum {
 	HMM_NEED_FAULT = 1 << 0,
 	HMM_NEED_WRITE_FAULT = 1 << 1,
@@ -60,37 +78,25 @@ static int hmm_pfns_fill(unsigned long addr, unsigned long end,
 }
 
 /*
- * hmm_vma_fault() - fault in a range lacking valid pmd or pte(s)
- * @addr: range virtual start address (inclusive)
- * @end: range virtual end address (exclusive)
- * @required_fault: HMM_NEED_* flags
- * @walk: mm_walk structure
- * Return: -EBUSY after page fault, or page fault error
+ * hmm_record_fault() - record a range that needs to be faulted in
  *
- * This function will be called whenever pmd_none() or pte_none() returns true,
- * or whenever there is no page directory covering the virtual address range.
+ * Called by the walk callbacks when they discover that part of the range
+ * needs a page fault.  The callback records what to fault and returns
+ * HMM_FAULT_PENDING; the outer loop in hmm_range_fault_locked() drops
+ * back out of walk_page_range() and invokes handle_mm_fault() from a context
+ * where no page-table or hugetlb_vma_lock is held.
  */
-static int hmm_vma_fault(unsigned long addr, unsigned long end,
-			 unsigned int required_fault, struct mm_walk *walk)
+static int hmm_record_fault(unsigned long addr, unsigned long end,
+			    unsigned int required_fault,
+			    struct mm_walk *walk)
 {
 	struct hmm_vma_walk *hmm_vma_walk = walk->private;
-	struct vm_area_struct *vma = walk->vma;
-	unsigned int fault_flags = FAULT_FLAG_REMOTE;
 
 	WARN_ON_ONCE(!required_fault);
 	hmm_vma_walk->last = addr;
-
-	if (required_fault & HMM_NEED_WRITE_FAULT) {
-		if (!(vma->vm_flags & VM_WRITE))
-			return -EPERM;
-		fault_flags |= FAULT_FLAG_WRITE;
-	}
-
-	for (; addr < end; addr += PAGE_SIZE)
-		if (handle_mm_fault(vma, addr, fault_flags, NULL) &
-		    VM_FAULT_ERROR)
-			return -EFAULT;
-	return -EBUSY;
+	hmm_vma_walk->end = end;
+	hmm_vma_walk->required_fault = required_fault;
+	return HMM_FAULT_PENDING;
 }
 
 static unsigned int hmm_pte_need_fault(const struct hmm_vma_walk *hmm_vma_walk,
@@ -174,7 +180,7 @@ static int hmm_vma_walk_hole(unsigned long addr, unsigned long end,
 		return hmm_pfns_fill(addr, end, range, HMM_PFN_ERROR);
 	}
 	if (required_fault)
-		return hmm_vma_fault(addr, end, required_fault, walk);
+		return hmm_record_fault(addr, end, required_fault, walk);
 	return hmm_pfns_fill(addr, end, range, 0);
 }
 
@@ -209,7 +215,7 @@ static int hmm_vma_handle_pmd(struct mm_walk *walk, unsigned long addr,
 	required_fault =
 		hmm_range_need_fault(hmm_vma_walk, hmm_pfns, npages, cpu_flags);
 	if (required_fault)
-		return hmm_vma_fault(addr, end, required_fault, walk);
+		return hmm_record_fault(addr, end, required_fault, walk);
 
 	pfn = pmd_pfn(pmd) + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
 	for (i = 0; addr < end; addr += PAGE_SIZE, i++, pfn++) {
@@ -328,7 +334,7 @@ static int hmm_vma_handle_pte(struct mm_walk *walk, unsigned long addr,
 fault:
 	pte_unmap(ptep);
 	/* Fault any virtual address we were asked to fault */
-	return hmm_vma_fault(addr, end, required_fault, walk);
+	return hmm_record_fault(addr, end, required_fault, walk);
 }
 
 #ifdef CONFIG_ARCH_SUPPORTS_PMD_SOFTLEAF
@@ -371,7 +377,7 @@ static int hmm_vma_handle_absent_pmd(struct mm_walk *walk, unsigned long start,
 					      npages, 0);
 	if (required_fault) {
 		if (softleaf_is_device_private(entry))
-			return hmm_vma_fault(addr, end, required_fault, walk);
+			return hmm_record_fault(addr, end, required_fault, walk);
 		else
 			return -EFAULT;
 	}
@@ -517,7 +523,7 @@ static int hmm_vma_walk_pud(pud_t *pudp, unsigned long start, unsigned long end,
 						      npages, cpu_flags);
 		if (required_fault) {
 			spin_unlock(ptl);
-			return hmm_vma_fault(addr, end, required_fault, walk);
+			return hmm_record_fault(addr, end, required_fault, walk);
 		}
 
 		pfn = pud_pfn(pud) + ((addr & ~PUD_MASK) >> PAGE_SHIFT);
@@ -564,21 +570,8 @@ static int hmm_vma_walk_hugetlb_entry(pte_t *pte, unsigned long hmask,
 	required_fault =
 		hmm_pte_need_fault(hmm_vma_walk, pfn_req_flags, cpu_flags);
 	if (required_fault) {
-		int ret;
-
 		spin_unlock(ptl);
-		hugetlb_vma_unlock_read(vma);
-		/*
-		 * Avoid deadlock: drop the vma lock before calling
-		 * hmm_vma_fault(), which will itself potentially take and
-		 * drop the vma lock. This is also correct from a
-		 * protection point of view, because there is no further
-		 * use here of either pte or ptl after dropping the vma
-		 * lock.
-		 */
-		ret = hmm_vma_fault(addr, end, required_fault, walk);
-		hugetlb_vma_lock_read(vma);
-		return ret;
+		return hmm_record_fault(addr, end, required_fault, walk);
 	}
 
 	pfn = pte_pfn(entry) + ((start & ~hmask) >> PAGE_SHIFT);
@@ -637,29 +630,66 @@ static const struct mm_walk_ops hmm_walk_ops = {
 	.walk_lock	= PGWALK_RDLOCK,
 };
 
-/**
- * hmm_range_fault - try to fault some address in a virtual address range
- * @range:	argument structure
- *
- * Returns 0 on success or one of the following error codes:
+/*
+ * hmm_do_fault - fault in a range recorded by a walk callback
  *
- * -EINVAL:	Invalid arguments or mm or virtual address is in an invalid vma
- *		(e.g., device file vma).
- * -ENOMEM:	Out of memory.
- * -EPERM:	Invalid permission (e.g., asking for write and range is read
- *		only).
- * -EBUSY:	The range has been invalidated and the caller needs to wait for
- *		the invalidation to finish.
- * -EFAULT:     A page was requested to be valid and could not be made valid
- *              ie it has no backing VMA or it is illegal to access
+ * Called from the outer loop in hmm_range_fault_locked() after a callback
+ * returned HMM_FAULT_PENDING.  At this point we hold only mmap_lock;
+ * the page-table spinlock and any hugetlb_vma_lock acquired by the walk
+ * framework have already been released by the unwind.
  *
- * This is similar to get_user_pages(), except that it can read the page tables
- * without mutating them (ie causing faults).
+ * Returns -EBUSY on success (all pages faulted, caller should re-walk).
+ * Returns a negative errno on failure.
  */
-int hmm_range_fault(struct hmm_range *range)
+static int hmm_do_fault(struct mm_struct *mm,
+			struct hmm_vma_walk *hmm_vma_walk)
+{
+	unsigned long addr = hmm_vma_walk->last;
+	unsigned long end = hmm_vma_walk->end;
+	unsigned int required_fault = hmm_vma_walk->required_fault;
+	unsigned int fault_flags = FAULT_FLAG_REMOTE;
+	struct vm_area_struct *vma;
+
+	if (hmm_vma_walk->locked)
+		fault_flags |= FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+
+	vma = vma_lookup(mm, addr);
+	if (!vma)
+		return -EFAULT;
+
+	if (required_fault & HMM_NEED_WRITE_FAULT) {
+		if (!(vma->vm_flags & VM_WRITE))
+			return -EPERM;
+		fault_flags |= FAULT_FLAG_WRITE;
+	}
+
+	for (; addr < end; addr += PAGE_SIZE) {
+		vm_fault_t ret;
+
+		ret = handle_mm_fault(vma, addr, fault_flags, NULL);
+
+		if (ret & (VM_FAULT_COMPLETED | VM_FAULT_RETRY)) {
+			*hmm_vma_walk->locked = 0;
+			return HMM_FAULT_UNLOCKED;
+		}
+
+		if (ret & VM_FAULT_ERROR) {
+			int err = vm_fault_to_errno(ret, 0);
+
+			if (err)
+				return err;
+			BUG();
+		}
+	}
+
+	return -EBUSY;
+}
+
+static int hmm_range_fault_locked(struct hmm_range *range, int *locked)
 {
 	struct hmm_vma_walk hmm_vma_walk = {
 		.range = range,
+		.locked = locked,
 		.last = range->start,
 	};
 	struct mm_struct *mm = range->notifier->mm;
@@ -674,6 +704,22 @@ int hmm_range_fault(struct hmm_range *range)
 			return -EBUSY;
 		ret = walk_page_range(mm, hmm_vma_walk.last, range->end,
 				      &hmm_walk_ops, &hmm_vma_walk);
+		/*
+		 * When HMM_FAULT_PENDING is returned a walk callback
+		 * recorded a range that needs handle_mm_fault();
+		 * hmm_do_fault() runs the fault outside walk_page_range()
+		 * (so no page-table or hugetlb_vma_lock is held) and
+		 * returns -EBUSY so the loop re-walks and picks up the
+		 * now-present entries.
+		 */
+		if (ret == HMM_FAULT_PENDING) {
+			ret = hmm_do_fault(mm, &hmm_vma_walk);
+			if (ret == HMM_FAULT_UNLOCKED) {
+				if (fatal_signal_pending(current))
+					return -EINTR;
+				return -EBUSY;
+			}
+		}
 		/*
 		 * When -EBUSY is returned the loop restarts with
 		 * hmm_vma_walk.last set to an address that has not been stored
@@ -683,8 +729,65 @@ int hmm_range_fault(struct hmm_range *range)
 	} while (ret == -EBUSY);
 	return ret;
 }
+
+/**
+ * hmm_range_fault - try to fault some address in a virtual address range
+ * @range:	argument structure
+ *
+ * Returns 0 on success or one of the following error codes:
+ *
+ * -EINVAL:	Invalid arguments or mm or virtual address is in an invalid vma
+ *		(e.g., device file vma).
+ * -ENOMEM:	Out of memory.
+ * -EPERM:	Invalid permission (e.g., asking for write and range is read
+ *		only).
+ * -EBUSY:	The range has been invalidated and the caller needs to wait for
+ *		the invalidation to finish.
+ * -EFAULT:     A page was requested to be valid and could not be made valid
+ *              ie it has no backing VMA or it is illegal to access
+ *
+ * This is similar to get_user_pages(), except that it can read the page tables
+ * without mutating them (ie causing faults).
+ *
+ * The mmap lock must be held by the caller and will remain held on return.
+ * For a variant that allows the mmap lock to be dropped during faults (e.g.,
+ * for userfaultfd support), see hmm_range_fault_unlocked().
+ */
+int hmm_range_fault(struct hmm_range *range)
+{
+	return hmm_range_fault_locked(range, NULL);
+}
 EXPORT_SYMBOL(hmm_range_fault);
 
+/**
+ * hmm_range_fault_unlocked - fault in a range, possibly dropping the mmap lock
+ * @range:	argument structure
+ *
+ * This is similar to hmm_range_fault(), except the caller must not hold the
+ * mmap lock. The function takes the mmap read lock internally and allows
+ * handle_mm_fault() to drop it during faults. If the lock is dropped, the
+ * function returns -EBUSY and the caller must restart the walk with a fresh
+ * mmu_interval_read_begin() sequence.
+ *
+ * Returns 0 on success or one of the error codes documented for
+ * hmm_range_fault(). -EINTR is returned if a fatal signal is pending during
+ * retry handling.
+ */
+int hmm_range_fault_unlocked(struct hmm_range *range)
+{
+	struct mm_struct *mm = range->notifier->mm;
+	int locked = 1;
+	int ret;
+
+	mmap_read_lock(mm);
+	ret = hmm_range_fault_locked(range, &locked);
+	if (locked)
+		mmap_read_unlock(mm);
+
+	return ret;
+}
+EXPORT_SYMBOL(hmm_range_fault_unlocked);
+
 /**
  * hmm_dma_map_alloc - Allocate HMM map structure
  * @dev: device to allocate structure for
diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c
index 2f2b9879d10051..71765c4d80f3e4 100644
--- a/tools/testing/selftests/mm/hmm-tests.c
+++ b/tools/testing/selftests/mm/hmm-tests.c
@@ -29,6 +29,10 @@
 #include <sys/mman.h>
 #include <sys/ioctl.h>
 #include <sys/time.h>
+#include <sys/syscall.h>
+#include <sys/eventfd.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
 
 /*
  * This is a private UAPI to the kernel test module so it isn't exported
@@ -2949,4 +2953,149 @@ TEST_F_TIMEOUT(hmm, benchmark_thp_migration, 120)
 					&thp_results, &regular_results);
 	}
 }
+/*
+ * Test that HMM can fault in pages backed by userfaultfd using the
+ * hmm_range_fault_unlocked() path. This exercises the lock-drop retry
+ * logic in the HMM framework.
+ */
+struct uffd_thread_args {
+	int uffd;
+	int stop_fd;
+	void *page_buffer;
+	unsigned long page_size;
+};
+
+static void *uffd_handler_thread(void *arg)
+{
+	struct uffd_thread_args *args = arg;
+	struct uffd_msg msg;
+	struct uffdio_copy copy;
+	struct pollfd pollfd[2];
+	int ret;
+
+	pollfd[0].fd = args->uffd;
+	pollfd[0].events = POLLIN;
+	pollfd[1].fd = args->stop_fd;
+	pollfd[1].events = POLLIN;
+
+	while (1) {
+		ret = poll(pollfd, 2, -1);
+		if (ret <= 0)
+			break;
+		if (pollfd[1].revents)
+			break;
+		if (!(pollfd[0].revents & POLLIN))
+			break;
+
+		ret = read(args->uffd, &msg, sizeof(msg));
+		if (ret != sizeof(msg))
+			break;
+
+		if (msg.event != UFFD_EVENT_PAGEFAULT)
+			break;
+
+		/* Fill the page with a known pattern */
+		memset(args->page_buffer, 0xAB, args->page_size);
+
+		copy.dst = msg.arg.pagefault.address & ~(args->page_size - 1);
+		copy.src = (unsigned long)args->page_buffer;
+		copy.len = args->page_size;
+		copy.mode = 0;
+		copy.copy = 0;
+
+		ret = ioctl(args->uffd, UFFDIO_COPY, &copy);
+		if (ret < 0)
+			break;
+	}
+
+	return NULL;
+}
+
+TEST_F(hmm, userfaultfd_read)
+{
+	struct hmm_buffer *buffer;
+	struct uffd_thread_args uffd_args;
+	unsigned long npages;
+	unsigned long size;
+	unsigned long i;
+	unsigned char *ptr;
+	pthread_t thread;
+	int uffd;
+	int stop_fd;
+	int ret;
+	struct uffdio_api api;
+	struct uffdio_register reg;
+	uint64_t stop = 1;
+	ssize_t nwrite;
+
+	npages = 4;
+	size = npages << self->page_shift;
+
+	/* Create userfaultfd */
+	uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+	if (uffd < 0)
+		SKIP(return, "userfaultfd not available");
+
+	api.api = UFFD_API;
+	api.features = 0;
+	ret = ioctl(uffd, UFFDIO_API, &api);
+	ASSERT_EQ(ret, 0);
+
+	buffer = malloc(sizeof(*buffer));
+	ASSERT_NE(buffer, NULL);
+
+	buffer->fd = -1;
+	buffer->size = size;
+	buffer->mirror = malloc(size);
+	ASSERT_NE(buffer->mirror, NULL);
+
+	/* Create anonymous mapping */
+	buffer->ptr = mmap(NULL, size,
+			   PROT_READ | PROT_WRITE,
+			   MAP_PRIVATE | MAP_ANONYMOUS,
+			   -1, 0);
+	ASSERT_NE(buffer->ptr, MAP_FAILED);
+
+	/* Register the region with userfaultfd */
+	reg.range.start = (unsigned long)buffer->ptr;
+	reg.range.len = size;
+	reg.mode = UFFDIO_REGISTER_MODE_MISSING;
+	ret = ioctl(uffd, UFFDIO_REGISTER, &reg);
+	ASSERT_EQ(ret, 0);
+
+	/* Set up the handler thread */
+	uffd_args.uffd = uffd;
+	stop_fd = eventfd(0, EFD_CLOEXEC);
+	ASSERT_GE(stop_fd, 0);
+	uffd_args.stop_fd = stop_fd;
+	uffd_args.page_buffer = malloc(self->page_size);
+	ASSERT_NE(uffd_args.page_buffer, NULL);
+	uffd_args.page_size = self->page_size;
+
+	ret = pthread_create(&thread, NULL, uffd_handler_thread, &uffd_args);
+	ASSERT_EQ(ret, 0);
+
+	/*
+	 * Use the unlocked read path which allows the mmap lock to be
+	 * dropped during the fault, enabling userfaultfd resolution.
+	 */
+	ret = hmm_dmirror_cmd(self->fd, HMM_DMIRROR_READ_UNLOCKED,
+			      buffer, npages);
+	ASSERT_EQ(ret, 0);
+	ASSERT_EQ(buffer->cpages, npages);
+
+	/* Verify the device read the data filled by the uffd handler */
+	ptr = buffer->mirror;
+	for (i = 0; i < size; ++i)
+		ASSERT_EQ(ptr[i], (unsigned char)0xAB);
+
+	nwrite = write(stop_fd, &stop, sizeof(stop));
+	ASSERT_EQ(nwrite, sizeof(stop));
+	pthread_join(thread, NULL);
+	close(stop_fd);
+	free(uffd_args.page_buffer);
+	close(uffd);
+	hmm_buffer_free(buffer);
+}
+
 TEST_HARNESS_MAIN

3/2 2026/07/07 02:03 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10610
							output: 22
							thoughts: 358
						

Thoughts:
**My Assessment of the HMM Patch**

Okay, so I'm looking at this HMM patch, and it seems like a fairly targeted change. The core of it revolves around allowing us to drop the mmap lock during page faults, specifically within the Heterogeneous Memory Management subsystem. This `hmm_range_fault_unlocked()` function is the key, and it's designed to be beneficial for regions managed by `userfaultfd`, which makes sense. Minimizing lock contention during fault handling is always a good thing, especially in this context.

The patch touches a good number of files: `mm/hmm.c`, the include files, the test code (`lib/test_hmm.c`), the Hyper-V driver (`drivers/hv/mshv_regions.c`), and a self-test. This indicates a good level of scope and impact, touching both core HMM functionality and the relevant user space interfaces and drivers.

Functionally, this looks sound. We've got the new `hmm_range_fault_unlocked` API, and the existing `hmm_range_fault` has been reworked to utilize a common internal function. The Hyper-V driver (`mshv_regions.c`) is updated to use the new API, so we have a concrete use case. And, importantly, there's a new `HMM_DMIRROR_READ_UNLOCKED` ioctl added to the test code, which gives us a dedicated mechanism to actually *test* this new unlocked functionality.

Reachable code is critical. I need to confirm that this code is actually *used*. `hmm_range_fault_unlocked` is exported, and it's being used by `test_hmm.c` (a test module, good!) and `mshv_regions.c` (Hyper-V). So, the reachability seems solid. But I need to confirm the build config options to verify they are active.

Now, to make sure the code is actually *enabled* in relevant configurations, I'll need to check the configuration options. I'll need to check for `CONFIG_TEST_HMM`, `CONFIG_HYPERV`, and `CONFIG_MSHV`. Let me fire up my `grepper` and see what the build settings are for this code. This will confirm the patch's integration into the standard kernel build flow.



4/2 2026/07/07 02:03 tool grepper
0m Args:
{
  "Expression": "hmm_range_fault_unlocked"
}

Results:
{
  "Output": "Documentation/mm/hmm.rst=218=should call::\nDocumentation/mm/hmm.rst-219-\nDocumentation/mm/hmm.rst:220:  int hmm_range_fault_unlocked(struct hmm_range *range);\nDocumentation/mm/hmm.rst-221-\nDocumentation/mm/hmm.rst=222=The caller must not hold ``mmap_read_lock`` before the call.\nDocumentation/mm/hmm.rst:223:``hmm_range_fault_unlocked()`` takes the mmap read lock internally and allows\nDocumentation/mm/hmm.rst-224-``handle_mm_fault()`` to drop it during fault handling. If the mmap lock is\n--\nDocumentation/mm/hmm.rst=229=A typical caller looks like this::\n--\nDocumentation/mm/hmm.rst-245-      range.notifier_seq = mmu_interval_read_begin(\u0026interval_sub);\nDocumentation/mm/hmm.rst:246:      ret = hmm_range_fault_unlocked(\u0026range);\nDocumentation/mm/hmm.rst-247-      if (ret) {\n--\ndrivers/hv/mshv_regions.c=379=int mshv_region_get(struct mshv_mem_region *region)\n--\ndrivers/hv/mshv_regions.c-394- *    If it has, releases the mutex and returns -EBUSY to match with\ndrivers/hv/mshv_regions.c:395: *    hmm_range_fault_unlocked() so the caller retries the range fault.\ndrivers/hv/mshv_regions.c-396- *\n--\ndrivers/hv/mshv_regions.c=399=static int mshv_region_hmm_fault_and_lock(struct mshv_mem_region *region,\n--\ndrivers/hv/mshv_regions.c-404-\trange-\u003enotifier_seq = mmu_interval_read_begin(range-\u003enotifier);\ndrivers/hv/mshv_regions.c:405:\tret = hmm_range_fault_unlocked(range);\ndrivers/hv/mshv_regions.c-406-\tif (ret)\n--\ninclude/linux/hmm.h=125=int hmm_range_fault(struct hmm_range *range);\ninclude/linux/hmm.h:126:int hmm_range_fault_unlocked(struct hmm_range *range);\ninclude/linux/hmm.h-127-\n--\nlib/test_hmm.c=392=static int dmirror_range_fault_unlocked(struct dmirror *dmirror,\n--\nlib/test_hmm.c-405-\t\trange-\u003enotifier_seq = mmu_interval_read_begin(range-\u003enotifier);\nlib/test_hmm.c:406:\t\tret = hmm_range_fault_unlocked(range);\nlib/test_hmm.c-407-\t\tif (ret) {\n--\nmm/hmm.c=688=static int hmm_range_fault_locked(struct hmm_range *range, int *locked)\n--\nmm/hmm.c-753- * For a variant that allows the mmap lock to be dropped during faults (e.g.,\nmm/hmm.c:754: * for userfaultfd support), see hmm_range_fault_unlocked().\nmm/hmm.c-755- */\n--\nmm/hmm.c=760=EXPORT_SYMBOL(hmm_range_fault);\n--\nmm/hmm.c-762-/**\nmm/hmm.c:763: * hmm_range_fault_unlocked - fault in a range, possibly dropping the mmap lock\nmm/hmm.c-764- * @range:\targument structure\n--\nmm/hmm.c-775- */\nmm/hmm.c:776:int hmm_range_fault_unlocked(struct hmm_range *range)\nmm/hmm.c-777-{\n--\nmm/hmm.c-788-}\nmm/hmm.c:789:EXPORT_SYMBOL(hmm_range_fault_unlocked);\nmm/hmm.c-790-\n--\ntools/testing/selftests/mm/hmm-tests.c=2901=TEST_F_TIMEOUT(hmm, benchmark_thp_migration, 120)\n--\ntools/testing/selftests/mm/hmm-tests.c-2957- * Test that HMM can fault in pages backed by userfaultfd using the\ntools/testing/selftests/mm/hmm-tests.c:2958: * hmm_range_fault_unlocked() path. This exercises the lock-drop retry\ntools/testing/selftests/mm/hmm-tests.c-2959- * logic in the HMM framework.\n"
}

5/2 2026/07/07 02:03 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12683
							output: 17
							thoughts: 29
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts:

**Analyzing Relevant Configuration Files**

Right, before proceeding further, I need to get a handle on the current state of affairs regarding the HMM testing and the hypervisor regions. I'm going to quickly check the configuration of `lib/test_hmm.c`. I need to ensure the test HMM is correctly configured and that there aren't any obvious red flags there. Then I'll shift gears and check the `drivers/hv/mshv_regions.c` config. I want to make certain that region settings, particularly those related to memory mapping or address space allocation, haven't been configured in a way that would interfere with the HMM tests. I'm looking for any potential conflicts or inconsistencies between these two configuration areas. It's a fundamental sanity check to rule out obvious configuration problems before I start deeper debugging or make code changes.



6/2 2026/07/07 02:03 tool grepper
0m Args:
{
  "Expression": "test_hmm"
}

Results:
{
  "Output": "MAINTAINERS=11853=F:\tinclude/linux/hmm*\nMAINTAINERS:11854:F:\tlib/test_hmm*\nMAINTAINERS-11855-F:\tmm/hmm*\n--\nlib/Makefile=102=obj-$(CONFIG_TEST_LOCKUP) += test_lockup.o\nlib/Makefile:103:obj-$(CONFIG_TEST_HMM) += test_hmm.o\nlib/Makefile-104-obj-$(CONFIG_TEST_FREE_PAGES) += test_free_pages.o\n--\nlib/test_hmm.c-32-\nlib/test_hmm.c:33:#include \"test_hmm_uapi.h\"\nlib/test_hmm.c-34-\n--\ntools/testing/selftests/mm/Makefile=181=TEST_FILES := test_vmalloc.sh\ntools/testing/selftests/mm/Makefile:182:TEST_FILES += test_hmm.sh\ntools/testing/selftests/mm/Makefile-183-TEST_FILES += va_high_addr_switch.sh\n--\ntools/testing/selftests/mm/hmm-tests.c-40- */\ntools/testing/selftests/mm/hmm-tests.c:41:#include \u003clib/test_hmm_uapi.h\u003e\ntools/testing/selftests/mm/hmm-tests.c-42-#include \u003cmm/gup_test.h\u003e\n--\ntools/testing/selftests/mm/run_vmtests.sh=341=CATEGORY=\"mremap\" run_test ./mremap_dontunmap\ntools/testing/selftests/mm/run_vmtests.sh-342-\ntools/testing/selftests/mm/run_vmtests.sh:343:CATEGORY=\"hmm\" run_test bash ./test_hmm.sh smoke\ntools/testing/selftests/mm/run_vmtests.sh-344-\n--\ntools/testing/selftests/mm/test_hmm.sh-11-\ntools/testing/selftests/mm/test_hmm.sh:12:TEST_NAME=\"test_hmm\"\ntools/testing/selftests/mm/test_hmm.sh:13:DRIVER=\"test_hmm\"\ntools/testing/selftests/mm/test_hmm.sh-14-\n"
}

7/2 2026/07/07 02:03 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13531
							output: 19
							thoughts: 33
						

Thoughts:
Okay, here's my interpretation of those thoughts, presented as a personal thought process, as requested:

**Navigating the Configuration Landscape**

Right, so I'm focusing on these configuration settings. `CONFIG_TEST_HMM` - that's pretty straightforward, it's the flag that controls the build inclusion for `test_hmm.c`. Easy enough. But now, I'm thinking about the bigger picture. I need to keep the code organized and avoid any configuration conflicts. That brings me to the next file I need to consider, which is `mshv_regions.c`. I have to now begin the process to think through its config options.



8/2 2026/07/07 02:04 tool grepper
0m Args:
{
  "Expression": "mshv_regions"
}

Results:
{
  "Output": "drivers/hv/Makefile=16=mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \\\ndrivers/hv/Makefile:17:\t       mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o\ndrivers/hv/Makefile-18-mshv_root-$(CONFIG_DEBUG_FS) += mshv_debugfs.o\n"
}

9/2 2026/07/07 02:04 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13860
							output: 19
							thoughts: 19
						

Thoughts:
Here's a summary of my thought process:

**Diving into the Hyper-V Driver Makefile**

Okay, so I need to track down where `mshv_root` is defined or used within the Hyper-V driver build system. My immediate instinct is to check the `Makefile` located in the `drivers/hv/` directory. That file is usually the central point of configuration for that entire part of the kernel, so it’s the logical first place to look. Hopefully, it's a straightforward definition or reference, and I won't have to trace through include files or conditional logic to find it. Time to open that file and get to it.



10/2 2026/07/07 02:04 tool grepper
0m Args:
{
  "Expression": "mshv_root"
}

Results:
{
  "Output": "drivers/hv/Makefile=4=obj-$(CONFIG_HYPERV_BALLOON)\t+= hv_balloon.o\ndrivers/hv/Makefile:5:obj-$(CONFIG_MSHV_ROOT)\t\t+= mshv_root.o\ndrivers/hv/Makefile-6-obj-$(CONFIG_MSHV_VTL)          += mshv_vtl.o\n--\ndrivers/hv/Makefile=15=hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o\ndrivers/hv/Makefile:16:mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \\\ndrivers/hv/Makefile:17:\t       mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o\ndrivers/hv/Makefile:18:mshv_root-$(CONFIG_DEBUG_FS) += mshv_debugfs.o\ndrivers/hv/Makefile:19:mshv_root-$(CONFIG_TRACEPOINTS) += mshv_trace.o\ndrivers/hv/Makefile-20-mshv_vtl-y := mshv_vtl_main.o\n--\ndrivers/hv/mshv_debugfs.c-16-#include \"mshv.h\"\ndrivers/hv/mshv_debugfs.c:17:#include \"mshv_root.h\"\ndrivers/hv/mshv_debugfs.c-18-\n--\ndrivers/hv/mshv_eventfd.c-25-#include \"mshv.h\"\ndrivers/hv/mshv_eventfd.c:26:#include \"mshv_root.h\"\ndrivers/hv/mshv_eventfd.c-27-\n--\ndrivers/hv/mshv_eventfd.h-13-#include \"mshv.h\"\ndrivers/hv/mshv_eventfd.h:14:#include \"mshv_root.h\"\ndrivers/hv/mshv_eventfd.h-15-\n--\ndrivers/hv/mshv_irq.c-14-#include \"mshv.h\"\ndrivers/hv/mshv_irq.c:15:#include \"mshv_root.h\"\ndrivers/hv/mshv_irq.c-16-\n--\ndrivers/hv/mshv_portid_table.c-8-#include \"mshv.h\"\ndrivers/hv/mshv_portid_table.c:9:#include \"mshv_root.h\"\ndrivers/hv/mshv_portid_table.c-10-\n--\ndrivers/hv/mshv_regions.c-4- *\ndrivers/hv/mshv_regions.c:5: * Memory region management for mshv_root module.\ndrivers/hv/mshv_regions.c-6- *\n--\ndrivers/hv/mshv_regions.c-17-\ndrivers/hv/mshv_regions.c:18:#include \"mshv_root.h\"\ndrivers/hv/mshv_regions.c-19-\n--\ndrivers/hv/mshv_root.h=187=struct hv_synic_pages {\n--\ndrivers/hv/mshv_root.h-192-\ndrivers/hv/mshv_root.h:193:struct mshv_root {\ndrivers/hv/mshv_root.h-194-\tspinlock_t pt_ht_lock;\n--\ndrivers/hv/mshv_root.h=361=static inline void mshv_debugfs_vp_remove(struct mshv_vp *vp) { }\n--\ndrivers/hv/mshv_root.h-363-\ndrivers/hv/mshv_root.h:364:extern struct mshv_root mshv_root;\ndrivers/hv/mshv_root.h-365-extern enum hv_scheduler_type hv_scheduler_type;\n--\ndrivers/hv/mshv_root_hv_call.c-4- *\ndrivers/hv/mshv_root_hv_call.c:5: * Hypercall helper functions used by the mshv_root module.\ndrivers/hv/mshv_root_hv_call.c-6- *\n--\ndrivers/hv/mshv_root_hv_call.c-14-\ndrivers/hv/mshv_root_hv_call.c:15:#include \"mshv_root.h\"\ndrivers/hv/mshv_root_hv_call.c-16-\n--\ndrivers/hv/mshv_root_hv_call.c=591=static bool mshv_use_overlay_gpfn(void)\n--\ndrivers/hv/mshv_root_hv_call.c-593-\treturn hv_l1vh_partition() \u0026\u0026\ndrivers/hv/mshv_root_hv_call.c:594:\t       mshv_root.vmm_caps.vmm_can_provide_overlay_gpfn;\ndrivers/hv/mshv_root_hv_call.c-595-}\n--\ndrivers/hv/mshv_root_main.c-4- *\ndrivers/hv/mshv_root_main.c:5: * The main part of the mshv_root module, providing APIs to create\ndrivers/hv/mshv_root_main.c-6- * and manage guest partitions.\n--\ndrivers/hv/mshv_root_main.c-35-#include \"mshv.h\"\ndrivers/hv/mshv_root_main.c:36:#include \"mshv_root.h\"\ndrivers/hv/mshv_root_main.c-37-\n--\ndrivers/hv/mshv_root_main.c=40=MODULE_DESCRIPTION(\"Microsoft Hyper-V root partition VMM interface /dev/mshv\");\n--\ndrivers/hv/mshv_root_main.c-48-\ndrivers/hv/mshv_root_main.c:49:struct mshv_root mshv_root;\ndrivers/hv/mshv_root_main.c-50-\n--\ndrivers/hv/mshv_root_main.c=170=static int mshv_ioctl_passthru_hvcall(struct mshv_partition *partition,\n--\ndrivers/hv/mshv_root_main.c-176-\tbool is_async;\ndrivers/hv/mshv_root_main.c:177:\tstruct mshv_root_hvcall args;\ndrivers/hv/mshv_root_main.c-178-\tstruct page *page;\n--\ndrivers/hv/mshv_root_main.c=1766=remove_partition(struct mshv_partition *partition)\ndrivers/hv/mshv_root_main.c-1767-{\ndrivers/hv/mshv_root_main.c:1768:\tspin_lock(\u0026mshv_root.pt_ht_lock);\ndrivers/hv/mshv_root_main.c-1769-\thlist_del_rcu(\u0026partition-\u003ept_hnode);\ndrivers/hv/mshv_root_main.c:1770:\tspin_unlock(\u0026mshv_root.pt_ht_lock);\ndrivers/hv/mshv_root_main.c-1771-\n--\ndrivers/hv/mshv_root_main.c=1875=mshv_partition *mshv_partition_find(u64 partition_id)\n--\ndrivers/hv/mshv_root_main.c-1879-\ndrivers/hv/mshv_root_main.c:1880:\thash_for_each_possible_rcu(mshv_root.pt_htable, p, pt_hnode,\ndrivers/hv/mshv_root_main.c-1881-\t\t\t\t   partition_id)\n--\ndrivers/hv/mshv_root_main.c=1912=add_partition(struct mshv_partition *partition)\ndrivers/hv/mshv_root_main.c-1913-{\ndrivers/hv/mshv_root_main.c:1914:\tspin_lock(\u0026mshv_root.pt_ht_lock);\ndrivers/hv/mshv_root_main.c-1915-\ndrivers/hv/mshv_root_main.c:1916:\thash_add_rcu(mshv_root.pt_htable, \u0026partition-\u003ept_hnode,\ndrivers/hv/mshv_root_main.c-1917-\t\t     partition-\u003ept_id);\ndrivers/hv/mshv_root_main.c-1918-\ndrivers/hv/mshv_root_main.c:1919:\tspin_unlock(\u0026mshv_root.pt_ht_lock);\ndrivers/hv/mshv_root_main.c-1920-\n--\ndrivers/hv/mshv_root_main.c=2130=mshv_dev_release(struct inode *inode, struct file *filp)\n--\ndrivers/hv/mshv_root_main.c-2134-\ndrivers/hv/mshv_root_main.c:2135:static int mshv_root_sched_online;\ndrivers/hv/mshv_root_main.c-2136-\n--\ndrivers/hv/mshv_root_main.c=2153=static int __init l1vh_retrieve_scheduler_type(enum hv_scheduler_type *out)\n--\ndrivers/hv/mshv_root_main.c-2159-\ndrivers/hv/mshv_root_main.c:2160:\tif (!mshv_root.vmm_caps.vmm_enable_integrated_scheduler)\ndrivers/hv/mshv_root_main.c-2161-\t\treturn 0;\n--\ndrivers/hv/mshv_root_main.c=2206=static int __init mshv_retrieve_scheduler_type(struct device *dev)\n--\ndrivers/hv/mshv_root_main.c-2235-\ndrivers/hv/mshv_root_main.c:2236:static int mshv_root_scheduler_init(unsigned int cpu)\ndrivers/hv/mshv_root_main.c-2237-{\n--\ndrivers/hv/mshv_root_main.c-2253-\ndrivers/hv/mshv_root_main.c:2254:static int mshv_root_scheduler_cleanup(unsigned int cpu)\ndrivers/hv/mshv_root_main.c-2255-{\n--\ndrivers/hv/mshv_root_main.c=2273=root_scheduler_init(struct device *dev)\n--\ndrivers/hv/mshv_root_main.c-2288-\ndrivers/hv/mshv_root_main.c:2289:\tret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, \"mshv_root_sched\",\ndrivers/hv/mshv_root_main.c:2290:\t\t\t\tmshv_root_scheduler_init,\ndrivers/hv/mshv_root_main.c:2291:\t\t\t\tmshv_root_scheduler_cleanup);\ndrivers/hv/mshv_root_main.c-2292-\n--\ndrivers/hv/mshv_root_main.c-2297-\ndrivers/hv/mshv_root_main.c:2298:\tmshv_root_sched_online = ret;\ndrivers/hv/mshv_root_main.c-2299-\n--\ndrivers/hv/mshv_root_main.c=2309=root_scheduler_deinit(void)\n--\ndrivers/hv/mshv_root_main.c-2313-\ndrivers/hv/mshv_root_main.c:2314:\tcpuhp_remove_state(mshv_root_sched_online);\ndrivers/hv/mshv_root_main.c-2315-\tfree_percpu(root_scheduler_input);\n--\ndrivers/hv/mshv_root_main.c=2319=static int __init mshv_init_vmm_caps(struct device *dev)\n--\ndrivers/hv/mshv_root_main.c-2324-\t\t\t\t\t\tHV_PARTITION_PROPERTY_VMM_CAPABILITIES,\ndrivers/hv/mshv_root_main.c:2325:\t\t\t\t\t\t0, \u0026mshv_root.vmm_caps,\ndrivers/hv/mshv_root_main.c:2326:\t\t\t\t\t\tsizeof(mshv_root.vmm_caps));\ndrivers/hv/mshv_root_main.c-2327-\tif (ret \u0026\u0026 hv_l1vh_partition()) {\n--\ndrivers/hv/mshv_root_main.c-2331-\ndrivers/hv/mshv_root_main.c:2332:\tdev_dbg(dev, \"vmm_caps = %#llx\\n\", mshv_root.vmm_caps.as_uint64[0]);\ndrivers/hv/mshv_root_main.c-2333-\n--\ndrivers/hv/mshv_root_main.c=2337=static int __init mshv_parent_partition_init(void)\n--\ndrivers/hv/mshv_root_main.c-2386-\ndrivers/hv/mshv_root_main.c:2387:\tspin_lock_init(\u0026mshv_root.pt_ht_lock);\ndrivers/hv/mshv_root_main.c:2388:\thash_init(mshv_root.pt_htable);\ndrivers/hv/mshv_root_main.c-2389-\n--\ndrivers/hv/mshv_synic.c-4- *\ndrivers/hv/mshv_synic.c:5: * mshv_root module's main interrupt handler and associated functionality.\ndrivers/hv/mshv_synic.c-6- *\n--\ninclude/uapi/linux/mshv.h=213=struct mshv_gpap_access_bitmap {\n--\ninclude/uapi/linux/mshv.h-222-/**\ninclude/uapi/linux/mshv.h:223: * struct mshv_root_hvcall - arguments for MSHV_ROOT_HVCALL\ninclude/uapi/linux/mshv.h-224- * @code: Hypercall code (HVCALL_*)\n--\ninclude/uapi/linux/mshv.h-235- */\ninclude/uapi/linux/mshv.h:236:struct mshv_root_hvcall {\ninclude/uapi/linux/mshv.h-237-\t__u16 code;\n--\ninclude/uapi/linux/mshv.h-255-/* Generic hypercall */\ninclude/uapi/linux/mshv.h:256:#define MSHV_ROOT_HVCALL\t\t_IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall)\ninclude/uapi/linux/mshv.h-257-\n--\ninclude/uapi/linux/mshv.h=310=struct mshv_get_set_vp_state {\n--\ninclude/uapi/linux/mshv.h-323- * Defined above in partition IOCTLs, avoid redefining it here\ninclude/uapi/linux/mshv.h:324: * #define MSHV_ROOT_HVCALL\t\t\t_IOWR(MSHV_IOCTL, 0x07, struct mshv_root_hvcall)\ninclude/uapi/linux/mshv.h-325- */\n"
}

11/2 2026/07/07 02:04 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18103
							output: 20
							thoughts: 64
						

Thoughts:
**Examining the MSHV Configuration**

Okay, so I'm looking at `CONFIG_MSHV_ROOT`. Right, this is the configuration variable that ties into `mshv_regions.c`. My mind is racing to remember how these pieces connect. A quick check of the build system confirms that `mshv_regions.c` is only compiled if `CONFIG_MSHV_ROOT` is enabled. That's good, that's what I expect. Now I just need to verify that `CONFIG_MSHV_ROOT` is actually defined, and then I can proceed.



12/2 2026/07/07 02:04 tool grepper
0m Args:
{
  "Expression": "config MSHV_ROOT"
}

Results:
{
  "Output": "drivers/hv/Kconfig=60=config HYPERV_VMBUS\n--\ndrivers/hv/Kconfig-67-\ndrivers/hv/Kconfig:68:config MSHV_ROOT\ndrivers/hv/Kconfig-69-\ttristate \"Microsoft Hyper-V root partition support\"\n"
}

13/2 2026/07/07 02:04 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 18399
							output: 19
							thoughts: 455
						

Thoughts:
**My Analysis of the HMM Patch**

Alright, let's break this down. First, `CONFIG_MSHV_ROOT` seems to be the controlling config here. The core of the change is focused on modifying `hmm_range_fault_unlocked` and, more importantly, `hmm_range_fault`. This `hmm_range_fault` is a critical HMM function, heavily used across various drivers like nouveau and amdgpu – a significant piece of core HMM functionality. The patch refactors it to use a locked version (`hmm_range_fault_locked`), which then leverages `hmm_do_fault` and `hmm_record_fault`. This represents a functional modification to the core HMM code, meaning fuzzing this is definitely warranted.

Let's nail down the focus symbols for fuzzing. We've got:

*   `hmm_range_fault_unlocked`
*   `hmm_range_fault`
*   `hmm_do_fault`
*   `hmm_record_fault`
*   `hmm_range_fault_locked`

For config options, we'll need `TEST_HMM` and `MSHV_ROOT` to reproduce the changes. Though `hmm_range_fault` is used by multiple drivers, focusing on `TEST_HMM` offers a cleaner way to test the changes.

I need to dig into `test_hmm.c` to identify any specific ioctls to target. Ah, it adds `HMM_DMIRROR_READ_UNLOCKED`. The corresponding handler is `dmirror_read_unlocked`, along with `dmirror_range_fault_unlocked` and `dmirror_fault_unlocked`.

So, the extended list of focus symbols becomes:

*   `hmm_range_fault_unlocked`
*   `hmm_range_fault`
*   `hmm_do_fault`
*   `dmirror_read_unlocked`
*   `dmirror_range_fault_unlocked`
*   `dmirror_fault_unlocked`
*   `mshv_region_hmm_fault_and_lock`

I need to confirm the required configs. Specifically `TEST_HMM` and `MSHV_ROOT`.

I should probably also check if there are other related configs. Typically `TEST_HMM` should also select `CONFIG_HMM_MIRROR`. I'll confirm that.



14/2 2026/07/07 02:04 tool grepper
0m Args:
{
  "Expression": "config TEST_HMM"
}

Results:
{
  "Output": "lib/Kconfig.debug=3318=config TEST_MEMINIT\n--\nlib/Kconfig.debug-3325-\nlib/Kconfig.debug:3326:config TEST_HMM\nlib/Kconfig.debug-3327-\ttristate \"Test HMM (Heterogeneous Memory Management)\"\n"
}

15/2 2026/07/07 02:04 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19493
							output: 35
							thoughts: 19
						

Thoughts:
Okay, here's my interpretation of that thought process, from an expert's perspective:

**Debugging the Kconfig Configuration**

Right, let's dive straight in. The task is related to the `TEST_HMM` configuration option. Given my experience, the first place I'd check for information about this option, its dependencies, and what it controls is going to be `lib/Kconfig.debug`. That's where I expect to find the core details for debug-related features within this codebase. It's the logical starting point for understanding how this feature is defined and handled within the build system. Let's see what `TEST_HMM` reveals.



16/2 2026/07/07 02:04 tool read-file
0m Args:
{
  "File": "lib/Kconfig.debug",
  "FirstLine": 3326,
  "LineCount": 20
}

Results:
{
  "Contents": "3326:\tconfig TEST_HMM\n3327:\t\ttristate \"Test HMM (Heterogeneous Memory Management)\"\n3328:\t\tdepends on TRANSPARENT_HUGEPAGE\n3329:\t\tdepends on DEVICE_PRIVATE\n3330:\t\tselect HMM_MIRROR\n3331:\t\tselect MMU_NOTIFIER\n3332:\t\thelp\n3333:\t\t  This is a pseudo device driver solely for testing HMM.\n3334:\t\t  Say M here if you want to build the HMM test module.\n3335:\t\t  Doing so will allow you to run tools/testing/selftest/vm/hmm-tests.\n3336:\t\n3337:\t\t  If unsure, say N.\n3338:\t\n3339:\tconfig TEST_FREE_PAGES\n3340:\t\ttristate \"Test freeing pages\"\n3341:\t\thelp\n3342:\t\t  Test that a memory leak does not occur due to a race between\n3343:\t\t  freeing a block of pages and a speculative page reference.\n3344:\t\t  Loading this module is safe if your kernel has the bug fixed.\n3345:\t\t  If the bug is not fixed, it will leak gigabytes of memory and\n"
}

17/2 2026/07/07 02:04 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20017
							output: 237
							thoughts: 627
						

Thoughts:
**My Assessment of the HMM Patch for Fuzzing**

Alright, let's break this down. First, I need to make sure that the build environment includes all necessary components. So, I'll be adding `TEST_HMM` and `MSHV_ROOT` to the configurations. Makes sense, given that the patch's core relates to the HMM subsystem and the MSHV region handling.

Now, let's identify the key focus areas for fuzzing. I've got a list of symbols to review: `hmm_range_fault_unlocked`, `hmm_range_fault`, `hmm_do_fault`, `hmm_record_fault`, `hmm_range_fault_locked`, `dmirror_read_unlocked`, `dmirror_range_fault_unlocked`, `dmirror_fault_unlocked`, and `mshv_region_hmm_fault_and_lock`.

Specifically, `hmm_range_fault` itself is an interesting point. While it's not a general hot-path function like `handle_mm_fault`, it's core to page fault handling for HMM devices, so it's a good target for fuzzing. I'll need to remember that. `hmm_do_fault` and `hmm_record_fault` are static functions within `mm/hmm.c`. The `dmirror_*` functions are within `lib/test_hmm.c`, and `mshv_region_hmm_fault_and_lock` is in `drivers/hv/mshv_regions.c`. I'll need to keep the locations of these functions in mind when setting up the fuzzing environment.

The patch's actual meat is the modification to the core HMM logic. It introduces a new API, `hmm_range_fault_unlocked`, for dropping the mmap lock during page faults. This has functional implications, as it aims to improve handling with userfaultfd regions. It also refactors the original `hmm_range_fault`. Furthermore, I've got to remember that the changes ripple through `test_hmm.c` and `mshv_regions.c` to use this new API.

So, in summary, this patch is a functional change impacting memory management and driver code. It introduces a new mechanism for lock dropping during page faults, so it warrants serious fuzzing. Now, I'll `set-results` and prepare for the fuzzing campaign.



18/2 2026/07/07 02:05 tool set-results
0m Args:
{
  "EnableConfigs": [
    "TEST_HMM",
    "MSHV_ROOT"
  ],
  "FocusSymbols": [
    "hmm_range_fault_unlocked",
    "hmm_range_fault",
    "hmm_do_fault",
    "hmm_record_fault",
    "hmm_range_fault_locked",
    "dmirror_read_unlocked",
    "dmirror_range_fault_unlocked",
    "dmirror_fault_unlocked",
    "mshv_region_hmm_fault_and_lock"
  ],
  "Reasoning": "The patch introduces a new API `hmm_range_fault_unlocked()` in the HMM subsystem to allow dropping the mmap lock during page faults, which is useful for regions managed by userfaultfd. It refactors the core `hmm_range_fault()` logic to support this and updates the `test_hmm` module and `mshv_regions` driver to utilize the new functionality. This is a functional change to memory management and driver code that should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "TEST_HMM",
    "MSHV_ROOT"
  ],
  "FocusSymbols": [
    "hmm_range_fault_unlocked",
    "hmm_range_fault",
    "hmm_do_fault",
    "hmm_record_fault",
    "hmm_range_fault_locked",
    "dmirror_read_unlocked",
    "dmirror_range_fault_unlocked",
    "dmirror_fault_unlocked",
    "mshv_region_hmm_fault_and_lock"
  ],
  "Reasoning": "The patch introduces a new API `hmm_range_fault_unlocked()` in the HMM subsystem to allow dropping the mmap lock during page faults, which is useful for regions managed by userfaultfd. It refactors the core `hmm_range_fault()` logic to support this and updates the `test_hmm` module and `mshv_regions` driver to utilize the new functionality. This is a functional change to memory management and driver code that should be fuzzed.",
  "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)