[RFC PATCH 10/57] mm/collapse: run a round and feed the outcomes back

From: Kiryl Shutsemau

Date: Sun Aug 16 2026 - 18:47:49 EST


From: "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx>

A candidate a round attempts either collapsed or did not, and if it did
not there is a reason. Selection needs those outcomes to decide what
comes next: carry on past the window, try the same region at a lower
order, or give the table up.

Fill in collapse_run_batch(): run the round, then walk the batch handing
each candidate's result to classification.

The walk covers the whole batch. A pass that refuses one candidate marks
it and carries on rather than truncating the round, so every candidate
has a result of its own to hand back. Only an outcome that condemns the
table cuts the walk short, and then nothing of that table re-enters
selection.

The round and the classification it feeds are both stubs, so nothing is
attempted and nothing is decided.

Assisted-by: Claude-Code:claude-opus-5
Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
---
mm/collapse.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)

diff --git a/mm/collapse.c b/mm/collapse.c
index 6dae5e35e61d..ad9e5a447854 100644
--- a/mm/collapse.c
+++ b/mm/collapse.c
@@ -115,8 +115,16 @@
struct collapse_candidate {
unsigned long addr;
unsigned int order;
+ enum scan_result result;
};

+/* Where a candidate sits in the table, in the PTE offsets selection counts in */
+static unsigned int candidate_offset(const struct collapse_candidate *cand,
+ unsigned long pmd_addr)
+{
+ return (cand->addr - pmd_addr) >> PAGE_SHIFT;
+}
+
void collapse_control_release(struct collapse_control *cc)
{
kfree(cc->candidates);
@@ -132,6 +140,17 @@ int collapse_control_init(struct collapse_control *cc)
return 0;
}

+/*
+ * Carry one batch of candidates through the passes. Every candidate comes back
+ * with a result of its own: the passes before the freeze mark what they refuse
+ * and carry on, each pass after it works on what the last left, so no failure
+ * truncates the round.
+ */
+static void collapse_round(struct mm_struct *mm, unsigned long pmd_addr,
+ struct collapse_control *cc)
+{
+}
+
/*
* Is @count past a limit stated per PMD, when only part of a table was scanned?
* Scale the comparison to the table so a partial scan is held to the same
@@ -455,6 +474,18 @@ static bool collapse_next_candidate(struct collapse_control *cc,
return false;
}

+/*
+ * Feed one candidate's outcome back into selection: its region is done, it
+ * re-enters the retry store at a lower order, or the table is abandoned.
+ * Returns false in that last case.
+ */
+static bool collapse_classify_result(struct collapse_control *cc,
+ unsigned int offset, unsigned int order,
+ enum scan_result result)
+{
+ return true;
+}
+
/*
* Run and classify the collected batch. Returns false when a candidate's
* outcome abandons the table.
@@ -462,9 +493,30 @@ static bool collapse_next_candidate(struct collapse_control *cc,
static bool collapse_run_batch(struct mm_struct *mm, unsigned long pmd_addr,
struct collapse_control *cc)
{
+ unsigned int i;
+
/* collapse_anon_pmd() only runs a round it has put something in */
VM_WARN_ON_ONCE(!cc->nr_candidates);

+ collapse_round(mm, pmd_addr, cc);
+
+ for (i = 0; i < cc->nr_candidates; i++) {
+ struct collapse_candidate *cand = &cc->candidates[i];
+ unsigned int offset = candidate_offset(cand, pmd_addr);
+
+ if (!collapse_classify_result(cc, offset, cand->order,
+ cand->result)) {
+ /*
+ * The table is abandoned: the candidates behind this one
+ * keep their results and are left unclassified, so
+ * nothing more of this table enters selection, and the
+ * abandoning result clears what earlier ones left there.
+ */
+ cc->nr_candidates = 0;
+ return false;
+ }
+ }
+
cc->nr_candidates = 0;
return true;
}
@@ -502,6 +554,7 @@ static void collapse_add_candidate(struct collapse_control *cc,
cc->nr_candidates++;
cand->addr = addr;
cand->order = order;
+ cand->result = SCAN_FAIL;
}

/*
--
2.54.0