All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Barry Song (Xiaomi)" <baohua@kernel.org>,
	akpm@linux-foundation.org, linux-mm@kvack.org
Cc: ehab.ababneh@intel.com, axelrasmussen@google.com,
	kasong@tencent.com, lance.yang@linux.dev, qi.zheng@linux.dev,
	shakeel.butt@linux.dev, weixugc@google.com, yuanchu@google.com,
	yuzhao@google.com, baoquan.he@linux.dev,
	baolin.wang@linux.alibaba.com
Subject: Re: [PATCH] mm/mglru: fix using folio_mark_accessed to replace folio_set_active
Date: Sat, 17 Oct 2026 21:31:35 +0000	[thread overview]
Message-ID: <sashiko-outbox-175822@kernel.org> (raw)
In-Reply-To: <20261016213115.70515-1-baohua@kernel.org>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mm/mglru: lru_gen_folio_seq() demotes non-workingset active file folios
- [Medium] mm/mglru: lru_gen_refault() miscounts WORKINGSET_ACTIVATE_FILE
--

commit fa50b3cc1b1d64cb457afbbf37dc545c6d4e06f1
Author: Barry Song (Xiaomi) <baohua@kernel.org>

mm/mglru: fix using folio_mark_accessed to replace folio_set_active

This commit reverts the use of PG_referenced on in-fault readahead file
folios from commit 6cbdd9726fb5 ("mm/mglru: use folio_mark_accessed to
replace folio_set_active"), and instead adjusts lru_gen_folio_seq() to place
file folios with PG_active set and PG_workingset clear into the
third-youngest generation.

> diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
> index ab69b9930893f..97f157aafd581 100644
> --- a/include/linux/mm_inline.h
> +++ b/include/linux/mm_inline.h
> @@ -319,7 +319,8 @@ static inline unsigned long lru_gen_folio_seq(const struct lruvec *lruvec,
>  	 * |<---------------------------- MAX_NR_GENS ---------------------------->|
>  	 */
>  	if (folio_test_active(folio))
> -		gen = MIN_NR_GENS - folio_test_workingset(folio);
> +		gen = MIN_NR_GENS - folio_test_workingset(folio) +
> +		      (type ? !folio_test_workingset(folio) : 0);

[Severity: High]
Does assigning gen = 3 (lrugen->max_seq - 2) for all file folios (type == 1)
that have PG_active set and PG_workingset clear break other MGLRU paths that
set PG_active expecting the folio to land in an active generation?

First, in mm/vmscan.c, evict_folios() promotes folios rejected by
shrink_folio_list() out of min_seq[type] by clearing LRU_REFS_FLAGS and
setting PG_active:

mm/vmscan.c:evict_folios() {
	...
		folio_set_lru_refs(folio, 0);
		if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type])
			folio_set_active(folio);
	}

	move_folios_to_lru(&list, false);
}

When 3 generations exist (lrugen->max_seq - lrugen->min_seq[1] == 2),
max(lrugen->max_seq - 2, lrugen->min_seq[1]) equals min_seq[1]. A rejected
!PG_workingset file folio (for example, a mapped file folio spared via
FOLIOREF_KEEP on its first rmap scan in lru_gen_set_refs(), a DMA-pinned
folio, or a folio failing try_to_unmap()) has its PTE accessed bit and
PG_referenced cleared, and move_folios_to_lru() -> lru_gen_add_folio() puts
it right back into min_seq[1] instead of promoting it. Even when 4
generations exist, evict_folios() clears PG_referenced and puts the folio in
lrugen->max_seq - 2, so the next rmap scan in lru_gen_set_refs() sees
!folio_test_referenced(folio) && !folio_test_workingset(folio) again and can
never promote it to PG_workingset.

Second, in mm/vmscan.c, shrink_folio_list() sets PG_reclaim on dirty or
writeback file folios and jumps to activate_locked, which sets PG_active:

