Skip to content

Commit 0814880

Browse files
Yu Zhaoakpm00
authored andcommitted
mm/mglru: fix underprotected page cache
Unmapped folios accessed through file descriptors can be underprotected. Those folios are added to the oldest generation based on: 1. The fact that they are less costly to reclaim (no need to walk the rmap and flush the TLB) and have less impact on performance (don't cause major PFs and can be non-blocking if needed again). 2. The observation that they are likely to be single-use. E.g., for client use cases like Android, its apps parse configuration files and store the data in heap (anon); for server use cases like MySQL, it reads from InnoDB files and holds the cached data for tables in buffer pools (anon). However, the oldest generation can be very short lived, and if so, it doesn't provide the PID controller with enough time to respond to a surge of refaults. (Note that the PID controller uses weighted refaults and those from evicted generations only take a half of the whole weight.) In other words, for a short lived generation, the moving average smooths out the spike quickly. To fix the problem: 1. For folios that are already on LRU, if they can be beyond the tracking range of tiers, i.e., five accesses through file descriptors, move them to the second oldest generation to give them more time to age. (Note that tiers are used by the PID controller to statistically determine whether folios accessed multiple times through file descriptors are worth protecting.) 2. When adding unmapped folios to LRU, adjust the placement of them so that they are not too close to the tail. The effect of this is similar to the above. On Android, launching 55 apps sequentially: Before After Change workingset_refault_anon 25641024 25598972 0% workingset_refault_file 115016834 106178438 -8% Link: https://lkml.kernel.org/r/20231208061407.2125867-1-yuzhao@google.com Fixes: ac35a49 ("mm: multi-gen LRU: minimal implementation") Signed-off-by: Yu Zhao <yuzhao@google.com> Reported-by: Charan Teja Kalla <quic_charante@quicinc.com> Tested-by: Kalesh Singh <kaleshsingh@google.com> Cc: T.J. Mercier <tjmercier@google.com> Cc: Kairui Song <ryncsn@gmail.com> Cc: Hillf Danton <hdanton@sina.com> Cc: Jaroslav Pulchart <jaroslav.pulchart@gooddata.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 55ac8bb commit 0814880

3 files changed

Lines changed: 18 additions & 13 deletions

File tree

include/linux/mm_inline.h

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -232,22 +232,27 @@ static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio,
232232
if (folio_test_unevictable(folio) || !lrugen->enabled)
233233
return false;
234234
/*
235-
* There are three common cases for this page:
236-
* 1. If it's hot, e.g., freshly faulted in or previously hot and
237-
* migrated, add it to the youngest generation.
238-
* 2. If it's cold but can't be evicted immediately, i.e., an anon page
239-
* not in swapcache or a dirty page pending writeback, add it to the
240-
* second oldest generation.
241-
* 3. Everything else (clean, cold) is added to the oldest generation.
235+
* There are four common cases for this page:
236+
* 1. If it's hot, i.e., freshly faulted in, add it to the youngest
237+
* generation, and it's protected over the rest below.
238+
* 2. If it can't be evicted immediately, i.e., a dirty page pending
239+
* writeback, add it to the second youngest generation.
240+
* 3. If it should be evicted first, e.g., cold and clean from
241+
* folio_rotate_reclaimable(), add it to the oldest generation.
242+
* 4. Everything else falls between 2 & 3 above and is added to the
243+
* second oldest generation if it's considered inactive, or the
244+
* oldest generation otherwise. See lru_gen_is_active().
242245
*/
243246
if (folio_test_active(folio))
244247
seq = lrugen->max_seq;
245248
else if ((type == LRU_GEN_ANON && !folio_test_swapcache(folio)) ||
246249
(folio_test_reclaim(folio) &&
247250
(folio_test_dirty(folio) || folio_test_writeback(folio))))
248-
seq = lrugen->min_seq[type] + 1;
249-
else
251+
seq = lrugen->max_seq - 1;
252+
else if (reclaiming || lrugen->min_seq[type] + MIN_NR_GENS >= lrugen->max_seq)
250253
seq = lrugen->min_seq[type];
254+
else
255+
seq = lrugen->min_seq[type] + 1;
251256

252257
gen = lru_gen_from_seq(seq);
253258
flags = (gen + 1UL) << LRU_GEN_PGOFF;

mm/vmscan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4232,7 +4232,7 @@ static bool sort_folio(struct lruvec *lruvec, struct folio *folio, struct scan_c
42324232
}
42334233

42344234
/* protected */
4235-
if (tier > tier_idx) {
4235+
if (tier > tier_idx || refs == BIT(LRU_REFS_WIDTH)) {
42364236
int hist = lru_hist_from_seq(lrugen->min_seq[type]);
42374237

42384238
gen = folio_inc_gen(lruvec, folio, false);

mm/workingset.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,10 +313,10 @@ static void lru_gen_refault(struct folio *folio, void *shadow)
313313
* 1. For pages accessed through page tables, hotter pages pushed out
314314
* hot pages which refaulted immediately.
315315
* 2. For pages accessed multiple times through file descriptors,
316-
* numbers of accesses might have been out of the range.
316+
* they would have been protected by sort_folio().
317317
*/
318-
if (lru_gen_in_fault() || refs == BIT(LRU_REFS_WIDTH)) {
319-
folio_set_workingset(folio);
318+
if (lru_gen_in_fault() || refs >= BIT(LRU_REFS_WIDTH) - 1) {
319+
set_mask_bits(&folio->flags, 0, LRU_REFS_MASK | BIT(PG_workingset));
320320
mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta);
321321
}
322322
unlock:

0 commit comments

Comments
 (0)