Skip to content

Commit 5a4d894

Browse files
nhatsmrtakpm00
authored andcommitted
cachestat: do not flush stats in recency check
syzbot detects that cachestat() is flushing stats, which can sleep, in its RCU read section (see [1]). This is done in the workingset_test_recent() step (which checks if the folio's eviction is recent). Move the stat flushing step to before the RCU read section of cachestat, and skip stat flushing during the recency check. [1]: https://lore.kernel.org/cgroups/[email protected]/ Link: https://lkml.kernel.org/r/[email protected] Fixes: b006847 ("mm: workingset: move the stats flush into workingset_test_recent()") Signed-off-by: Nhat Pham <[email protected]> Reported-by: [email protected] Closes: https://lore.kernel.org/cgroups/[email protected]/ Debugged-by: Johannes Weiner <[email protected]> Suggested-by: Johannes Weiner <[email protected]> Acked-by: Johannes Weiner <[email protected]> Acked-by: Shakeel Butt <[email protected]> Cc: Al Viro <[email protected]> Cc: David Hildenbrand <[email protected]> Cc: "Huang, Ying" <[email protected]> Cc: Kairui Song <[email protected]> Cc: Matthew Wilcox (Oracle) <[email protected]> Cc: Ryan Roberts <[email protected]> Cc: Yosry Ahmed <[email protected]> Cc: <[email protected]> [6.8+] Signed-off-by: Andrew Morton <[email protected]>
1 parent 9fd154b commit 5a4d894

File tree

3 files changed

+17
-5
lines changed

3 files changed

+17
-5
lines changed

include/linux/swap.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ static inline swp_entry_t page_swap_entry(struct page *page)
354354
}
355355

356356
/* linux/mm/workingset.c */
357-
bool workingset_test_recent(void *shadow, bool file, bool *workingset);
357+
bool workingset_test_recent(void *shadow, bool file, bool *workingset,
358+
bool flush);
358359
void workingset_age_nonresident(struct lruvec *lruvec, unsigned long nr_pages);
359360
void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg);
360361
void workingset_refault(struct folio *folio, void *shadow);

mm/filemap.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4248,6 +4248,9 @@ static void filemap_cachestat(struct address_space *mapping,
42484248
XA_STATE(xas, &mapping->i_pages, first_index);
42494249
struct folio *folio;
42504250

4251+
/* Flush stats (and potentially sleep) outside the RCU read section. */
4252+
mem_cgroup_flush_stats_ratelimited(NULL);
4253+
42514254
rcu_read_lock();
42524255
xas_for_each(&xas, folio, last_index) {
42534256
int order;
@@ -4311,7 +4314,7 @@ static void filemap_cachestat(struct address_space *mapping,
43114314
goto resched;
43124315
}
43134316
#endif
4314-
if (workingset_test_recent(shadow, true, &workingset))
4317+
if (workingset_test_recent(shadow, true, &workingset, false))
43154318
cs->nr_recently_evicted += nr_pages;
43164319

43174320
goto resched;

mm/workingset.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,10 +412,12 @@ void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg)
412412
* @file: whether the corresponding folio is from the file lru.
413413
* @workingset: where the workingset value unpacked from shadow should
414414
* be stored.
415+
* @flush: whether to flush cgroup rstat.
415416
*
416417
* Return: true if the shadow is for a recently evicted folio; false otherwise.
417418
*/
418-
bool workingset_test_recent(void *shadow, bool file, bool *workingset)
419+
bool workingset_test_recent(void *shadow, bool file, bool *workingset,
420+
bool flush)
419421
{
420422
struct mem_cgroup *eviction_memcg;
421423
struct lruvec *eviction_lruvec;
@@ -467,10 +469,16 @@ bool workingset_test_recent(void *shadow, bool file, bool *workingset)
467469

468470
/*
469471
* Flush stats (and potentially sleep) outside the RCU read section.
472+
*
473+
* Note that workingset_test_recent() itself might be called in RCU read
474+
* section (for e.g, in cachestat) - these callers need to skip flushing
475+
* stats (via the flush argument).
476+
*
470477
* XXX: With per-memcg flushing and thresholding, is ratelimiting
471478
* still needed here?
472479
*/
473-
mem_cgroup_flush_stats_ratelimited(eviction_memcg);
480+
if (flush)
481+
mem_cgroup_flush_stats_ratelimited(eviction_memcg);
474482

475483
eviction_lruvec = mem_cgroup_lruvec(eviction_memcg, pgdat);
476484
refault = atomic_long_read(&eviction_lruvec->nonresident_age);
@@ -558,7 +566,7 @@ void workingset_refault(struct folio *folio, void *shadow)
558566

559567
mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + file, nr);
560568

561-
if (!workingset_test_recent(shadow, file, &workingset))
569+
if (!workingset_test_recent(shadow, file, &workingset, true))
562570
return;
563571

564572
folio_set_active(folio);

0 commit comments

Comments
 (0)