mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
mm/vmscan: reduce lru_lock contention via vmstat-derived scan-balance cost
The anon/file scan balance in get_scan_count() is driven by two scalars in
struct lruvec, anon_cost and file_cost, accumulated by every reclaim
producer under lruvec->lru_lock. The acquisition sites for cost work
specifically are:
- shrink_inactive_list() re-takes lru_lock at function exit purely
to call lru_note_cost_unlock_irq() with (nr_pageout, nr_scanned -
nr_reclaimed). One acquisition per inactive shrink.
- shrink_active_list() does the same with (0, nr_rotated). One
acquisition per active shrink.
- workingset_refault() takes the lock via folio_lruvec_lock_irq()
purely to record the refault cost. One acquisition per refault.
- prepare_scan_control() takes lru_lock just to snapshot the two
scalars into sc->{anon,file}_cost.
- lru_note_cost_unlock_irq() itself walks parent_lruvec and
re-acquires lru_lock on each ancestor to propagate the update,
adding O(memcg-depth) acquisitions per producer call.
This hurts because lru_lock is already a heavy contention point on
memory-heavy workloads: every isolate_lru_folios(), move_folios_to_lru()
and folio_add_lru() takes it. The cost work itself is trivial (two scalar
bumps and one comparison), but it contends with and causes contention for
actual LRU manipulation. The parent_lruvec() walk also multiplies
cost-update overhead by memcg hierarchy depth.
Replace the producer-side accumulators with a read-side accumulator fed
from per-LRU vmstat counters. The old producer formula was:
cost = nr_io * SWAP_CLUSTER_MAX + nr_rotated
Reuse NR_VMSCAN_WRITE for reclaim-driven anon pageout submissions. It is
already bumped by writeout() for the same successful outcome that fed
reclaim_stat.nr_pageout. Reclaim does not submit filesystem folios from
this path, so there is no file pageout term. Charge NR_VMSCAN_WRITE via
lruvec_stat_mod_folio() and include it in memcg_node_stat_items so it can
be sampled per lruvec and aggregated through the memcg hierarchy.
Add explicit PGROTATE_{ANON,FILE} node_stat counters for the remaining
producer-local input. They are bumped from shrink_inactive_list() by
nr_scanned - nr_reclaimed and from shrink_active_list() by nr_rotated.
WORKINGSET_RESTORE_{ANON,FILE} already captures the refault IO that
lru_note_cost_refault() used to bill.
Add a per-side struct lru_cost { count, last_rotated, last_io } to struct
lruvec. In prepare_scan_control() the two monotonic inputs are sampled
separately - rotated from PGROTATE_ANON/FILE, io from
WORKINGSET_RESTORE_BASE + f plus (for anon) NR_VMSCAN_WRITE - and the raw
per-side deltas are computed against cost->last_rotated and cost->last_io
before the SWAP_CLUSTER_MAX IO weighting is applied. Extracting the
deltas from the individual counters (rather than from a pre-weighted sum)
keeps the unsigned modular subtraction bounded by the true per-counter
growth, so a signed-long wraparound of any underlying vmstat still yields
the correct delta on 32-bit. The weighted delta is folded into
cost->count. Since one vmstat delta can cover many producer events
between reclaim passes, halve cost->count on both sides until their sum is
back within the lrusize/4 bound instead of halving only once.
Moving accumulation and decay to the reclaim side also improves the cost
model across reclaim gaps. With producer-side decay, events that happen
while reclaim is idle still age each other before reclaim ever samples the
costs. If a workload refaults a large anon set and then a smaller file
set before reclaim runs again, the later file activity can age the earlier
anon activity out of the cost model. The new scheme observes the whole
between-reclaim delta and decays anon and file proportionally, so the
scan-balance history better represents what happened since the last
reclaim pass.
A dedicated per-lruvec spinlock, cost_lock, serialises the delta
extraction, the cost->count update and the halving loop against concurrent
reclaimers in the same memcg+node.
Hierarchy aggregation is now implicit in the vmstat accounting. The
producer-side parent_lruvec() walk and lru_reparent_memcg() cost splice
existed only because anon_cost/file_cost were private lruvec fields. With
the cost expressed as lruvec vmstats, rstat propagates the underlying
counters through the memcg hierarchy and prepare_scan_control() consumes
the same ratelimited rstat view as the surrounding reclaim heuristics.
NR_VMSCAN_WRITE is accounted at writeout(), so reclaim_stat.nr_pageout is
no longer needed and is removed.
memcg-v1's memory.stat anon_cost/file_cost is now sourced from
cost[].count instead of the removed lruvec anon_cost/file_cost fields.
The reported values only refresh when prepare_scan_control() runs and are
bounded at ~lrusize/4 by the halving loop; the scan-balance signal they
express is unchanged.
Under pure MGLRU the scan-balance signal itself is not consumed (both
prepare_scan_control() and get_scan_count() are short-circuited on the
MGLRU paths, and MGLRU's own type/tier selection comes from
read_ctrl_pos() on lrugen->{avg_refaulted,avg_total,refaulted,evicted},
not from anon_cost/file_cost). NR_VMSCAN_WRITE naturally covers writeout
from either reclaim implementation, and PGROTATE_{ANON,FILE} are bumped
from evict_folios() so per-memcg observability of rotation-driven reclaim
work stays consistent across both implementations.
Link: https://lore.kernel.org/20260720164207.450685-3-usama.arif@linux.dev
Signed-off-by: Usama Arif <usama.arif@linux.dev>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Baoquan He <baoquan.he@linux.dev>
Cc: Chris Li <chrisl@kernel.org>
Cc: David Hildenbrand <david@kernel.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Kairui Song <kasong@tencent.com>
Cc: Liam R. Howlett <liam@infradead.org>
Cc: Lorenzo Stoakes <ljs@kernel.org>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Nhat Pham <nphamcs@gmail.com>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: Wei Xu <weixugc@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
ae04312411
commit
6df3a36fa8
+13
-2
@@ -323,6 +323,8 @@ enum node_stat_item {
|
||||
PGSCAN_PROACTIVE,
|
||||
PGSCAN_ANON,
|
||||
PGSCAN_FILE,
|
||||
PGROTATE_ANON,
|
||||
PGROTATE_FILE,
|
||||
PGREFILL,
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
NR_HUGETLB,
|
||||
@@ -755,6 +757,12 @@ void lru_gen_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent,
|
||||
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
struct lru_cost {
|
||||
unsigned long count;
|
||||
unsigned long last_rotated;
|
||||
unsigned long last_io;
|
||||
};
|
||||
|
||||
struct lruvec {
|
||||
struct list_head lists[NR_LRU_LISTS];
|
||||
/* per lruvec lru_lock for memcg */
|
||||
@@ -763,9 +771,12 @@ struct lruvec {
|
||||
* These track the cost of reclaiming one LRU - file or anon -
|
||||
* over the other. As the observed cost of reclaiming one LRU
|
||||
* increases, the reclaim scan balance tips toward the other.
|
||||
* Updated and decayed at prepare_scan_control() time; cost_lock
|
||||
* serialises that update.
|
||||
*/
|
||||
unsigned long anon_cost;
|
||||
unsigned long file_cost;
|
||||
struct lru_cost cost[ANON_AND_FILE];
|
||||
/* Protects cost[]. */
|
||||
spinlock_t cost_lock;
|
||||
/* Non-resident age, driven by LRU movement */
|
||||
atomic_long_t nonresident_age;
|
||||
/* Refaults at the time of last reclaim cycle */
|
||||
|
||||
@@ -20,7 +20,6 @@ struct reclaim_stat {
|
||||
unsigned nr_congested;
|
||||
unsigned nr_writeback;
|
||||
unsigned nr_immediate;
|
||||
unsigned nr_pageout;
|
||||
unsigned nr_activate[ANON_AND_FILE];
|
||||
unsigned nr_ref_keep;
|
||||
unsigned nr_unmap_fail;
|
||||
|
||||
-69
@@ -265,73 +265,6 @@ void folio_rotate_reclaimable(struct folio *folio)
|
||||
folio_batch_add_and_move(folio, lru_move_tail);
|
||||
}
|
||||
|
||||
void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,
|
||||
unsigned int nr_io, unsigned int nr_rotated)
|
||||
__releases(lruvec->lru_lock)
|
||||
__releases(rcu)
|
||||
{
|
||||
unsigned long cost;
|
||||
|
||||
/*
|
||||
* Reflect the relative cost of incurring IO and spending CPU
|
||||
* time on rotations. This doesn't attempt to make a precise
|
||||
* comparison, it just says: if reloads are about comparable
|
||||
* between the LRU lists, or rotations are overwhelmingly
|
||||
* different between them, adjust scan balance for CPU work.
|
||||
*/
|
||||
cost = nr_io * SWAP_CLUSTER_MAX + nr_rotated;
|
||||
if (!cost) {
|
||||
spin_unlock_irq(&lruvec->lru_lock);
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
unsigned long lrusize;
|
||||
|
||||
/* Record cost event */
|
||||
if (file)
|
||||
lruvec->file_cost += cost;
|
||||
else
|
||||
lruvec->anon_cost += cost;
|
||||
|
||||
/*
|
||||
* Decay previous events
|
||||
*
|
||||
* Because workloads change over time (and to avoid
|
||||
* overflow) we keep these statistics as a floating
|
||||
* average, which ends up weighing recent refaults
|
||||
* more than old ones.
|
||||
*/
|
||||
lrusize = lruvec_page_state(lruvec, NR_INACTIVE_ANON) +
|
||||
lruvec_page_state(lruvec, NR_ACTIVE_ANON) +
|
||||
lruvec_page_state(lruvec, NR_INACTIVE_FILE) +
|
||||
lruvec_page_state(lruvec, NR_ACTIVE_FILE);
|
||||
|
||||
if (lruvec->file_cost + lruvec->anon_cost > lrusize / 4) {
|
||||
lruvec->file_cost /= 2;
|
||||
lruvec->anon_cost /= 2;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&lruvec->lru_lock);
|
||||
lruvec = parent_lruvec(lruvec);
|
||||
if (!lruvec) {
|
||||
rcu_read_unlock();
|
||||
break;
|
||||
}
|
||||
spin_lock_irq(&lruvec->lru_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void lru_note_cost_refault(struct folio *folio)
|
||||
{
|
||||
struct lruvec *lruvec;
|
||||
|
||||
lruvec = folio_lruvec_lock_irq(folio);
|
||||
lru_note_cost_unlock_irq(lruvec, folio_is_file_lru(folio),
|
||||
folio_nr_pages(folio), 0);
|
||||
}
|
||||
|
||||
static void lru_activate(struct lruvec *lruvec, struct folio *folio)
|
||||
{
|
||||
long nr_pages = folio_nr_pages(folio);
|
||||
@@ -1162,8 +1095,6 @@ void lru_reparent_memcg(struct mem_cgroup *memcg, struct mem_cgroup *parent, int
|
||||
|
||||
child_lruvec = mem_cgroup_lruvec(memcg, NODE_DATA(nid));
|
||||
parent_lruvec = mem_cgroup_lruvec(parent, NODE_DATA(nid));
|
||||
parent_lruvec->anon_cost += child_lruvec->anon_cost;
|
||||
parent_lruvec->file_cost += child_lruvec->file_cost;
|
||||
|
||||
for_each_lru(lru)
|
||||
lruvec_reparent_lru(child_lruvec, parent_lruvec, lru, nid);
|
||||
|
||||
@@ -41,9 +41,6 @@ void workingset_refault(struct folio *folio, void *shadow);
|
||||
void workingset_activation(struct folio *folio);
|
||||
|
||||
/* mm/folio.c */
|
||||
void lru_note_cost_unlock_irq(struct lruvec *lruvec, bool file,
|
||||
unsigned int nr_io, unsigned int nr_rotated);
|
||||
void lru_note_cost_refault(struct folio *folio);
|
||||
void folio_add_lru_vma(struct folio *folio, struct vm_area_struct *vma);
|
||||
|
||||
static inline bool folio_may_be_lru_cached(struct folio *folio)
|
||||
|
||||
+2
-2
@@ -2287,8 +2287,8 @@ void memcg1_stat_format(struct mem_cgroup *memcg, struct seq_buf *s)
|
||||
for_each_online_pgdat(pgdat) {
|
||||
mz = memcg->nodeinfo[pgdat->node_id];
|
||||
|
||||
anon_cost += mz->lruvec.anon_cost;
|
||||
file_cost += mz->lruvec.file_cost;
|
||||
anon_cost += mz->lruvec.cost[WORKINGSET_ANON].count;
|
||||
file_cost += mz->lruvec.cost[WORKINGSET_FILE].count;
|
||||
}
|
||||
seq_buf_printf(s, "anon_cost %lu\n", anon_cost);
|
||||
seq_buf_printf(s, "file_cost %lu\n", file_cost);
|
||||
|
||||
+4
-1
@@ -399,6 +399,7 @@ static const unsigned int memcg_node_stat_items[] = {
|
||||
NR_SHMEM_THPS,
|
||||
NR_FILE_THPS,
|
||||
NR_ANON_THPS,
|
||||
NR_VMSCAN_WRITE,
|
||||
NR_VMALLOC,
|
||||
NR_KERNEL_STACK_KB,
|
||||
NR_PAGETABLE,
|
||||
@@ -425,6 +426,8 @@ static const unsigned int memcg_node_stat_items[] = {
|
||||
PGSCAN_PROACTIVE,
|
||||
PGSCAN_ANON,
|
||||
PGSCAN_FILE,
|
||||
PGROTATE_ANON,
|
||||
PGROTATE_FILE,
|
||||
PGREFILL,
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
NR_HUGETLB,
|
||||
@@ -524,7 +527,7 @@ unsigned long lruvec_page_state(struct lruvec *lruvec, enum node_stat_item idx)
|
||||
* reading from per-CPU delta skew must present as zero.
|
||||
*
|
||||
* XXX: This helper (and its node/global peers) exists because we place
|
||||
* monotonically-incremented event counters (PGROTATE_*, PGRECLAIM_PAGEOUT_*)
|
||||
* monotonically-incremented event counters (NR_VMSCAN_WRITE and PGROTATE_*)
|
||||
* into enum node_stat_item.
|
||||
*/
|
||||
unsigned long lruvec_page_state_monotonic(struct lruvec *lruvec,
|
||||
|
||||
@@ -79,6 +79,7 @@ void lruvec_init(struct lruvec *lruvec)
|
||||
|
||||
memset(lruvec, 0, sizeof(struct lruvec));
|
||||
spin_lock_init(&lruvec->lru_lock);
|
||||
spin_lock_init(&lruvec->cost_lock);
|
||||
zswap_lruvec_state_init(lruvec);
|
||||
|
||||
for_each_lru(lru)
|
||||
|
||||
+79
-14
@@ -670,7 +670,7 @@ static pageout_t pageout(struct swap_io_ctx *ctx, struct address_space *mapping,
|
||||
folio_clear_reclaim(folio);
|
||||
|
||||
trace_mm_vmscan_write_folio(folio);
|
||||
node_stat_add_folio(folio, NR_VMSCAN_WRITE);
|
||||
lruvec_stat_mod_folio(folio, NR_VMSCAN_WRITE, folio_nr_pages(folio));
|
||||
return PAGE_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1412,8 +1412,6 @@ retry:
|
||||
sc->nr_scanned -= (nr_pages - 1);
|
||||
nr_pages = 1;
|
||||
}
|
||||
stat->nr_pageout += nr_pages;
|
||||
|
||||
if (folio_test_writeback(folio))
|
||||
goto keep;
|
||||
if (folio_test_dirty(folio))
|
||||
@@ -2036,10 +2034,10 @@ static unsigned long shrink_inactive_list(unsigned long nr_to_scan,
|
||||
item = PGSTEAL_KSWAPD + reclaimer_offset(sc);
|
||||
mod_lruvec_state(lruvec, item, nr_reclaimed);
|
||||
mod_lruvec_state(lruvec, PGSTEAL_ANON + file, nr_reclaimed);
|
||||
if (nr_scanned > nr_reclaimed)
|
||||
mod_lruvec_state(lruvec, PGROTATE_ANON + file,
|
||||
nr_scanned - nr_reclaimed);
|
||||
|
||||
lruvec_lock_irq(lruvec);
|
||||
lru_note_cost_unlock_irq(lruvec, file, stat.nr_pageout,
|
||||
nr_scanned - nr_reclaimed);
|
||||
handle_reclaim_writeback(nr_taken, pgdat, sc, &stat);
|
||||
trace_mm_vmscan_lru_shrink_inactive(pgdat->node_id,
|
||||
nr_scanned, nr_reclaimed, &stat, sc->priority, file);
|
||||
@@ -2145,9 +2143,9 @@ static void shrink_active_list(unsigned long nr_to_scan,
|
||||
count_vm_events(PGDEACTIVATE, nr_deactivate);
|
||||
count_memcg_events(lruvec_memcg(lruvec), PGDEACTIVATE, nr_deactivate);
|
||||
mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, -nr_taken);
|
||||
if (nr_rotated)
|
||||
mod_lruvec_state(lruvec, PGROTATE_ANON + file, nr_rotated);
|
||||
|
||||
lruvec_lock_irq(lruvec);
|
||||
lru_note_cost_unlock_irq(lruvec, file, 0, nr_rotated);
|
||||
trace_mm_vmscan_lru_shrink_active(pgdat->node_id, nr_taken, nr_activate,
|
||||
nr_deactivate, nr_rotated, sc->priority, file);
|
||||
}
|
||||
@@ -2280,8 +2278,10 @@ enum scan_balance {
|
||||
|
||||
static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)
|
||||
{
|
||||
unsigned long file;
|
||||
struct lru_cost *anon_cost, *file_cost;
|
||||
struct lruvec *target_lruvec;
|
||||
unsigned long lrusize;
|
||||
unsigned long file;
|
||||
|
||||
if (lru_gen_enabled() && !lru_gen_switching())
|
||||
return;
|
||||
@@ -2297,11 +2297,69 @@ static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)
|
||||
|
||||
/*
|
||||
* Determine the scan balance between anon and file LRUs.
|
||||
*
|
||||
* The cost model is based on rotations, refaults and
|
||||
* reclaim-driven writes (anon only) on each side.
|
||||
*
|
||||
* These event counters are monotonic, so each reclaim cycle
|
||||
* the delta since the last scan is extracted and incorporated
|
||||
* into a decaying average. This ensures currency, as workloads
|
||||
* change over time, and avoids overflow in the calculations.
|
||||
*
|
||||
* Use lruvec_page_state_monotonic() so unsigned subtraction
|
||||
* yields the correct delta across a signed-long wraparound of
|
||||
* the underlying counter (a real hazard on 32-bit that the
|
||||
* clamp in lruvec_page_state() would otherwise turn into a huge
|
||||
* spurious delta).
|
||||
*/
|
||||
spin_lock_irq(&target_lruvec->lru_lock);
|
||||
sc->anon_cost = target_lruvec->anon_cost;
|
||||
sc->file_cost = target_lruvec->file_cost;
|
||||
spin_unlock_irq(&target_lruvec->lru_lock);
|
||||
spin_lock(&target_lruvec->cost_lock);
|
||||
|
||||
for (int f = 0; f <= 1; f++) {
|
||||
struct lru_cost *cost = &target_lruvec->cost[f];
|
||||
unsigned long rotated, io, nr_rotated, nr_io;
|
||||
|
||||
rotated = lruvec_page_state_monotonic(target_lruvec,
|
||||
PGROTATE_ANON + f);
|
||||
io = lruvec_page_state_monotonic(target_lruvec,
|
||||
WORKINGSET_RESTORE_BASE + f);
|
||||
if (f == WORKINGSET_ANON)
|
||||
io += lruvec_page_state_monotonic(target_lruvec,
|
||||
NR_VMSCAN_WRITE);
|
||||
|
||||
nr_rotated = rotated - cost->last_rotated;
|
||||
nr_io = io - cost->last_io;
|
||||
|
||||
/*
|
||||
* Reflect the relative cost of incurring IO and spending
|
||||
* CPU time on rotations. This doesn't attempt to make a
|
||||
* precise comparison, it just says: if reloads are about
|
||||
* comparable between the LRU lists, or rotations are
|
||||
* overwhelmingly different between them, adjust scan
|
||||
* balance for CPU work.
|
||||
*/
|
||||
cost->count += nr_io * SWAP_CLUSTER_MAX + nr_rotated;
|
||||
|
||||
cost->last_rotated = rotated;
|
||||
cost->last_io = io;
|
||||
}
|
||||
|
||||
anon_cost = &target_lruvec->cost[WORKINGSET_ANON];
|
||||
file_cost = &target_lruvec->cost[WORKINGSET_FILE];
|
||||
|
||||
lrusize = lruvec_page_state(target_lruvec, NR_INACTIVE_ANON) +
|
||||
lruvec_page_state(target_lruvec, NR_ACTIVE_ANON) +
|
||||
lruvec_page_state(target_lruvec, NR_INACTIVE_FILE) +
|
||||
lruvec_page_state(target_lruvec, NR_ACTIVE_FILE);
|
||||
|
||||
while (anon_cost->count + file_cost->count > lrusize / 4) {
|
||||
anon_cost->count /= 2;
|
||||
file_cost->count /= 2;
|
||||
}
|
||||
|
||||
sc->anon_cost = anon_cost->count;
|
||||
sc->file_cost = file_cost->count;
|
||||
|
||||
spin_unlock(&target_lruvec->cost_lock);
|
||||
|
||||
/*
|
||||
* Target desirable inactive:active list ratios for the anon
|
||||
@@ -4826,7 +4884,8 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
|
||||
struct reclaim_stat stat;
|
||||
struct lru_gen_mm_walk *walk;
|
||||
int scanned, reclaimed;
|
||||
int isolated = 0, type, type_scanned;
|
||||
int isolated = 0, nr_isolated = 0, type, type_scanned;
|
||||
unsigned long total_reclaimed = 0;
|
||||
bool skip_retry = false;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
@@ -4838,6 +4897,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
|
||||
|
||||
scanned = isolate_folios(nr_to_scan, lruvec, sc, swappiness,
|
||||
&list, &isolated, &type, &type_scanned);
|
||||
nr_isolated = isolated;
|
||||
|
||||
/* Scanning may have emptied the oldest gen, flush it */
|
||||
if (scanned)
|
||||
@@ -4850,6 +4910,7 @@ static int evict_folios(unsigned long nr_to_scan, struct lruvec *lruvec,
|
||||
retry:
|
||||
reclaimed = shrink_folio_list(&list, pgdat, sc, &stat, false, memcg);
|
||||
sc->nr_reclaimed += reclaimed;
|
||||
total_reclaimed += reclaimed;
|
||||
/* Retry pass is only meant for clean folios without new isolation */
|
||||
if (isolated)
|
||||
handle_reclaim_writeback(isolated, pgdat, sc, &stat);
|
||||
@@ -4901,6 +4962,10 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (nr_isolated > total_reclaimed)
|
||||
mod_lruvec_state(lruvec, PGROTATE_ANON + type,
|
||||
nr_isolated - total_reclaimed);
|
||||
|
||||
return scanned;
|
||||
}
|
||||
|
||||
|
||||
@@ -1301,6 +1301,8 @@ const char * const vmstat_text[] = {
|
||||
[I(PGSCAN_PROACTIVE)] = "pgscan_proactive",
|
||||
[I(PGSCAN_ANON)] = "pgscan_anon",
|
||||
[I(PGSCAN_FILE)] = "pgscan_file",
|
||||
[I(PGROTATE_ANON)] = "pgrotate_anon",
|
||||
[I(PGROTATE_FILE)] = "pgrotate_file",
|
||||
[I(PGREFILL)] = "pgrefill",
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
[I(NR_HUGETLB)] = "nr_hugetlb",
|
||||
|
||||
@@ -584,11 +584,6 @@ void workingset_refault(struct folio *folio, void *shadow)
|
||||
/* Folio was active prior to eviction */
|
||||
if (workingset) {
|
||||
folio_set_workingset(folio);
|
||||
/*
|
||||
* XXX: Move to folio_add_lru() when it supports new vs
|
||||
* putback
|
||||
*/
|
||||
lru_note_cost_refault(folio);
|
||||
mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + file, nr);
|
||||
}
|
||||
out:
|
||||
|
||||
Reference in New Issue
Block a user