The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Baoquan He <baoquan.he@linux.dev>
To: kasong@tencent.com
Cc: linux-mm@kvack.org, Andrew Morton <akpm@linux-foundation.org>,
	Johannes Weiner <hannes@cmpxchg.org>,
	Muchun Song <muchun.song@linux.dev>,
	Qi Zheng <qi.zheng@linux.dev>,
	Ying Huang <ying.huang@linux.alibaba.com>,
	Chris Li <chrisl@kernel.org>, Nico Pache <nico.pache@linux.dev>,
	Usama Arif <usama.arif@linux.dev>,
	Michal Hocko <mhocko@kernel.org>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	Shakeel Butt <shakeel.butt@linux.dev>,
	David Hildenbrand <david@kernel.org>,
	Lorenzo Stoakes <ljs@kernel.org>, Barry Song <baohua@kernel.org>,
	Axel Rasmussen <axelrasmussen@google.com>,
	Yuanchu Xie <yuanchu@google.com>, Wei Xu <weixugc@google.com>,
	Vlastimil Babka <vbabka@kernel.org>,
	Suren Baghdasaryan <surenb@google.com>,
	Kemeng Shi <shikemeng@huaweicloud.com>,
	Nhat Pham <nphamcs@gmail.com>,
	Youngjun Park <youngjun.park@lge.com>, Zi Yan <ziy@nvidia.com>,
	Gregory Price <gourry@gourry.net>,
	"Matthew Wilcox (Oracle)" <willy@infradead.org>,
	Baolin Wang <baolin.wang@linux.alibaba.com>,
	Ryan Roberts <ryan.roberts@arm.com>, Dev Jain <dev.jain@arm.com>,
	Lance Yang <lance.yang@linux.dev>,
	Hugh Dickins <hughd@google.com>, SeongJae Park <sj@kernel.org>,
	David Rientjes <rientjes@google.com>, Yu Zhao <yuzhao@google.com>,
	Vernon Yang <vernon2gm@gmail.com>,
	Zicheng Wang <wangzicheng@honor.com>,
	Chen Ridong <chenridong@xiaomi.com>,
	Tal Zussman <tz2294@columbia.edu>, Kairui Song <ryncsn@gmail.com>,
	linux-kernel@vger.kernel.org, cgroups@vger.kernel.org
Subject: Re: [PATCH RFC 09/15] mm/mglru: frequency guided workingset promotion (MGLRU-FG)
Date: Tue, 18 Aug 2026 15:12:03 +0800	[thread overview]
Message-ID: <aoQFw_SWJBAdO8w-@MiWiFi-R3L-srv> (raw)
In-Reply-To: <20260804-mglru-fg-v1-9-4d8dad39dad6@tencent.com>

On 08/04/26 at 03:47am, Kairui Song via B4 Relay wrote:
...snip...
> +int folio_inc_lru_refs(struct folio *folio, bool is_fault, bool is_exec)
> +{
> +	int max_gen, min_gen;
> +	int type, refs, gen, new_gen;
> +	unsigned long new_flags, old_flags, max_seq;
> +	struct lru_gen_folio *lrugen;
> +	struct lruvec *lruvec;
> +
> +	type = folio_is_file_lru(folio);
> +	lruvec = folio_lruvec_live_get(folio);
> +	lrugen = &lruvec->lrugen;
> +
> +	old_flags = READ_ONCE(*folio_flags(folio, 0));
> +	do {
> +		new_flags = old_flags;
> +		gen = lru_gen_from_flags(old_flags);
> +		refs = lru_refs_from_flags(old_flags) + 1;
> +		new_gen = gen;
> +		if (!(old_flags & BIT(PG_lru)) || gen < 0)
> +			goto out;
> +
> +		max_seq = READ_ONCE(lrugen->max_seq);
> +		max_gen = lru_gen_from_seq(max_seq);
> +		min_gen = lru_gen_from_seq(READ_ONCE(lrugen->min_seq[type]));
> +		if (gen == max_gen)
> +			goto out;
> +

I am a little confused about the new mechanism. In the current mglru, it
does have the issue both mm walk and fd read set PG_referenced at the
1st access, this is a obvious drawback. Now with the change, the ref
count is clearer, while the mm walk and fd read accessing is still mixed.
Imagine the cases below:
 - one fd read; then mm walk; directly move to max_gen;
 - one mm walk; then several times fd read; promote to next gen;

Can I understand the final effect as:
1) explicti ref count;
2) more drastically promote mm walk based on the mixing ref counting;
   - compared with the old behaviour: move to next gen when 2nd mm walk

