From: "Kiryl Shutsemau (Meta)" <[email protected]>

A round is the unit the engine actually works in, and nothing reports one.
The per-candidate events say which windows were taken and which were
refused.  They do not say how large the batch was, how much of it landed,
or the number that matters most for whether batching was the right idea:
how long a faulter on a source is held up.

That wait has a definite span.  A thread touching a source sleeps on the
folio lock the freeze took, and wakes when the putback drops it.  So the
interval from the first freeze to the last putback is what the round costs
anyone unlucky enough to touch it.

Add mm_collapse_round: that interval in microseconds, with the candidates
collected, the ones installed, and the outcome.  It is the thing to watch
if a larger batch is ever proposed.

collapse_finish() returns the number of candidates installed, and this is
its first consumer.

Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Kiryl Shutsemau (Meta) <[email protected]>
---
 include/trace/events/huge_memory.h | 31 ++++++++++++++++++++++++++++++
 mm/collapse.c                      | 17 +++++++++++++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/include/trace/events/huge_memory.h 
b/include/trace/events/huge_memory.h
index c2314e26111c..d7c0195ace92 100644
--- a/include/trace/events/huge_memory.h
+++ b/include/trace/events/huge_memory.h
@@ -160,6 +160,37 @@ TRACE_EVENT(mm_collapse_scan,
                __print_symbolic(__entry->result, SCAN_STATUS))
 );
 
+TRACE_EVENT(mm_collapse_round,
+
+       TP_PROTO(struct mm_struct *mm, unsigned int nr_candidates,
+                unsigned int nr_installed, int result, u64 freeze_to_wake_us),
+
+       TP_ARGS(mm, nr_candidates, nr_installed, result, freeze_to_wake_us),
+
+       TP_STRUCT__entry(
+               __field(struct mm_struct *, mm)
+               __field(unsigned int, nr_candidates)
+               __field(unsigned int, nr_installed)
+               __field(int, result)
+               __field(u64, freeze_to_wake_us)
+       ),
+
+       TP_fast_assign(
+               __entry->mm = mm;
+               __entry->nr_candidates = nr_candidates;
+               __entry->nr_installed = nr_installed;
+               __entry->result = result;
+               __entry->freeze_to_wake_us = freeze_to_wake_us;
+       ),
+
+       TP_printk("mm=%p, nr_candidates=%u, nr_installed=%u, result=%s, 
freeze_to_wake_us=%llu",
+               __entry->mm,
+               __entry->nr_candidates,
+               __entry->nr_installed,
+               __print_symbolic(__entry->result, SCAN_STATUS),
+               __entry->freeze_to_wake_us)
+);
+
 TRACE_EVENT(mm_collapse_faultin,
 
        TP_PROTO(struct mm_struct *mm, unsigned int nr_faults, int result),
diff --git a/mm/collapse.c b/mm/collapse.c
index 1b5db42b6991..d0d28e8dfcea 100644
--- a/mm/collapse.c
+++ b/mm/collapse.c
@@ -8,6 +8,7 @@
 #include <linux/huge_mm.h>
 #include <linux/hugetlb.h>     /* x86 flush_tlb_range() uses hstate_vma() */
 #include <linux/leafops.h>
+#include <linux/math64.h>
 #include <linux/memcontrol.h>
 #include <linux/mm.h>
 #include <linux/mmu_notifier.h>
@@ -20,6 +21,7 @@
 #include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/swap.h>
+#include <linux/timekeeping.h>
 #include <linux/userfaultfd_k.h>
 #include <linux/vmstat.h>
 
@@ -1803,6 +1805,8 @@ static void collapse_round(struct mm_struct *mm, unsigned 
long pmd_addr,
        struct mmu_notifier_range range;
        struct vm_area_struct *vma;
        enum scan_result result;
+       unsigned int nr_installed;
+       u64 latency = 0;
        pmd_t *pmd;
 
        collapse_reserve(mm, cc);
@@ -1838,6 +1842,13 @@ static void collapse_round(struct mm_struct *mm, 
unsigned long pmd_addr,
                                cc->batch_start, cc->batch_end);
        mmu_notifier_invalidate_range_start(&range);
 
+       /*
+        * What the faulters on this batch's sources are made to wait: they 
sleep
+        * from the freeze that took their folio's lock to the putback that 
drops
+        * it.  Measured per round rather than argued about.
+        */
+       latency = ktime_get_ns();
+
        /*
         * None of these can fail as a whole: the freeze takes the sources it
         * can and drops the candidates it cannot, and each pass after it works
@@ -1849,12 +1860,16 @@ static void collapse_round(struct mm_struct *mm, 
unsigned long pmd_addr,
        collapse_install(vma, cc, pmd);
        collapse_putback(vma, cc);
 
+       latency = ktime_get_ns() - latency;
+
        mmu_notifier_invalidate_range_end(&range);
 
 out_unlock:
        mmap_read_unlock(mm);
 out:
-       collapse_finish(mm, cc, result);
+       nr_installed = collapse_finish(mm, cc, result);
+       trace_mm_collapse_round(mm, cc->nr_candidates, nr_installed, result,
+                               div_u64(latency, NSEC_PER_USEC));
 }
 
 /*
-- 
2.54.0


Reply via email to