diff --git a/mm/vmscan.c b/mm/vmscan.c index c5ab1e73339f..bac175d257e5 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4774,10 +4774,13 @@ static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swapp int scanned; int reclaimed; LIST_HEAD(list); + LIST_HEAD(clean); struct page *page; + struct page *next; enum vm_event_item item; struct reclaim_stat stat; struct lru_gen_mm_walk *walk; + bool skip_retry = false; struct mem_cgroup *memcg = lruvec_memcg(lruvec); struct pglist_data *pgdat = lruvec_pgdat(lruvec); @@ -4794,20 +4797,37 @@ static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swapp if (list_empty(&list)) return scanned; - +retry: reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false); + sc->nr_reclaimed += reclaimed; - list_for_each_entry(page, &list, lru) { - /* restore LRU_REFS_FLAGS cleared by isolate_page() */ - if (PageWorkingset(page)) - SetPageReferenced(page); + list_for_each_entry_safe_reverse(page, next, &list, lru) { + if (!page_evictable(page)) { + list_del(&page->lru); + putback_lru_page(page); + continue; + } - /* don't add rejected pages to the oldest generation */ if (PageReclaim(page) && - (PageDirty(page) || PageWriteback(page))) - ClearPageActive(page); - else - SetPageActive(page); + (PageDirty(page) || PageWriteback(page))) { + /* restore LRU_REFS_FLAGS cleared by isolate_page() */ + if (PageWorkingset(page)) + SetPageReferenced(page); + continue; + } + + if (skip_retry || PageActive(page) || PageReferenced(page) || + page_mapped(page) || PageLocked(page) || + PageDirty(page) || PageWriteback(page)) { + /* don't add rejected pages to the oldest generation */ + set_mask_bits(&page->flags, LRU_REFS_MASK | LRU_REFS_FLAGS, + BIT(PG_active)); + continue; + } + + /* retry pages that may have missed rotate_reclaimable_page() */ + list_move(&page->lru, &clean); + sc->nr_scanned -= thp_nr_pages(page); } spin_lock_irq(&lruvec->lru_lock); @@ -4829,7 +4849,13 @@ static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swapp mem_cgroup_uncharge_list(&list); free_unref_page_list(&list); - sc->nr_reclaimed += reclaimed; + INIT_LIST_HEAD(&list); + list_splice_init(&clean, &list); + + if (!list_empty(&list)) { + skip_retry = true; + goto retry; + } if (need_swapping && type == LRU_GEN_ANON) *need_swapping = true;