I can only see one benefit and one significant change. Do I understand
it correctly, and is it worth?

Thanks
Baoquan

> +		if (is_fault || is_exec) {
> +			/* Promote second page table access or executable */
> +			if (refs > LRU_REFS_REFERENCED || is_exec)
> +				new_gen = max_gen;
> +			else
> +				new_gen = (gen + 1UL) % MAX_NR_GENS;
> +			refs = min(refs, LRU_REFS_PROTECTED);
> +		} else if (refs > LRU_REFS_MAX) {
> +			/* LRU refs counting overflow, bump the gen */
> +			new_gen = (gen + 1UL) % MAX_NR_GENS;
> +			refs = LRU_REFS_PROTECTED;
> +		} else if (gen == min_gen && refs >= LRU_REFS_WORKINGSET) {
> +			/* Defer eviction of just accessed workingset */
> +			new_gen = (gen + 1UL) % MAX_NR_GENS;
> +			refs = min(refs, LRU_REFS_PROTECTED);
>  		}
> +out:
> +		refs = min(refs, LRU_REFS_MAX);
> +		lru_refs_set_flags(&new_flags, refs);
> +		if (new_gen >= 0)
> +			lru_gen_set_flags(&new_flags, new_gen);
> +	} while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
>  
> -		folio_set_lru_refs(folio, 1);
> -		return false;
> +	if (new_gen != gen) {
> +		/*
> +		 * Gen can only go forward, so concurrent aging is
> +		 * usually fine, except when multiple aging increase
> +		 * max_seq multiple times, new_gen may have go beyond
> +		 * the new max_seq's current gen border and causes
> +		 * hotness inversion. In that very unlikely case,
> +		 * just activate the folio.
> +		 */
> +		lru_gen_update_size(lruvec, folio, gen, new_gen);
> +		if (unlikely(READ_ONCE(lrugen->max_seq) - max_seq > MIN_NR_GENS))
> +			folio_activate(folio);
>  	}
>  
> -	/* Promote on second access */
> -	if (folio_lru_refs(folio) > 1) {
> -		folio_set_lru_refs(folio, 0);
> -		folio_set_workingset(folio);
> -	} else {
> -		folio_mark_accessed(folio);
> -	}
> -	return true;
> +	folio_lruvec_live_put(lruvec);
> +	return refs;
> +}
> +
> +/*
> + * Update the folio's lru refs indicator during a page table walk.
> + * max_seq is stable since this runs inside the aging process.
> + *
> + * Returns the old generation and stores the new generation in @new_gen when
> + * the folio is promoted (to max_gen) or advanced by one generation.
> + * Returns -1 if no gen change occurred.
> + */
> +static int folio_inc_lru_refs_walk(struct folio *folio, struct lruvec *lruvec,
> +				   const vma_flags_t *vma_flags, int *new_gen)
> +{
> +	unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
> +	unsigned long max_seq = READ_ONCE(lruvec->lrugen.max_seq);
> +	int refs, gen, max_gen, ret;
> +
> +	max_gen = lru_gen_from_seq(max_seq);
> +
> +	do {
> +		gen = lru_gen_from_flags(old_flags);
> +		refs = lru_refs_from_flags(old_flags) + 1;
> +		new_flags = old_flags;
> +
> +		if (gen >= 0 && gen != max_gen) {
> +			ret = gen;
> +			/* Promote second page table access or executable */
> +			if (refs > LRU_REFS_REFERENCED || is_exec_file_folio(folio, vma_flags))
> +				*new_gen = max_gen;
> +			else
> +				*new_gen = (gen + 1) % MAX_NR_GENS;
> +			lru_gen_set_flags(&new_flags, *new_gen);
> +			lru_refs_set_flags(&new_flags, min(refs, LRU_REFS_PROTECTED));
> +		} else {
> +			ret = -1;
> +			lru_refs_set_flags(&new_flags, min(refs, LRU_REFS_MAX));
> +		}
> +	} while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
> +
> +	return ret;
> +}
> +
> +/*
> + * Update the folio's lru refs indicator while the folio is isolated.
> + * Only used on mapped folios upon the final eviction, when the folio is
> + * off the LRU list (isolated).
> + *
> + * Increments the refs count (capped at LRU_REFS_PROTECTED).  Returns true
> + * if the caller should activate the folio (second access or
> + * executable), false to keep it in the eviction list.
> + */
> +static bool folio_inc_lru_refs_isolated(struct folio *folio, const vma_flags_t *vma_flags)
> +{
> +	unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
> +	int refs;
> +
> +	do {
> +		new_flags = old_flags;
> +		refs = lru_refs_from_flags(old_flags) + 1;
> +		lru_refs_set_flags(&new_flags, min(refs, LRU_REFS_PROTECTED));
> +	} while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
> +
> +	/* Promote second page table access or executable */
> +	return refs > LRU_REFS_REFERENCED || is_exec_file_folio(folio, vma_flags);
>  }
>  #else
> -static bool lru_gen_set_refs(struct folio *folio, const vma_flags_t *vma_flags)
> +static bool folio_inc_lru_refs_isolated(struct folio *folio, const vma_flags_t *vma_flags)
>  {
>  	return false;
>  }
> @@ -896,7 +1035,8 @@ static enum folio_references folio_check_references(struct folio *folio,
>  		if (!referenced_ptes)
>  			return FOLIOREF_RECLAIM;
>  
> -		return lru_gen_set_refs(folio, &vma_flags) ? FOLIOREF_ACTIVATE : FOLIOREF_KEEP;
> +		return folio_inc_lru_refs_isolated(folio, &vma_flags) ?
> +		       FOLIOREF_ACTIVATE : FOLIOREF_KEEP;
>  	}
>  
>  	referenced_folio = folio_test_clear_referenced(folio);
> @@ -3262,59 +3402,31 @@ static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv)
>   *                          the aging
>   ******************************************************************************/
>  
> -/* promote pages accessed through page tables */
> -static int folio_update_gen(struct folio *folio, int new_gen, const vma_flags_t *vma_flags)
> -{
> -	unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
> -	int old_gen;
> -
> -	/*
> -	 * See the comment on LRU_REFS_FLAGS, and activate file-backed
> -	 * executable folios after first usage to avoid typical IO
> -	 * thrashing from reclaiming.
> -	 */
> -	if (!folio_test_referenced(folio) && !folio_test_workingset(folio) &&
> -	    !is_exec_file_folio(folio, vma_flags)) {
> -		folio_set_lru_refs(folio, 1);
> -		return -1;
> -	}
> -
> -	do {
> -		old_gen = lru_gen_from_flags(old_flags);
> -		new_flags = old_flags;
> -
> -		/* lru_gen_del_folio() has isolated this page? */
> -		if (old_gen < 0)
> -			break;
> -
> -		lru_gen_set_flags(&new_flags, new_gen);
> -		lru_refs_set_flags(&new_flags, 0);
> -		new_flags |= BIT(PG_workingset);
> -	} while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
> -
> -	return old_gen;
> -}
> -
> -/* protect pages accessed multiple times through file descriptors */
> +/*
> + * Force bump a folio's generation. Used for PID protection or defer the
> + * eviction of temporarily unevictable folio.
> + */
>  static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio)
>  {
> +	int refs;
>  	int type = folio_is_file_lru(folio);
>  	struct lru_gen_folio *lrugen = &lruvec->lrugen;
>  	int old_gen, new_gen, min_gen = lru_gen_from_seq(lrugen->min_seq[type]);
>  	unsigned long new_flags, old_flags = READ_ONCE(*folio_flags(folio, 0));
>  
>  	do {
> +		new_flags = old_flags;
> +		refs = lru_refs_from_flags(old_flags);
>  		old_gen = lru_gen_from_flags(old_flags);
>  		VM_WARN_ON_ONCE_FOLIO(old_gen < 0, folio);
>  
> -		/* folio_update_gen() has promoted this page? */
> +		/* folio has been promoted? */
>  		if (old_gen >= 0 && old_gen != min_gen)
>  			return old_gen;
>  
> -		new_flags = old_flags;
>  		new_gen = (old_gen + 1) % MAX_NR_GENS;
>  		lru_gen_set_flags(&new_flags, new_gen);
> -		lru_refs_set_flags(&new_flags, 0);
> +		lru_refs_set_flags(&new_flags, min(refs, LRU_REFS_WORKINGSET));
>  	} while (!try_cmpxchg(folio_flags(folio, 0), &old_flags, new_flags));
>  
>  	lru_gen_update_size(lruvec, folio, old_gen, new_gen);
> @@ -3518,21 +3630,17 @@ static void walk_update_folio(struct lru_gen_mm_walk *walk, struct vm_area_struc
>  	if (!folio)
>  		return;
>  
> -	new_gen = lru_gen_from_seq(READ_ONCE(lruvec->lrugen.max_seq));
> -
>  	if (dirty && !folio_test_dirty(folio) &&
>  	    !(folio_test_anon(folio) && folio_test_swapbacked(folio) &&
>  	      !folio_test_swapcache(folio)))
>  		folio_mark_dirty(folio);
>  
>  	if (walk) {
> -		old_gen = folio_update_gen(folio, new_gen, &vma->flags);
> -		if (old_gen >= 0 && old_gen != new_gen)
> +		old_gen = folio_inc_lru_refs_walk(folio, lruvec, &vma->flags, &new_gen);
> +		if (old_gen >= 0)
>  			update_batch_size(walk, folio, old_gen, new_gen);
> -	} else if (lru_gen_set_refs(folio, &vma->flags)) {
> -		old_gen = folio_lru_gen(folio);
> -		if (old_gen >= 0 && old_gen != new_gen)
> -			folio_activate(folio);
> +	} else {
> +		folio_inc_lru_refs(folio, true, is_exec_file_folio(folio, &vma->flags));
>  	}
>  }
>  
> @@ -3917,7 +4025,8 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)
>  		while (!list_empty(head)) {
>  			struct folio *folio = lru_to_folio(head);
>  			int refs = folio_lru_refs(folio);
> -			bool workingset = folio_test_workingset(folio);
> +			int delta = folio_nr_pages(folio);
> +			int tier = lru_tier_from_refs(refs);
>  
>  			VM_WARN_ON_ONCE_FOLIO(folio_test_unevictable(folio), folio);
>  			VM_WARN_ON_ONCE_FOLIO(folio_test_active(folio), folio);
> @@ -3927,14 +4036,8 @@ static bool inc_min_seq(struct lruvec *lruvec, int type, int swappiness)
>  			new_gen = folio_inc_gen(lruvec, folio);
>  			list_move_tail(&folio->lru, &lrugen->folios[new_gen][type][zone]);
>  
> -			/* don't count the workingset being lazily promoted */
> -			if (refs + workingset != BIT(LRU_REFS_WIDTH) + 1) {
> -				int tier = lru_tier_from_refs(refs, workingset);
> -				int delta = folio_nr_pages(folio);
> -
> -				WRITE_ONCE(lrugen->protected[hist][type][tier],
> -					   lrugen->protected[hist][type][tier] + delta);
> -			}
> +			WRITE_ONCE(lrugen->protected[hist][type][tier],
> +				   lrugen->protected[hist][type][tier] + delta);
>  
>  			if (!--remaining)
>  				return false;
> @@ -4649,8 +4752,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
>  	int zone = folio_zonenum(folio);
>  	int delta = folio_nr_pages(folio);
>  	int refs = folio_lru_refs(folio);
> -	bool workingset = folio_test_workingset(folio);
> -	int tier = lru_tier_from_refs(refs, workingset);
> +	int tier = lru_tier_from_refs(refs);
>  	struct lru_gen_folio *lrugen = &lruvec->lrugen;
>  
>  	VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
> @@ -4672,17 +4774,15 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
>  	}
>  
>  	/* protected */
> -	if (tier > tier_idx || refs + workingset == BIT(LRU_REFS_WIDTH) + 1) {
> +	if (tier > tier_idx) {
> +		int hist = lru_hist_from_seq(lrugen->min_seq[type]);
> +
>  		gen = folio_inc_gen(lruvec, folio);
>  		list_move(&folio->lru, &lrugen->folios[gen][type][zone]);
>  
> -		/* don't count the workingset being lazily promoted */
> -		if (refs + workingset != BIT(LRU_REFS_WIDTH) + 1) {
> -			int hist = lru_hist_from_seq(lrugen->min_seq[type]);
> +		WRITE_ONCE(lrugen->protected[hist][type][tier],
> +			   lrugen->protected[hist][type][tier] + delta);
>  
> -			WRITE_ONCE(lrugen->protected[hist][type][tier],
> -				   lrugen->protected[hist][type][tier] + delta);
> -		}
>  		return true;
>  	}
>  
> @@ -4710,10 +4810,6 @@ static bool isolate_folio(struct lruvec *lruvec, struct folio *folio, struct sca
>  		return false;
>  	}
>  
> -	/* see the comment on LRU_REFS_FLAGS */
> -	if (!folio_test_referenced(folio))
> -		folio_set_lru_refs(folio, 0);
> -
>  	success = lru_gen_del_folio(lruvec, folio, true);
>  	VM_WARN_ON_ONCE_FOLIO(!success, folio);
>  
> @@ -4801,13 +4897,13 @@ static int get_tier_idx(struct lruvec *lruvec, int type)
>  	struct ctrl_pos sp, pv = {};
>  
>  	/*
> -	 * To leave a margin for fluctuations, use a larger gain factor (2:3).
> +	 * To leave a margin for fluctuations, use a larger gain factor (1:2).
>  	 * This value is chosen because any other tier would have at least twice
>  	 * as many refaults as the first tier.
>  	 */
> -	read_ctrl_pos(lruvec, type, 0, 1, 2, &sp);
>  	for (tier = 1; tier < MAX_NR_TIERS; tier++) {
> -		read_ctrl_pos(lruvec, type, tier, tier + 1, 3, &pv);
> +		read_ctrl_pos(lruvec, type, 0, tier, 1, &sp);
> +		read_ctrl_pos(lruvec, type, tier, tier + 1, 2, &pv);
>  		if (!positive_ctrl_err(&sp, &pv))
>  			break;
>  	}
> @@ -4930,10 +5026,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
>  		}
>  
>  		/* don't add rejected folios to the oldest generation */
> -		if (lru_gen_folio_seq(lruvec, folio, false) == min_seq[type]) {
> -			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);
> diff --git a/mm/workingset.c b/mm/workingset.c
> index 5438e9390011..452fe8554990 100644
> --- a/mm/workingset.c
> +++ b/mm/workingset.c
> @@ -189,6 +189,13 @@
>  #define EVICTION_MASK	(~0UL >> EVICTION_SHIFT)
>  #define EVICTION_MASK_ANON	(~0UL >> EVICTION_SHIFT_ANON)
>  
> +/*
> + * LRU refs uses LRU_REFS_WIDTH + 2 bits, the 2 bits are PG_workingset and
> + * PG_referenced. But here we record PG_workingset separately (to reuse
> + * pack_shadow).
> + */
> +#define LRU_REFS_BITS ((LRU_REFS_WIDTH + 2) - 1)
> +
>  /*
>   * Eviction timestamps need to be able to cover the full range of
>   * actionable refaults. However, bits are tight in the xarray
> @@ -242,13 +249,12 @@ static void *lru_gen_eviction(struct folio *folio)
>  	int type = folio_is_file_lru(folio);
>  	int delta = folio_nr_pages(folio);
>  	int refs = folio_lru_refs(folio);
> -	bool workingset = folio_test_workingset(folio);
> -	int tier = lru_tier_from_refs(refs, workingset);
> +	int tier = lru_tier_from_refs(refs);
>  	struct mem_cgroup *memcg;
>  	struct pglist_data *pgdat = folio_pgdat(folio);
>  	unsigned short memcg_id;
>  
> -	BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH >
> +	BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_BITS >
>  		     BITS_PER_LONG - max(EVICTION_SHIFT, EVICTION_SHIFT_ANON));
>  
>  	rcu_read_lock();
> @@ -256,14 +262,14 @@ static void *lru_gen_eviction(struct folio *folio)
>  	lruvec = mem_cgroup_lruvec(memcg, pgdat);
>  	lrugen = &lruvec->lrugen;
>  	min_seq = READ_ONCE(lrugen->min_seq[type]);
> -	token = (min_seq << LRU_REFS_WIDTH) | max(refs - 1, 0);
> +	token = (min_seq << LRU_REFS_BITS) | refs >> 1;
>  
>  	hist = lru_hist_from_seq(min_seq);
>  	atomic_long_add(delta, &lrugen->evicted[hist][type][tier]);
>  	memcg_id = mem_cgroup_private_id(memcg);
>  	rcu_read_unlock();
>  
> -	return pack_shadow(memcg_id, pgdat, token, workingset, type);
> +	return pack_shadow(memcg_id, pgdat, token, refs & 1, type);
>  }
>  
>  /*
> @@ -284,11 +290,24 @@ static bool lru_gen_test_recent(void *shadow, struct lruvec **lruvec,
>  	*lruvec = mem_cgroup_lruvec(memcg, pgdat);
>  
>  	max_seq = READ_ONCE((*lruvec)->lrugen.max_seq);
> -	max_seq &= (file ? EVICTION_MASK : EVICTION_MASK_ANON) >> LRU_REFS_WIDTH;
> +	max_seq &= (file ? EVICTION_MASK : EVICTION_MASK_ANON) >> LRU_REFS_BITS;
>  
> -	return abs_diff(max_seq, *token >> LRU_REFS_WIDTH) < MAX_NR_GENS;
> +	return abs_diff(max_seq, *token >> LRU_REFS_BITS) < MAX_NR_GENS;
>  }
>  
> +/*
> + * Restore the refs of a refaulted folio from its shadow entry.
> + *
> + * Any folio that was accessed at least once before eviction (refs >=
> + * LRU_REFS_REFERENCED) is activated on a fault-driven refault, giving it a
> + * strong gen placement. Non-fault refaults (e.g. readahead) are not
> + * activated regardless of refs.
> + *
> + * The restored refs is capped at LRU_REFS_PROTECTED to prevent stale
> + * high-tier history from carrying over across eviction cycles. The
> + * WORKINGSET_RESTORE stat is bumped only for refs >= LRU_REFS_WORKINGSET
> + * to track genuine workingset restoration.
> + */
>  static void lru_gen_refault(struct folio *folio, void *shadow)
>  {
>  	bool recent;
> @@ -314,21 +333,29 @@ static void lru_gen_refault(struct folio *folio, void *shadow)
>  	lrugen = &lruvec->lrugen;
>  
>  	hist = lru_hist_from_seq(READ_ONCE(lrugen->min_seq[type]));
> -	refs = (token & (BIT(LRU_REFS_WIDTH) - 1)) + 1;
> -	tier = lru_tier_from_refs(refs, workingset);
> +	refs = ((token & (BIT(LRU_REFS_BITS) - 1)) << 1) + workingset;
> +	tier = lru_tier_from_refs(refs);
>  
>  	atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]);
>  
> -	if (workingset) {
> -		/* Send refaulted workingset folios to active generations. */
> +	/*
> +	 * Activate a fault-driven refault: the folio was accessed at
> +	 * least once before eviction and would have been promoted had
> +	 * it stayed in memory.
> +	 */
> +	if (refs >= LRU_REFS_REFERENCED) {
>  		if (lru_gen_in_fault()) {
>  			folio_set_active(folio);
>  			mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta);
>  		}
> -		folio_set_workingset(folio);
> +		/* Cap restored refs to prevent stale high-tier carry-over */
> +		folio_set_lru_refs(folio, min(refs, LRU_REFS_PROTECTED));
> +	}
> +
> +	/* WORKINGSET_RESTORE tracks genuine workingset-level refaults */
> +	if (refs >= LRU_REFS_WORKINGSET)
>  		mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta);
> -	} else
> -		set_mask_bits(&folio->flags.f, LRU_REFS_MASK, (refs - 1UL) << LRU_REFS_PGOFF);
> +
>  unlock:
>  	rcu_read_unlock();
>  }
> 
> -- 
> 2.55.0
> 
> 

  parent reply	other threads:[~2026-08-18  7:12 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-03 19:46 [PATCH RFC 00/15] mm/mglru: frequency guided promotion (MGLRU-FG) and flag cleanup Kairui Song via B4 Relay
2026-08-03 19:46 ` [PATCH RFC 01/15] mm/memcontrol: make lru_zone_size atomic and simplify sanity check Kairui Song via B4 Relay
2026-08-03 19:46 ` [PATCH RFC 02/15] mm/memcontrol: allow update of LRU statistic without holding LRU lock Kairui Song via B4 Relay
2026-08-03 19:46 ` [PATCH RFC 03/15] mm/mglru: introduce and always use helpers for manipulating page flags Kairui Song via B4 Relay
2026-08-03 19:47 ` [PATCH RFC 04/15] mm/mglru: make generation page counters atomic Kairui Song via B4 Relay
2026-08-18  2:40   ` Baoquan He
2026-08-03 19:47 ` [PATCH RFC 05/15] mm/mglru: move max_seq read into walk_update_folio Kairui Song via B4 Relay
2026-08-18  2:23   ` Baoquan He
2026-08-03 19:47 ` [PATCH RFC 06/15] mm/mglru: use explicit tier range in read_ctrl_pos() Kairui Song via B4 Relay
2026-08-14  5:25   ` Barry Song
2026-08-15 10:36     ` Kairui Song
2026-08-18  2:52   ` Baoquan He
2026-08-03 19:47 ` [PATCH RFC 07/15] mm/mglru: move refault workingset activation into lru_gen_refault Kairui Song via B4 Relay
2026-08-18  3:20   ` Baoquan He
2026-08-03 19:47 ` [PATCH RFC 08/15] mm/memcg: add folio-based lruvec live helper Kairui Song via B4 Relay
2026-08-04  7:48   ` Lian Wang
2026-08-04  8:38     ` Kairui Song
2026-08-03 19:47 ` [PATCH RFC 09/15] mm/mglru: frequency guided workingset promotion (MGLRU-FG) Kairui Song via B4 Relay
2026-08-04  3:07   ` Kairui Song
2026-08-14  7:19   ` Barry Song
2026-08-14 18:05     ` Kairui Song
2026-08-15  0:13       ` Barry Song
2026-08-15  9:20         ` Kairui Song
2026-08-18  7:12   ` Baoquan He [this message]
2026-08-18  7:40     ` Kairui Song
2026-08-03 19:47 ` [PATCH RFC 10/15] mm/mglru: make folio lru referenced times count a generic API Kairui Song via B4 Relay
2026-08-04  7:49   ` Lian Wang
2026-08-04  9:02     ` Kairui Song
2026-08-18  7:19   ` Baoquan He
2026-08-03 19:47 ` [PATCH RFC 11/15] mm/mglru: replace folio workinset check and update with new helper Kairui Song via B4 Relay
2026-08-03 19:47 ` [PATCH RFC 12/15] mm/smap: report workingset folios as referenced Kairui Song via B4 Relay
2026-08-04  1:21   ` Johannes Weiner
2026-08-04  2:11     ` Kairui Song
2026-08-03 19:47 ` [PATCH RFC 13/15] mm/huge_memory: mark file folio as accessed more accurately on split Kairui Song via B4 Relay
2026-08-03 19:47 ` [PATCH RFC 14/15] mm/khugepaged: consider workingset folios as referenced Kairui Song via B4 Relay
2026-08-03 19:47 ` [PATCH RFC 15/15] mm/madvise: convert to new lru refs API and better support for MGLRU Kairui Song via B4 Relay
2026-08-04  5:26 ` [syzbot ci] Re: mm/mglru: frequency guided promotion (MGLRU-FG) and flag cleanup syzbot ci
2026-08-04  5:56   ` Kairui Song

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=aoQFw_SWJBAdO8w-@MiWiFi-R3L-srv \
    --to=baoquan.he@linux.dev \
    --cc=akpm@linux-foundation.org \
    --cc=axelrasmussen@google.com \
    --cc=baohua@kernel.org \
    --cc=baolin.wang@linux.alibaba.com \
    --cc=cgroups@vger.kernel.org \
    --cc=chenridong@xiaomi.com \
    --cc=chrisl@kernel.org \
    --cc=david@kernel.org \
    --cc=dev.jain@arm.com \
    --cc=gourry@gourry.net \
    --cc=hannes@cmpxchg.org \
    --cc=hughd@google.com \
    --cc=kasong@tencent.com \
    --cc=lance.yang@linux.dev \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=mhocko@kernel.org \
    --cc=muchun.song@linux.dev \
    --cc=nico.pache@linux.dev \
    --cc=nphamcs@gmail.com \
    --cc=qi.zheng@linux.dev \
    --cc=rientjes@google.com \
    --cc=roman.gushchin@linux.dev \
    --cc=ryan.roberts@arm.com \
    --cc=ryncsn@gmail.com \
    --cc=shakeel.butt@linux.dev \
    --cc=shikemeng@huaweicloud.com \
    --cc=sj@kernel.org \
    --cc=surenb@google.com \
    --cc=tz2294@columbia.edu \
    --cc=usama.arif@linux.dev \
    --cc=vbabka@kernel.org \
    --cc=vernon2gm@gmail.com \
    --cc=wangzicheng@honor.com \
    --cc=weixugc@google.com \
    --cc=willy@infradead.org \
    --cc=ying.huang@linux.alibaba.com \
    --cc=youngjun.park@lge.com \
    --cc=yuanchu@google.com \
    --cc=yuzhao@google.com \
    --cc=ziy@nvidia.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox