projects/linux·commit dabc522c

mm: have folio_mark_accessed() return whether the folio is hot

dabc522c · Linus Torvalds <[email protected]> · 2026-10-13 01:41:42 -07:00

Each call of folio_mark_accessed() moves a folio one step up what the
LRU records of its accesses: on the classic LRU from inactive to
inactive and referenced, active, and active and referenced; on the
multi-gen LRU through PG_referenced, the refs bits and PG_workingset.
Have it return whether the folio was already at the top of that when
it was called, which is when the call leaves it as it was: active and
referenced and not idle on the classic LRU, PG_referenced, all the
refs bits and PG_workingset set on the multi-gen LRU.
lru_gen_inc_refs() returns that for the multi-gen LRU.

An unevictable folio counts as hot once it is referenced: it stays in
memory in any case.  On the multi-gen LRU it is hot from the first
call, because lru_gen_inc_refs() returns before it changes anything of
an unevictable folio.  A dropbehind folio is not hot, though the call
leaves it as it was too: it is to be dropped after the read.

No caller uses the value yet.  A later patch of this series marks the
folios that it calls hot, for reads from the page cache without a
folio reference.

Assisted-by: LLM
Signed-off-by: Linus Torvalds <[email protected]>
Commit
dabc522cee5b86a99be3d852239ef8147a772862
Parents
24a19b95

Changes

2 files changed · +33 −15

FileStatusAddDelContent
include/linux/swap.hmodified+1−1View file
mm/folio.cmodified+32−14View file

include/linux/swap.h

View file
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 5658a16..67aa527 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -295,7 +295,7 @@ extern unsigned long totalreserve_pages;
/* linux/mm/folio.c */
void folio_add_lru(struct folio *folio);
-void folio_mark_accessed(struct folio *folio);
+bool folio_mark_accessed(struct folio *folio);
void lru_add_drain_all(void);
enum lru_cache_drained {

mm/folio.c

View file
diff --git a/mm/folio.c b/mm/folio.c
index 50a6dbe..bda8e52 100644
--- a/mm/folio.c
+++ b/mm/folio.c
@@ -352,28 +352,34 @@ static void __lru_cache_activate_folio(struct folio *folio)
#ifdef CONFIG_LRU_GEN
-static void lru_gen_inc_refs(struct folio *folio)
+/*
+ * Return true if @folio is hot: unevictable, or its flags already held as
+ * many accesses as they can
+ */
+static bool lru_gen_inc_refs(struct folio *folio)
{
unsigned long new_flags, old_flags = READ_ONCE(folio->flags.f);
if (folio_test_unevictable(folio))
- return;
+ return true;
/* see the comment on LRU_REFS_FLAGS */
if (!folio_test_referenced(folio)) {
set_mask_bits(&folio->flags.f, LRU_REFS_MASK, BIT(PG_referenced));
- return;
+ return false;
}
do {
if ((old_flags & LRU_REFS_MASK) == LRU_REFS_MASK) {
- if (!folio_test_workingset(folio))
- folio_set_workingset(folio);
- return;
+ if (folio_test_workingset(folio))
+ return true;
+ folio_set_workingset(folio);
+ return false;
}
new_flags = old_flags + BIT(LRU_REFS_PGOFF);
} while (!try_cmpxchg(&folio->flags.f, &old_flags, new_flags));
+ return false;
}
static bool lru_gen_clear_refs(struct folio *folio)
@@ -396,8 +402,9 @@ static bool lru_gen_clear_refs(struct folio *folio)
#else /* !CONFIG_LRU_GEN */
-static void lru_gen_inc_refs(struct folio *folio)
+static bool lru_gen_inc_refs(struct folio *folio)
{
+ return false;
}
static bool lru_gen_clear_refs(struct folio *folio)
@@ -419,15 +426,20 @@ static bool lru_gen_clear_refs(struct folio *folio)
*
* When a newly allocated folio is not yet visible, so safe for non-atomic ops,
* __folio_set_referenced() may be substituted for folio_mark_accessed().
+ *
+ * Return: true if @folio is hot: it was already as accessed as the LRU
+ * records. On the classic LRU that is active or unevictable, referenced
+ * and not idle; on the multi-gen LRU, unevictable, or PG_referenced, all
+ * the refs bits and PG_workingset set. False for a dropbehind folio.
*/
-void folio_mark_accessed(struct folio *folio)
+bool folio_mark_accessed(struct folio *folio)
{
+ bool hot = false;
+
if (folio_test_dropbehind(folio))
- return;
- if (lru_gen_enabled()) {
- lru_gen_inc_refs(folio);
- return;
- }
+ return false;
+ if (lru_gen_enabled())
+ return lru_gen_inc_refs(folio);
if (!folio_test_referenced(folio)) {
folio_set_referenced(folio);
@@ -437,6 +449,7 @@ void folio_mark_accessed(struct folio *folio)
* this list is never rotated or maintained, so marking an
* unevictable page accessed has no effect.
*/
+ hot = true;
} else if (!folio_test_active(folio)) {
/*
* If the folio is on the LRU, queue it for activation via
@@ -450,9 +463,14 @@ void folio_mark_accessed(struct folio *folio)
__lru_cache_activate_folio(folio);
folio_clear_referenced(folio);
workingset_activation(folio);
+ } else {
+ hot = true;
}
- if (folio_test_idle(folio))
+ if (folio_test_idle(folio)) {
folio_clear_idle(folio);
+ hot = false;
+ }
+ return hot;
}
EXPORT_SYMBOL(folio_mark_accessed);