mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-19 07:39:54 +02:00
UPSTREAM: 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/000000000000f71227061bdf97e0@google.com/ Bug: 254441685 Link: https://lkml.kernel.org/r/20240627201737.3506959-1-nphamcs@gmail.com Fixes:b006847222
("mm: workingset: move the stats flush into workingset_test_recent()") Signed-off-by: Nhat Pham <nphamcs@gmail.com> Reported-by: syzbot+b7f13b2d0cc156edf61a@syzkaller.appspotmail.com Closes: https://lore.kernel.org/cgroups/000000000000f71227061bdf97e0@google.com/ Debugged-by: Johannes Weiner <hannes@cmpxchg.org> Suggested-by: Johannes Weiner <hannes@cmpxchg.org> Acked-by: Johannes Weiner <hannes@cmpxchg.org> Acked-by: Shakeel Butt <shakeel.butt@linux.dev> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: David Hildenbrand <david@redhat.com> Cc: "Huang, Ying" <ying.huang@intel.com> Cc: Kairui Song <kasong@tencent.com> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Ryan Roberts <ryan.roberts@arm.com> Cc: Yosry Ahmed <yosryahmed@google.com> Cc: <stable@vger.kernel.org> [6.8+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> (cherry picked from commit5a4d8944d6
) Signed-off-by: Lee Jones <joneslee@google.com> Change-Id: Ie0e4d94fb8b83baafe640b2874d007ac14386bc2
This commit is contained in:
parent
3e6bc54909
commit
90248f12cc
|
@ -368,7 +368,8 @@ static inline swp_entry_t page_swap_entry(struct page *page)
|
|||
}
|
||||
|
||||
/* linux/mm/workingset.c */
|
||||
bool workingset_test_recent(void *shadow, bool file, bool *workingset);
|
||||
bool workingset_test_recent(void *shadow, bool file, bool *workingset,
|
||||
bool flush);
|
||||
void workingset_age_nonresident(struct lruvec *lruvec, unsigned long nr_pages);
|
||||
void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg);
|
||||
void workingset_refault(struct folio *folio, void *shadow);
|
||||
|
|
|
@ -4200,6 +4200,9 @@ static void filemap_cachestat(struct address_space *mapping,
|
|||
XA_STATE(xas, &mapping->i_pages, first_index);
|
||||
struct folio *folio;
|
||||
|
||||
/* Flush stats (and potentially sleep) outside the RCU read section. */
|
||||
mem_cgroup_flush_stats_ratelimited(NULL);
|
||||
|
||||
rcu_read_lock();
|
||||
xas_for_each(&xas, folio, last_index) {
|
||||
int order;
|
||||
|
@ -4263,7 +4266,7 @@ static void filemap_cachestat(struct address_space *mapping,
|
|||
goto resched;
|
||||
}
|
||||
#endif
|
||||
if (workingset_test_recent(shadow, true, &workingset))
|
||||
if (workingset_test_recent(shadow, true, &workingset, false))
|
||||
cs->nr_recently_evicted += nr_pages;
|
||||
|
||||
goto resched;
|
||||
|
|
|
@ -412,10 +412,12 @@ void *workingset_eviction(struct folio *folio, struct mem_cgroup *target_memcg)
|
|||
* @file: whether the corresponding folio is from the file lru.
|
||||
* @workingset: where the workingset value unpacked from shadow should
|
||||
* be stored.
|
||||
* @flush: whether to flush cgroup rstat.
|
||||
*
|
||||
* Return: true if the shadow is for a recently evicted folio; false otherwise.
|
||||
*/
|
||||
bool workingset_test_recent(void *shadow, bool file, bool *workingset)
|
||||
bool workingset_test_recent(void *shadow, bool file, bool *workingset,
|
||||
bool flush)
|
||||
{
|
||||
struct mem_cgroup *eviction_memcg;
|
||||
struct lruvec *eviction_lruvec;
|
||||
|
@ -467,10 +469,16 @@ bool workingset_test_recent(void *shadow, bool file, bool *workingset)
|
|||
|
||||
/*
|
||||
* Flush stats (and potentially sleep) outside the RCU read section.
|
||||
*
|
||||
* Note that workingset_test_recent() itself might be called in RCU read
|
||||
* section (for e.g, in cachestat) - these callers need to skip flushing
|
||||
* stats (via the flush argument).
|
||||
*
|
||||
* XXX: With per-memcg flushing and thresholding, is ratelimiting
|
||||
* still needed here?
|
||||
*/
|
||||
mem_cgroup_flush_stats_ratelimited(eviction_memcg);
|
||||
if (flush)
|
||||
mem_cgroup_flush_stats_ratelimited(eviction_memcg);
|
||||
|
||||
eviction_lruvec = mem_cgroup_lruvec(eviction_memcg, pgdat);
|
||||
refault = atomic_long_read(&eviction_lruvec->nonresident_age);
|
||||
|
@ -560,7 +568,7 @@ void workingset_refault(struct folio *folio, void *shadow)
|
|||
|
||||
mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + file, nr);
|
||||
|
||||
if (!workingset_test_recent(shadow, file, &workingset))
|
||||
if (!workingset_test_recent(shadow, file, &workingset, true))
|
||||
return;
|
||||
|
||||
folio_set_active(folio);
|
||||
|
|
Loading…
Reference in New Issue
Block a user