mm/vmscan.c:shrink_folio_list() {
	...
		if (folio_test_dirty(folio)) {
			if (folio_is_file_lru(folio)) {
				node_stat_mod_folio(folio, NR_VMSCAN_IMMEDIATE,
						nr_pages);
				if (!folio_test_reclaim(folio))
					folio_set_reclaim(folio);

				goto activate_locked;
			}
	...
activate_locked:
	...
		folio_set_active(folio);
}

Because lru_gen_folio_seq() checks folio_test_active(folio) before the
PG_reclaim && (folio_test_dirty(folio) || folio_test_writeback(folio))
branch (which assigns gen = MIN_NR_GENS = 2), won't setting PG_active on a
!PG_workingset dirty or writeback file folio downgrade it from gen = 2
(active) to gen = 3 (inactive, or min_seq[1] when 3 generations exist)?

Third, when a !PG_workingset file folio is explicitly activated via
folio_activate() -> lru_activate(), or when it is isolated from the active
generation lrugen->max_seq - 1 for migration/compaction via
lru_gen_del_folio() (which sets BIT(PG_active) when !reclaiming &&
lru_gen_is_active(lruvec, gen)), re-adding it via lru_gen_add_folio() now
places it into lrugen->max_seq - 2 (LRU_INACTIVE_FILE). Doesn't this demote
activated and migrated active file folios into an inactive generation?

>  	else if (reclaiming)
>  		gen = MAX_NR_GENS;
>  	else if ((!folio_is_file_lru(folio) && !folio_test_swapcache(folio)) ||
> @@ -327,7 +328,7 @@ static inline unsigned long lru_gen_folio_seq(const struct lruvec *lruvec,
>  		  (folio_test_dirty(folio) || folio_test_writeback(folio))))
>  		gen = MIN_NR_GENS;
>  	else
> -		gen = MAX_NR_GENS - (folio_test_workingset(folio) || folio_test_referenced(folio));
> +		gen = MAX_NR_GENS - folio_test_workingset(folio);
>  
>  	return max(READ_ONCE(lrugen->max_seq) - gen + 1, READ_ONCE(lrugen->min_seq[type]));
>  }

[ ... ]

> diff --git a/mm/workingset.c b/mm/workingset.c
> index 1504f91cdca5f..10d4ce202cca4 100644
> --- a/mm/workingset.c
> +++ b/mm/workingset.c
> @@ -319,13 +319,11 @@ static void lru_gen_refault(struct folio *folio, void *shadow)
>  
>  	atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]);
>  
> +	/* see folio_add_lru() where folio_set_active() will be called */
> +	if (lru_gen_in_fault())
> +		mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta);
> +
>  	if (workingset) {
> -		/*
> -		 * see folio_add_lru(), where folio_set_active() is
> -		 * called for workingset folios
> -		 */
> -		if (lru_gen_in_fault())
> -			mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta);
>  		folio_set_workingset(folio);
>  		mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta);
>  	} else

[Severity: Medium]
When a file folio (type == 1) refaults in a page fault with workingset ==
false, should WORKINGSET_ACTIVATE_FILE still be incremented here?

With workingset == false, PG_workingset is not set. Although folio_add_lru()
calls folio_set_active(folio), lru_gen_add_folio() clears PG_active and
lru_gen_folio_seq() now maps a file folio with PG_active and !PG_workingset
to gen = 3 (lrugen->max_seq - 2).

In include/linux/mm_inline.h, lru_gen_update_size() only accounts
lrugen->max_seq and lrugen->max_seq - 1 as active:

include/linux/mm_inline.h:lru_gen_update_size() {
	...
	/* addition */
	if (old_gen < 0) {
		if (lru_gen_is_active(lruvec, new_gen))
			lru += LRU_ACTIVE;
		__update_lru_size(lruvec, lru, zone, delta);
		return;
	}
	...
}

When 3 or more generations exist, the folio is added to LRU_INACTIVE_FILE,
so moving the mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type,
delta) call outside if (workingset) counts an activation for a file folio
that is placed in an inactive generation.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261016213115.70515-1-baohua@kernel.org?part=1


      reply	other threads:[~2026-10-17 21:31 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-16 21:31 [PATCH] mm/mglru: fix using folio_mark_accessed to replace folio_set_active Barry Song (Xiaomi)
2026-10-17 21:31 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-175822@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=baoquan.he@linux.dev \
    --cc=ehab.ababneh@intel.com \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=linux-mm@kvack.org \
    --cc=qi.zheng@linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=shakeel.butt@linux.dev \
    --cc=weixugc@google.com \
    --cc=yuanchu@google.com \
    --cc=yuzhao@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.